From a1a2cb0661ad4d26c7b4810cdb5cc205845d7bca Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 2 Jul 2015 10:05:53 +0200 Subject: [PATCH 0001/1983] mach: imx: Allow different iMX6 types to be built separately The shared code for the iMX6 families require that all the various types of SOCs are selected or the code won't build. This commit adds proper ifdefs so any combination of chips can be selected to be built into the kernel. --- arch/arm/mach-imx/Makefile | 10 ++-- arch/arm/mach-imx/busfreq-imx6.c | 53 ++++++++++++++--- arch/arm/mach-imx/busfreq_ddr3.c | 6 ++ arch/arm/mach-imx/busfreq_lpddr2.c | 4 ++ arch/arm/mach-imx/clk-gate2.c | 5 +- arch/arm/mach-imx/clk-pfd.c | 5 +- arch/arm/mach-imx/clk-pllv3.c | 5 +- arch/arm/mach-imx/common-imx6.c | 96 ++++++++++++++++++++++++++++++ arch/arm/mach-imx/mach-imx6q.c | 78 ------------------------ arch/arm/mach-imx/pm-imx6.c | 4 ++ 10 files changed, 169 insertions(+), 97 deletions(-) create mode 100644 arch/arm/mach-imx/common-imx6.c diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 27f1976fa4d127..57dc4ba2ee23cd 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -102,9 +102,9 @@ obj-$(CONFIG_HAVE_IMX_MCC) += mcc_api.o mcc_common.o mcc_linux.o mcc_imx6sx.o AFLAGS_headsmp.o :=-Wa,-march=armv7-a obj-$(CONFIG_SMP) += headsmp.o platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o -obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o -obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o mach-imx6sx.o mu.o +obj-$(CONFIG_SOC_IMX6Q) += common-imx6.o clk-imx6q.o mach-imx6q.o +obj-$(CONFIG_SOC_IMX6SL) += common-imx6.o clk-imx6sl.o mach-imx6sl.o +obj-$(CONFIG_SOC_IMX6SX) += common-imx6.o clk-imx6sx.o mach-imx6sx.o mu.o ifeq ($(CONFIG_SUSPEND),y) AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a @@ -122,8 +122,8 @@ obj-$(CONFIG_SOC_IMX6SL) += busfreq_lpddr2.o lpddr2_freq_imx6.o imx6sl_lpm_wfi.o AFLAGS_lpddr2_freq_imx6sx.o :=-Wa,-march=armv7-a AFLAGS_ddr3_freq_imx6sx.o :=-Wa,-march=armv7-a AFLAGS_imx6sx_low_power_idle.o :=-Wa,-march=armv7-a -obj-$(CONFIG_SOC_IMX6SX) += ddr3_freq_imx6sx.o lpddr2_freq_imx6sx.o imx6sx_low_power_idle.o \ - busfreq_lpddr2.o lpddr2_freq_imx6.o +obj-$(CONFIG_SOC_IMX6SX) += ddr3_freq_imx6sx.o busfreq_ddr3.o lpddr2_freq_imx6sx.o \ + imx6sx_low_power_idle.o busfreq_lpddr2.o lpddr2_freq_imx6.o endif diff --git a/arch/arm/mach-imx/busfreq-imx6.c b/arch/arm/mach-imx/busfreq-imx6.c index af7982ffffb8f5..8b5d8260ff983c 100644 --- a/arch/arm/mach-imx/busfreq-imx6.c +++ b/arch/arm/mach-imx/busfreq-imx6.c @@ -58,7 +58,9 @@ #define MMDC_MDMISC_DDR_TYPE_DDR3 0 #define MMDC_MDMISC_DDR_TYPE_LPDDR2 1 +#ifdef CONFIG_SOC_IMX6SX static int ddr_type; +#endif int high_bus_freq_mode; int med_bus_freq_mode; int audio_bus_freq_mode; @@ -73,7 +75,9 @@ unsigned long ddr_freq_change_iram_phys; static int bus_freq_scaling_initialized; static struct device *busfreq_dev; static int busfreq_suspended; +#ifdef CONFIG_SOC_IMX6SL static u32 org_arm_rate; +#endif static int bus_freq_scaling_is_active; static int high_bus_count, med_bus_count, audio_bus_count, low_bus_count; static unsigned int ddr_low_rate; @@ -121,7 +125,9 @@ static struct clk *pll1; static struct clk *pll1_bypass; static struct clk *pll1_bypass_src; +#ifdef CONFIG_SOC_IMX6SL static u32 pll2_org_rate; +#endif static struct delayed_work low_bus_freq_handler; static struct delayed_work bus_freq_daemon; @@ -148,6 +154,7 @@ int unregister_busfreq_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_busfreq_notifier); +#ifdef CONFIG_SOC_IMX6SX static bool check_m4_sleep(void) { unsigned long timeout = jiffies + msecs_to_jiffies(500); @@ -255,7 +262,9 @@ static void exit_lpm_imx6sx(void) if (audio_bus_freq_mode) clk_disable_unprepare(pll2_400); } +#endif +#ifdef CONFIG_SOC_IMX6SL static void enter_lpm_imx6sl(void) { if (high_bus_freq_mode) { @@ -427,6 +436,7 @@ static void exit_lpm_imx6sl(void) ultra_low_bus_freq_mode = 0; } } +#endif static void reduce_bus_freq(void) { @@ -435,11 +445,19 @@ static void reduce_bus_freq(void) busfreq_notify(LOW_BUSFREQ_EXIT); else if (!audio_bus_count) busfreq_notify(LOW_BUSFREQ_ENTER); - if (cpu_is_imx6sl()) + switch(__mxc_cpu_type) { + case MXC_CPU_IMX6SL: +#ifdef CONFIG_SOC_IMX6SL enter_lpm_imx6sl(); - else if (cpu_is_imx6sx()) +#endif + break; + case MXC_CPU_IMX6SX: +#ifdef CONFIG_SOC_IMX6SX enter_lpm_imx6sx(); - else { +#endif + break; + default: +#ifdef CONFIG_SOC_IMX6Q if (cpu_is_imx6dl()) /* Set axi to periph_clk */ imx_clk_set_parent(axi_sel_clk, periph_clk); @@ -465,6 +483,8 @@ static void reduce_bus_freq(void) low_bus_freq_mode = 1; audio_bus_freq_mode = 0; } +#endif + break; } clk_disable_unprepare(pll3); @@ -554,11 +574,20 @@ static int set_high_bus_freq(int high_bus_freq) busfreq_notify(LOW_BUSFREQ_EXIT); clk_prepare_enable(pll3); - if (cpu_is_imx6sl()) + + switch(__mxc_cpu_type) { + case MXC_CPU_IMX6SL: +#ifdef CONFIG_SOC_IMX6SL exit_lpm_imx6sl(); - else if (cpu_is_imx6sx()) +#endif + break; + case MXC_CPU_IMX6SX: +#ifdef CONFIG_SOC_IMX6SX exit_lpm_imx6sx(); - else { +#endif + break; + default: +#ifdef CONFIG_SOC_IMX6Q if (high_bus_freq) { clk_prepare_enable(pll2_400); update_ddr_freq_imx6q(ddr_normal_rate); @@ -581,6 +610,8 @@ static int set_high_bus_freq(int high_bus_freq) } if (audio_bus_freq_mode) clk_disable_unprepare(pll2_400); +#endif + break; } high_bus_freq_mode = 1; @@ -1159,8 +1190,10 @@ static int busfreq_probe(struct platform_device *pdev) (ddr_freq_change_iram_phys & 0xFFF00000) | TT_ATTRIB_NON_CACHEABLE_1M; } +#if defined(CONFIG_SOC_IMX6SX) || defined(CONFIG_SOC_IMX6SL) if (cpu_is_imx6sl()) { err = init_mmdc_lpddr2_settings(pdev); +#ifdef CONFIG_SOC_IMX6SX } else if (cpu_is_imx6sx()) { ddr_type = imx_mmdc_get_ddr_type(); /* check whether it is a DDR3 or LPDDR2 board */ @@ -1172,9 +1205,13 @@ static int busfreq_probe(struct platform_device *pdev) if (imx_src_is_m4_enabled() && (clk_get_rate(m4_clk) > LPAPM_CLK)) high_bus_count++; - } else { - err = init_mmdc_ddr3_settings_imx6q(pdev); +#endif } +#endif +#ifdef CONFIG_SOC_IMX6Q + else + err = init_mmdc_ddr3_settings_imx6q(pdev); +#endif if (err) { dev_err(busfreq_dev, "Busfreq init of MMDC failed\n"); diff --git a/arch/arm/mach-imx/busfreq_ddr3.c b/arch/arm/mach-imx/busfreq_ddr3.c index b84c9d82049137..379847ce1c37bf 100644 --- a/arch/arm/mach-imx/busfreq_ddr3.c +++ b/arch/arm/mach-imx/busfreq_ddr3.c @@ -101,7 +101,9 @@ extern unsigned long mx6_ddr3_freq_change_end asm("mx6_ddr3_freq_change_end"); extern unsigned long imx6sx_ddr3_freq_change_start asm("imx6sx_ddr3_freq_change_start"); extern unsigned long imx6sx_ddr3_freq_change_end asm("imx6sx_ddr3_freq_change_end"); #ifdef CONFIG_SMP +#ifdef CONFIG_SOC_IMX6Q static unsigned long wfe_freq_change_iram_base; +#endif u32 *wait_for_ddr_freq_update; static unsigned int online_cpus; static u32 *irqs_used; @@ -400,6 +402,7 @@ int update_ddr_freq_imx6q(int ddr_rate) return 0; } +#ifdef CONFIG_SOC_IMX6SX int init_mmdc_ddr3_settings_imx6sx(struct platform_device *busfreq_pdev) { int i; @@ -481,7 +484,9 @@ int init_mmdc_ddr3_settings_imx6sx(struct platform_device *busfreq_pdev) return 0; } +#endif +#ifdef CONFIG_SOC_IMX6Q int init_mmdc_ddr3_settings_imx6q(struct platform_device *busfreq_pdev) { int i; @@ -648,3 +653,4 @@ int init_mmdc_ddr3_settings_imx6q(struct platform_device *busfreq_pdev) return 0; } +#endif diff --git a/arch/arm/mach-imx/busfreq_lpddr2.c b/arch/arm/mach-imx/busfreq_lpddr2.c index 757001e257241e..9f5469871604ea 100644 --- a/arch/arm/mach-imx/busfreq_lpddr2.c +++ b/arch/arm/mach-imx/busfreq_lpddr2.c @@ -53,7 +53,9 @@ extern unsigned int ddr_normal_rate; extern int low_bus_freq_mode; extern int ultra_low_bus_freq_mode; extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode); +#ifdef CONFIG_SOC_IMX6SX extern void imx6sx_lpddr2_freq_change(u32 freq, int bus_freq_mode); +#endif extern unsigned long save_ttbr1(void); extern void restore_ttbr1(unsigned long ttbr1); extern unsigned long ddr_freq_change_iram_base; @@ -101,10 +103,12 @@ int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev) mx6_change_lpddr2_freq = (void *)fncpy( (void *)ddr_freq_change_iram_base, &mx6_lpddr2_freq_change, ddr_code_size); +#ifdef CONFIG_SOC_IMX6SX else if (cpu_is_imx6sx()) mx6_change_lpddr2_freq = (void *)fncpy( (void *)ddr_freq_change_iram_base, &imx6sx_lpddr2_freq_change, ddr_code_size); +#endif curr_ddr_rate = ddr_normal_rate; diff --git a/arch/arm/mach-imx/clk-gate2.c b/arch/arm/mach-imx/clk-gate2.c index 7e0887779713c4..ef8479fb26ecec 100644 --- a/arch/arm/mach-imx/clk-gate2.c +++ b/arch/arm/mach-imx/clk-gate2.c @@ -58,6 +58,7 @@ static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable) { struct clk_gate2 *gate = to_clk_gate2(hw); +#ifdef CONFIG_SOC_IMX6SX if (imx_src_is_m4_enabled()) { if (!amp_power_mutex || !shared_mem) { if (enable) @@ -80,9 +81,9 @@ static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable) clk_gate2_do_hardware(gate, enable); imx_sema4_mutex_unlock(amp_power_mutex); - } else { + } else +#endif clk_gate2_do_hardware(gate, enable); - } } static int clk_gate2_enable(struct clk_hw *hw) diff --git a/arch/arm/mach-imx/clk-pfd.c b/arch/arm/mach-imx/clk-pfd.c index 896fc78ddd9779..f1038b49956fb6 100644 --- a/arch/arm/mach-imx/clk-pfd.c +++ b/arch/arm/mach-imx/clk-pfd.c @@ -53,6 +53,7 @@ static void clk_pfd_do_shared_clks(struct clk_hw *hw, bool enable) { struct clk_pfd *pfd = to_clk_pfd(hw); +#ifdef CONFIG_SOC_IMX6SX if (imx_src_is_m4_enabled()) { if (!amp_power_mutex || !shared_mem) { if (enable) @@ -75,9 +76,9 @@ static void clk_pfd_do_shared_clks(struct clk_hw *hw, bool enable) clk_pfd_do_hardware(pfd, enable); imx_sema4_mutex_unlock(amp_power_mutex); - } else { + } else +#endif clk_pfd_do_hardware(pfd, enable); - } } static int clk_pfd_enable(struct clk_hw *hw) diff --git a/arch/arm/mach-imx/clk-pllv3.c b/arch/arm/mach-imx/clk-pllv3.c index 728280ec7f928a..49ef833aa56497 100644 --- a/arch/arm/mach-imx/clk-pllv3.c +++ b/arch/arm/mach-imx/clk-pllv3.c @@ -96,6 +96,7 @@ static int clk_pllv3_do_hardware(struct clk_hw *hw, bool enable) static void clk_pllv3_do_shared_clks(struct clk_hw *hw, bool enable) { +#ifdef CONFIG_SOC_IMX6SX if (imx_src_is_m4_enabled()) { if (!amp_power_mutex || !shared_mem) { if (enable) @@ -117,9 +118,9 @@ static void clk_pllv3_do_shared_clks(struct clk_hw *hw, bool enable) clk_pllv3_do_hardware(hw, enable); imx_sema4_mutex_unlock(amp_power_mutex); - } else { + } else +#endif clk_pllv3_do_hardware(hw, enable); - } } static int clk_pllv3_prepare(struct clk_hw *hw) diff --git a/arch/arm/mach-imx/common-imx6.c b/arch/arm/mach-imx/common-imx6.c new file mode 100644 index 00000000000000..47303fe0778e67 --- /dev/null +++ b/arch/arm/mach-imx/common-imx6.c @@ -0,0 +1,96 @@ +/* + * Copyright 2011-2015 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include +#include +#include +#include + +#include "common.h" +#define OCOTP_MACn(n) (0x00000620 + (n) * 0x10) + +void __init imx6_enet_mac_init(const char *compatible) +{ + struct device_node *ocotp_np, *enet_np, *from = NULL; + void __iomem *base; + struct property *newmac; + u32 macaddr_low; + u32 macaddr_high = 0; + u32 macaddr1_high = 0; + u8 *macaddr; + int i; + + for (i = 0; i < 2; i++) { + enet_np = of_find_compatible_node(from, NULL, compatible); + if (!enet_np) + return; + + from = enet_np; + + if (of_get_mac_address(enet_np)) + goto put_enet_node; + + ocotp_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); + if (!ocotp_np) { + pr_warn("failed to find ocotp node\n"); + goto put_enet_node; + } + + base = of_iomap(ocotp_np, 0); + if (!base) { + pr_warn("failed to map ocotp\n"); + goto put_ocotp_node; + } + + macaddr_low = readl_relaxed(base + OCOTP_MACn(1)); + if (i) + macaddr1_high = readl_relaxed(base + OCOTP_MACn(2)); + else + macaddr_high = readl_relaxed(base + OCOTP_MACn(0)); + + newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL); + if (!newmac) + goto put_ocotp_node; + + newmac->value = newmac + 1; + newmac->length = 6; + newmac->name = kstrdup("local-mac-address", GFP_KERNEL); + if (!newmac->name) { + kfree(newmac); + goto put_ocotp_node; + } + + macaddr = newmac->value; + if (i) { + macaddr[5] = (macaddr_low >> 16) & 0xff; + macaddr[4] = (macaddr_low >> 24) & 0xff; + macaddr[3] = macaddr1_high & 0xff; + macaddr[2] = (macaddr1_high >> 8) & 0xff; + macaddr[1] = (macaddr1_high >> 16) & 0xff; + macaddr[0] = (macaddr1_high >> 24) & 0xff; + } else { + macaddr[5] = macaddr_high & 0xff; + macaddr[4] = (macaddr_high >> 8) & 0xff; + macaddr[3] = (macaddr_high >> 16) & 0xff; + macaddr[2] = (macaddr_high >> 24) & 0xff; + macaddr[1] = macaddr_low & 0xff; + macaddr[0] = (macaddr_low >> 8) & 0xff; + } + + of_update_property(enet_np, newmac); + +put_ocotp_node: + of_node_put(ocotp_np); +put_enet_node: + of_node_put(enet_np); + } +} + diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 30a4aef274c3cd..eebbd4c0e67704 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -350,84 +350,6 @@ static void __init imx6q_csi_mux_init(void) } } -#define OCOTP_MACn(n) (0x00000620 + (n) * 0x10) -void __init imx6_enet_mac_init(const char *compatible) -{ - struct device_node *ocotp_np, *enet_np, *from = NULL; - void __iomem *base; - struct property *newmac; - u32 macaddr_low; - u32 macaddr_high = 0; - u32 macaddr1_high = 0; - u8 *macaddr; - int i; - - for (i = 0; i < 2; i++) { - enet_np = of_find_compatible_node(from, NULL, compatible); - if (!enet_np) - return; - - from = enet_np; - - if (of_get_mac_address(enet_np)) - goto put_enet_node; - - ocotp_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); - if (!ocotp_np) { - pr_warn("failed to find ocotp node\n"); - goto put_enet_node; - } - - base = of_iomap(ocotp_np, 0); - if (!base) { - pr_warn("failed to map ocotp\n"); - goto put_ocotp_node; - } - - macaddr_low = readl_relaxed(base + OCOTP_MACn(1)); - if (i) - macaddr1_high = readl_relaxed(base + OCOTP_MACn(2)); - else - macaddr_high = readl_relaxed(base + OCOTP_MACn(0)); - - newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL); - if (!newmac) - goto put_ocotp_node; - - newmac->value = newmac + 1; - newmac->length = 6; - newmac->name = kstrdup("local-mac-address", GFP_KERNEL); - if (!newmac->name) { - kfree(newmac); - goto put_ocotp_node; - } - - macaddr = newmac->value; - if (i) { - macaddr[5] = (macaddr_low >> 16) & 0xff; - macaddr[4] = (macaddr_low >> 24) & 0xff; - macaddr[3] = macaddr1_high & 0xff; - macaddr[2] = (macaddr1_high >> 8) & 0xff; - macaddr[1] = (macaddr1_high >> 16) & 0xff; - macaddr[0] = (macaddr1_high >> 24) & 0xff; - } else { - macaddr[5] = macaddr_high & 0xff; - macaddr[4] = (macaddr_high >> 8) & 0xff; - macaddr[3] = (macaddr_high >> 16) & 0xff; - macaddr[2] = (macaddr_high >> 24) & 0xff; - macaddr[1] = macaddr_low & 0xff; - macaddr[0] = (macaddr_low >> 8) & 0xff; - } - - of_update_property(enet_np, newmac); - -put_ocotp_node: - of_node_put(ocotp_np); -put_enet_node: - of_node_put(enet_np); - } -} - static inline void imx6q_enet_init(void) { imx6_enet_mac_init("fsl,imx6q-fec"); diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c index 03f0ed293e5801..e074468ce72352 100644 --- a/arch/arm/mach-imx/pm-imx6.c +++ b/arch/arm/mach-imx/pm-imx6.c @@ -660,6 +660,7 @@ static int imx6q_pm_enter(suspend_state_t state) unsigned int console_saved_reg[11] = {0}; static unsigned int ccm_ccgr4, ccm_ccgr6; +#ifdef CONFIG_SOC_IMX6SX if (imx_src_is_m4_enabled()) { if (imx_gpc_is_m4_sleeping() && imx_mu_is_m4_in_low_freq()) { imx_gpc_hold_m4_in_sleep(); @@ -677,6 +678,7 @@ static int imx6q_pm_enter(suspend_state_t state) return 0; } } +#endif if (!iram_tlb_base_addr) { pr_warn("No IRAM/OCRAM memory allocated for suspend/resume \ @@ -763,10 +765,12 @@ static int imx6q_pm_enter(suspend_state_t state) return -EINVAL; } +#ifdef CONFIG_SOC_IMX6SX if (imx_src_is_m4_enabled()) { imx_mu_enable_m4_irqs_in_gic(false); imx_gpc_release_m4_in_sleep(); } +#endif return 0; } From b6f7377fb68fec63035c72253e2d3f214340235a Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 18 May 2015 16:41:32 +0200 Subject: [PATCH 0002/1983] mmc: core: Extend mmc_delay in mmc_power_cycle for UHS transition When transitioning to and from UHS mode I found that sometimes certain SDHC cards would fail on the iMX6 based hummngboard platform. Since 1ms is the minimum I have extended this to 3ms and that provides enough time for the card to power off and on properly. Signed-off-by: Jon Nettleton --- drivers/mmc/core/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ea8bcba1516332..e324d7bd29e534 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1605,7 +1605,7 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr) { mmc_power_off(host); /* Wait at least 1 ms according to SD spec */ - mmc_delay(1); + mmc_delay(3); mmc_power_up(host, ocr); } From d5d5e23f15f79b5981e8c973f125f638995c44ee Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 2 Jul 2015 14:52:55 +0200 Subject: [PATCH 0003/1983] dts: cubox-i/hummingboard: Bring dts files up to date with mainline This is an amalgum of all these upstream patches. commit 3274af07c924fbb682bd4a8eb25bcf41f899d50a Author: George Joseph Date: Tue Mar 24 10:31:35 2015 -0600 ARM: dts: cubox: Map gpio-keys to gpio3 8 The Cubox has a recessed button between the HDMI and RJ-45 connectors that wasn't mapped in the device tree, so I've mapped it to gpio-keys BTN_0. Signed-off-by: George Joseph Tested-by: George Joseph Signed-off-by: Shawn Guo commit 42919c5c1487dcd7e886da7c3818e28b1455b00a Author: Russell King Date: Mon Mar 2 20:00:50 2015 +0000 ARM: dts: Re-license SolidRun iMX6 platform DT GPL v2/X11 Update SolidRun iMX6 platforms DT descriptions to be dual-licensed. Signed-off-by: Russell King Acked-by: Sascha Hauer Acked-by: Rabeeh Khoury Signed-off-by: Shawn Guo commit 7cdbec1f5d30fca90a73099505ea833ad3b6d027 Author: Philipp Zabel Date: Mon Mar 9 17:40:35 2015 +0100 ARM: dts: hummingboard/cubox-i: enable front LED PWM explicitly All PWM users should explicitly enable the used PWMs in their device tree so they can be disabled by default in imx6qdl.dtsi. Signed-off-by: Philipp Zabel Signed-off-by: Shawn Guo commit 962af8612148dad1927ab74707a27cc400103c86 Author: Russell King Date: Sat Aug 23 10:11:31 2014 +0100 ARM: dts: hummingboard/cubox-i: change SPDIF output to be more descriptive Signed-off-by: Russell King Signed-off-by: Shawn Guo commit f9908c178c273728166ccc21544821a42015f81d Author: Russell King Date: Sat Aug 23 10:11:26 2014 +0100 ARM: dts: hummingboard/cubox-i: add USB OC pinctrl configuration Hummingboard has no over current hardware, so disable the over current detection for both ports. Cubox-i has over current hardware, so appropriately configure this. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 548220832123658e65f6b711f845d613e3cc02f0 Author: Russell King Date: Tue Jun 24 11:20:03 2014 +0100 ARM: dts: cubox-i: disable spread-spectrum for Cubox-i eSATA Spread-spectrum doesn't work with Cubox-i hardware. eSATA devices are detected, but then fail on normal IO. Therefore, disable this feature. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 4c463178a9ef94e71014baf4bce2443f8070ae77 Author: Russell King Date: Tue Jun 24 11:19:58 2014 +0100 ARM: dts: cubox-i: add eSATA DT configuration Add the transmit level, boost and attenuation parameters necessary for the eSATA interface on Cubox-i to work. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 589681b20688267a06a068d77ef10fc7c34168ba Author: Russell King Date: Tue Jun 17 14:41:01 2014 +0100 ARM: dts: hummingboard/cubox-i: move usb otg configuration to platform level The configuration of the USB OTG is a platform configuration decision, not a microsom decision. Move this configuration out to the platform level files. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit eea53bb16d0ff19c1fe2d29c359100df739a95d7 Author: Russell King Date: Tue Jun 17 14:40:56 2014 +0100 ARM: dts: cubox-i: add support for PWM-driven front panel LED The front panel LED on the Cubox-i is driven by one of the iMX6 PWM channels, and is wired between the PWM output and supply. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit f34d0d5dd86e794477c8d23622111f9709085041 Author: Russell King Date: Fri Apr 18 11:01:27 2014 +0100 ARM: imx: add HDMI support for SolidRun HummingBoard and Cubox-i Add the HDMI DT configuration for the SolidRun HummingBoard and Cubox-i. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 87f9260bf1fb7ec4c3f88c6bb353df17ca1e1c19 Author: Russell King Date: Sat Feb 8 21:18:24 2014 +0000 ARM: dts: fix spdif pinmux configuration The spdif pinmux configuration must be connected to the spdif device to take effect, not the spdif-transmitter. Signed-off-by: Russell King commit 971488f1149f09fd2fad0e7973780b752809b9f6 Author: Russell King Date: Thu Jan 16 16:12:38 2014 +0000 ARM: imx: initial SolidRun Cubox-i support Add support for the SolidRun Cubox-i devices. This commit adds similar basic support as the HummingBoard. Further devices will be supported in future patches. Signed-off-by: Russell King commit e9786f26e2276709be8dc0c242e16cc2441f42be Author: Fabio Estevam Date: Wed Apr 8 21:41:53 2015 -0300 ARM: dts: imx6qdl-hummingboard: Add PCIe support Add PCIe support. Based on a patch from Rabeeh Khoury from the solid-run tree. Signed-off-by: Fabio Estevam commit 861533516d32aa4faf1211ceb5dd45b54a52d35e Author: Jon Nettleton Date: Mon May 18 16:37:37 2015 +0200 ARM: dts: hummingboard: add support for GPIO pins This adds support for the GPIOs on the 24-pin header. Based on patch by Rabeeh Khoury. 1. Sets bit 30 (SION enabled) on the 8 GPIO pins (instead of bit 31) 2. Sets the 8 GPIOs to be default 100k Ohm pull down 3. Adds documentation on gpio pin mapping Signed-off-by: Rabeeh Khoury commit 833437554d6b781803c2245d4274ffd59f21f58d Author: Jon Nettleton Date: Mon May 18 16:28:57 2015 +0200 ARM: dts: hummingboard: add support for UHS sdhc cards This adds the changes needed to enable 1.8v signal switching and power reset needed to enable UHS modes for sdhc cards. It is based on Russell King's initial patch for the cubox-i. Signed-off-by: Russell King commit 8e047c120fb2ccb7b7aba8f5e9224e463f86bebc Author: Russell King Date: Mon Mar 30 13:06:57 2015 +0100 ARM: dts: hummingboard: add sgtl5000 support for Hummingboard Pro Add the DT description for the SGTL5000 found on the Hummingboard Pro model. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit ffbae6b719227662f3baca349d1e17ac7e652ec4 Author: Rabeeh Khoury Date: Mon Mar 2 20:03:59 2015 +0000 ARM: dts: hummingboard: Setup pwm lines Setup pwm lines as follows - pwm1: In case HummingBoard base carrier; this pin drives through a serial capacitor the mono out of the audio jack. In case HummingBoard pro the this pad can be reached by wiring to C8 capacitors on the board. pwm2: Setup pwm2 on gpio-1 but leave the default function of the iopad as a gpio. The user can change the io pad mux in user space and therefore use this function on gpio-1 (pin number 7 on the 26 pin header). pwm3,pwm4: unused Signed-off-by: Rabeeh Khoury [tweaked alias for pwm pinctrl group --rmk] Signed-off-by: Russell King Signed-off-by: Shawn Guo commit a931bbbc64f671340f98dfd3f74f3a2901a60cfc Author: Russell King Date: Mon Mar 2 20:03:54 2015 +0000 ARM: dts: hummingboard: enable PCF8523 RTC support Enable the commented out PCF8523 RTC support for Hummingboard pro base boards. Signed-off-by: Russell King Acked-by: Philipp Zabel Signed-off-by: Shawn Guo commit 42919c5c1487dcd7e886da7c3818e28b1455b00a Author: Russell King Date: Mon Mar 2 20:00:50 2015 +0000 ARM: dts: Re-license SolidRun iMX6 platform DT GPL v2/X11 Update SolidRun iMX6 platforms DT descriptions to be dual-licensed. Signed-off-by: Russell King Acked-by: Sascha Hauer Acked-by: Rabeeh Khoury Signed-off-by: Shawn Guo commit d56ac1929cfe00e2071524a6fcbb340f7faef66e Author: Russell King Date: Sat Aug 23 15:34:07 2014 +0100 ARM: dts: hummingboard: fix configuration of IR input Add the IOMUX setting for the IR input, rather than relying on the boot loader. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit af3f973b8706b9754eab4f3eccfd332a570046f9 Author: Rabeeh Khoury Date: Sat Aug 23 10:11:47 2014 +0100 ARM: dts: hummingboard: gpio-ir on gpio 3,5 HummingBoard after rev 2.0 and the production one starting rev 3.0 uses gpio 3,5 (EIM_DA5 pad) as the gpio infra red receiver input. Since the original Carrier1 board is obsolete and we are retiring it, update the DT file for this. This will mean IR reception will not work on Carrier1 with this DT file. Signed-off-by: Rabeeh Khoury Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 4011009d2300f23e60a780ac3fb3609aafd30874 Author: Rabeeh Khoury Date: Sat Aug 23 10:11:42 2014 +0100 ARM: dts: hummingboard: add mSATA support for iMX6 quad/dual HummingBoard Initial patch from Rabeeh, but with the electrical properties added. Signed-off-by: Rabeeh Khoury Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 4cd4f509c5dc3f935911c49f9813e2fc29063a6b Author: Rabeeh Khoury Date: Sat Aug 23 10:11:36 2014 +0100 ARM: dts: hummingboard: Split HummingBoard DT to support s/dl and d/q Signed-off-by: Rabeeh Khoury Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 962af8612148dad1927ab74707a27cc400103c86 Author: Russell King Date: Sat Aug 23 10:11:31 2014 +0100 ARM: dts: hummingboard/cubox-i: change SPDIF output to be more descriptive Signed-off-by: Russell King Signed-off-by: Shawn Guo commit f9908c178c273728166ccc21544821a42015f81d Author: Russell King Date: Sat Aug 23 10:11:26 2014 +0100 ARM: dts: hummingboard/cubox-i: add USB OC pinctrl configuration Hummingboard has no over current hardware, so disable the over current detection for both ports. Cubox-i has over current hardware, so appropriately configure this. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 589681b20688267a06a068d77ef10fc7c34168ba Author: Russell King Date: Tue Jun 17 14:41:01 2014 +0100 ARM: dts: hummingboard/cubox-i: move usb otg configuration to platform level The configuration of the USB OTG is a platform configuration decision, not a microsom decision. Move this configuration out to the platform level files. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit f34d0d5dd86e794477c8d23622111f9709085041 Author: Russell King Date: Fri Apr 18 11:01:27 2014 +0100 ARM: imx: add HDMI support for SolidRun HummingBoard and Cubox-i Add the HDMI DT configuration for the SolidRun HummingBoard and Cubox-i. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 48f51963641b30add337b4edb6d48ec351c6daf7 Author: Sascha Hauer Date: Wed May 7 15:19:00 2014 +0200 ARM: dts: Add stdout-path property to i.MX boards This adds the stdout-path property to various i.MX boards. Values of the property have been taken from barebox, so they should be correct. Also, the older linux,stdout-path property is converted to stdout-path. Signed-off-by: Sascha Hauer Signed-off-by: Shawn Guo commit 87f9260bf1fb7ec4c3f88c6bb353df17ca1e1c19 Author: Russell King Date: Sat Feb 8 21:18:24 2014 +0000 ARM: dts: fix spdif pinmux configuration The spdif pinmux configuration must be connected to the spdif device to take effect, not the spdif-transmitter. Signed-off-by: Russell King commit 208d7baf8085ed639ec896274f1679d4718f2ff3 Author: Russell King Date: Fri Sep 27 20:07:26 2013 +0100 ARM: imx: initial SolidRun HummingBoard support Add support for the SolidRun HummingBoard. This commit adds support for the following interfaces on this board: - Consumer Ir receiver - S/PDIF output - Both USB interfaces - Gigabit Ethernet using AR8035 - UART port Signed-off-by: Russell King commit ac2f0d42dea599b3c196616a9986fe0494a9accc Author: Fabio Estevam Date: Sat Jun 6 16:08:23 2015 +0200 ARM: dts: cubox-i: add support for Broadcom Wifi/Bluetooth devices Add DT support for the Broadcom Wifi/Bluetooth devices found on the microsom board. [Modified original Russell King's patch to use mmc-pwrseq] Signed-off-by: Russell King Signed-off-by: Fabio Estevam Signed-off-by: Russell King commit 42919c5c1487dcd7e886da7c3818e28b1455b00a Author: Russell King Date: Mon Mar 2 20:00:50 2015 +0000 ARM: dts: Re-license SolidRun iMX6 platform DT GPL v2/X11 Update SolidRun iMX6 platforms DT descriptions to be dual-licensed. Signed-off-by: Russell King Acked-by: Sascha Hauer Acked-by: Rabeeh Khoury Signed-off-by: Shawn Guo commit bf8147208eab4a026558dcd686f76410de6c1643 Author: Rabeeh Khoury Date: Sat Aug 23 10:11:21 2014 +0100 ARM: dts: microsom-ar8035: MDIO pad must be set open drain This patch is important for the MicroSOM implementation due to the following details - 1. VIH of the Atheros phy is 1.7V. 2. NVCC_ENET which is the power domain of the MDIO pad is driven by the PHY's LDO (i.e. either 1.8v or 2.5v). 3. The MicroSOM implements an onbouard 1.6kohm pull up to 3.3v (R3000). In the case the PHY's LDO was 1.8v then there would be only a 100mV margin for the signal to be acknowledged as high (1.8v-1.7v). Due to that setting the pad as an open drain will let the 1.6kohm pull that signal high to 3.3 that assures enough margins to the PHY to be acked as '1' logic. Signed-off-by: Rabeeh Khoury Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 589681b20688267a06a068d77ef10fc7c34168ba Author: Russell King Date: Tue Jun 17 14:41:01 2014 +0100 ARM: dts: hummingboard/cubox-i: move usb otg configuration to platform level The configuration of the USB OTG is a platform configuration decision, not a microsom decision. Move this configuration out to the platform level files. Signed-off-by: Russell King Signed-off-by: Shawn Guo commit 2e3b9650561ae791ca0bd8c5f4868ef4df3cb842 Author: Russell King Date: Sun Apr 6 23:32:25 2014 +0100 ARM: dt: microsom: don't set bit 7 for ethernet mux settings Bit 6,7 are marked as reserved for the ethernet RGMII pins, so avoid setting these bits. Signed-off-by: Russell King Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx6dl-cubox-i.dts | 38 ++ arch/arm/boot/dts/imx6dl-hummingboard.dts | 198 ++-------- arch/arm/boot/dts/imx6q-cubox-i.dts | 42 ++ arch/arm/boot/dts/imx6q-hummingboard.dts | 59 +++ arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 120 +++++- arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 366 ++++++++++++++++++ .../arm/boot/dts/imx6qdl-microsom-ar8035.dtsi | 62 ++- arch/arm/boot/dts/imx6qdl-microsom.dtsi | 144 ++++++- 9 files changed, 854 insertions(+), 176 deletions(-) create mode 100644 arch/arm/boot/dts/imx6q-hummingboard.dts create mode 100644 arch/arm/boot/dts/imx6qdl-hummingboard.dtsi diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 699bf8f2f17682..5047b94a89d41c 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -167,6 +167,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6dl-wandboard.dtb \ imx6q-arm2.dtb \ imx6q-cubox-i.dtb \ + imx6q-hummingboard.dtb \ imx6q-phytec-pbab01.dtb \ imx6q-sabreauto.dtb \ imx6q-sabreauto-flexcan1.dtb \ diff --git a/arch/arm/boot/dts/imx6dl-cubox-i.dts b/arch/arm/boot/dts/imx6dl-cubox-i.dts index 58aa8f2b0f260f..e0b7fe8e18f886 100644 --- a/arch/arm/boot/dts/imx6dl-cubox-i.dts +++ b/arch/arm/boot/dts/imx6dl-cubox-i.dts @@ -1,5 +1,43 @@ /* * Copyright (C) 2014 Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. */ /dts-v1/; diff --git a/arch/arm/boot/dts/imx6dl-hummingboard.dts b/arch/arm/boot/dts/imx6dl-hummingboard.dts index 0391aabfdd7bbc..7369d2d7da3e54 100644 --- a/arch/arm/boot/dts/imx6dl-hummingboard.dts +++ b/arch/arm/boot/dts/imx6dl-hummingboard.dts @@ -1,163 +1,51 @@ /* - * Copyright (C) 2013,2014 Russell King + * Copyright (C) 2014 Rabeeh Khoury (rabeeh@solid-run.com) + * Based on dt work by Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. */ /dts-v1/; #include "imx6dl.dtsi" -#include "imx6qdl-microsom.dtsi" -#include "imx6qdl-microsom-ar8035.dtsi" +#include "imx6qdl-hummingboard.dtsi" / { - model = "SolidRun HummingBoard DL/Solo"; - compatible = "solidrun,hummingboard", "fsl,imx6dl"; - - ir_recv: ir-receiver { - compatible = "gpio-ir-receiver"; - gpios = <&gpio1 2 1>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_gpio1_2>; - }; - - regulators { - compatible = "simple-bus"; - - reg_3p3v: 3p3v { - compatible = "regulator-fixed"; - regulator-name = "3P3V"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - reg_usbh1_vbus: usb-h1-vbus { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio1 0 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_usbh1_vbus>; - regulator-name = "usb_h1_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - }; - - reg_usbotg_vbus: usb-otg-vbus { - compatible = "regulator-fixed"; - enable-active-high; - gpio = <&gpio3 22 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_usbotg_vbus>; - regulator-name = "usb_otg_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - }; - }; - - sound-spdif { - compatible = "fsl,imx-audio-spdif"; - model = "imx-spdif"; - /* IMX6 doesn't implement this yet */ - spdif-controller = <&spdif>; - spdif-out; - }; -}; - -&flexcan1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; - status = "okay"; -}; - -&i2c1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_i2c1>; - - /* - * Not fitted on Carrier-1 board... yet - status = "okay"; - - rtc: pcf8523@68 { - compatible = "nxp,pcf8523"; - reg = <0x68>; - }; - */ -}; - -&iomuxc { - hummingboard { - pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { - fsl,pins = < - MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000 - MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000 - >; - }; - - pinctrl_hummingboard_gpio1_2: hummingboard-gpio1_2 { - fsl,pins = < - MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 - >; - }; - - pinctrl_hummingboard_i2c1: hummingboard-i2c1 { - fsl,pins = < - MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 - MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 - >; - }; - - pinctrl_hummingboard_spdif: hummingboard-spdif { - fsl,pins = ; - }; - - pinctrl_hummingboard_usbh1_vbus: hummingboard-usbh1-vbus { - fsl,pins = ; - }; - - pinctrl_hummingboard_usbotg_vbus: hummingboard-usbotg-vbus { - fsl,pins = ; - }; - - pinctrl_hummingboard_usdhc2_aux: hummingboard-usdhc2-aux { - fsl,pins = < - MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 - >; - }; - - pinctrl_hummingboard_usdhc2: hummingboard-usdhc2 { - fsl,pins = < - MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 - MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 - MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 - MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 - MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 - MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 - >; - }; - }; -}; - -&spdif { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_spdif>; - status = "okay"; -}; - -&usbh1 { - vbus-supply = <®_usbh1_vbus>; - status = "okay"; -}; - -&usbotg { - vbus-supply = <®_usbotg_vbus>; - status = "okay"; -}; - -&usdhc2 { - pinctrl-names = "default"; - pinctrl-0 = < - &pinctrl_hummingboard_usdhc2_aux - &pinctrl_hummingboard_usdhc2 - >; - vmmc-supply = <®_3p3v>; - cd-gpios = <&gpio1 4 0>; - status = "okay"; + model = "SolidRun HummingBoard Solo/DualLite"; + compatible = "solidrun,hummingboard/dl", "fsl,imx6dl"; }; diff --git a/arch/arm/boot/dts/imx6q-cubox-i.dts b/arch/arm/boot/dts/imx6q-cubox-i.dts index bc5f31e3e892f2..670bd8c4c84751 100644 --- a/arch/arm/boot/dts/imx6q-cubox-i.dts +++ b/arch/arm/boot/dts/imx6q-cubox-i.dts @@ -1,5 +1,43 @@ /* * Copyright (C) 2014 Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. */ /dts-v1/; @@ -13,4 +51,8 @@ &sata { status = "okay"; + fsl,transmit-level-mV = <1104>; + fsl,transmit-boost-mdB = <0>; + fsl,transmit-atten-16ths = <9>; + fsl,no-spread-spectrum; }; diff --git a/arch/arm/boot/dts/imx6q-hummingboard.dts b/arch/arm/boot/dts/imx6q-hummingboard.dts new file mode 100644 index 00000000000000..0f6044553a2490 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-hummingboard.dts @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Rabeeh Khoury (rabeeh@solid-run.com) + * Based on dt work by Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. + */ +/dts-v1/; + +#include "imx6q.dtsi" +#include "imx6qdl-hummingboard.dtsi" + +/ { + model = "SolidRun HummingBoard Dual/Quad"; + compatible = "solidrun,hummingboard/q", "fsl,imx6q"; +}; + +&sata { + status = "okay"; + fsl,transmit-level-mV = <1025>; + fsl,transmit-boost-mdB = <3330>; + fsl,transmit-atten-16ths = <9>; + fsl,receive-eq-mdB = <3000>; +}; diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi index c2a24888a27689..2a41035a7854e6 100644 --- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi @@ -1,8 +1,48 @@ /* * Copyright (C) 2014 Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. */ #include "imx6qdl-microsom.dtsi" #include "imx6qdl-microsom-ar8035.dtsi" +#include +#include / { ir_recv: ir-receiver { @@ -12,6 +52,19 @@ pinctrl-0 = <&pinctrl_cubox_i_ir>; }; + pwmleds { + compatible = "pwm-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_pwm1>; + + front { + active-low; + label = "imx6:red:front"; + max-brightness = <248>; + pwms = <&pwm1 0 50000>; + }; + }; + regulators { compatible = "simple-bus"; @@ -48,11 +101,30 @@ sound-spdif { compatible = "fsl,imx-audio-spdif"; - model = "imx-spdif"; + model = "Integrated SPDIF"; /* IMX6 doesn't implement this yet */ spdif-controller = <&spdif>; spdif-out; }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&pinctrl_gpio_key>; + pinctrl-names = "default"; + + button_0 { + label = "Button 0"; + gpios = <&gpio3 8 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_i2c2>; + status = "okay"; }; &i2c3 { @@ -69,6 +141,19 @@ &iomuxc { cubox_i { + pinctrl_cubox_i_hdmi: cubox-i-hdmi { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_cubox_i_i2c2: cubox-i-i2c2 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + pinctrl_cubox_i_i2c3: cubox-i-i2c3 { fsl,pins = < MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 @@ -82,14 +167,33 @@ >; }; + pinctrl_cubox_i_pwm1: cubox-i-pwm1-front-led { + fsl,pins = ; + }; + pinctrl_cubox_i_spdif: cubox-i-spdif { fsl,pins = ; }; + pinctrl_cubox_i_usbh1: cubox-i-usbh1 { + fsl,pins = ; + }; + pinctrl_cubox_i_usbh1_vbus: cubox-i-usbh1-vbus { fsl,pins = ; }; + pinctrl_cubox_i_usbotg: cubox-i-usbotg { + /* + * The Cubox-i pulls ID low, but as it's pointless + * leaving it as a pull-up, even if it is just 10uA. + */ + fsl,pins = < + MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x13059 + MX6QDL_PAD_KEY_COL4__USB_OTG_OC 0x1b0b0 + >; + }; + pinctrl_cubox_i_usbotg_vbus: cubox-i-usbotg-vbus { fsl,pins = ; }; @@ -111,9 +215,19 @@ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 >; }; + + pinctrl_gpio_key: gpio-key { + fsl,pins = < + MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x17059 + >; + }; }; }; +&pwm1 { + status = "okay"; +}; + &spdif { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_cubox_i_spdif>; @@ -121,11 +235,15 @@ }; &usbh1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_usbh1>; vbus-supply = <®_usbh1_vbus>; status = "okay"; }; &usbotg { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_usbotg>; vbus-supply = <®_usbotg_vbus>; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi new file mode 100644 index 00000000000000..d85240f0b5698c --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2013,2014 Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. + */ +#include "imx6qdl-microsom.dtsi" +#include "imx6qdl-microsom-ar8035.dtsi" + +/ { + chosen { + stdout-path = &uart1; + }; + + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio3 5 1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_gpio3_5>; + }; + + regulators { + compatible = "simple-bus"; + + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + gpio = <&gpio4 30 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usdhc2_pwr>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_usbh1_vbus: usb-h1-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 0 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usbh1_vbus>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_usbotg_vbus: usb-otg-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio3 22 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usbotg_vbus>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + }; + + sound-sgtl5000 { + audio-codec = <&sgtl5000>; + audio-routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + compatible = "fsl,imx-audio-sgtl5000"; + model = "On-board Codec"; + mux-ext-port = <5>; + mux-int-port = <1>; + ssi-controller = <&ssi1>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "On-board SPDIF"; + /* IMX6 doesn't implement this yet */ + spdif-controller = <&spdif>; + spdif-out; + }; +}; + +&audmux { + status = "okay"; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; + status = "okay"; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_i2c1>; + status = "okay"; + + /* Pro baseboard model */ + rtc: pcf8523@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + }; + + /* Pro baseboard model */ + sgtl5000: sgtl5000@0a { + clocks = <&clks IMX6QDL_CLK_CKO>; + compatible = "fsl,sgtl5000"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_sgtl5000>; + reg = <0x0a>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_3p3v>; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_i2c2>; + status = "okay"; +}; + +&iomuxc { + hummingboard { + pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { + fsl,pins = < + MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000 + MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000 + >; + }; + + pinctrl_hummingboard_gpio3_5: hummingboard-gpio3_5 { + fsl,pins = < + MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x1b0b1 + >; + }; + + pinctrl_hummingboard_hdmi: hummingboard-hdmi { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_hog: hoggrp { + fsl,pins = < + /* + * 26 pin header GPIO description. The pins + * numbering as following - + * GPIO number | GPIO (bank,num) | PIN number + * ------------+-----------------+------------ + * gpio1 | (1,1) | IO7 + * gpio73 | (3,9) | IO11 + * gpio72 | (3,8) | IO12 + * gpio71 | (3,7) | IO13 + * gpio70 | (3,6) | IO15 + * gpio194 | (7,2) | IO16 + * gpio195 | (7,3) | IO18 + * gpio67 | (3,3) | IO22 + * + * Notice the gpioX and GPIO (Y,Z) mapping forumla : + * X = (Y-1) * 32 + Z + */ + MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x400130b1 + MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x400130b1 + MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x400130b1 + MX6QDL_PAD_EIM_DA7__GPIO3_IO07 0x400130b1 + MX6QDL_PAD_EIM_DA6__GPIO3_IO06 0x400130b1 + MX6QDL_PAD_SD3_CMD__GPIO7_IO02 0x400130b1 + MX6QDL_PAD_SD3_CLK__GPIO7_IO03 0x400130b1 + MX6QDL_PAD_EIM_DA3__GPIO3_IO03 0x400130b1 + >; + }; + + pinctrl_hummingboard_i2c1: hummingboard-i2c1 { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + pinctrl_hummingboard_i2c2: hummingboard-i2c2 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_hummingboard_pcie_reset: hummingboard-pcie-reset { + fsl,pins = < + MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0x1b0b1 + >; + }; + + pinctrl_hummingboard_pwm1: pwm1grp { + fsl,pins = ; + }; + + pinctrl_hummingboard_sgtl5000: hummingboard-sgtl5000 { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x130b0 + MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x130b0 + MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x110b0 + MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x130b0 + MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x130b0 + >; + }; + + pinctrl_hummingboard_spdif: hummingboard-spdif { + fsl,pins = ; + }; + + pinctrl_hummingboard_usbh1_vbus: hummingboard-usbh1-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard_usbotg_id: hummingboard-usbotg-id { + /* + * Similar to pinctrl_usbotg_2, but we want it + * pulled down for a fixed host connection. + */ + fsl,pins = ; + }; + + pinctrl_hummingboard_usbotg_vbus: hummingboard-usbotg-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard_usdhc2_pwr: hummingboard-usdhc2-pwr { + fsl,pins = ; + }; + + pinctrl_hummingboard_usdhc2_aux: hummingboard-usdhc2-aux { + fsl,pins = < + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 + MX6QDL_PAD_KEY_ROW1__SD2_VSELECT 0x1b071 + >; + }; + + pinctrl_hummingboard_usdhc2: hummingboard-usdhc2 { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 + >; + }; + + pinctrl_hummingboard_usdhc2_100mhz: hummingboard-usdhc2-100mhz { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130b9 + >; + }; + + pinctrl_hummingboard_usdhc2_200mhz: hummingboard-usdhc2-200mhz { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170f9 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100f9 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130f9 + >; + }; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_pcie_reset>; + reset-gpio = <&gpio3 4 0>; + status = "okay"; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_pwm1>; + status = "okay"; +}; + +&pwm2 { + pinctrl-names = "default"; + status = "okay"; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_spdif>; + status = "okay"; +}; + +&ssi1 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&usbh1 { + disable-over-current; + vbus-supply = <®_usbh1_vbus>; + status = "okay"; +}; + +&usbotg { + disable-over-current; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usbotg_id>; + vbus-supply = <®_usbotg_vbus>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = < + &pinctrl_hummingboard_usdhc2_aux + &pinctrl_hummingboard_usdhc2 + >; + pinctrl-1 = < + &pinctrl_hummingboard_usdhc2_aux + &pinctrl_hummingboard_usdhc2_100mhz + >; + pinctrl-2 = < + &pinctrl_hummingboard_usdhc2_aux + &pinctrl_hummingboard_usdhc2_200mhz + >; + vmmc-supply = <®_3p3v>; + cd-gpios = <&gpio1 4 0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi b/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi index a3cb2fff8f6121..4a1820309cdb82 100644 --- a/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi +++ b/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi @@ -3,6 +3,44 @@ * * This describes the hookup for an AR8035 to the iMX6 on the SolidRun * MicroSOM. + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. */ &fec { pinctrl-names = "default"; @@ -17,7 +55,7 @@ enet { pinctrl_microsom_enet_ar8035: microsom-enet-ar8035 { fsl,pins = < - MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b8b0 MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 /* AR8035 reset */ MX6QDL_PAD_KEY_ROW4__GPIO4_IO15 0x130b0 @@ -26,25 +64,25 @@ /* GPIO16 -> AR8035 25MHz */ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0xc0000000 MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x80000000 - MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b030 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b030 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b030 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b030 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b030 /* AR8035 CLK_25M --> ENET_REF_CLK (V22) */ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x0a0b1 /* AR8035 pin strapping: IO voltage: pull up */ - MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b030 /* AR8035 pin strapping: PHYADDR#0: pull down */ - MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x130b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x13030 /* AR8035 pin strapping: PHYADDR#1: pull down */ - MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x130b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x13030 /* AR8035 pin strapping: MODE#1: pull up */ - MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b030 /* AR8035 pin strapping: MODE#3: pull up */ - MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b030 /* AR8035 pin strapping: MODE#0: pull down */ - MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x130b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x13030 /* * As the RMII pins are also connected to RGMII diff --git a/arch/arm/boot/dts/imx6qdl-microsom.dtsi b/arch/arm/boot/dts/imx6qdl-microsom.dtsi index d729d0b15f251b..ec6c7c7a406f0e 100644 --- a/arch/arm/boot/dts/imx6qdl-microsom.dtsi +++ b/arch/arm/boot/dts/imx6qdl-microsom.dtsi @@ -1,9 +1,110 @@ /* * Copyright (C) 2013,2014 Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. */ +#include +/ { + clk_sdio: sdio-clock { + compatible = "gpio-gate-clock"; + #clock-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_osc>; + enable-gpios = <&gpio5 5 GPIO_ACTIVE_HIGH>; + }; + + regulators { + compatible = "simple-bus"; + + reg_brcm: brcm-reg { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_reg>; + regulator-name = "brcm_reg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + startup-delay-us = <200000>; + }; + }; + + usdhc1_pwrseq: usdhc1_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, + <&gpio6 0 GPIO_ACTIVE_LOW>; + clocks = <&clk_sdio>; + clock-names = "ext_clock"; + }; +}; &iomuxc { microsom { + pinctrl_microsom_brcm_bt: microsom-brcm-bt { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x40013070 + MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x40013070 + MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x40013070 + >; + }; + + pinctrl_microsom_brcm_osc: microsom-brcm-osc { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x40013070 + >; + }; + + pinctrl_microsom_brcm_reg: microsom-brcm-reg { + fsl,pins = < + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40013070 + >; + }; + + pinctrl_microsom_brcm_wifi: microsom-brcm-wifi { + fsl,pins = < + MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x1b0b0 + MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x40013070 + MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x40013070 + MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x40013070 + >; + }; + pinctrl_microsom_uart1: microsom-uart1 { fsl,pins = < MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 @@ -11,12 +112,24 @@ >; }; - pinctrl_microsom_usbotg: microsom-usbotg { - /* - * Similar to pinctrl_usbotg_2, but we want it - * pulled down for a fixed host connection. - */ - fsl,pins = ; + pinctrl_microsom_uart4: microsom-uart4 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1 + MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1 + >; + }; + + pinctrl_microsom_usdhc1: microsom-usdhc1 { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 + >; }; }; }; @@ -27,7 +140,22 @@ status = "okay"; }; -&usbotg { +/* UART4 - Connected to optional BRCM Wifi/BT/FM */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_bt &pinctrl_microsom_uart4>; + fsl,uart-has-rtscts; + status = "okay"; +}; + +/* USDHC1 - Connected to optional BRCM Wifi/BT/FM */ +&usdhc1 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_microsom_usbotg>; + pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_microsom_usdhc1>; + bus-width = <4>; + mmc-pwrseq = <&usdhc1_pwrseq>; + keep-power-in-suspend; + non-removable; + vmmc-supply = <®_brcm>; + status = "okay"; }; From 4db2fef5a07f29ca5835c2cdd5b3d61897601def Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 2 Jul 2015 15:26:31 +0200 Subject: [PATCH 0004/1983] Revert "mmc: sdhci: Don't signal the sdio irq if it's not setup" This reverts commit 94732f734786f4388aecf47e238b933fa779d61d. --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b945bae40f7ba7..a515b04f7ddf0c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2619,7 +2619,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) /* * We have to delay this as it calls back into the driver. */ - if (cardint && host->mmc->sdio_irqs) + if (cardint) mmc_signal_sdio_irq(host->mmc); return result; From eb46f3461b6397e0fa8fbf4d24e221b6a6857f7e Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 2 Jul 2015 15:29:20 +0200 Subject: [PATCH 0005/1983] Revert "ENGR00292140 mmc: sdhci: fix possible sleep in atomic in sdio_irq enable function" This reverts commit 63f1013acf24b8df85e7c45dbb3f634a6f59ca47. --- drivers/mmc/host/sdhci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a515b04f7ddf0c..e95debaef9fd12 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1713,6 +1713,9 @@ static int sdhci_get_ro(struct mmc_host *mmc) static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { + if (enable) + sdhci_runtime_pm_get(host); + if (host->flags & SDHCI_DEVICE_DEAD) goto out; @@ -1731,6 +1734,9 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); out: mmiowb(); + + if (!enable) + sdhci_runtime_pm_put(host); } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1738,15 +1744,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; - if (enable) - sdhci_runtime_pm_get(host); - spin_lock_irqsave(&host->lock, flags); sdhci_enable_sdio_irq_nolock(host, enable); spin_unlock_irqrestore(&host->lock, flags); - - if (!enable) - sdhci_runtime_pm_put(host); } static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, From d0a8ae145a202deece5032a34a7df3e218b055de Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 2 Jul 2015 15:29:30 +0200 Subject: [PATCH 0006/1983] Revert "ENGR00289279 mmc: sdhci: get runtime pm when sdio irq is enabled" This reverts commit 1fc2158934dc8e99c017dc0b9b00954ab6f31357. --- drivers/mmc/host/sdhci.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e95debaef9fd12..374b54c42f3ff9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1713,9 +1713,6 @@ static int sdhci_get_ro(struct mmc_host *mmc) static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { - if (enable) - sdhci_runtime_pm_get(host); - if (host->flags & SDHCI_DEVICE_DEAD) goto out; @@ -1734,9 +1731,6 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); out: mmiowb(); - - if (!enable) - sdhci_runtime_pm_put(host); } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) From e881bd15893469c94d0bb5514ca7d460859d1627 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 15 Feb 2014 00:55:18 +0000 Subject: [PATCH 0007/1983] genirq: Provide synchronize_hardirq() Upstream-commit: 18258f7239a61d8929b8e0c7b6d46c446459074c synchronize_irq() waits for hard irq and threaded handlers to complete before returning. For some special cases we only need to make sure that the hard interrupt part of the irq line is not in progress when we disabled the - possibly shared - interrupt at the device level. A proper use case for this was provided by Russell. The sdhci driver requires some irq triggered functions to be run in thread context. The current implementation of the thread context is a sdio private kthread construct, which has quite some shortcomings. These can be avoided when the thread is directly associated to the device interrupt via the generic threaded irq infrastructure. Though there is a corner case related to run time power management where one side disables the device interrupts at the device level and needs to make sure, that an already running hard interrupt handler has completed before proceeding further. Though that hard interrupt handler might wake the associated thread, which in turn can request the runtime PM to reenable the device. Using synchronize_irq() leads to an immediate deadlock of the irq thread waiting for the PM lock and the synchronize_irq() waiting for the irq thread to complete. Due to the fact that it is sufficient for this case to ensure that no hard irq handler is executing a new function which avoids the check for the thread is required. Add a function, which just monitors the hard irq parts and ignores the threaded handlers. Signed-off-by: Thomas Gleixner Tested-by: Russell King Cc: Chris Ball Acked-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20140215003823.653236081@linutronix.de --- include/linux/hardirq.h | 1 + kernel/irq/manage.c | 70 +++++++++++++++++++++++++++++------------ 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 12d5f972f23f46..cba442ec3c6658 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -9,6 +9,7 @@ extern void synchronize_irq(unsigned int irq); +extern void synchronize_hardirq(unsigned int irq); #if defined(CONFIG_TINY_RCU) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index ebb8a9e937fa5a..355c28cd369a53 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -32,24 +32,10 @@ static int __init setup_forced_irqthreads(char *arg) early_param("threadirqs", setup_forced_irqthreads); #endif -/** - * synchronize_irq - wait for pending IRQ handlers (on other CPUs) - * @irq: interrupt number to wait for - * - * This function waits for any pending IRQ handlers for this interrupt - * to complete before returning. If you use this function while - * holding a resource the IRQ handler may need you will deadlock. - * - * This function may be called - with care - from IRQ context. - */ -void synchronize_irq(unsigned int irq) +static void __synchronize_hardirq(struct irq_desc *desc) { - struct irq_desc *desc = irq_to_desc(irq); bool inprogress; - if (!desc) - return; - do { unsigned long flags; @@ -67,12 +53,56 @@ void synchronize_irq(unsigned int irq) /* Oops, that failed? */ } while (inprogress); +} - /* - * We made sure that no hardirq handler is running. Now verify - * that no threaded handlers are active. - */ - wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active)); +/** + * synchronize_hardirq - wait for pending hard IRQ handlers (on other CPUs) + * @irq: interrupt number to wait for + * + * This function waits for any pending hard IRQ handlers for this + * interrupt to complete before returning. If you use this + * function while holding a resource the IRQ handler may need you + * will deadlock. It does not take associated threaded handlers + * into account. + * + * Do not use this for shutdown scenarios where you must be sure + * that all parts (hardirq and threaded handler) have completed. + * + * This function may be called - with care - from IRQ context. + */ +void synchronize_hardirq(unsigned int irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + + if (desc) + __synchronize_hardirq(desc); +} +EXPORT_SYMBOL(synchronize_hardirq); + +/** + * synchronize_irq - wait for pending IRQ handlers (on other CPUs) + * @irq: interrupt number to wait for + * + * This function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ +void synchronize_irq(unsigned int irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + + if (desc) { + __synchronize_hardirq(desc); + /* + * We made sure that no hardirq handler is + * running. Now verify that no threaded handlers are + * active. + */ + wait_event(desc->wait_for_threads, + !atomic_read(&desc->threads_active)); + } } EXPORT_SYMBOL(synchronize_irq); From 74d8d1595686f427927097f1afbd647b6b7abe01 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:52:48 +0200 Subject: [PATCH 0008/1983] mmc: sdio_irq: rework sdio irq handling Upstream-commit: bf3b5ec66bd03d66e9ea729aaca013ea1047a797 Rather than the SDIO support spawning it's own thread for handling card interrupts, use the generic IRQ infrastructure for this, triggering it from the host interface's interrupt handling directly. This avoids a race between the parent thread waiting to receive an interrupt response from the card, and the slow startup from the sdio irq thread, which can occur as a result of high system load (eg, while udev is running.) Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio_irq.c | 41 ++++++++++++++++++++++++++++--------- include/linux/mmc/host.h | 3 +++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index aaa90460ed234c..5cc13c8d35bbf3 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -90,6 +90,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host) return ret; } +void sdio_run_irqs(struct mmc_host *host) +{ + mmc_claim_host(host); + host->sdio_irq_pending = true; + process_sdio_pending_irqs(host); + mmc_release_host(host); +} +EXPORT_SYMBOL_GPL(sdio_run_irqs); + static int sdio_irq_thread(void *_host) { struct mmc_host *host = _host; @@ -189,14 +198,20 @@ static int sdio_card_irq_get(struct mmc_card *card) WARN_ON(!host->claimed); if (!host->sdio_irqs++) { - atomic_set(&host->sdio_irq_thread_abort, 0); - host->sdio_irq_thread = - kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", - mmc_hostname(host)); - if (IS_ERR(host->sdio_irq_thread)) { - int err = PTR_ERR(host->sdio_irq_thread); - host->sdio_irqs--; - return err; + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + atomic_set(&host->sdio_irq_thread_abort, 0); + host->sdio_irq_thread = + kthread_run(sdio_irq_thread, host, + "ksdioirqd/%s", mmc_hostname(host)); + if (IS_ERR(host->sdio_irq_thread)) { + int err = PTR_ERR(host->sdio_irq_thread); + host->sdio_irqs--; + return err; + } + } else { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 1); + mmc_host_clk_release(host); } } @@ -211,8 +226,14 @@ static int sdio_card_irq_put(struct mmc_card *card) BUG_ON(host->sdio_irqs < 1); if (!--host->sdio_irqs) { - atomic_set(&host->sdio_irq_thread_abort, 1); - kthread_stop(host->sdio_irq_thread); + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + atomic_set(&host->sdio_irq_thread_abort, 1); + kthread_stop(host->sdio_irq_thread); + } else { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 0); + mmc_host_clk_release(host); + } } return 0; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 99f5709ac343df..bfd0769159eaf2 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -282,6 +282,7 @@ struct mmc_host { MMC_CAP2_PACKED_WR) #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ #define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */ +#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 16) mmc_pm_flag_t pm_caps; /* supported pm features */ @@ -397,6 +398,8 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) wake_up_process(host->sdio_irq_thread); } +void sdio_run_irqs(struct mmc_host *host); + #ifdef CONFIG_REGULATOR int mmc_regulator_get_ocrmask(struct regulator *supply); int mmc_regulator_set_ocr(struct mmc_host *mmc, From dd99ea45f3fac304ea5f461b592ecde02fc956dc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:52:48 +0200 Subject: [PATCH 0009/1983] mmc: sdhci: clean up interrupt handling Upstream-commit: 41005003bcaf4ecfc80e02af01247f2670269816 mmc: sdhci: clean up interrupt handling sdhci interrupt handling is a mess; there is a lot of code doing very similar things. Let's clean this up a bit: 1. set's clear down cmd, data and bus power interrupts in one go - we're always going to handle these. 2. use a do { } while () loop for looping while there are pending interrupts. 3. group clearing of bits in intmask into one place. This results in the code becoming simpler and easier to read. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 124 ++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 66 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 374b54c42f3ff9..c7f9accbb24f65 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2507,7 +2507,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result; struct sdhci_host *host = dev_id; - u32 intmask, unexpected = 0; + u32 intmask, mask, unexpected = 0; int cardint = 0, max_loops = 16; spin_lock(&host->lock); @@ -2520,88 +2520,80 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) } intmask = sdhci_readl(host, SDHCI_INT_STATUS); - if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; goto out; } -again: - DBG("*** %s got interrupt: 0x%08x\n", - mmc_hostname(host->mmc), intmask); - - if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { - u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - - /* - * There is a observation on i.mx esdhc. INSERT bit will be - * immediately set again when it gets cleared, if a card is - * inserted. We have to mask the irq to prevent interrupt - * storm which will freeze the system. And the REMOVE gets - * the same situation. - * - * More testing are needed here to ensure it works for other - * platforms though. - */ - sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : - SDHCI_INT_CARD_REMOVE); - sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : - SDHCI_INT_CARD_INSERT); - - sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | - SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); - intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); - tasklet_schedule(&host->card_tasklet); - } + do { + /* Clear selected interrupts. */ + mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | + SDHCI_INT_BUS_POWER); + sdhci_writel(host, mask, SDHCI_INT_STATUS); - if (intmask & SDHCI_INT_CMD_MASK) { - sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, - SDHCI_INT_STATUS); - sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); - } + DBG("*** %s got interrupt: 0x%08x\n", + mmc_hostname(host->mmc), intmask); - if (intmask & SDHCI_INT_DATA_MASK) { - sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, - SDHCI_INT_STATUS); - sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); - } + if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; - intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); + /* + * There is a observation on i.mx esdhc. INSERT + * bit will be immediately set again when it gets + * cleared, if a card is inserted. We have to mask + * the irq to prevent interrupt storm which will + * freeze the system. And the REMOVE gets the + * same situation. + * + * More testing are needed here to ensure it works + * for other platforms though. + */ + sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : + SDHCI_INT_CARD_REMOVE); + sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : + SDHCI_INT_CARD_INSERT); + + sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); + tasklet_schedule(&host->card_tasklet); + } - intmask &= ~SDHCI_INT_ERROR; + if (intmask & SDHCI_INT_CMD_MASK) + sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); - if (intmask & SDHCI_INT_BUS_POWER) { - pr_err("%s: Card is consuming too much power!\n", - mmc_hostname(host->mmc)); - sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS); - } + if (intmask & SDHCI_INT_DATA_MASK) + sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); - intmask &= ~SDHCI_INT_BUS_POWER; + if (intmask & SDHCI_INT_BUS_POWER) + pr_err("%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); - if (intmask & SDHCI_INT_CARD_INT) - cardint = 1; + if (intmask & SDHCI_INT_CARD_INT) + cardint = 1; - intmask &= ~SDHCI_INT_CARD_INT; + intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | + SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | + SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | + SDHCI_INT_CARD_INT); - if (intmask) { - unexpected |= intmask; - sdhci_writel(host, intmask, SDHCI_INT_STATUS); - } + if (intmask) { + unexpected |= intmask; + sdhci_writel(host, intmask, SDHCI_INT_STATUS); + } - result = IRQ_HANDLED; + result = IRQ_HANDLED; - intmask = sdhci_readl(host, SDHCI_INT_STATUS); + intmask = sdhci_readl(host, SDHCI_INT_STATUS); - /* - * If we know we'll call the driver to signal SDIO IRQ, disregard - * further indications of Card Interrupt in the status to avoid a - * needless loop. - */ - if (cardint) - intmask &= ~SDHCI_INT_CARD_INT; - if (intmask && --max_loops) - goto again; + /* + * If we know we'll call the driver to signal SDIO IRQ, + * disregard further indications of Card Interrupt in + * the status to avoid a needless loop. + */ + if (cardint) + intmask &= ~SDHCI_INT_CARD_INT; + } while (intmask && --max_loops); out: spin_unlock(&host->lock); From 09ab469d90a8eec00ff289bd62ee3e75fed35951 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:52:49 +0200 Subject: [PATCH 0010/1983] mmc: sdhci: clean up sdio interrupt enable handling Upstream-commit: ef104333a234f66cd13fd16632086a21b90fced6 We don't need to change the SDHCI_SDIO_IRQ_ENABLED flag when we're merely receiving an interrupt - IRQ handling thread in the MMC core will either re-enable or disable the interrupt via the enable_sdio_irq callback, which will update this status appropriately. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c7f9accbb24f65..abd4ee3e3d6961 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1713,24 +1713,14 @@ static int sdhci_get_ro(struct mmc_host *mmc) static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { - if (host->flags & SDHCI_DEVICE_DEAD) - goto out; - - if (enable) - host->flags |= SDHCI_SDIO_IRQ_ENABLED; - else - host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; - /* SDIO IRQ will be enabled as appropriate in runtime resume */ - if (host->runtime_suspended) - goto out; - - if (enable) - sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); - else - sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); -out: - mmiowb(); + if (!(host->flags & SDHCI_DEVICE_DEAD) || host->runtime_suspended) { + if (enable) + sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); + else + sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); + mmiowb(); + } } static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) @@ -1738,9 +1728,18 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; + sdhci_runtime_pm_get(host); + spin_lock_irqsave(&host->lock, flags); + if (enable) + host->flags |= SDHCI_SDIO_IRQ_ENABLED; + else + host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; + sdhci_enable_sdio_irq_nolock(host, enable); spin_unlock_irqrestore(&host->lock, flags); + + sdhci_runtime_pm_put(host); } static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, @@ -2806,7 +2805,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) host->runtime_suspended = false; /* Enable SDIO IRQ */ - if ((host->flags & SDHCI_SDIO_IRQ_ENABLED)) + if (host->flags & SDHCI_SDIO_IRQ_ENABLED) sdhci_enable_sdio_irq_nolock(host, true); /* Enable Card Detection */ From e5041a72973de4257b22354e4ee70fdbe4ca049f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:03 +0200 Subject: [PATCH 0011/1983] mmc: sdhci: convert to new SDIO IRQ handling Upstream-commit: 781e989cf593c71d26bdca74f5e77b3651fc060e mmc: sdhci: convert to new SDIO IRQ handling Use a generic threaded interrupt handler for SDIO interrupt handling, rather than allowing the SDIO core code to buggily spawn its own thread. This results in host drivers to be more in control of how SDIO interrupts are acknowledged in the hardware, rather than having the internals of the SDIO core placed upon them, possibly resulting in sub-standard handling. At least one SDHCI implementation specifies a very specific sequence to deal with a card interrupt. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 62 ++++++++++++++++++++++++--------------- include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index abd4ee3e3d6961..fde5ad1d00befb 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2504,10 +2504,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) static irqreturn_t sdhci_irq(int irq, void *dev_id) { - irqreturn_t result; + irqreturn_t result = IRQ_NONE; struct sdhci_host *host = dev_id; u32 intmask, mask, unexpected = 0; - int cardint = 0, max_loops = 16; + int max_loops = 16; spin_lock(&host->lock); @@ -2568,8 +2568,11 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) pr_err("%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); - if (intmask & SDHCI_INT_CARD_INT) - cardint = 1; + if (intmask & SDHCI_INT_CARD_INT) { + sdhci_enable_sdio_irq_nolock(host, false); + host->thread_isr |= SDHCI_INT_CARD_INT; + result = IRQ_WAKE_THREAD; + } intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | @@ -2581,17 +2584,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) sdhci_writel(host, intmask, SDHCI_INT_STATUS); } - result = IRQ_HANDLED; + if (result == IRQ_NONE) + result = IRQ_HANDLED; intmask = sdhci_readl(host, SDHCI_INT_STATUS); - - /* - * If we know we'll call the driver to signal SDIO IRQ, - * disregard further indications of Card Interrupt in - * the status to avoid a needless loop. - */ - if (cardint) - intmask &= ~SDHCI_INT_CARD_INT; } while (intmask && --max_loops); out: spin_unlock(&host->lock); @@ -2601,15 +2597,33 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) mmc_hostname(host->mmc), unexpected); sdhci_dumpregs(host); } - /* - * We have to delay this as it calls back into the driver. - */ - if (cardint) - mmc_signal_sdio_irq(host->mmc); return result; } +static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) +{ + struct sdhci_host *host = dev_id; + unsigned long flags; + u32 isr; + + spin_lock_irqsave(&host->lock, flags); + isr = host->thread_isr; + host->thread_isr = 0; + spin_unlock_irqrestore(&host->lock, flags); + + if (isr & SDHCI_INT_CARD_INT) { + sdio_run_irqs(host->mmc); + + spin_lock_irqsave(&host->lock, flags); + if (host->flags & SDHCI_SDIO_IRQ_ENABLED) + sdhci_enable_sdio_irq_nolock(host, true); + spin_unlock_irqrestore(&host->lock, flags); + } + + return isr ? IRQ_HANDLED : IRQ_NONE; +} + /*****************************************************************************\ * * * Suspend/resume * @@ -2681,8 +2695,9 @@ int sdhci_resume_host(struct sdhci_host *host) } if (!device_may_wakeup(mmc_dev(host->mmc))) { - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); + ret = request_threaded_irq(host->irq, sdhci_irq, + sdhci_thread_irq, IRQF_SHARED, + mmc_hostname(host->mmc), host); if (ret) return ret; } else { @@ -2761,7 +2776,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); spin_unlock_irqrestore(&host->lock, flags); - synchronize_irq(host->irq); + synchronize_hardirq(host->irq); spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = true; @@ -3030,6 +3045,7 @@ int sdhci_add_host(struct sdhci_host *host) } mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12) host->flags |= SDHCI_AUTO_CMD12; @@ -3318,8 +3334,8 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_init(host, 0); - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(mmc), host); + ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, + IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { pr_err("%s: Failed to request IRQ %d: %d\n", mmc_hostname(mmc), host->irq, ret); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 28914bb9c0133c..9854db510e346c 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -181,6 +181,8 @@ struct sdhci_host { unsigned int ocr_avail_mmc; u32 ocr_mask; /* available voltages */ + u32 thread_isr; + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ From 37ce278400fadba7993c5e0ff09c3f435cf7423b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:04 +0200 Subject: [PATCH 0012/1983] mmc: sdhci: push card_tasklet into threaded irq handler Upstream-commit: 3560db8e247aa35bc6b287ec7ec51cd41abd512e There's no requirement to have the card tasklet separate now that we have a threaded interrupt handler, so kill this and move the called code into the threaded part of the handler. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 23 +++++++++-------------- include/linux/mmc/sdhci.h | 3 +-- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index fde5ad1d00befb..189c1b1a643473 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2204,15 +2204,6 @@ static const struct mmc_host_ops sdhci_ops = { * * \*****************************************************************************/ -static void sdhci_tasklet_card(unsigned long param) -{ - struct sdhci_host *host = (struct sdhci_host*)param; - - sdhci_card_event(host->mmc); - - mmc_detect_change(host->mmc, msecs_to_jiffies(200)); -} - static void sdhci_tasklet_finish(unsigned long param) { struct sdhci_host *host; @@ -2555,7 +2546,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); - tasklet_schedule(&host->card_tasklet); + + host->thread_isr |= intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + result = IRQ_WAKE_THREAD; } if (intmask & SDHCI_INT_CMD_MASK) @@ -2612,6 +2606,11 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id) host->thread_isr = 0; spin_unlock_irqrestore(&host->lock, flags); + if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + sdhci_card_event(host->mmc); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); + } + if (isr & SDHCI_INT_CARD_INT) { sdio_run_irqs(host->mmc); @@ -3316,8 +3315,6 @@ int sdhci_add_host(struct sdhci_host *host) /* * Init tasklets. */ - tasklet_init(&host->card_tasklet, - sdhci_tasklet_card, (unsigned long)host); tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host); @@ -3382,7 +3379,6 @@ int sdhci_add_host(struct sdhci_host *host) free_irq(host->irq, host); #endif untasklet: - tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); return ret; @@ -3426,7 +3422,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) del_timer_sync(&host->timer); - tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); if (host->vmmc) { diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 9854db510e346c..54886aaf2714ac 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -168,8 +168,7 @@ struct sdhci_host { dma_addr_t adma_addr; /* Mapped ADMA descr. table */ dma_addr_t align_addr; /* Mapped bounce buffer */ - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct finish_tasklet; + struct tasklet_struct finish_tasklet; /* Tasklet structures */ struct timer_list timer; /* Timer for timeouts */ From e7414159b1bb836de586f72c8028a448e644eba3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:14 +0200 Subject: [PATCH 0013/1983] mmc: sdhci: allow sdio interrupts while sdhci runtime suspended Upstream-commit: be138554a7923658ded799b0e8794d9c1d08a6e5 Allow SDIO interrupts to be received while the SDHCI host is runtime suspended. We do this by leaving the AHB clock enabled while the host is runtime suspended so we can access the SDHCI registers, and so read and raise the SDIO card interrupt. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 12 ++++++++---- drivers/mmc/host/sdhci.c | 7 +++---- drivers/mmc/host/sdhci.h | 5 +++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 868b4712a5d8e7..8402ab264269aa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1213,8 +1213,10 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) ret = sdhci_runtime_suspend_host(host); - clk_disable_unprepare(imx_data->clk_per); - clk_disable_unprepare(imx_data->clk_ipg); + if (!sdhci_sdio_irq_enabled(host)) { + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + } clk_disable_unprepare(imx_data->clk_ahb); if (imx_data->socdata->flags & ESDHC_FLAG_BUSFREQ) @@ -1232,8 +1234,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) if (imx_data->socdata->flags & ESDHC_FLAG_BUSFREQ) request_bus_freq(BUS_FREQ_HIGH); - clk_prepare_enable(imx_data->clk_per); - clk_prepare_enable(imx_data->clk_ipg); + if (!sdhci_sdio_irq_enabled(host)) { + clk_prepare_enable(imx_data->clk_per); + clk_prepare_enable(imx_data->clk_ipg); + } clk_prepare_enable(imx_data->clk_ahb); return sdhci_runtime_resume_host(host); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 189c1b1a643473..2447ccf02c4c97 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1713,8 +1713,7 @@ static int sdhci_get_ro(struct mmc_host *mmc) static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { - /* SDIO IRQ will be enabled as appropriate in runtime resume */ - if (!(host->flags & SDHCI_DEVICE_DEAD) || host->runtime_suspended) { + if (!(host->flags & SDHCI_DEVICE_DEAD)) { if (enable) sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); else @@ -2502,7 +2501,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) spin_lock(&host->lock); - if (host->runtime_suspended) { + if (host->runtime_suspended && !sdhci_sdio_irq_enabled(host)) { spin_unlock(&host->lock); pr_warning("%s: got irq while runtime suspended\n", mmc_hostname(host->mmc)); @@ -2772,7 +2771,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) } spin_lock_irqsave(&host->lock, flags); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK & ~SDHCI_INT_CARD_INT); spin_unlock_irqrestore(&host->lock, flags); synchronize_hardirq(host->irq); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 51bd04abddc977..03cb2504eba098 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -398,6 +398,11 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead); extern void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd); +static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) +{ + return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); +} + #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); extern int sdhci_resume_host(struct sdhci_host *host); From c6b985ab780a862f38bc1a7011993bc53c1ba367 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:14 +0200 Subject: [PATCH 0014/1983] mmc: sdhci: more efficient interrupt enable register handling Upstream-commit: b537f94ce19583de1882f539a5cc49aa99260aca Rather than wasting cycles read-modify-writing the interrupt enable registers, cache the value locally instead. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 98 +++++++++++++++++++-------------------- include/linux/mmc/sdhci.h | 3 ++ 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2447ccf02c4c97..e4ff545d2fa57b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -134,27 +134,6 @@ static void sdhci_dumpregs(struct sdhci_host *host) * * \*****************************************************************************/ -static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) -{ - u32 ier; - - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - ier &= ~clear; - ier |= set; - sdhci_writel(host, ier, SDHCI_INT_ENABLE); - sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); -} - -static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs) -{ - sdhci_clear_set_irqs(host, 0, irqs); -} - -static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) -{ - sdhci_clear_set_irqs(host, irqs, 0); -} - static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) { u32 present, irqs; @@ -170,9 +149,12 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; if (enable) - sdhci_unmask_irqs(host, irqs); + host->ier |= irqs; else - sdhci_mask_irqs(host, irqs); + host->ier &= ~irqs; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static void sdhci_enable_card_detection(struct sdhci_host *host) @@ -188,17 +170,12 @@ static void sdhci_disable_card_detection(struct sdhci_host *host) static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; - u32 uninitialized_var(ier); - if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) return; } - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - if (host->ops->platform_reset_enter) host->ops->platform_reset_enter(host, mask); @@ -229,8 +206,10 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) if (host->ops->platform_reset_exit) host->ops->platform_reset_exit(host, mask); - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) - sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); + if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) { + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); + } if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) @@ -247,11 +226,14 @@ static void sdhci_init(struct sdhci_host *host, int soft) else sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, - SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | - SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | - SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | - SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); + host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | + SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | + SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | + SDHCI_INT_RESPONSE; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); if (soft) { /* force clock reconfiguration */ @@ -726,9 +708,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; if (host->flags & SDHCI_REQ_USE_DMA) - sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); + host->ier = (host->ier & ~pio_irqs) | dma_irqs; else - sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); + host->ier = (host->ier & ~dma_irqs) | pio_irqs; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) @@ -1715,9 +1700,12 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable) { if (!(host->flags & SDHCI_DEVICE_DEAD)) { if (enable) - sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); + host->ier |= SDHCI_INT_CARD_INT; else - sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); + host->ier &= ~SDHCI_INT_CARD_INT; + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); mmiowb(); } } @@ -1859,7 +1847,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host; u16 ctrl; - u32 ier; int tuning_loop_counter = MAX_TUNING_LOOP; unsigned long timeout; int err = 0; @@ -1913,8 +1900,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * to make sure we don't hit a controller bug, we _only_ * enable Buffer Read Ready interrupt here. */ - ier = sdhci_readl(host, SDHCI_INT_ENABLE); - sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); /* * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number @@ -2047,7 +2034,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) err = 0; - sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); @@ -2538,10 +2526,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) * More testing are needed here to ensure it works * for other platforms though. */ - sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT : - SDHCI_INT_CARD_REMOVE); - sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE : - SDHCI_INT_CARD_INSERT); + host->ier &= ~(SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE); + host->ier |= present ? SDHCI_INT_CARD_REMOVE : + SDHCI_INT_CARD_INSERT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); @@ -2672,7 +2662,9 @@ int sdhci_suspend_host(struct sdhci_host *host) } if (!device_may_wakeup(mmc_dev(host->mmc))) { - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + host->ier = 0; + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); } else { sdhci_enable_irq_wakeups(host); @@ -2771,7 +2763,9 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) } spin_lock_irqsave(&host->lock, flags); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK & ~SDHCI_INT_CARD_INT); + host->ier &= SDHCI_INT_CARD_INT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); spin_unlock_irqrestore(&host->lock, flags); synchronize_hardirq(host->irq); @@ -3374,7 +3368,8 @@ int sdhci_add_host(struct sdhci_host *host) #ifdef SDHCI_USE_LEDS_CLASS reset: sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); #endif untasklet: @@ -3416,7 +3411,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) if (!dead) sdhci_reset(host, SDHCI_RESET_ALL); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + sdhci_writel(host, 0, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); del_timer_sync(&host->timer); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 54886aaf2714ac..248841b0ead1e6 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -182,6 +182,9 @@ struct sdhci_host { u32 thread_isr; + /* cached registers */ + u32 ier; + wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ From 86c5196fa0c294d414fb4b0de15960dfcc004c21 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:14 +0200 Subject: [PATCH 0015/1983] mmc: sdhci: plug hole in disabling card detection interrupts Upstream-commit: 5b4f1f6c496aa3a90b617d1319274bf2017e639d When we disable card detection interrupts, we should disable both the insert and remove interrupts irrespective of the current state - this avoids races between the hardware card detect changing state before we've read that updated state and altered the interrupt mask. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e4ff545d2fa57b..972675e13db54a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -136,7 +136,7 @@ static void sdhci_dumpregs(struct sdhci_host *host) static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) { - u32 present, irqs; + u32 present; int gpio_cd = mmc_gpio_get_cd(host->mmc); if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || @@ -144,14 +144,14 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) !IS_ERR_VALUE(gpio_cd)) return; - present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; - - if (enable) - host->ier |= irqs; - else - host->ier &= ~irqs; + if (enable) { + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; + host->ier |= present ? SDHCI_INT_CARD_REMOVE : + SDHCI_INT_CARD_INSERT; + } else { + host->ier &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); + } sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); From 83d3a63d8c916db71123e7a89aa694127474eea8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:26 +0200 Subject: [PATCH 0016/1983] mmc: sdhci: convert generic bus width setup to library function Upstream-commit: 2317f56c055fcad524bf6a873df48a754e7ebc4d Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 2 ++ drivers/mmc/host/sdhci-bcm-kona.c | 1 + drivers/mmc/host/sdhci-bcm2835.c | 1 + drivers/mmc/host/sdhci-cns3xxx.c | 1 + drivers/mmc/host/sdhci-dove.c | 1 + drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++-- drivers/mmc/host/sdhci-of-arasan.c | 1 + drivers/mmc/host/sdhci-of-esdhc.c | 6 ++-- drivers/mmc/host/sdhci-of-hlwd.c | 1 + drivers/mmc/host/sdhci-pci.c | 6 ++-- drivers/mmc/host/sdhci-pltfm.c | 1 + drivers/mmc/host/sdhci-pxav2.c | 6 ++-- drivers/mmc/host/sdhci-pxav3.c | 1 + drivers/mmc/host/sdhci-s3c.c | 8 ++---- drivers/mmc/host/sdhci-sirf.c | 1 + drivers/mmc/host/sdhci-spear.c | 2 +- drivers/mmc/host/sdhci-tegra.c | 5 ++-- drivers/mmc/host/sdhci.c | 45 +++++++++++++++--------------- drivers/mmc/host/sdhci.h | 5 ++-- 19 files changed, 50 insertions(+), 50 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 9ce17f6e4014d9..b138bfdd384310 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -102,10 +102,12 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) static const struct sdhci_ops sdhci_acpi_ops_dflt = { .enable_dma = sdhci_acpi_enable_dma, + .set_bus_width = sdhci_set_bus_width, }; static const struct sdhci_ops sdhci_acpi_ops_int = { .enable_dma = sdhci_acpi_enable_dma, + .set_bus_width = sdhci_set_bus_width, .hw_reset = sdhci_acpi_int_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index e5565aa5ed3611..84b0ead3da8610 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -208,6 +208,7 @@ static struct sdhci_ops sdhci_bcm_kona_ops = { .get_max_clock = sdhci_bcm_kona_get_max_clk, .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, + .set_bus_width = sdhci_set_bus_width, .card_event = sdhci_bcm_kona_card_event, }; diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index f6d8d67c545f88..7ab69af979b4dd 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -133,6 +133,7 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { .read_b = bcm2835_sdhci_readb, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_min_clock = bcm2835_sdhci_get_min_clock, + .set_bus_width = sdhci_set_bus_width, }; static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index f2cc26633cb244..5e0cc9c478874e 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -82,6 +82,7 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) static const struct sdhci_ops sdhci_cns3xxx_ops = { .get_max_clock = sdhci_cns3xxx_get_max_clk, .set_clock = sdhci_cns3xxx_set_clock, + .set_bus_width = sdhci_set_bus_width, }; static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 8424839660f844..7eef4890fd228c 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -86,6 +86,7 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) static const struct sdhci_ops sdhci_dove_ops = { .read_w = sdhci_dove_readw, .read_l = sdhci_dove_readl, + .set_bus_width = sdhci_set_bus_width, }; static const struct sdhci_pltfm_data sdhci_dove_pdata = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 8402ab264269aa..4cd9818673233c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -684,7 +684,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) return -ENOSYS; } -static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) { u32 ctrl; @@ -702,8 +702,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) @@ -909,7 +907,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_max_clock = esdhc_pltfm_get_max_clock, .get_min_clock = esdhc_pltfm_get_min_clock, .get_ro = esdhc_pltfm_get_ro, - .platform_bus_width = esdhc_pltfm_bus_width, + .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, }; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index f7c7cf62437d5d..9bb1dd263a45bb 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -54,6 +54,7 @@ static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) static struct sdhci_ops sdhci_arasan_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_arasan_get_timeout_clock, + .set_bus_width = sdhci_set_bus_width, }; static struct sdhci_pltfm_data sdhci_arasan_pdata = { diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0b249970b1197f..86b8326e77c30b 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -269,7 +269,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; } -static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) { u32 ctrl; @@ -289,8 +289,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, ESDHC_CTRL_BUSWIDTH_MASK, ctrl); - - return 0; } static const struct sdhci_ops sdhci_esdhc_ops = { @@ -310,7 +308,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .platform_resume = esdhc_of_resume, #endif .adma_workaround = esdhci_of_adma_workaround, - .platform_bus_width = esdhc_pltfm_bus_width, + .set_bus_width = esdhc_pltfm_set_bus_width, }; static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 57c514a81ca558..4d5d0015e392f7 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -58,6 +58,7 @@ static const struct sdhci_ops sdhci_hlwd_ops = { .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, + .set_bus_width = sdhci_set_bus_width, }; static const struct sdhci_pltfm_data sdhci_hlwd_pdata = { diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 19bfa0ad70c4de..bf2aa35e9d0894 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1023,7 +1023,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) return 0; } -static int sdhci_pci_bus_width(struct sdhci_host *host, int width) +static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -1044,8 +1044,6 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width) } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) @@ -1073,7 +1071,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) static const struct sdhci_ops sdhci_pci_ops = { .enable_dma = sdhci_pci_enable_dma, - .platform_bus_width = sdhci_pci_bus_width, + .set_bus_width = sdhci_pci_set_bus_width, .hw_reset = sdhci_pci_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index bef250e954188c..40b0fe224f9d40 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -45,6 +45,7 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock); static const struct sdhci_ops sdhci_pltfm_ops = { + .set_bus_width = sdhci_set_bus_width, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index d51e061ec576a6..d24c282e5eb8a4 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -88,7 +88,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) } } -static int pxav2_mmc_set_width(struct sdhci_host *host, int width) +static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; u16 tmp; @@ -107,14 +107,12 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width) } writew(tmp, host->ioaddr + SD_CE_ATA_2); writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - - return 0; } static const struct sdhci_ops pxav2_sdhci_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .platform_reset_exit = pxav2_set_private_registers, - .platform_bus_width = pxav2_mmc_set_width, + .set_bus_width = pxav2_mmc_set_bus_width, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 561c6b4907a1d3..6867349b716302 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -172,6 +172,7 @@ static const struct sdhci_ops pxav3_sdhci_ops = { .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_bus_width = sdhci_set_bus_width, }; static struct sdhci_pltfm_data sdhci_pxav3_pdata = { diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 6debda9521556c..1c4ebd69d4e5e6 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -330,14 +330,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) } /** - * sdhci_s3c_platform_bus_width - support 8bit buswidth + * sdhci_s3c_set_bus_width - support 8bit buswidth * @host: The SDHCI host being queried * @width: MMC_BUS_WIDTH_ macro for the bus width being requested * * We have 8-bit width support but is not a v3 controller. * So we add platform_bus_width() and support 8bit width. */ -static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) +static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -359,15 +359,13 @@ static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width) } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - - return 0; } static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, .set_clock = sdhci_s3c_set_clock, .get_min_clock = sdhci_s3c_get_min_clock, - .platform_bus_width = sdhci_s3c_platform_bus_width, + .set_bus_width = sdhci_s3c_set_bus_width, }; static void sdhci_s3c_notify_change(struct platform_device *dev, int state) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 696122c1b468b3..16fcd48f9556cd 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -29,6 +29,7 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) static struct sdhci_ops sdhci_sirf_ops = { .get_max_clock = sdhci_sirf_get_max_clk, + .set_bus_width = sdhci_set_bus_width, }; static struct sdhci_pltfm_data sdhci_sirf_pdata = { diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 2dba9f8d176031..549e9e0edb27ea 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -37,7 +37,7 @@ struct spear_sdhci { /* sdhci ops */ static const struct sdhci_ops sdhci_pltfm_ops = { - /* Nothing to do for now. */ + .set_bus_width = sdhci_set_bus_width, }; /* gpio card detection interrupt handler */ diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index a835898a68dd3b..feed799b827a8c 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -127,7 +127,7 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) } } -static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) +static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) { u32 ctrl; @@ -144,7 +144,6 @@ static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width) ctrl &= ~SDHCI_CTRL_4BITBUS; } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - return 0; } static const struct sdhci_ops tegra_sdhci_ops = { @@ -152,7 +151,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .read_l = tegra_sdhci_readl, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, - .platform_bus_width = tegra_sdhci_buswidth, + .set_bus_width = tegra_sdhci_set_bus_width, .platform_reset_exit = tegra_sdhci_reset_exit, }; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 972675e13db54a..5c1bb33a1451f4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1416,6 +1416,27 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->lock, flags); } +void sdhci_set_bus_width(struct sdhci_host *host, int width) +{ + u8 ctrl; + + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + if (width == MMC_BUS_WIDTH_8) { + ctrl &= ~SDHCI_CTRL_4BITBUS; + if (host->version >= SDHCI_SPEC_300) + ctrl |= SDHCI_CTRL_8BITBUS; + } else { + if (host->version >= SDHCI_SPEC_300) + ctrl &= ~SDHCI_CTRL_8BITBUS; + if (width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + else + ctrl &= ~SDHCI_CTRL_4BITBUS; + } + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_set_bus_width); + static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; @@ -1461,29 +1482,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); - /* - * If your platform has 8-bit width support but is not a v3 controller, - * or if it requires special setup code, you should implement that in - * platform_bus_width(). - */ - if (host->ops->platform_bus_width) { - host->ops->platform_bus_width(host, ios->bus_width); - } else { - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); - if (ios->bus_width == MMC_BUS_WIDTH_8) { - ctrl &= ~SDHCI_CTRL_4BITBUS; - if (host->version >= SDHCI_SPEC_300) - ctrl |= SDHCI_CTRL_8BITBUS; - } else { - if (host->version >= SDHCI_SPEC_300) - ctrl &= ~SDHCI_CTRL_8BITBUS; - if (ios->bus_width == MMC_BUS_WIDTH_4) - ctrl |= SDHCI_CTRL_4BITBUS; - else - ctrl &= ~SDHCI_CTRL_4BITBUS; - } - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - } + host->ops->set_bus_width(host, ios->bus_width); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 03cb2504eba098..14d53a17876bcc 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -282,8 +282,7 @@ struct sdhci_ops { unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host); unsigned int (*get_max_timeout_counter)(struct sdhci_host *host); - int (*platform_bus_width)(struct sdhci_host *host, - int width); + void (*set_bus_width)(struct sdhci_host *host, int width); void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); unsigned int (*get_ro)(struct sdhci_host *host); @@ -403,6 +402,8 @@ static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); } +void sdhci_set_bus_width(struct sdhci_host *host, int width); + #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); extern int sdhci_resume_host(struct sdhci_host *host); From fcf313ad85ea071ac78ab933e9059588404db3fd Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:46 +0200 Subject: [PATCH 0017/1983] mmc: sdhci: convert reset into a library function Upstream-commit: 03231f9b781f24205c0af0398ce3cbef70090939 Rather than having platform_reset_enter/platform_reset_exit methods, turn the core of the reset handling into a library function which platforms can call at the appropriate moment in their (new) reset method. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 2 ++ drivers/mmc/host/sdhci-bcm-kona.c | 1 + drivers/mmc/host/sdhci-bcm2835.c | 1 + drivers/mmc/host/sdhci-cns3xxx.c | 1 + drivers/mmc/host/sdhci-dove.c | 1 + drivers/mmc/host/sdhci-esdhc-imx.c | 1 + drivers/mmc/host/sdhci-of-arasan.c | 1 + drivers/mmc/host/sdhci-of-esdhc.c | 1 + drivers/mmc/host/sdhci-of-hlwd.c | 1 + drivers/mmc/host/sdhci-pci.c | 1 + drivers/mmc/host/sdhci-pltfm.c | 1 + drivers/mmc/host/sdhci-pxav2.c | 6 ++-- drivers/mmc/host/sdhci-pxav3.c | 6 ++-- drivers/mmc/host/sdhci-s3c.c | 1 + drivers/mmc/host/sdhci-sirf.c | 1 + drivers/mmc/host/sdhci-spear.c | 1 + drivers/mmc/host/sdhci-tegra.c | 6 ++-- drivers/mmc/host/sdhci.c | 47 +++++++++++++++--------------- drivers/mmc/host/sdhci.h | 4 +-- 19 files changed, 53 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index b138bfdd384310..1ded6690be4328 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -103,11 +103,13 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) static const struct sdhci_ops sdhci_acpi_ops_dflt = { .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static const struct sdhci_ops sdhci_acpi_ops_int = { .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, .hw_reset = sdhci_acpi_int_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 84b0ead3da8610..7a200dfa98e5d8 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -209,6 +209,7 @@ static struct sdhci_ops sdhci_bcm_kona_ops = { .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, .card_event = sdhci_bcm_kona_card_event, }; diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 7ab69af979b4dd..289b1c80d5fc43 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -134,6 +134,7 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_min_clock = bcm2835_sdhci_get_min_clock, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 5e0cc9c478874e..87af66bb1ea89f 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -83,6 +83,7 @@ static const struct sdhci_ops sdhci_cns3xxx_ops = { .get_max_clock = sdhci_cns3xxx_get_max_clk, .set_clock = sdhci_cns3xxx_set_clock, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 7eef4890fd228c..4c0191bff85d32 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -87,6 +87,7 @@ static const struct sdhci_ops sdhci_dove_ops = { .read_w = sdhci_dove_readw, .read_l = sdhci_dove_readl, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static const struct sdhci_pltfm_data sdhci_dove_pdata = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 4cd9818673233c..d3f27368665edc 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -909,6 +909,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_ro = esdhc_pltfm_get_ro, .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, + .reset = sdhci_reset, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 9bb1dd263a45bb..faef2174058465 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -55,6 +55,7 @@ static struct sdhci_ops sdhci_arasan_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_arasan_get_timeout_clock, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static struct sdhci_pltfm_data sdhci_arasan_pdata = { diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 86b8326e77c30b..4530f9957f202d 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -309,6 +309,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { #endif .adma_workaround = esdhci_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, + .reset = sdhci_reset, }; static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 4d5d0015e392f7..fb01958cb18e61 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -59,6 +59,7 @@ static const struct sdhci_ops sdhci_hlwd_ops = { .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static const struct sdhci_pltfm_data sdhci_hlwd_pdata = { diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index bf2aa35e9d0894..b26da197775e68 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1072,6 +1072,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) static const struct sdhci_ops sdhci_pci_ops = { .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_pci_set_bus_width, + .reset = sdhci_reset, .hw_reset = sdhci_pci_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 40b0fe224f9d40..bfbf467b61c7d7 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -46,6 +46,7 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock); static const struct sdhci_ops sdhci_pltfm_ops = { .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index d24c282e5eb8a4..2eee0c8b88ebcf 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -51,11 +51,13 @@ #define MMC_CARD 0x1000 #define MMC_WIDTH 0x0100 -static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask) +static void pxav2_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + sdhci_reset(host, mask); + if (mask == SDHCI_RESET_ALL) { u16 tmp = 0; @@ -111,8 +113,8 @@ static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) static const struct sdhci_ops pxav2_sdhci_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, - .platform_reset_exit = pxav2_set_private_registers, .set_bus_width = pxav2_mmc_set_bus_width, + .reset = pxav2_reset, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 6867349b716302..ddc894029c683d 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -57,11 +57,13 @@ #define SDCE_MISC_INT (1<<2) #define SDCE_MISC_INT_EN (1<<1) -static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask) +static void pxav3_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + sdhci_reset(host, mask); + if (mask == SDHCI_RESET_ALL) { /* * tune timing of read data/command when crc error happen @@ -168,11 +170,11 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) } static const struct sdhci_ops pxav3_sdhci_ops = { - .platform_reset_exit = pxav3_set_private_registers, .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = sdhci_set_bus_width, + .reset = pxav3_reset, }; static struct sdhci_pltfm_data sdhci_pxav3_pdata = { diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 1c4ebd69d4e5e6..619047be00c936 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -366,6 +366,7 @@ static struct sdhci_ops sdhci_s3c_ops = { .set_clock = sdhci_s3c_set_clock, .get_min_clock = sdhci_s3c_get_min_clock, .set_bus_width = sdhci_s3c_set_bus_width, + .reset = sdhci_reset, }; static void sdhci_s3c_notify_change(struct platform_device *dev, int state) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 16fcd48f9556cd..5d79e10e1ba2c7 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -30,6 +30,7 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) static struct sdhci_ops sdhci_sirf_ops = { .get_max_clock = sdhci_sirf_get_max_clk, .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; static struct sdhci_pltfm_data sdhci_sirf_pdata = { diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 549e9e0edb27ea..ef990974a52243 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -38,6 +38,7 @@ struct spear_sdhci { /* sdhci ops */ static const struct sdhci_ops sdhci_pltfm_ops = { .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, }; /* gpio card detection interrupt handler */ diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index feed799b827a8c..7754c0319fdaf3 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -108,12 +108,14 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) return mmc_gpio_get_ro(host->mmc); } -static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) +static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = pltfm_host->priv; const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + sdhci_reset(host, mask); + if (!(mask & SDHCI_RESET_ALL)) return; @@ -152,7 +154,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, .set_bus_width = tegra_sdhci_set_bus_width, - .platform_reset_exit = tegra_sdhci_reset_exit, + .reset = tegra_sdhci_reset, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5c1bb33a1451f4..b274bbf1da89ae 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -167,17 +167,9 @@ static void sdhci_disable_card_detection(struct sdhci_host *host) sdhci_set_card_detection(host, false); } -static void sdhci_reset(struct sdhci_host *host, u8 mask) +void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; - if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT)) - return; - } - - if (host->ops->platform_reset_enter) - host->ops->platform_reset_enter(host, mask); sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); @@ -202,9 +194,18 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) timeout--; mdelay(1); } +} +EXPORT_SYMBOL_GPL(sdhci_reset); + +static void sdhci_do_reset(struct sdhci_host *host, u8 mask) +{ + if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { + if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT)) + return; + } - if (host->ops->platform_reset_exit) - host->ops->platform_reset_exit(host, mask); + host->ops->reset(host, mask); if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) { sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); @@ -222,9 +223,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); static void sdhci_init(struct sdhci_host *host, int soft) { if (soft) - sdhci_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); else - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | @@ -964,8 +965,8 @@ static void sdhci_finish_data(struct sdhci_host *host) * upon error conditions. */ if (data->error) { - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); } sdhci_send_command(host, data->stop); @@ -1588,7 +1589,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) * it on each ios seems to solve the problem. */ if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) - sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); spin_unlock_irqrestore(&host->lock, flags); @@ -2159,8 +2160,8 @@ static void sdhci_card_event(struct mmc_host *mmc) pr_err("%s: Resetting controller.\n", mmc_hostname(host->mmc)); - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); @@ -2230,8 +2231,8 @@ static void sdhci_tasklet_finish(unsigned long param) /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); + sdhci_do_reset(host, SDHCI_RESET_CMD); + sdhci_do_reset(host, SDHCI_RESET_DATA); } host->mrq = NULL; @@ -2870,7 +2871,7 @@ int sdhci_add_host(struct sdhci_host *host) if (debug_quirks2) host->quirks2 = debug_quirks2; - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); host->version = sdhci_readw(host, SDHCI_HOST_VERSION); host->version = (host->version & SDHCI_SPEC_VER_MASK) @@ -3366,7 +3367,7 @@ int sdhci_add_host(struct sdhci_host *host) #ifdef SDHCI_USE_LEDS_CLASS reset: - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); sdhci_writel(host, 0, SDHCI_INT_ENABLE); sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); @@ -3408,7 +3409,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) #endif if (!dead) - sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_do_reset(host, SDHCI_RESET_ALL); sdhci_writel(host, 0, SDHCI_INT_ENABLE); sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 14d53a17876bcc..086cf6def7d074 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -286,8 +286,7 @@ struct sdhci_ops { void (*platform_send_init_74_clocks)(struct sdhci_host *host, u8 power_mode); unsigned int (*get_ro)(struct sdhci_host *host); - void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); - void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); + void (*reset)(struct sdhci_host *host, u8 mask); int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); @@ -403,6 +402,7 @@ static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) } void sdhci_set_bus_width(struct sdhci_host *host, int width); +void sdhci_reset(struct sdhci_host *host, u8 mask); #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); From 1c4062cee567f93a1080d48b0f2a85eff62f216b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:46 +0200 Subject: [PATCH 0018/1983] mmc: sdhci: move FSL ESDHC reset handling quirk into esdhc code Upstream-commit: 0718e59ae259f7c48155b4e852d8b0632d59028e The Freescale esdhc driver is the only driver which needs the interrupt registers restored after a reset. Move this quirk to be part of the ESDHC driver implementation. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 10 +++++++++- drivers/mmc/host/sdhci-esdhc.h | 3 +-- drivers/mmc/host/sdhci.c | 5 ----- include/linux/mmc/sdhci.h | 2 -- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d3f27368665edc..d732b02598ef1f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -897,6 +897,14 @@ static unsigned int esdhc_get_max_timeout_counter(struct sdhci_host *host) return 1 << 28; } +static void esdhc_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, @@ -909,7 +917,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_ro = esdhc_pltfm_get_ro, .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, - .reset = sdhci_reset, + .reset = esdhc_reset, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a7d9f95a7b03dd..de69bddc3afc55 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -22,8 +22,7 @@ SDHCI_QUIRK_NO_BUSY_IRQ | \ SDHCI_QUIRK_NONSTANDARD_CLOCK | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ - SDHCI_QUIRK_PIO_NEEDS_DELAY | \ - SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) + SDHCI_QUIRK_PIO_NEEDS_DELAY) #define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_CLOCK_MASK 0x0000fff0 diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b274bbf1da89ae..b54423ff73c93c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -207,11 +207,6 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) host->ops->reset(host, mask); - if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) { - sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); - sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); - } - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) host->ops->enable_dma(host); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 248841b0ead1e6..c69e34a9f94398 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -66,8 +66,6 @@ struct sdhci_host { #define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) -/* Controller losing signal/interrupt enable states after reset */ -#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) /* Controller has to be forced to use block size of 2048 bytes */ #define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) /* Controller cannot do multi-block transfers */ From 27698dfe045d40a0c424fd242c0e00bb2e149c46 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:46 +0200 Subject: [PATCH 0019/1983] mmc: sdhci: avoid sync'ing the SG if there's no misalignment Upstream-commit: de0b65a786ae83c8f6dfb712f65b9a36af70a981 On read, we don't need to sync the whole scatterlist and then check whether any segments need copying - if we check first, we avoid potentially expensive cache handling. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b54423ff73c93c..ae1d1e8d9e01b7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -605,6 +605,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host, u8 *align; char *buffer; unsigned long flags; + bool has_unaligned; if (data->flags & MMC_DATA_READ) direction = DMA_FROM_DEVICE; @@ -617,7 +618,15 @@ static void sdhci_adma_table_post(struct sdhci_host *host, dma_unmap_single(mmc_dev(host->mmc), host->align_addr, 128 * 4, direction); - if (data->flags & MMC_DATA_READ) { + /* Do a quick scan of the SG list for any unaligned mappings */ + has_unaligned = false; + for_each_sg(data->sg, sg, host->sg_count, i) + if (sg_dma_address(sg) & 3) { + has_unaligned = true; + break; + } + + if (has_unaligned && data->flags & MMC_DATA_READ) { dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, data->sg_len, direction); From c7d91e14bb1c49e08a3a353837d211f654a451c5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:46 +0200 Subject: [PATCH 0020/1983] mmc: sdhci: convert ADMA descriptors to a coherent allocation Upstream-commit: d1e49f77d7c7b75fdc022e1d46c1549bbc91c5b7 Rather than using the streaming API, use the coherent allocator to provide this memory, thereby eliminating cache flushing of it each time we map and unmap it. This results in a 7.5% increase in transfer speed with a UHS-1 card operating in 3.3v mode at a clock of 49.5MHz. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 43 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ae1d1e8d9e01b7..5e010817b4e928 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -44,6 +44,8 @@ #define MAX_TUNING_LOOP 40 +#define ADMA_SIZE ((128 * 2 + 1) * 4) + static unsigned int debug_quirks = 0; static unsigned int debug_quirks2; @@ -485,11 +487,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, else direction = DMA_TO_DEVICE; - /* - * The ADMA descriptor table is mapped further down as we - * need to fill it with data first. - */ - host->align_addr = dma_map_single(mmc_dev(host->mmc), host->align_buffer, 128 * 4, direction); if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr)) @@ -549,7 +546,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, * If this triggers then we have a calculation bug * somewhere. :/ */ - WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4); + WARN_ON((desc - host->adma_desc) > ADMA_SIZE); } if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) { @@ -577,17 +574,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, host->align_addr, 128 * 4, direction); } - host->adma_addr = dma_map_single(mmc_dev(host->mmc), - host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); - if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr)) - goto unmap_entries; - BUG_ON(host->adma_addr & 0x3); - return 0; -unmap_entries: - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, direction); unmap_align: dma_unmap_single(mmc_dev(host->mmc), host->align_addr, 128 * 4, direction); @@ -612,9 +600,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host, else direction = DMA_TO_DEVICE; - dma_unmap_single(mmc_dev(host->mmc), host->adma_addr, - (128 * 2 + 1) * 4, DMA_TO_DEVICE); - dma_unmap_single(mmc_dev(host->mmc), host->align_addr, 128 * 4, direction); @@ -2935,15 +2920,29 @@ int sdhci_add_host(struct sdhci_host *host) * (128) and potentially one alignment transfer for * each of those entries. */ - host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL); + host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc), + ADMA_SIZE, &host->adma_addr, + GFP_KERNEL); host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); if (!host->adma_desc || !host->align_buffer) { - kfree(host->adma_desc); + dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + host->adma_desc, host->adma_addr); kfree(host->align_buffer); pr_warning("%s: Unable to allocate ADMA " "buffers. Falling back to standard DMA.\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; + host->adma_desc = NULL; + host->align_buffer = NULL; + } else if (host->adma_addr & 3) { + pr_warning("%s: unable to allocate aligned ADMA descriptor\n", + mmc_hostname(mmc)); + host->flags &= ~SDHCI_USE_ADMA; + dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + host->adma_desc, host->adma_addr); + kfree(host->align_buffer); + host->adma_desc = NULL; + host->align_buffer = NULL; } } @@ -3435,7 +3434,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) regulator_put(host->vqmmc); } - kfree(host->adma_desc); + if (host->adma_desc) + dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + host->adma_desc, host->adma_addr); kfree(host->align_buffer); host->adma_desc = NULL; From 216100769723c83dcf39b51cbbcd84dd5c3a6a8b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:46 +0200 Subject: [PATCH 0021/1983] mmc: sdhci: clean up sdhci_update_clock()/sdhci_set_clock() Upstream-commit: 91138ca51d8cdbefceb1ed1cd2dbfdd763af6cdf Only one caller to sdhci_set_clock() needs to check whether the requested clock frequency was the same as the currently set frequency, yet we work around this in several other sites via sdhci_update_clock(). Rather than doing this, move those checks out into sdhci_do_set_ios(), which then allows sdhci_update_clock() to be eliminated. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-cns3xxx.c | 3 --- drivers/mmc/host/sdhci.c | 21 +++++---------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 87af66bb1ea89f..fa11f63b78a065 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -30,9 +30,6 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk; unsigned long timeout; - if (clock == host->clock) - return; - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5e010817b4e928..a47456084ba515 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1121,9 +1121,6 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk = 0; unsigned long timeout; - if (clock && clock == host->clock) - return; - host->mmc->actual_clock = 0; if (host->ops->set_clock) { @@ -1230,15 +1227,6 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) host->clock = clock; } -static inline void sdhci_update_clock(struct sdhci_host *host) -{ - unsigned int clock; - - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); -} - static int sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; @@ -1456,7 +1444,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); - sdhci_set_clock(host, ios->clock); + if (!ios->clock || ios->clock != host->clock) + sdhci_set_clock(host, ios->clock); if (ios->power_mode == MMC_POWER_OFF) vdd_bit = sdhci_set_power(host, -1); @@ -1524,7 +1513,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ - sdhci_update_clock(host); + sdhci_set_clock(host, host->clock); } @@ -1568,7 +1557,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - sdhci_update_clock(host); + sdhci_set_clock(host, host->clock); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -2216,7 +2205,7 @@ static void sdhci_tasklet_finish(unsigned long param) /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ - sdhci_update_clock(host); + sdhci_set_clock(host, host->clock); /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ From cc5b4846f3f7a9e9da317fab8f85ad8e1116b082 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:47 +0200 Subject: [PATCH 0022/1983] mmc: sdhci: move setting host->clock into sdhci_do_set_ios() Upstream-commit: 373073efd06528867df963724a93f29c7d5534d0 We don't need implementations to do this, since the only time it's necessary is when we change the clock, and the only place that happens is in sdhci_do_set_ios(). So, move it there, and remove it from the iMX platform backend. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-cns3xxx.c | 4 +--- drivers/mmc/host/sdhci-esdhc-imx.c | 4 +--- drivers/mmc/host/sdhci-of-esdhc.c | 4 +--- drivers/mmc/host/sdhci-s3c.c | 3 --- drivers/mmc/host/sdhci.c | 9 ++++----- 5 files changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index fa11f63b78a065..f11fb908ad7868 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -33,7 +33,7 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) - goto out; + return; while (host->max_clk / div > clock) { /* @@ -72,8 +72,6 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); -out: - host->clock = clock; } static const struct sdhci_ops sdhci_cns3xxx_ops = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d732b02598ef1f..456c01d652c746 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -621,7 +621,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, host->ioaddr + ESDHC_VENDOR_SPEC); } - goto out; + return; } if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) @@ -661,8 +661,6 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, } mdelay(1); -out: - host->clock = clock; } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 4530f9957f202d..d814b3ecb1f710 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -205,7 +205,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) u32 temp; if (clock == 0) - goto out; + return; /* Workaround to reduce the clock frequency for p1010 esdhc */ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { @@ -238,8 +238,6 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); mdelay(1); -out: - host->clock = clock; } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 619047be00c936..e7865d1da1f24b 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -299,7 +299,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) /* If the clock is going off, set to 0 at clock control register */ if (clock == 0) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); - host->clock = clock; return; } @@ -307,8 +306,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); - host->clock = clock; - clk = SDHCI_CLOCK_INT_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a47456084ba515..caf67612625724 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1132,7 +1132,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) - goto out; + return; if (host->version >= SDHCI_SPEC_300) { if (sdhci_readw(host, SDHCI_HOST_CONTROL2) & @@ -1222,9 +1222,6 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - -out: - host->clock = clock; } static int sdhci_set_power(struct sdhci_host *host, unsigned short power) @@ -1444,8 +1441,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); - if (!ios->clock || ios->clock != host->clock) + if (!ios->clock || ios->clock != host->clock) { sdhci_set_clock(host, ios->clock); + host->clock = ios->clock; + } if (ios->power_mode == MMC_POWER_OFF) vdd_bit = sdhci_set_power(host, -1); From 16ac44900dbba351b1a7f91fcb4367990a617035 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:53:47 +0200 Subject: [PATCH 0023/1983] mmc: sdhci: move setting mmc->actual_clock into set_clock handlers Upstream-commit: 1650d0c71a209c7d6bdddda8a7e187c537ceb71a Move the setting of mmc->actual_clock to zero into the set_clock handlers themselves. This will allow us to clean up the calling logic for the set_clock() method, and turn sdhci_set_clock() into a library function. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-cns3xxx.c | 2 ++ drivers/mmc/host/sdhci-esdhc-imx.c | 2 ++ drivers/mmc/host/sdhci-of-esdhc.c | 3 ++- drivers/mmc/host/sdhci-s3c.c | 4 ++++ drivers/mmc/host/sdhci.c | 4 ++-- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index f11fb908ad7868..416f4a4c2e3551 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -30,6 +30,8 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk; unsigned long timeout; + host->mmc->actual_clock = 0; + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 456c01d652c746..c8e8de3bdfaf49 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -616,6 +616,8 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, u32 temp, val; if (clock == 0) { + host->mmc->actual_clock = 0; + if (esdhc_is_usdhc(imx_data)) { val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index d814b3ecb1f710..c4f8cd3f83c839 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -199,11 +199,12 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { - int pre_div = 2; int div = 1; u32 temp; + host->mmc->actual_clock = 0; + if (clock == 0) return; diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index e7865d1da1f24b..18579555a5f798 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -188,6 +188,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) int src; u32 ctrl; + host->mmc->actual_clock = 0; + /* don't bother if the clock is going off. */ if (clock == 0) return; @@ -296,6 +298,8 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) unsigned long timeout; u16 clk = 0; + host->mmc->actual_clock = 0; + /* If the clock is going off, set to 0 at clock control register */ if (clock == 0) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index caf67612625724..3ef2447e149dc0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1121,14 +1121,14 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk = 0; unsigned long timeout; - host->mmc->actual_clock = 0; - if (host->ops->set_clock) { host->ops->set_clock(host, clock); if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) return; } + host->mmc->actual_clock = 0; + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) From da0dc8aad628df8838429cce89a786184e7d4aca Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:38 +0200 Subject: [PATCH 0024/1983] mmc: sdhci: convert sdhci_set_clock() into a library function Upstream-commit: 1771059cf5f9c09e37ef6315df8acf120f2642fc Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 2 ++ drivers/mmc/host/sdhci-bcm-kona.c | 1 + drivers/mmc/host/sdhci-bcm2835.c | 1 + drivers/mmc/host/sdhci-cns3xxx.c | 3 +-- drivers/mmc/host/sdhci-dove.c | 1 + drivers/mmc/host/sdhci-esdhc.h | 1 - drivers/mmc/host/sdhci-of-arasan.c | 1 + drivers/mmc/host/sdhci-of-hlwd.c | 1 + drivers/mmc/host/sdhci-pci.c | 1 + drivers/mmc/host/sdhci-pltfm.c | 1 + drivers/mmc/host/sdhci-pxav2.c | 1 + drivers/mmc/host/sdhci-pxav3.c | 1 + drivers/mmc/host/sdhci-s3c.c | 19 ++++++++++++++----- drivers/mmc/host/sdhci-sirf.c | 1 + drivers/mmc/host/sdhci-spear.c | 1 + drivers/mmc/host/sdhci-tegra.c | 1 + drivers/mmc/host/sdhci.c | 17 ++++++----------- drivers/mmc/host/sdhci.h | 1 + include/linux/mmc/sdhci.h | 2 -- 19 files changed, 36 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 1ded6690be4328..c20964e577e991 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -101,12 +101,14 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) } static const struct sdhci_ops sdhci_acpi_ops_dflt = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; static const struct sdhci_ops sdhci_acpi_ops_int = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 7a200dfa98e5d8..0a285161746154 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -205,6 +205,7 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, } static struct sdhci_ops sdhci_bcm_kona_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_bcm_kona_get_max_clk, .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 289b1c80d5fc43..74906d6008e121 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -131,6 +131,7 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { .read_l = bcm2835_sdhci_readl, .read_w = bcm2835_sdhci_readw, .read_b = bcm2835_sdhci_readb, + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_min_clock = bcm2835_sdhci_get_min_clock, .set_bus_width = sdhci_set_bus_width, diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 416f4a4c2e3551..587d73ef33ff0f 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -89,8 +89,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_INVERTED_WRITE_PROTECT | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_NONSTANDARD_CLOCK, + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; static int sdhci_cns3xxx_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 4c0191bff85d32..4f4216115aed4c 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -86,6 +86,7 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) static const struct sdhci_ops sdhci_dove_ops = { .read_w = sdhci_dove_readw, .read_l = sdhci_dove_readl, + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index de69bddc3afc55..3497cfaf683c53 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -20,7 +20,6 @@ #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ SDHCI_QUIRK_NO_BUSY_IRQ | \ - SDHCI_QUIRK_NONSTANDARD_CLOCK | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ SDHCI_QUIRK_PIO_NEEDS_DELAY) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index faef2174058465..f0ee594f25d1ba 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -52,6 +52,7 @@ static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) } static struct sdhci_ops sdhci_arasan_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_timeout_clock = sdhci_arasan_get_timeout_clock, .set_bus_width = sdhci_set_bus_width, diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index fb01958cb18e61..a4a1f0f2c0a084 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -58,6 +58,7 @@ static const struct sdhci_ops sdhci_hlwd_ops = { .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index b26da197775e68..4e19793b7230c4 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1070,6 +1070,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) } static const struct sdhci_ops sdhci_pci_ops = { + .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_pci_set_bus_width, .reset = sdhci_reset, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index bfbf467b61c7d7..1fb89f44bd5846 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -45,6 +45,7 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock); static const struct sdhci_ops sdhci_pltfm_ops = { + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 2eee0c8b88ebcf..db5257bf032e3d 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -112,6 +112,7 @@ static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) } static const struct sdhci_ops pxav2_sdhci_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = pxav2_mmc_set_bus_width, .reset = pxav2_reset, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index ddc894029c683d..501d0b6896c38e 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -170,6 +170,7 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) } static const struct sdhci_ops pxav3_sdhci_ops = { + .set_clock = sdhci_set_clock, .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 18579555a5f798..63496bd62196ff 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -57,6 +57,8 @@ struct sdhci_s3c { struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; + + bool no_divider; }; /** @@ -69,6 +71,7 @@ struct sdhci_s3c { */ struct sdhci_s3c_drv_data { unsigned int sdhci_quirks; + bool no_divider; }; static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host) @@ -153,7 +156,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost, * If controller uses a non-standard clock division, find the best clock * speed possible with selected clock source and skip the division. */ - if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (ourhost->no_divider) { rate = clk_round_rate(clksrc, wanted); return wanted - rate; } @@ -191,8 +194,10 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) host->mmc->actual_clock = 0; /* don't bother if the clock is going off. */ - if (clock == 0) + if (clock == 0) { + sdhci_set_clock(host, clock); return; + } for (src = 0; src < MAX_BUS_CLK; src++) { delta = sdhci_s3c_consider_clock(ourhost, src, clock); @@ -242,6 +247,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock) if (clock < 25 * 1000000) ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2); writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3); + + sdhci_set_clock(host, clock); } /** @@ -617,8 +624,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev) /* Setup quirks for the controller */ host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC; host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; - if (drv_data) + if (drv_data) { host->quirks |= drv_data->sdhci_quirks; + sc->no_divider = drv_data->no_divider; + } #ifndef CONFIG_MMC_SDHCI_S3C_DMA @@ -667,7 +676,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) * If controller does not have internal clock divider, * we can use overriding functions instead of default. */ - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) { + if (sc->no_divider) { sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock; sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock; sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock; @@ -813,7 +822,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = { #if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { - .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK, + .no_divider = true, }; #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data) #else diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 5d79e10e1ba2c7..3b775348b4702f 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -28,6 +28,7 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) } static struct sdhci_ops sdhci_sirf_ops = { + .set_clock = sdhci_set_clock, .get_max_clock = sdhci_sirf_get_max_clk, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index ef990974a52243..f365a3fc7612a3 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -37,6 +37,7 @@ struct spear_sdhci { /* sdhci ops */ static const struct sdhci_ops sdhci_pltfm_ops = { + .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 7754c0319fdaf3..a0a8b5cc3b0cda 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -153,6 +153,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .read_l = tegra_sdhci_readl, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, + .set_clock = sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, }; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3ef2447e149dc0..3d0350fba2be6e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1114,19 +1114,13 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) return preset; } -static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div = 0; /* Initialized for compiler warning */ int real_div = div, clk_mul = 1; u16 clk = 0; unsigned long timeout; - if (host->ops->set_clock) { - host->ops->set_clock(host, clock); - if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) - return; - } - host->mmc->actual_clock = 0; sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); @@ -1223,6 +1217,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clk |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } +EXPORT_SYMBOL_GPL(sdhci_set_clock); static int sdhci_set_power(struct sdhci_host *host, unsigned short power) { @@ -1442,7 +1437,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_enable_preset_value(host, false); if (!ios->clock || ios->clock != host->clock) { - sdhci_set_clock(host, ios->clock); + host->ops->set_clock(host, ios->clock); host->clock = ios->clock; } @@ -1512,7 +1507,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, host->clock); } @@ -1556,7 +1551,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, host->clock); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -2204,7 +2199,7 @@ static void sdhci_tasklet_finish(unsigned long param) /* Some controllers need this kick or reset won't work here */ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, host->clock); /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 086cf6def7d074..30134906997e71 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -401,6 +401,7 @@ static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED); } +void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); void sdhci_set_bus_width(struct sdhci_host *host, int width); void sdhci_reset(struct sdhci_host *host, u8 mask); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index c69e34a9f94398..c421b3ef2eb804 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -62,8 +62,6 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) /* Controller reports inverted write-protect state */ #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) -/* Controller has nonstandard clock management */ -#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) /* Controller does not like fast PIO transfers */ #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) /* Controller has to be forced to use block size of 2048 bytes */ From 7ab18afb8267075c4bf584c1c85207d3957cb72b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:38 +0200 Subject: [PATCH 0025/1983] mmc: sdhci-esdhc-imx: avoid DMA to kernel stack Upstream-commit: 9d2fc80fb15b99736ecd1895934ce4512e76fa3f sdhci-esdhc-imx tries to DMA to the kernel stack when tuning the interface, which causes dma-debug to complain. Fix this by kmallocing a buffer to hold the received tuning pattern. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c8e8de3bdfaf49..99c0b7ce8cf692 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -727,13 +727,12 @@ static void esdhc_request_done(struct mmc_request *mrq) complete(&mrq->completion); } -static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, + struct scatterlist *sg) { struct mmc_command cmd = {0}; struct mmc_request mrq = {NULL}; struct mmc_data data = {0}; - struct scatterlist sg; - char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN]; cmd.opcode = opcode; cmd.arg = 0; @@ -742,11 +741,9 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; data.blocks = 1; data.flags = MMC_DATA_READ; - data.sg = &sg; + data.sg = sg; data.sg_len = 1; - sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern)); - mrq.cmd = &cmd; mrq.cmd->mrq = &mrq; mrq.data = &data; @@ -786,13 +783,21 @@ static void esdhc_post_tuning(struct sdhci_host *host) static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) { + struct scatterlist sg; + char *tuning_pattern; int min, max, avg, ret; + tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL); + if (!tuning_pattern) + return -ENOMEM; + + sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN); + /* find the mininum delay first which can pass tuning */ min = ESDHC_TUNE_CTRL_MIN; while (min < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, min); - if (!esdhc_send_tuning_cmd(host, opcode)) + if (!esdhc_send_tuning_cmd(host, opcode, &sg)) break; min += ESDHC_TUNE_CTRL_STEP; } @@ -801,7 +806,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) max = min + ESDHC_TUNE_CTRL_STEP; while (max < ESDHC_TUNE_CTRL_MAX) { esdhc_prepare_tuning(host, max); - if (esdhc_send_tuning_cmd(host, opcode)) { + if (esdhc_send_tuning_cmd(host, opcode, &sg)) { max -= ESDHC_TUNE_CTRL_STEP; break; } @@ -811,9 +816,11 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) /* use average delay to get the best timing */ avg = (min + max) / 2; esdhc_prepare_tuning(host, avg); - ret = esdhc_send_tuning_cmd(host, opcode); + ret = esdhc_send_tuning_cmd(host, opcode, &sg); esdhc_post_tuning(host); + kfree(tuning_pattern); + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", ret ? "failed" : "passed", avg, ret); From 4a43da8a96ee0891c6ef4107c4fe11f640bebe9e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:38 +0200 Subject: [PATCH 0026/1983] mmc: sdhci-esdhc-imx: comment runtime_pm_get_sync() in esdhc_prepare_tuning() Upstream-commit: 10cf4963007ccfad335f57a598d5fd3f7c83c606 It is far from obvious what this is doing, and it looks like it's an unbalanced runtime_pm_get() call. However, the put is inside sdhci_tasklet_finish(), so it's not unbalanced at all. This should be documented so people know what's going on here. Do so. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 99c0b7ce8cf692..a85b5e52c7aa59 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -711,6 +711,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) /* FIXME: delay a bit for card to be ready for next tuning due to errors */ mdelay(1); + /* This is balanced by the runtime put in sdhci_tasklet_finish */ pm_runtime_get_sync(host->mmc->parent); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | From db2f252a4d0dc6c38fee422d5d55c8cfaa59d4a5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:39 +0200 Subject: [PATCH 0027/1983] mmc: sdhci-esdhc-imx: fix lockdep splat upon tuning Upstream-commit: cb399da401c64f527ced132179f66ac022e63d7b ================================= [ INFO: inconsistent lock state ] 3.14.0-rc1+ #490 Not tainted --------------------------------- inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. kworker/u8:0/6 [HC0[0]:SC0[0]:HE1:SE1] takes: (&(&host->lock)->rlock#2){?.-...}, at: [] esdhc_send_tuning_cmd+0x104/0x14c {IN-HARDIRQ-W} state was registered at: [] mark_lock+0x15c/0x6f8 [] __lock_acquire+0xabc/0x1ca0 [] lock_acquire+0xa0/0x130 [] _raw_spin_lock+0x34/0x44 [] sdhci_irq+0x20/0xa40 [] handle_irq_event_percpu+0x74/0x284 [] handle_irq_event+0x44/0x64 [] handle_fasteoi_irq+0xac/0x140 [] generic_handle_irq+0x28/0x38 [] handle_IRQ+0x40/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x44/0x58 [] irq_exit+0xc0/0x120 [] handle_IRQ+0x44/0x98 [] gic_handle_irq+0x30/0x64 [] __irq_svc+0x44/0x58 [] printk+0x3c/0x44 [] _regulator_get+0x1b4/0x1e0 [] regulator_get+0x18/0x1c [] mmc_add_host+0x30/0x1c0 [] sdhci_add_host+0x804/0xbbc [] sdhci_esdhc_imx_probe+0x380/0x674 [] platform_drv_probe+0x20/0x50 [] driver_probe_device+0x120/0x234 [] __driver_attach+0x9c/0xa0 [] bus_for_each_dev+0x5c/0x90 [] driver_attach+0x24/0x28 [] bus_add_driver+0xe4/0x1d8 [] driver_register+0x80/0xfc [] __platform_driver_register+0x50/0x64 [] sdhci_esdhc_imx_driver_init+0x18/0x20 [] do_one_initcall+0x3c/0x164 [] kernel_init_freeable+0x104/0x1d0 [] kernel_init+0x10/0x118 [] ret_from_fork+0x14/0x2c irq event stamp: 5933 hardirqs last enabled at (5933): [] _raw_spin_unlock_irqrestore+0x38/0x4c hardirqs last disabled at (5932): [] _raw_spin_lock_irqsave+0x24/0x60 softirqs last enabled at (5914): [] __do_softirq+0x260/0x360 softirqs last disabled at (5909): [] irq_exit+0xc0/0x120 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&host->lock)->rlock#2); lock(&(&host->lock)->rlock#2); *** DEADLOCK *** 2 locks held by kworker/u8:0/6: #0: (kmmcd){.+.+.+}, at: [] process_one_work+0x134/0x4e8 #1: ((&(&host->detect)->work)){+.+.+.}, at: [] process_one_work+0x134/0x4e8 stack backtrace: CPU: 2 PID: 6 Comm: kworker/u8:0 Not tainted 3.14.0-rc1+ #490 Workqueue: kmmcd mmc_rescan Backtrace: [] (dump_backtrace) from [] (show_stack+0x18/0x1c) [] (show_stack) from [] (dump_stack+0x70/0x8c) [] (dump_stack) from [] (print_usage_bug+0x274/0x2e4) [] (print_usage_bug) from [] (mark_lock+0x5d4/0x6f8) [] (mark_lock) from [] (__lock_acquire+0x5d4/0x1ca0) [] (__lock_acquire) from [] (lock_acquire+0xa0/0x130) [] (lock_acquire) from [] (_raw_spin_lock+0x34/0x44) [] (_raw_spin_lock) from [] (esdhc_send_tuning_cmd+0x104/0x14c) [] (esdhc_send_tuning_cmd) from [] (esdhc_executing_tuning+0x40/0x100) [] (esdhc_executing_tuning) from [] (sdhci_execute_tuning+0xcc/0x754) [] (sdhci_execute_tuning) from [] (mmc_sd_init_card+0x65c/0x694) [] (mmc_sd_init_card) from [] (mmc_attach_sd+0xb0/0x184) [] (mmc_attach_sd) from [] (mmc_rescan+0x26c/0x2e8) [] (mmc_rescan) from [] (process_one_work+0x1b8/0x4e8) [] (process_one_work) from [] (worker_thread+0x13c/0x3f8) [] (worker_thread) from [] (kthread+0xcc/0xe8) [] (kthread) from [] (ret_from_fork+0x14/0x2c) Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a85b5e52c7aa59..479f37233f2b4a 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -754,14 +754,12 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode, mrq.done = esdhc_request_done; init_completion(&(mrq.completion)); - disable_irq(host->irq); - spin_lock(&host->lock); + spin_lock_irq(&host->lock); host->mrq = &mrq; sdhci_send_command(host, mrq.cmd); - spin_unlock(&host->lock); - enable_irq(host->irq); + spin_unlock_irq(&host->lock); wait_for_completion(&mrq.completion); From cc3eb2dc0d6b34fe8401e0e40896c14fe6ce1e67 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:49 +0200 Subject: [PATCH 0028/1983] mmc: sdhci: convert sdhci_set_uhs_signaling() into a library function Upstream-commit: 96d7b78cfc2fd6b1539704e2d33239dbaa097cc4 Add sdhci_set_uhs_signaling() and always call the set_uhs_signaling method. This avoids quirks being added into sdhci_set_uhs_signaling(). Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 2 ++ drivers/mmc/host/sdhci-bcm-kona.c | 1 + drivers/mmc/host/sdhci-bcm2835.c | 1 + drivers/mmc/host/sdhci-cns3xxx.c | 1 + drivers/mmc/host/sdhci-dove.c | 1 + drivers/mmc/host/sdhci-of-arasan.c | 1 + drivers/mmc/host/sdhci-of-esdhc.c | 1 + drivers/mmc/host/sdhci-of-hlwd.c | 1 + drivers/mmc/host/sdhci-pci.c | 1 + drivers/mmc/host/sdhci-pltfm.c | 1 + drivers/mmc/host/sdhci-pxav2.c | 1 + drivers/mmc/host/sdhci-pxav3.c | 1 + drivers/mmc/host/sdhci-s3c.c | 1 + drivers/mmc/host/sdhci-sirf.c | 1 + drivers/mmc/host/sdhci-spear.c | 1 + drivers/mmc/host/sdhci-tegra.c | 1 + drivers/mmc/host/sdhci.c | 23 ++++++++++++++++++++++- drivers/mmc/host/sdhci.h | 1 + 18 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index c20964e577e991..2ece31d8819674 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -105,6 +105,7 @@ static const struct sdhci_ops sdhci_acpi_ops_dflt = { .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_ops sdhci_acpi_ops_int = { @@ -112,6 +113,7 @@ static const struct sdhci_ops sdhci_acpi_ops_int = { .enable_dma = sdhci_acpi_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_acpi_int_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 0a285161746154..e05c39e6292f76 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -211,6 +211,7 @@ static struct sdhci_ops sdhci_bcm_kona_ops = { .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, .card_event = sdhci_bcm_kona_card_event, }; diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 74906d6008e121..46af9a439d7b48 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -136,6 +136,7 @@ static const struct sdhci_ops bcm2835_sdhci_ops = { .get_min_clock = bcm2835_sdhci_get_min_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = { diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 587d73ef33ff0f..14b74075589afd 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -81,6 +81,7 @@ static const struct sdhci_ops sdhci_cns3xxx_ops = { .set_clock = sdhci_cns3xxx_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 4f4216115aed4c..8d6d0a293f1709 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -89,6 +89,7 @@ static const struct sdhci_ops sdhci_dove_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_dove_pdata = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index f0ee594f25d1ba..5bd1092310f2e6 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -57,6 +57,7 @@ static struct sdhci_ops sdhci_arasan_ops = { .get_timeout_clock = sdhci_arasan_get_timeout_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_arasan_pdata = { diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index c4f8cd3f83c839..fcaeae5f55b8ff 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -309,6 +309,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .adma_workaround = esdhci_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index a4a1f0f2c0a084..b341661369a20a 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -61,6 +61,7 @@ static const struct sdhci_ops sdhci_hlwd_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_hlwd_pdata = { diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 4e19793b7230c4..19de28bb8a156e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1074,6 +1074,7 @@ static const struct sdhci_ops sdhci_pci_ops = { .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_pci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, }; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 1fb89f44bd5846..7e834fb78f427d 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -48,6 +48,7 @@ static const struct sdhci_ops sdhci_pltfm_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index db5257bf032e3d..3c0f3c0a1cc869 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -116,6 +116,7 @@ static const struct sdhci_ops pxav2_sdhci_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = pxav2_mmc_set_bus_width, .reset = pxav2_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; #ifdef CONFIG_OF diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 501d0b6896c38e..08fb95d2fc5290 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -176,6 +176,7 @@ static const struct sdhci_ops pxav3_sdhci_ops = { .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = sdhci_set_bus_width, .reset = pxav3_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_pxav3_pdata = { diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 63496bd62196ff..a633418ddcfacc 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -375,6 +375,7 @@ static struct sdhci_ops sdhci_s3c_ops = { .get_min_clock = sdhci_s3c_get_min_clock, .set_bus_width = sdhci_s3c_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static void sdhci_s3c_notify_change(struct platform_device *dev, int state) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 3b775348b4702f..17004531d089e6 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -32,6 +32,7 @@ static struct sdhci_ops sdhci_sirf_ops = { .get_max_clock = sdhci_sirf_get_max_clk, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static struct sdhci_pltfm_data sdhci_sirf_pdata = { diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index f365a3fc7612a3..94ae64d23ef369 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -40,6 +40,7 @@ static const struct sdhci_ops sdhci_pltfm_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; /* gpio card detection interrupt handler */ diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index a0a8b5cc3b0cda..d06b6ff6043219 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -156,6 +156,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .set_clock = sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, .reset = tegra_sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3d0350fba2be6e..4f81b3b9fdf390 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1407,6 +1407,28 @@ void sdhci_set_bus_width(struct sdhci_host *host, int width) } EXPORT_SYMBOL_GPL(sdhci_set_bus_width); +void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) +{ + u16 ctrl_2; + + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* Select Bus Speed Mode for host */ + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; + if ((timing == MMC_TIMING_MMC_HS200) || + (timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; + else if (timing == MMC_TIMING_UHS_SDR12) + ctrl_2 |= SDHCI_CTRL_UHS_SDR12; + else if (timing == MMC_TIMING_UHS_SDR25) + ctrl_2 |= SDHCI_CTRL_UHS_SDR25; + else if (timing == MMC_TIMING_UHS_SDR50) + ctrl_2 |= SDHCI_CTRL_UHS_SDR50; + else if (timing == MMC_TIMING_UHS_DDR50) + ctrl_2 |= SDHCI_CTRL_UHS_DDR50; + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); +} +EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); + static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; @@ -1510,7 +1532,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) host->ops->set_clock(host, host->clock); } - /* Reset SD Clock Enable */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk &= ~SDHCI_CLOCK_CARD_EN; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 30134906997e71..c6c406fb1ca7a7 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -404,6 +404,7 @@ static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host) void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); void sdhci_set_bus_width(struct sdhci_host *host, int width); void sdhci_reset(struct sdhci_host *host, u8 mask); +void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); From d74fdee1b9c57bf11a053f7020e4dea8ca27e202 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:50 +0200 Subject: [PATCH 0029/1983] mmc: sdhci: cache timing information locally Upstream-commit: d975f121011a58223c7936ab483c3374a83236c3 Rather than reading back the timing information from the registers, cache it locally. This allows implementations to translate the UHS timing by overriding the set_uhs_signaling() method as required without also having to emulate the SDHCI_HOST_CONTROL2 register. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 43 ++++++++++++--------------------------- include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4f81b3b9fdf390..4c43ea7176bbe4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1085,24 +1085,23 @@ static void sdhci_finish_command(struct sdhci_host *host) static u16 sdhci_get_preset_value(struct sdhci_host *host) { - u16 ctrl, preset = 0; + u16 preset = 0; - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - - switch (ctrl & SDHCI_CTRL_UHS_MASK) { - case SDHCI_CTRL_UHS_SDR12: + switch (host->timing) { + case MMC_TIMING_UHS_SDR12: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR12); break; - case SDHCI_CTRL_UHS_SDR25: + case MMC_TIMING_UHS_SDR25: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR25); break; - case SDHCI_CTRL_UHS_SDR50: + case MMC_TIMING_UHS_SDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR50); break; - case SDHCI_CTRL_UHS_SDR104: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104); break; - case SDHCI_CTRL_UHS_DDR50: + case MMC_TIMING_UHS_DDR50: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; default: @@ -1537,25 +1536,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - if (host->ops->set_uhs_signaling) - host->ops->set_uhs_signaling(host, ios->timing); - else { - ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* Select Bus Speed Mode for host */ - ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; - if ((ios->timing == MMC_TIMING_MMC_HS200) || - (ios->timing == MMC_TIMING_UHS_SDR104)) - ctrl_2 |= SDHCI_CTRL_UHS_SDR104; - else if (ios->timing == MMC_TIMING_UHS_SDR12) - ctrl_2 |= SDHCI_CTRL_UHS_SDR12; - else if (ios->timing == MMC_TIMING_UHS_SDR25) - ctrl_2 |= SDHCI_CTRL_UHS_SDR25; - else if (ios->timing == MMC_TIMING_UHS_SDR50) - ctrl_2 |= SDHCI_CTRL_UHS_SDR50; - else if (ios->timing == MMC_TIMING_UHS_DDR50) - ctrl_2 |= SDHCI_CTRL_UHS_DDR50; - sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); - } + host->ops->set_uhs_signaling(host, ios->timing); + host->timing = ios->timing; if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN) && ((ios->timing == MMC_TIMING_UHS_SDR12) || @@ -1860,12 +1842,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * If the Host Controller supports the HS200 mode then the * tuning function has to be executed. */ - if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && + if (host->timing == MMC_TIMING_UHS_SDR50 && (host->flags & SDHCI_SDR50_NEEDS_TUNING || host->flags & SDHCI_SDR104_NEEDS_TUNING)) requires_tuning_nonuhs = true; - if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || + if (host->timing == MMC_TIMING_MMC_HS200 || + host->timing == MMC_TIMING_UHS_SDR104 || requires_tuning_nonuhs) ctrl |= SDHCI_CTRL_EXEC_TUNING; else { diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index c421b3ef2eb804..6da8aaf67f1bbb 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -176,6 +176,8 @@ struct sdhci_host { unsigned int ocr_avail_mmc; u32 ocr_mask; /* available voltages */ + unsigned timing; /* Current timing */ + u32 thread_isr; /* cached registers */ From 13d9040ef714663c3c70d520061301b4de9f4af4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:54:50 +0200 Subject: [PATCH 0030/1983] mmc: sdhci: clean up sdhci_execute_tuning() decision Upstream-commit: 4b6f37d3a379b09840c482bc4dcb9c3b4f7246c1 Clean up the code in sdhci_execute_tuning() so the decision whether to execute tuning is clearer - and despite this reflecting what the original code was doing, it shows that it may not be what the author actually intended. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4c43ea7176bbe4..369437fdbc42e7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1820,21 +1820,16 @@ static int sdhci_card_busy(struct mmc_host *mmc) static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { - struct sdhci_host *host; + struct sdhci_host *host = mmc_priv(mmc); u16 ctrl; int tuning_loop_counter = MAX_TUNING_LOOP; unsigned long timeout; int err = 0; - bool requires_tuning_nonuhs = false; unsigned long flags; - host = mmc_priv(mmc); - sdhci_runtime_pm_get(host); spin_lock_irqsave(&host->lock, flags); - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* * The Host Controller needs tuning only in case of SDR104 mode * and for SDR50 mode when Use Tuning for SDR50 is set in the @@ -1842,16 +1837,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * If the Host Controller supports the HS200 mode then the * tuning function has to be executed. */ - if (host->timing == MMC_TIMING_UHS_SDR50 && - (host->flags & SDHCI_SDR50_NEEDS_TUNING || - host->flags & SDHCI_SDR104_NEEDS_TUNING)) - requires_tuning_nonuhs = true; - - if (host->timing == MMC_TIMING_MMC_HS200 || - host->timing == MMC_TIMING_UHS_SDR104 || - requires_tuning_nonuhs) - ctrl |= SDHCI_CTRL_EXEC_TUNING; - else { + switch (host->timing) { + case MMC_TIMING_MMC_HS200: + case MMC_TIMING_UHS_SDR104: + break; + + case MMC_TIMING_UHS_SDR50: + if (host->flags & SDHCI_SDR50_NEEDS_TUNING || + host->flags & SDHCI_SDR104_NEEDS_TUNING) + break; + /* FALLTHROUGH */ + + default: spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); return 0; @@ -1864,6 +1861,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) return err; } + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl |= SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); /* From 2acf47336c7b588fe6a475037702561cacf0d245 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:05 +0200 Subject: [PATCH 0031/1983] mmc: sdhci-esdhc-imx: remove emulation of uhs_mode Upstream-commit: 850a29b859407e4c35252c817d542f95bf4a5771 We no longer need to emulate the uhs_mode field of the host control2 register - the main sdhci driver never reads this back to evaluate the current mode as it caches the current mode instead. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 479f37233f2b4a..04c0951a63f991 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -171,7 +171,6 @@ struct pltfm_imx_data { MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ } multiblock_status; - u32 uhs_mode; u32 is_ddr; }; @@ -394,7 +393,6 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) ret |= SDHCI_CTRL_TUNED_CLK; - ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK); ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; return ret; @@ -441,7 +439,6 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) else new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); - imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); if (val & SDHCI_CTRL_TUNED_CLK) @@ -857,28 +854,20 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } -static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; - switch (uhs) { + switch (timing) { case MMC_TIMING_UHS_SDR12: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12; - break; case MMC_TIMING_UHS_SDR25: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25; - break; case MMC_TIMING_UHS_SDR50: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; - break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: - imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; break; case MMC_TIMING_UHS_DDR50: - imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50; writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | ESDHC_MIX_CTRL_DDREN, host->ioaddr + ESDHC_MIX_CTRL); @@ -895,7 +884,7 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) break; } - return esdhc_change_pinstate(host, uhs); + return esdhc_change_pinstate(host, timing); } static unsigned int esdhc_get_max_timeout_counter(struct sdhci_host *host) From 22c02606b5a6e01ac67c2492ec6b6ebcce92d1a3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:05 +0200 Subject: [PATCH 0032/1983] mmc: sdhci-of-esdhc: remove platform_suspend/platform_resume callbacks Upstream-commit: 723f7924e80e97e365e70206b6f6f5ebdd8e7ccf We don't need these hooks in order to insert code in these paths, we can just provide our own handlers and call the main sdhci handlers as appropriate. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-of-esdhc.c | 55 ++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index fcaeae5f55b8ff..605815e52f5f58 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -241,20 +241,6 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) mdelay(1); } -#ifdef CONFIG_PM -static u32 esdhc_proctl; -static void esdhc_of_suspend(struct sdhci_host *host) -{ - esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); -} - -static void esdhc_of_resume(struct sdhci_host *host) -{ - esdhc_of_enable_dma(host); - sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); -} -#endif - static void esdhc_of_platform_init(struct sdhci_host *host) { u32 vvn; @@ -302,16 +288,47 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .get_max_clock = esdhc_of_get_max_clock, .get_min_clock = esdhc_of_get_min_clock, .platform_init = esdhc_of_platform_init, -#ifdef CONFIG_PM - .platform_suspend = esdhc_of_suspend, - .platform_resume = esdhc_of_resume, -#endif .adma_workaround = esdhci_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; +#ifdef CONFIG_PM + +static u32 esdhc_proctl; +static int esdhc_of_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + + esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + + return sdhci_suspend_host(host); +} + +static void esdhc_of_resume(device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + int ret = sdhci_resume_host(host); + + if (ret == 0) { + /* Isn't this already done by sdhci_resume_host() ? --rmk */ + esdhc_of_enable_dma(host); + sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); + } + + return ret; +} + +static const struct dev_pm_ops esdhc_pmops = { + .suspend = esdhci_of_suspend, + .resume = esdhci_of_resume, +}; +#define ESDHC_PMOPS (&esdhc_pmops) +#else +#define ESDHC_PMOPS NULL +#endif + static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { /* * card detection could be handled via GPIO @@ -373,7 +390,7 @@ static struct platform_driver sdhci_esdhc_driver = { .name = "sdhci-esdhc", .owner = THIS_MODULE, .of_match_table = sdhci_esdhc_of_match, - .pm = SDHCI_PLTFM_PMOPS, + .pm = ESDHC_PMOPS, }, .probe = sdhci_esdhc_probe, .remove = sdhci_esdhc_remove, From 48e6d5d9da2a1b3b46a2753c7ff039949bd38a74 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:17 +0200 Subject: [PATCH 0033/1983] mmc: sdhci: remove platform_suspend/platform_resume callbacks Upstream-commit: c314b2b10b4c76f1280675ca0b071a1d8ee65f68 The only user (sdhci-of-esdhc) no longer uses these callbacks, so lets remove them to discourage any further use. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 6 ------ drivers/mmc/host/sdhci.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 369437fdbc42e7..1de3c3ee6dddc7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2625,9 +2625,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { - if (host->ops->platform_suspend) - host->ops->platform_suspend(host); - sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ @@ -2684,9 +2681,6 @@ int sdhci_resume_host(struct sdhci_host *host) sdhci_enable_card_detection(host); - if (host->ops->platform_resume) - host->ops->platform_resume(host); - /* Set the re-tuning expiration flag */ if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c6c406fb1ca7a7..c6ea2401003af4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -290,8 +290,6 @@ struct sdhci_ops { int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); - void (*platform_suspend)(struct sdhci_host *host); - void (*platform_resume)(struct sdhci_host *host); void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); From 1a5d15c5a1166a6a71b118340f25578faa45f63f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:17 +0200 Subject: [PATCH 0034/1983] mmc: sdhci-tegra: get rid of special PRESENT_STATE register handling Upstream-commit: b4f3b7c8b13c6f0f84781a5d07fa553b3df4dfe6 sdhci-tegra provides a get_ro method, which overrides the checking of the write protect bit in the PRESENT_STATE register in sdhci.c: if (host->flags & SDHCI_DEVICE_DEAD) is_readonly = 0; else if (host->ops->get_ro) is_readonly = host->ops->get_ro(host); else is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT); This means it's pointless detecting accesses to this register and manually setting the SDHCI_WRITE_PROTECT as it has no effect. This means that the whole of tegra_sdhci_readl() can be removed and we can use the builtin sdhci readl functionality here. Signed-off-by: Russell King Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-tegra.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index d06b6ff6043219..985247649f4619 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -48,19 +48,6 @@ struct sdhci_tegra { int power_gpio; }; -static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) -{ - u32 val; - - if (unlikely(reg == SDHCI_PRESENT_STATE)) { - /* Use wp_gpio here instead? */ - val = readl(host->ioaddr + reg); - return val | SDHCI_WRITE_PROTECT; - } - - return readl(host->ioaddr + reg); -} - static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -150,7 +137,6 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, - .read_l = tegra_sdhci_readl, .read_w = tegra_sdhci_readw, .write_l = tegra_sdhci_writel, .set_clock = sdhci_set_clock, From 11809efaff3ddf806d2538d4bef40d22275d040a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:17 +0200 Subject: [PATCH 0035/1983] mmc: sdhci: move regulator handling into sdhci_set_power() Upstream-commit: e921a8b6c47012bef94fe778f6e00df4207d5ed9 Move the regulator handling into sdhci_set_power() rather than being in sdhci_do_set_ios(). This wraps all power control up into this function. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 72 +++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1de3c3ee6dddc7..658f1c68185887 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1218,7 +1218,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } EXPORT_SYMBOL_GPL(sdhci_set_clock); -static int sdhci_set_power(struct sdhci_host *host, unsigned short power) +static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; @@ -1241,7 +1241,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) } if (host->pwr == pwr) - return -1; + return; host->pwr = pwr; @@ -1249,38 +1249,43 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) sdhci_runtime_pm_bus_off(host); - return 0; - } - - /* - * Spec says that we should clear the power reg before setting - * a new value. Some controllers don't seem to like this though. - */ - if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) - sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + power = 0; + } else { + /* + * Spec says that we should clear the power reg before setting + * a new value. Some controllers don't seem to like this though. + */ + if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - /* - * At least the Marvell CaFe chip gets confused if we set the voltage - * and set turn on power at the same time, so set the voltage first. - */ - if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + /* + * At least the Marvell CaFe chip gets confused if we set the + * voltage and set turn on power at the same time, so set the + * voltage first. + */ + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - pwr |= SDHCI_POWER_ON; + pwr |= SDHCI_POWER_ON; - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) - sdhci_runtime_pm_bus_on(host); + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_on(host); - /* - * Some controllers need an extra 10ms delay of 10ms before they - * can apply clock after applying power - */ - if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) - mdelay(10); + /* + * Some controllers need an extra 10ms delay of 10ms before + * they can apply clock after applying power + */ + if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) + mdelay(10); + } - return power; + if (host->vmmc) { + spin_unlock_irq(&host->lock); + mmc_regulator_set_ocr(host->mmc, host->vmmc, power); + spin_lock_irq(&host->lock); + } } /*****************************************************************************\ @@ -1431,7 +1436,6 @@ EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling); static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; - int vdd_bit = -1; u8 ctrl; spin_lock_irqsave(&host->lock, flags); @@ -1463,15 +1467,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } if (ios->power_mode == MMC_POWER_OFF) - vdd_bit = sdhci_set_power(host, -1); + sdhci_set_power(host, -1); else - vdd_bit = sdhci_set_power(host, ios->vdd); - - if (host->vmmc && vdd_bit != -1) { - spin_unlock_irqrestore(&host->lock, flags); - mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); - spin_lock_irqsave(&host->lock, flags); - } + sdhci_set_power(host, ios->vdd); if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); From d39c72fa2514af9b42b1608ac7fbf03fd261f879 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:17 +0200 Subject: [PATCH 0036/1983] mmc: sdhci: move remaining power handling into sdhci_set_power() Upstream-commit: 24fbb3ca1468861e5ac33a26c4130610ae8d0e20 Move the remaining parts of the power handling in sdhci_do_set_ios() into sdhci_set_power(). Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 658f1c68185887..0bba2ac316df35 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1218,12 +1218,13 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } EXPORT_SYMBOL_GPL(sdhci_set_clock); -static void sdhci_set_power(struct sdhci_host *host, unsigned short power) +static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) { u8 pwr = 0; - if (power != (unsigned short)-1) { - switch (1 << power) { + if (mode != MMC_POWER_OFF) { + switch (1 << vdd) { case MMC_VDD_165_195: pwr = SDHCI_POWER_180; break; @@ -1249,7 +1250,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) sdhci_runtime_pm_bus_off(host); - power = 0; + vdd = 0; } else { /* * Spec says that we should clear the power reg before setting @@ -1283,7 +1284,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) if (host->vmmc) { spin_unlock_irq(&host->lock); - mmc_regulator_set_ocr(host->mmc, host->vmmc, power); + mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd); spin_lock_irq(&host->lock); } } @@ -1466,10 +1467,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) host->clock = ios->clock; } - if (ios->power_mode == MMC_POWER_OFF) - sdhci_set_power(host, -1); - else - sdhci_set_power(host, ios->vdd); + sdhci_set_power(host, ios->power_mode, ios->vdd); if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); From d211b16ddc265561fd31bba622578cac5646528f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:17 +0200 Subject: [PATCH 0037/1983] mmc: sdhci: track whether preset mode is currently enabled in hardware Upstream-commit: da91a8f9c0f56d75b35bfe2e2456187ab55b3639 Track whether preset mode is currently enabled in hardware, and use that when making decisions elsewhere in the code rather than reading the register and checking the bit. Signed-off-by: Russell King Tested-by: Markus Pargmann Tested-by: Stephen Warren Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 44 +++++++++++++++++++++++---------------- include/linux/mmc/sdhci.h | 1 + 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0bba2ac316df35..3fa115848235dd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -209,9 +209,14 @@ static void sdhci_do_reset(struct sdhci_host *host, u8 mask) host->ops->reset(host, mask); - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { - if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) - host->ops->enable_dma(host); + if (mask & SDHCI_RESET_ALL) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->ops->enable_dma) + host->ops->enable_dma(host); + } + + /* Resetting the controller clears many */ + host->preset_enabled = false; } } @@ -1128,8 +1133,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) return; if (host->version >= SDHCI_SPEC_300) { - if (sdhci_readw(host, SDHCI_HOST_CONTROL2) & - SDHCI_CTRL_PRESET_VAL_ENABLE) { + if (host->preset_enabled) { u16 pre_val; clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); @@ -1494,13 +1498,13 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) (ios->timing == MMC_TIMING_UHS_SDR25)) ctrl |= SDHCI_CTRL_HISPD; - ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); - if (!(ctrl_2 & SDHCI_CTRL_PRESET_VAL_ENABLE)) { + if (!host->preset_enabled) { sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * We only need to set Driver Strength if the * preset value enable is not set. */ + ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; @@ -2016,26 +2020,30 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) { - u16 ctrl; - /* Host Controller v3.00 defines preset value registers */ if (host->version < SDHCI_SPEC_300) return; - ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); - /* * We only enable or disable Preset Value if they are not already * enabled or disabled respectively. Otherwise, we bail out. */ - if (enable && !(ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { - ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; - sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - host->flags |= SDHCI_PV_ENABLED; - } else if (!enable && (ctrl & SDHCI_CTRL_PRESET_VAL_ENABLE)) { - ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + if (host->preset_enabled != enable) { + u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + if (enable) + ctrl |= SDHCI_CTRL_PRESET_VAL_ENABLE; + else + ctrl &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - host->flags &= ~SDHCI_PV_ENABLED; + + if (enable) + host->flags |= SDHCI_PV_ENABLED; + else + host->flags &= ~SDHCI_PV_ENABLED; + + host->preset_enabled = enable; } } diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 6da8aaf67f1bbb..778a667cffd25e 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -147,6 +147,7 @@ struct sdhci_host { bool runtime_suspended; /* Host is runtime suspended */ bool bus_on; /* Bus power prevents runtime suspend */ + bool preset_enabled; /* Preset is enabled */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ From c8d93882699b5cfb12e1a38e97cda954794d9cb2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:56:48 +0200 Subject: [PATCH 0038/1983] mmc: sdhci: fix SDHCI dependencies Signed-off-by: Russell King --- drivers/mmc/host/Kconfig | 63 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 1384f67abe2190..615eea35c2a690 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -25,8 +25,7 @@ config MMC_PXA If unsure, say N. config MMC_SDHCI - tristate "Secure Digital Host Controller Interface support" - depends on HAS_DMA + tristate help This selects the generic Secure Digital Host Controller Interface. It is used by manufacturers such as Texas Instruments(R), Ricoh(R) @@ -59,7 +58,8 @@ config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER config MMC_SDHCI_PCI tristate "SDHCI support on PCI bus" - depends on MMC_SDHCI && PCI + depends on PCI && HAS_DMA + select MMC_SDHCI help This selects the PCI Secure Digital Host Controller Interface. Most controllers found today are PCI devices. @@ -83,7 +83,8 @@ config MMC_RICOH_MMC config MMC_SDHCI_ACPI tristate "SDHCI support for ACPI enumerated SDHCI controllers" - depends on MMC_SDHCI && ACPI + depends on ACPI && HAS_DMA + select MMC_SDHCI help This selects support for ACPI enumerated SDHCI controllers, identified by ACPI Compatibility ID PNP0D40 or specific @@ -94,8 +95,8 @@ config MMC_SDHCI_ACPI If unsure, say N. config MMC_SDHCI_PLTFM - tristate "SDHCI platform and OF driver helper" - depends on MMC_SDHCI + tristate + select MMC_SDHCI help This selects the common helper functions support for Secure Digital Host Controller Interface based platform and OF drivers. @@ -106,8 +107,8 @@ config MMC_SDHCI_PLTFM config MMC_SDHCI_OF_ARASAN tristate "SDHCI OF support for the Arasan SDHCI controllers" - depends on MMC_SDHCI_PLTFM - depends on OF + depends on OF && HAS_DMA + select MMC_SDHCI_PLTFM help This selects the Arasan Secure Digital Host Controller Interface (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. @@ -118,9 +119,9 @@ config MMC_SDHCI_OF_ARASAN config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" - depends on MMC_SDHCI_PLTFM - depends on PPC_OF + depends on PPC_OF && HAS_DMA select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER + select MMC_SDHCI_PLTFM help This selects the Freescale eSDHC controller support. @@ -130,9 +131,9 @@ config MMC_SDHCI_OF_ESDHC config MMC_SDHCI_OF_HLWD tristate "SDHCI OF support for the Nintendo Wii SDHCI controllers" - depends on MMC_SDHCI_PLTFM - depends on PPC_OF + depends on PPC_OF && HAS_DMA select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER + select MMC_SDHCI_PLTFM help This selects the Secure Digital Host Controller Interface (SDHCI) found in the "Hollywood" chipset of the Nintendo Wii video game @@ -144,8 +145,8 @@ config MMC_SDHCI_OF_HLWD config MMC_SDHCI_CNS3XXX tristate "SDHCI support on the Cavium Networks CNS3xxx SoC" - depends on ARCH_CNS3XXX - depends on MMC_SDHCI_PLTFM + depends on ARCH_CNS3XXX && HAS_DMA + select MMC_SDHCI_PLTFM help This selects the SDHCI support for CNS3xxx System-on-Chip devices. @@ -155,9 +156,9 @@ config MMC_SDHCI_CNS3XXX config MMC_SDHCI_ESDHC_IMX tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller" - depends on ARCH_MXC - depends on MMC_SDHCI_PLTFM + depends on ARCH_MXC && HAS_DMA select MMC_SDHCI_IO_ACCESSORS + select MMC_SDHCI_PLTFM help This selects the Freescale eSDHC/uSDHC controller support found on i.MX25, i.MX35 i.MX5x and i.MX6x. @@ -168,9 +169,9 @@ config MMC_SDHCI_ESDHC_IMX config MMC_SDHCI_DOVE tristate "SDHCI support on Marvell's Dove SoC" - depends on ARCH_DOVE - depends on MMC_SDHCI_PLTFM + depends on ARCH_DOVE && HAS_DMA select MMC_SDHCI_IO_ACCESSORS + select MMC_SDHCI_PLTFM help This selects the Secure Digital Host Controller Interface in Marvell's Dove SoC. @@ -181,9 +182,9 @@ config MMC_SDHCI_DOVE config MMC_SDHCI_TEGRA tristate "SDHCI platform support for the Tegra SD/MMC Controller" - depends on ARCH_TEGRA - depends on MMC_SDHCI_PLTFM + depends on ARCH_TEGRA && HAS_DMA select MMC_SDHCI_IO_ACCESSORS + select MMC_SDHCI_PLTFM help This selects the Tegra SD/MMC controller. If you have a Tegra platform with SD or MMC devices, say Y or M here. @@ -192,7 +193,8 @@ config MMC_SDHCI_TEGRA config MMC_SDHCI_S3C tristate "SDHCI support on Samsung S3C SoC" - depends on MMC_SDHCI && PLAT_SAMSUNG + depends on PLAT_SAMSUNG && HAS_DMA + select MMC_SDHCI help This selects the Secure Digital Host Controller Interface (SDHCI) often referrered to as the HSMMC block in some of the Samsung S3C @@ -204,8 +206,8 @@ config MMC_SDHCI_S3C config MMC_SDHCI_SIRF tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs" - depends on ARCH_SIRF - depends on MMC_SDHCI_PLTFM + depends on ARCH_SIRF && HAS_DMA + select MMC_SDHCI_PLTFM help This selects the SDHCI support for SiRF System-on-Chip devices. @@ -215,8 +217,7 @@ config MMC_SDHCI_SIRF config MMC_SDHCI_PXAV3 tristate "Marvell MMP2 SD Host Controller support (PXAV3)" - depends on CLKDEV_LOOKUP - select MMC_SDHCI + depends on CLKDEV_LOOKUP && HAS_DMA select MMC_SDHCI_PLTFM default CPU_MMP2 help @@ -228,8 +229,7 @@ config MMC_SDHCI_PXAV3 config MMC_SDHCI_PXAV2 tristate "Marvell PXA9XX SD Host Controller support (PXAV2)" - depends on CLKDEV_LOOKUP - select MMC_SDHCI + depends on CLKDEV_LOOKUP && HAS_DMA select MMC_SDHCI_PLTFM default CPU_PXA910 help @@ -241,7 +241,8 @@ config MMC_SDHCI_PXAV2 config MMC_SDHCI_SPEAR tristate "SDHCI support on ST SPEAr platform" - depends on MMC_SDHCI && PLAT_SPEAR + depends on PLAT_SPEAR && HAS_DMA + select MMC_SDHCI help This selects the Secure Digital Host Controller Interface (SDHCI) often referrered to as the HSMMC block in some of the ST SPEAR range @@ -263,7 +264,7 @@ config MMC_SDHCI_S3C_DMA config MMC_SDHCI_BCM_KONA tristate "SDHCI support on Broadcom KONA platform" - depends on ARCH_BCM + depends on ARCH_BCM && HAS_DMA select MMC_SDHCI_PLTFM help This selects the Broadcom Kona Secure Digital Host Controller @@ -274,9 +275,9 @@ config MMC_SDHCI_BCM_KONA config MMC_SDHCI_BCM2835 tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" - depends on ARCH_BCM2835 - depends on MMC_SDHCI_PLTFM + depends on ARCH_BCM2835 && HAS_DMA select MMC_SDHCI_IO_ACCESSORS + select MMC_SDHCI_PLTFM help This selects the BCM2835 SD/MMC controller. If you have a BCM2835 platform with SD or MMC devices, say Y or M here. From 50d2b64e4018b24c64c038dc28e393a78fb29ae1 Mon Sep 17 00:00:00 2001 From: Corneliu Doban Date: Mon, 9 Feb 2015 16:06:29 -0800 Subject: [PATCH 0039/1983] mmc: sdhci: do not set AUTO_CMD12 for multi-block CMD53 For CMD53 in block mode, the host does not need to stop the transfer, as it stops when the block count (present in CMD53) is reached. Signed-off-by: Corneliu Doban Signed-off-by: Scott Branden Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3fa115848235dd..766a8dd14b43c3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "sdhci.h" @@ -896,7 +897,8 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, * If we are sending CMD23, CMD12 never gets sent * on successful completion (so no Auto-CMD12). */ - if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) + if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12) && + (cmd->opcode != SD_IO_RW_EXTENDED)) mode |= SDHCI_TRNS_AUTO_CMD12; else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { mode |= SDHCI_TRNS_AUTO_CMD23; From 3be90122c12dcee55e094c0ee4599794477a287c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 3 Jul 2015 12:23:33 +0200 Subject: [PATCH 0040/1983] dts: cbi/hb: fix the mmc order to match previous kernels Freescale decided to re-order the mmc device order to match the physical layout. This is not how previous kernels and upstream do it so we fix it for the CBi/HB. --- arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 5 +++++ arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi index 2a41035a7854e6..1a5cf7b5a2f677 100644 --- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi @@ -45,6 +45,11 @@ #include / { + aliases { + mmc0 = &usdhc2; + mmc1 = &usdhc1; + }; + ir_recv: ir-receiver { compatible = "gpio-ir-receiver"; gpios = <&gpio3 9 1>; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index d85240f0b5698c..5c08c409e7aed5 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -43,6 +43,11 @@ #include "imx6qdl-microsom-ar8035.dtsi" / { + aliases { + mmc0 = &usdhc2; + mmc1 = &usdhc1; + }; + chosen { stdout-path = &uart1; }; From 46be8186714fff2ea76d0eaf4adc57c031cf2562 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 3 Jul 2015 12:26:14 +0200 Subject: [PATCH 0041/1983] media: mxc: fix Kconfig for ov5640 mipi Selecting MXC_CAMERA_OV5640_MIPI will cause a build failure as it has dependencies that aren't being selected and built. --- drivers/media/platform/mxc/capture/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig index e8728d9dedeb5d..aa504fdba57ad0 100644 --- a/drivers/media/platform/mxc/capture/Kconfig +++ b/drivers/media/platform/mxc/capture/Kconfig @@ -24,6 +24,8 @@ config MXC_CAMERA_OV5642 config MXC_CAMERA_OV5640_MIPI tristate "OmniVision ov5640 camera support using mipi" depends on !VIDEO_MXC_EMMA_CAMERA && I2C + select MXC_MIPI_CSI2 + select VIDEO_V4L2_MXC_INT_DEVICE ---help--- If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here. From 4ad5a2f8d1000de5728fbcbba264eb598645d030 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 3 Jul 2015 13:14:43 +0200 Subject: [PATCH 0042/1983] pci: imx: Fix IMX6SX powersave depends the IMX6SX power saving option obviously depends on building for SOC_IMX6SX. Since I have fixed it so you can build IMX6Q support without selection IMX6SX we fix this option to depend on the SOC. --- drivers/pci/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index bf9b8493c30e3d..2feb1c6f6a2e5c 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -32,7 +32,7 @@ config PCI_IMX6 config PCI_IMX6SX_EXTREMELY_PWR_SAVE bool "Freescale i.MX6SX PCIe controller extremely power save mode" - depends on PCI_IMX6 + depends on PCI_IMX6 && SOC_IMX6SX help If you want extremely power save on iMX6SX PCIe, you can enable the ultra low power mode during suspend/resume here. Note: iMX6SX From cb7015a7ebce3aac1fcbe6487bb21d898b19d325 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 6 Jul 2015 14:52:05 +0200 Subject: [PATCH 0043/1983] dts: microsom-ar8035: fix enet reference pll Commit d24a5df30aed6e85e19e9bfe92f184b933debbdb changed the default clock of enet_ref_pll to 125Mhz which breaks our ethernet. Since it is only one board that needs this it would make sense to revert that commit and just patch that single device-tree. Since This is already accepted I will patch our device-tree to work around this commit. --- arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi b/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi index 4a1820309cdb82..5ab7a4d7803fd4 100644 --- a/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi +++ b/arch/arm/boot/dts/imx6qdl-microsom-ar8035.dtsi @@ -49,6 +49,8 @@ phy-reset-duration = <2>; phy-reset-gpios = <&gpio4 15 0>; status = "okay"; + assigned-clocks = <&clks IMX6QDL_CLK_ENET_REF>; + assigned-clock-rates = <25000000>; }; &iomuxc { From d8d108b9cfe71354f606313d7fe5cecdfa100281 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 29 May 2015 08:40:19 +0200 Subject: [PATCH 0044/1983] net: fec: probe phy and autonegotiate on fec probe. The phy autonegotiation can take up to 4 seconds to happen when connected to a gigabit switch. Instead of waiting for userspace to open the network device and then probing the phy we probe the phy immediately after the netdev is registered and start the autonegiotiation process. This means that as soon as userspace is ready we have link and can configure the network accordingly. --- drivers/net/ethernet/freescale/fec_main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 70564a43ca20c7..90ec0bb33621c8 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2792,10 +2792,11 @@ fec_enet_open(struct net_device *ndev) if (ret) goto err_enet_alloc; - /* Probe and connect to PHY when open the interface */ - ret = fec_enet_mii_probe(ndev); - if (ret) - goto err_enet_mii_probe; + if (!fep->phy_dev) { + ret = fec_enet_mii_probe(ndev); + if (ret) + goto err_enet_alloc; + } fec_restart(ndev); napi_enable(&fep->napi); @@ -2818,8 +2819,6 @@ fec_enet_open(struct net_device *ndev) return 0; -err_enet_mii_probe: - fec_enet_free_buffers(ndev); err_enet_alloc: if (!fep->mii_bus_share) pinctrl_pm_select_sleep_state(&fep->pdev->dev); @@ -3407,13 +3406,17 @@ fec_probe(struct platform_device *pdev) /* Carrier starts down, phylib will bring it up */ netif_carrier_off(ndev); - fec_enet_clk_enable(ndev, false); - pinctrl_pm_select_sleep_state(&pdev->dev); ret = register_netdev(ndev); if (ret) goto failed_register; + ret = fec_enet_mii_probe(ndev); + if (ret) + goto failed_register; + + phy_start_aneg(fep->phy_dev); + device_init_wakeup(&ndev->dev, fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET); From bb2a3b7538a36d52e2ccea9a5c9d6aac2c3f980c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 29 May 2015 09:57:07 +0200 Subject: [PATCH 0045/1983] net: phy: turn off carrier until phydev has link Instead of having every net device disable the carrier on probe, we let the state machine disable the carrier whenever we get PHY_NOLINK from the state machine and phydev->link is not set. --- drivers/net/phy/phy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 65cfc5a436c703..03ace3bab3dbc9 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -766,6 +766,8 @@ void phy_state_machine(struct work_struct *work) phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); phydev->adjust_link(phydev->attached_dev); + } else { + netif_carrier_off(phydev->attached_dev); } break; case PHY_FORCING: From 12f67a122fe1ca93135d68ff0d91cee67879608c Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 9 Jan 2014 14:29:52 -0700 Subject: [PATCH 0046/1983] mxc_hdmi: enable overflow interrupt after initialization complete If enabled too early, a flood of interrupts can happen, and as console_lock is held, you cannot see any messages being printed. Signed-off-by: Troy Kisky --- drivers/video/mxc/mxc_hdmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 10b54fb5fe3322..eea87a7ab6e380 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -1268,9 +1268,6 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) || (hdmi->blank != FB_BLANK_UNBLANK)) return; - if (!hdmi->hdmi_data.video_mode.mDVI) - hdmi_enable_overflow_interrupts(); - /*check csc whether needed activated in HDMI mode */ cscon = (isColorSpaceConversion(hdmi) && !hdmi->hdmi_data.video_mode.mDVI); @@ -1287,6 +1284,8 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) } hdmi->phy_enabled = true; + if (!hdmi->hdmi_data.video_mode.mDVI) + hdmi_enable_overflow_interrupts(); } static void hdmi_config_AVI(struct mxc_hdmi *hdmi) From 5f7259b8c91b7b20d9a8d946bbb45a966f995520 Mon Sep 17 00:00:00 2001 From: wolfgar Date: Sat, 26 Apr 2014 16:50:32 +0200 Subject: [PATCH 0047/1983] mxc: hdmi: cec: libCEC compatibility patch This commit pushes all changes I did in the 3.0.35 kernel to improve/fix its behavior with libcec port for imx6 It also fixes CEC clock handling in case of FB mode change event and of HDMI cable disconnection --- drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 327 ++++++++++++---------------- drivers/video/mxc/mxc_hdmi.c | 14 +- 2 files changed, 147 insertions(+), 194 deletions(-) diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c index d0113eee23d17f..3a8aff40a86abb 100644 --- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c @@ -55,6 +55,8 @@ #define MESSAGE_TYPE_CONNECTED 4 #define MESSAGE_TYPE_SEND_SUCCESS 5 +#define CEC_TX_INPROGRESS -1 +#define CEC_TX_AVAIL 0 struct hdmi_cec_priv { int receive_error; @@ -63,7 +65,9 @@ struct hdmi_cec_priv { bool cec_state; u8 last_msg[MAX_MESSAGE_LEN]; u8 msg_len; - u8 latest_cec_stat; + int tx_answer; + u16 latest_cec_stat; + u8 link_status; spinlock_t irq_lock; struct delayed_work hdmi_cec_work; struct mutex lock; @@ -76,6 +80,7 @@ struct hdmi_cec_event { struct list_head list; }; + static LIST_HEAD(head); static int hdmi_cec_major; @@ -84,11 +89,14 @@ static struct hdmi_cec_priv hdmi_cec_data; static u8 open_count; static wait_queue_head_t hdmi_cec_queue; +static wait_queue_head_t tx_cec_queue; + static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) { struct hdmi_cec_priv *hdmi_cec = data; - u8 cec_stat = 0; + u16 cec_stat = 0; unsigned long flags; + u8 phy_stat0; spin_lock_irqsave(&hdmi_cec->irq_lock, flags); @@ -96,16 +104,24 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) cec_stat = hdmi_readb(HDMI_IH_CEC_STAT0); hdmi_writeb(cec_stat, HDMI_IH_CEC_STAT0); - + phy_stat0 = hdmi_readb(HDMI_PHY_STAT0) & 0x02; + if (hdmi_cec->link_status ^ phy_stat0) { + /* HPD value changed */ + hdmi_cec->link_status = phy_stat0; + if (hdmi_cec->link_status) + cec_stat |= 0x80; /* Connected */ + else + cec_stat |= 0x100; /* Disconnected */ + } if ((cec_stat & (HDMI_IH_CEC_STAT0_ERROR_INIT | \ HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | \ - HDMI_IH_CEC_STAT0_DONE)) == 0) { + HDMI_IH_CEC_STAT0_DONE | 0x180)) == 0) { spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); return IRQ_HANDLED; } - pr_debug("HDMI CEC interrupt received\n"); - hdmi_cec->latest_cec_stat = cec_stat; + /* FIXME : there is a race with latest_cec_stat */ + hdmi_cec->latest_cec_stat = cec_stat ; schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20)); @@ -118,115 +134,70 @@ void mxc_hdmi_cec_handle(u16 cec_stat) { u8 val = 0, i = 0; struct hdmi_cec_event *event = NULL; - - /* The current transmission is successful (for initiator only). */ + /*The current transmission is successful (for initiator only).*/ if (!open_count) return; if (cec_stat & HDMI_IH_CEC_STAT0_DONE) { - - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory!\n", __func__); - return; - } - - memset(event, 0, sizeof(struct hdmi_cec_event)); - event->event_type = MESSAGE_TYPE_SEND_SUCCESS; - - mutex_lock(&hdmi_cec_data.lock); - list_add_tail(&event->list, &head); - mutex_unlock(&hdmi_cec_data.lock); - - wake_up(&hdmi_cec_queue); + hdmi_cec_data.tx_answer = cec_stat; + wake_up(&tx_cec_queue); } - - /* EOM is detected so that the received data is ready - * in the receiver data buffer - */ + /*EOM is detected so that the received data is ready in the receiver data buffer*/ if (cec_stat & HDMI_IH_CEC_STAT0_EOM) { - hdmi_writeb(0x02, HDMI_IH_CEC_STAT0); - event = vmalloc(sizeof(struct hdmi_cec_event)); if (NULL == event) { pr_err("%s: Not enough memory!\n", __func__); return; } memset(event, 0, sizeof(struct hdmi_cec_event)); - event->msg_len = hdmi_readb(HDMI_CEC_RX_CNT); if (!event->msg_len) { pr_err("%s: Invalid CEC message length!\n", __func__); return; } event->event_type = MESSAGE_TYPE_RECEIVE_SUCCESS; - for (i = 0; i < event->msg_len; i++) event->msg[i] = hdmi_readb(HDMI_CEC_RX_DATA0+i); hdmi_writeb(0x0, HDMI_CEC_LOCK); - mutex_lock(&hdmi_cec_data.lock); list_add_tail(&event->list, &head); mutex_unlock(&hdmi_cec_data.lock); - wake_up(&hdmi_cec_queue); } - - /* An error is detected on cec line (for initiator only). */ + /*An error is detected on cec line (for initiator only). */ if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_INIT) { - mutex_lock(&hdmi_cec_data.lock); hdmi_cec_data.send_error++; - if (hdmi_cec_data.send_error > 5) { - pr_err("%s:Re-transmission is attempted more than 5 times!\n", - __func__); + if (hdmi_cec_data.send_error > 2) { + pr_err("%s:Re-transmission is attempted more than 2 times!\n", __func__); hdmi_cec_data.send_error = 0; mutex_unlock(&hdmi_cec_data.lock); + hdmi_cec_data.tx_answer = cec_stat; + wake_up(&tx_cec_queue); return; } - - for (i = 0; i < hdmi_cec_data.msg_len; i++) { - hdmi_writeb(hdmi_cec_data.last_msg[i], - HDMI_CEC_TX_DATA0 + i); - } + for (i = 0; i < hdmi_cec_data.msg_len; i++) + hdmi_writeb(hdmi_cec_data.last_msg[i], HDMI_CEC_TX_DATA0+i); hdmi_writeb(hdmi_cec_data.msg_len, HDMI_CEC_TX_CNT); - val = hdmi_readb(HDMI_CEC_CTRL); val |= 0x01; hdmi_writeb(val, HDMI_CEC_CTRL); mutex_unlock(&hdmi_cec_data.lock); } - - /* A frame is not acknowledged in a directly addressed message. - * Or a frame is negatively acknowledged in - * a broadcast message (for initiator only). - */ + /*A frame is not acknowledged in a directly addressed message. Or a frame is negatively acknowledged in + a broadcast message (for initiator only).*/ if (cec_stat & HDMI_IH_CEC_STAT0_NACK) { - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory\n", __func__); - return; - } - memset(event, 0, sizeof(struct hdmi_cec_event)); - event->event_type = MESSAGE_TYPE_NOACK; - - mutex_lock(&hdmi_cec_data.lock); - list_add_tail(&event->list, &head); - mutex_unlock(&hdmi_cec_data.lock); - - wake_up(&hdmi_cec_queue); + hdmi_cec_data.tx_answer = cec_stat; + wake_up(&tx_cec_queue); } - - /* An error is notified by a follower. - * Abnormal logic data bit error (for follower). - */ + /*An error is notified by a follower. Abnormal logic data bit error (for follower).*/ if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL) { hdmi_cec_data.receive_error++; } - - /* HDMI cable connected */ + /*HDMI cable connected*/ if (cec_stat & 0x80) { + pr_info("HDMI link connected\n"); event = vmalloc(sizeof(struct hdmi_cec_event)); if (NULL == event) { pr_err("%s: Not enough memory\n", __func__); @@ -234,16 +205,14 @@ void mxc_hdmi_cec_handle(u16 cec_stat) } memset(event, 0, sizeof(struct hdmi_cec_event)); event->event_type = MESSAGE_TYPE_CONNECTED; - mutex_lock(&hdmi_cec_data.lock); list_add_tail(&event->list, &head); mutex_unlock(&hdmi_cec_data.lock); - wake_up(&hdmi_cec_queue); } - - /* HDMI cable disconnected */ + /*HDMI cable disconnected*/ if (cec_stat & 0x100) { + pr_info("HDMI link disconnected\n"); event = vmalloc(sizeof(struct hdmi_cec_event)); if (NULL == event) { pr_err("%s: Not enough memory!\n", __func__); @@ -251,30 +220,24 @@ void mxc_hdmi_cec_handle(u16 cec_stat) } memset(event, 0, sizeof(struct hdmi_cec_event)); event->event_type = MESSAGE_TYPE_DISCONNECTED; - mutex_lock(&hdmi_cec_data.lock); list_add_tail(&event->list, &head); mutex_unlock(&hdmi_cec_data.lock); - wake_up(&hdmi_cec_queue); } - return; } EXPORT_SYMBOL(mxc_hdmi_cec_handle); - static void mxc_hdmi_cec_worker(struct work_struct *work) { u8 val; - mxc_hdmi_cec_handle(hdmi_cec_data.latest_cec_stat); - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | - HDMI_IH_CEC_STAT0_ARB_LOST; + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); } /*! - * @brief open function for vpu file operation + * @brief open function for cec file operation * * @return 0 on success or negative error code on error */ @@ -285,13 +248,11 @@ static int hdmi_cec_open(struct inode *inode, struct file *filp) mutex_unlock(&hdmi_cec_data.lock); return -EBUSY; } - open_count = 1; filp->private_data = (void *)(&hdmi_cec_data); hdmi_cec_data.Logical_address = 15; hdmi_cec_data.cec_state = false; mutex_unlock(&hdmi_cec_data.lock); - return 0; } @@ -299,36 +260,40 @@ static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct hdmi_cec_event *event = NULL; - pr_debug("function : %s\n", __func__); + if (!open_count) return -ENODEV; - mutex_lock(&hdmi_cec_data.lock); if (false == hdmi_cec_data.cec_state) { mutex_unlock(&hdmi_cec_data.lock); return -EACCES; } - mutex_unlock(&hdmi_cec_data.lock); - /* delete from list */ - mutex_lock(&hdmi_cec_data.lock); if (list_empty(&head)) { - mutex_unlock(&hdmi_cec_data.lock); - return -EACCES; + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&hdmi_cec_data.lock); + return -EAGAIN; + } else { + do { + mutex_unlock(&hdmi_cec_data.lock); + if (wait_event_interruptible(hdmi_cec_queue, (!list_empty(&head)))) + return -ERESTARTSYS; + mutex_lock(&hdmi_cec_data.lock); + } while (list_empty(&head)); + } } + event = list_first_entry(&head, struct hdmi_cec_event, list); list_del(&event->list); mutex_unlock(&hdmi_cec_data.lock); - if (copy_to_user(buf, event, - sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) { + sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) { vfree(event); return -EFAULT; } vfree(event); - - return sizeof(struct hdmi_cec_event); + return (sizeof(struct hdmi_cec_event) - sizeof(struct list_head)); } static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, @@ -339,58 +304,77 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, u8 msg_len = 0, val = 0; pr_debug("function : %s\n", __func__); + if (!open_count) return -ENODEV; - mutex_lock(&hdmi_cec_data.lock); if (false == hdmi_cec_data.cec_state) { mutex_unlock(&hdmi_cec_data.lock); return -EACCES; } + /* Ensure that there is only one writer who is the only listener of tx_cec_queue */ + if (hdmi_cec_data.tx_answer != CEC_TX_AVAIL) { + mutex_unlock(&hdmi_cec_data.lock); + return -EBUSY; + } mutex_unlock(&hdmi_cec_data.lock); - if (count > MAX_MESSAGE_LEN) return -EINVAL; - - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.send_error = 0; memset(&msg, 0, MAX_MESSAGE_LEN); ret = copy_from_user(&msg, buf, count); - if (ret) { - ret = -EACCES; - goto end; - } - + if (ret) + return -EACCES; + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.send_error = 0; + hdmi_cec_data.tx_answer = CEC_TX_INPROGRESS; msg_len = count; hdmi_writeb(msg_len, HDMI_CEC_TX_CNT); - for (i = 0; i < msg_len; i++) { + for (i = 0; i < msg_len; i++) hdmi_writeb(msg[i], HDMI_CEC_TX_DATA0+i); - } - val = hdmi_readb(HDMI_CEC_CTRL); val |= 0x01; hdmi_writeb(val, HDMI_CEC_CTRL); memcpy(hdmi_cec_data.last_msg, msg, msg_len); hdmi_cec_data.msg_len = msg_len; + mutex_unlock(&hdmi_cec_data.lock); - i = 0; - val = hdmi_readb(HDMI_CEC_CTRL); - while ((val & 0x01) == 0x1) { - msleep(50); - i++; - if (i > 3) { - ret = -EIO; - goto end; - } - val = hdmi_readb(HDMI_CEC_CTRL); + ret = wait_event_interruptible_timeout(tx_cec_queue, hdmi_cec_data.tx_answer != CEC_TX_INPROGRESS, HZ); + + if (ret < 0) { + ret = -ERESTARTSYS; + goto tx_out; } -end: - mutex_unlock(&hdmi_cec_data.lock); + if (hdmi_cec_data.tx_answer & HDMI_IH_CEC_STAT0_DONE) + /* msg correctly sent */ + ret = msg_len; + else + ret = -EIO; + tx_out: + hdmi_cec_data.tx_answer = CEC_TX_AVAIL; return ret; } + +static void hdmi_stop_device(void) +{ + u8 val; + + hdmi_writeb(0x10, HDMI_CEC_CTRL); + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | \ + HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; + hdmi_writeb(val, HDMI_CEC_MASK); + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + hdmi_writeb(0x0, HDMI_CEC_POLARITY); + val = hdmi_readb(HDMI_MC_CLKDIS); + val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.cec_state = false; + mutex_unlock(&hdmi_cec_data.lock); +} + /*! * @brief IO ctrl function for vpu file operation * @param cmd IO ctrl command @@ -402,93 +386,59 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, int ret = 0, status = 0; u8 val = 0, msg = 0; struct mxc_edid_cfg hdmi_edid_cfg; - pr_debug("function : %s\n", __func__); if (!open_count) return -ENODEV; - switch (cmd) { case HDMICEC_IOC_SETLOGICALADDRESS: mutex_lock(&hdmi_cec_data.lock); if (false == hdmi_cec_data.cec_state) { mutex_unlock(&hdmi_cec_data.lock); + pr_err("Trying to set logical address while not started\n"); return -EACCES; } - hdmi_cec_data.Logical_address = (u8)arg; - if (hdmi_cec_data.Logical_address <= 7) { val = 1 << hdmi_cec_data.Logical_address; hdmi_writeb(val, HDMI_CEC_ADDR_L); hdmi_writeb(0, HDMI_CEC_ADDR_H); - } else if (hdmi_cec_data.Logical_address > 7 && - hdmi_cec_data.Logical_address <= 15) { + } else if (hdmi_cec_data.Logical_address > 7 && hdmi_cec_data.Logical_address <= 15) { val = 1 << (hdmi_cec_data.Logical_address - 8); hdmi_writeb(val, HDMI_CEC_ADDR_H); hdmi_writeb(0, HDMI_CEC_ADDR_L); - } else { + } else ret = -EINVAL; - } - - /* Send Polling message with same source - * and destination address - */ + /*Send Polling message with same source and destination address*/ if (0 == ret && 15 != hdmi_cec_data.Logical_address) { - msg = (hdmi_cec_data.Logical_address << 4) | - hdmi_cec_data.Logical_address; + msg = (hdmi_cec_data.Logical_address << 4)|hdmi_cec_data.Logical_address; hdmi_writeb(1, HDMI_CEC_TX_CNT); hdmi_writeb(msg, HDMI_CEC_TX_DATA0); - val = hdmi_readb(HDMI_CEC_CTRL); val |= 0x01; hdmi_writeb(val, HDMI_CEC_CTRL); } - mutex_unlock(&hdmi_cec_data.lock); break; - case HDMICEC_IOC_STARTDEVICE: val = hdmi_readb(HDMI_MC_CLKDIS); val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; hdmi_writeb(val, HDMI_MC_CLKDIS); - hdmi_writeb(0x02, HDMI_CEC_CTRL); - - val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | - HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; + /* Force read unlock */ + hdmi_writeb(0x0, HDMI_CEC_LOCK); + val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; hdmi_writeb(val, HDMI_CEC_POLARITY); - - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | - HDMI_IH_CEC_STAT0_ARB_LOST; + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; hdmi_writeb(val, HDMI_CEC_MASK); hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - + hdmi_cec_data.link_status = hdmi_readb(HDMI_PHY_STAT0) & 0x02; mutex_lock(&hdmi_cec_data.lock); hdmi_cec_data.cec_state = true; mutex_unlock(&hdmi_cec_data.lock); break; - case HDMICEC_IOC_STOPDEVICE: - hdmi_writeb(0x10, HDMI_CEC_CTRL); - - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | - HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | - HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | - HDMI_IH_CEC_STAT0_DONE; - hdmi_writeb(val, HDMI_CEC_MASK); - hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - - hdmi_writeb(0x0, HDMI_CEC_POLARITY); - - val = hdmi_readb(HDMI_MC_CLKDIS); - val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = false; - mutex_unlock(&hdmi_cec_data.lock); + hdmi_stop_device(); break; - case HDMICEC_IOC_GETPHYADDRESS: hdmi_get_edid_cfg(&hdmi_edid_cfg); status = copy_to_user((void __user *)arg, @@ -497,29 +447,25 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, if (status) ret = -EFAULT; break; - default: ret = -EINVAL; break; } - - return ret; + return ret; } /*! -* @brief Release function for vpu file operation -* @return 0 on success or negative error code on error -*/ + * @brief Release function for vpu file operation + * @return 0 on success or negative error code on error + */ static int hdmi_cec_release(struct inode *inode, struct file *filp) { mutex_lock(&hdmi_cec_data.lock); - if (open_count) { open_count = 0; hdmi_cec_data.cec_state = false; hdmi_cec_data.Logical_address = 15; } - mutex_unlock(&hdmi_cec_data.lock); return 0; @@ -531,20 +477,18 @@ static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait) pr_debug("function : %s\n", __func__); - if (!open_count) - return -ENODEV; - - if (false == hdmi_cec_data.cec_state) - return -EACCES; - poll_wait(file, &hdmi_cec_queue, wait); + /* Always writable */ + mask = (POLLOUT | POLLWRNORM); + mutex_lock(&hdmi_cec_data.lock); if (!list_empty(&head)) - mask |= (POLLIN | POLLRDNORM); - + mask |= (POLLIN | POLLRDNORM); + mutex_unlock(&hdmi_cec_data.lock); return mask; } + const struct file_operations hdmi_cec_fops = { .owner = THIS_MODULE, .read = hdmi_cec_read, @@ -563,20 +507,18 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) struct pinctrl *pinctrl; int irq = platform_get_irq(pdev, 0); - hdmi_cec_major = register_chrdev(hdmi_cec_major, - "mxc_hdmi_cec", &hdmi_cec_fops); + hdmi_cec_major = register_chrdev(hdmi_cec_major, "mxc_hdmi_cec", &hdmi_cec_fops); if (hdmi_cec_major < 0) { dev_err(&pdev->dev, "hdmi_cec: unable to get a major for HDMI CEC\n"); err = -EBUSY; goto out; } - + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (unlikely(res == NULL)) { dev_err(&pdev->dev, "hdmi_cec:No HDMI irq line provided\n"); goto err_out_chrdev; } - spin_lock_init(&hdmi_cec_data.irq_lock); err = devm_request_irq(&pdev->dev, irq, mxc_hdmi_cec_isr, IRQF_SHARED, @@ -592,8 +534,8 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) goto err_out_chrdev; } - temp_class = device_create(hdmi_cec_class, NULL, - MKDEV(hdmi_cec_major, 0), NULL, "mxc_hdmi_cec"); + temp_class = device_create(hdmi_cec_class, NULL, MKDEV(hdmi_cec_major, 0), + NULL, "mxc_hdmi_cec"); if (IS_ERR(temp_class)) { err = PTR_ERR(temp_class); goto err_out_class; @@ -606,15 +548,14 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) } init_waitqueue_head(&hdmi_cec_queue); + init_waitqueue_head(&tx_cec_queue); INIT_LIST_HEAD(&head); mutex_init(&hdmi_cec_data.lock); - hdmi_cec_data.Logical_address = 15; - + hdmi_cec_data.tx_answer = CEC_TX_AVAIL; platform_set_drvdata(pdev, &hdmi_cec_data); - INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker); dev_info(&pdev->dev, "HDMI CEC initialized\n"); @@ -631,12 +572,14 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) static int hdmi_cec_dev_remove(struct platform_device *pdev) { + if (hdmi_cec_data.cec_state) + hdmi_stop_device(); if (hdmi_cec_major > 0) { device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0)); class_destroy(hdmi_cec_class); unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec"); hdmi_cec_major = 0; - } +} return 0; } @@ -650,9 +593,9 @@ static struct platform_driver mxc_hdmi_cec_driver = { .probe = hdmi_cec_dev_probe, .remove = hdmi_cec_dev_remove, .driver = { - .name = "mxc_hdmi_cec", + .name = "mxc_hdmi_cec", .of_match_table = imx_hdmi_cec_match, - }, + }, }; module_platform_driver(mxc_hdmi_cec_driver); diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index eea87a7ab6e380..68ea29551ea37c 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -1696,8 +1696,12 @@ static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi) hdmi_writeb(0x16, HDMI_FC_CH1PREAM); hdmi_writeb(0x21, HDMI_FC_CH2PREAM); + /* Save CEC clock */ + clkdis = hdmi_readb(HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE; + clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; + /* Enable pixel clock and tmds data path */ - clkdis = 0x7F; + clkdis = 0x7F & clkdis; clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; hdmi_writeb(clkdis, HDMI_MC_CLKDIS); @@ -1977,10 +1981,16 @@ static void mxc_hdmi_power_off(struct mxc_dispdrv_handle *disp, static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) { + u8 clkdis; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + /* Save CEC clock */ + clkdis = hdmi_readb(HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE; + clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; + /* Disable All HDMI clock */ - hdmi_writeb(0xff, HDMI_MC_CLKDIS); + hdmi_writeb(0xff & clkdis, HDMI_MC_CLKDIS); mxc_hdmi_phy_disable(hdmi); From 9e67c55b9bf38dd8592595114373fe900e940c68 Mon Sep 17 00:00:00 2001 From: Robert Winkler Date: Fri, 19 Jul 2013 19:00:41 -0700 Subject: [PATCH 0048/1983] Add support for DVI monitors This is a combination patch that merges all the work done to better support DVI monitors. Initial patch Signed-off-by: Robert Winkler dynamic hot-plug interrupt setup (to return original HDMI hot plug event generation for std HDMI mode, while keeping DVI enhancements if we work in DVI mode) Disable interrupts while screen is blanked and in DVI mode. Many DVI monitors don't properly support hotplug and instead continually poll the rx signals every few seconds to see if the host is alive. This will be picked up as a hotplug event and wake everything up. Handling non-CEA edid parsing and configuration. This includes checking for a non-HDMI connection in the audio driver startup and bailing out. Default to HDMI mode if we fail to get an edid. If we don't receive a valid EDID default to HDMI mode. This is an HDMI port and selecting DVI while getting a wider range of resolutions you lose support for audio and cec. Lets choose modern and more functional interface as the fallback default. Co-Authored-By: Matus Kral Co-Authored-By: Jon Nettleton --- drivers/mfd/mxc-hdmi-core.c | 20 +++- drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 57 +++++++---- drivers/video/mxc/mxc_hdmi.c | 150 ++++++++++++++++------------ include/linux/mfd/mxc-hdmi-core.h | 8 +- include/video/mxc_hdmi.h | 7 ++ sound/soc/fsl/fsl_hdmi.c | 15 +-- 6 files changed, 163 insertions(+), 94 deletions(-) diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 76e79b7d9cfda8..bb5b8bce38dac6 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -39,7 +39,6 @@ #include #include #include -#include struct mxc_hdmi_data { struct platform_device *pdev; @@ -60,6 +59,7 @@ static struct clk *pixel_clk; static int hdmi_ratio; int mxc_hdmi_ipu_id; int mxc_hdmi_disp_id; +static int hdmi_core_edid_status; static struct mxc_edid_cfg hdmi_core_edid_cfg; static int hdmi_core_init; static unsigned int hdmi_dma_running; @@ -69,6 +69,17 @@ static unsigned int hdmi_blank_state; static unsigned int hdmi_abort_state; static spinlock_t hdmi_audio_lock, hdmi_blank_state_lock, hdmi_cable_state_lock; +void hdmi_set_dvi_mode(unsigned int state) +{ + if (state) { + mxc_hdmi_abort_stream(); + hdmi_cec_stop_device(); + } else { + hdmi_cec_start_device(); + } +} +EXPORT_SYMBOL(hdmi_set_dvi_mode); + unsigned int hdmi_set_cable_state(unsigned int state) { unsigned long flags; @@ -587,23 +598,26 @@ void hdmi_set_sample_rate(unsigned int rate) } EXPORT_SYMBOL(hdmi_set_sample_rate); -void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg) +void hdmi_set_edid_cfg(int edid_status, struct mxc_edid_cfg *cfg) { unsigned long flags; spin_lock_irqsave(&edid_spinlock, flags); + hdmi_core_edid_status = edid_status; memcpy(&hdmi_core_edid_cfg, cfg, sizeof(struct mxc_edid_cfg)); spin_unlock_irqrestore(&edid_spinlock, flags); } EXPORT_SYMBOL(hdmi_set_edid_cfg); -void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg) +int hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg) { unsigned long flags; spin_lock_irqsave(&edid_spinlock, flags); memcpy(cfg, &hdmi_core_edid_cfg, sizeof(struct mxc_edid_cfg)); spin_unlock_irqrestore(&edid_spinlock, flags); + + return hdmi_core_edid_status; } EXPORT_SYMBOL(hdmi_get_edid_cfg); diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c index 3a8aff40a86abb..ac70a0cb7ca404 100644 --- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c @@ -83,6 +83,8 @@ struct hdmi_cec_event { static LIST_HEAD(head); +static int hdmi_cec_ready = 0; +static int hdmi_cec_started; static int hdmi_cec_major; static struct class *hdmi_cec_class; static struct hdmi_cec_priv hdmi_cec_data; @@ -356,11 +358,40 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, return ret; } +void hdmi_cec_start_device(void) +{ + u8 val; + + if (!hdmi_cec_ready || hdmi_cec_started) + return; + + val = hdmi_readb(HDMI_MC_CLKDIS); + val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + hdmi_writeb(0x02, HDMI_CEC_CTRL); + /* Force read unlock */ + hdmi_writeb(0x0, HDMI_CEC_LOCK); + val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; + hdmi_writeb(val, HDMI_CEC_POLARITY); + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; + hdmi_writeb(val, HDMI_CEC_MASK); + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + hdmi_cec_data.link_status = hdmi_readb(HDMI_PHY_STAT0) & 0x02; + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.cec_state = true; + mutex_unlock(&hdmi_cec_data.lock); -static void hdmi_stop_device(void) + hdmi_cec_started = 1; +} +EXPORT_SYMBOL(hdmi_cec_start_device); + +void hdmi_cec_stop_device(void) { u8 val; + if (!hdmi_cec_ready || !hdmi_cec_started) + return; + hdmi_writeb(0x10, HDMI_CEC_CTRL); val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | \ HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; @@ -373,7 +404,10 @@ static void hdmi_stop_device(void) mutex_lock(&hdmi_cec_data.lock); hdmi_cec_data.cec_state = false; mutex_unlock(&hdmi_cec_data.lock); + + hdmi_cec_started = 0; } +EXPORT_SYMBOL(hdmi_cec_stop_device); /*! * @brief IO ctrl function for vpu file operation @@ -420,24 +454,10 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, mutex_unlock(&hdmi_cec_data.lock); break; case HDMICEC_IOC_STARTDEVICE: - val = hdmi_readb(HDMI_MC_CLKDIS); - val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - hdmi_writeb(0x02, HDMI_CEC_CTRL); - /* Force read unlock */ - hdmi_writeb(0x0, HDMI_CEC_LOCK); - val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; - hdmi_writeb(val, HDMI_CEC_POLARITY); - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; - hdmi_writeb(val, HDMI_CEC_MASK); - hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - hdmi_cec_data.link_status = hdmi_readb(HDMI_PHY_STAT0) & 0x02; - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = true; - mutex_unlock(&hdmi_cec_data.lock); + hdmi_cec_start_device(); break; case HDMICEC_IOC_STOPDEVICE: - hdmi_stop_device(); + hdmi_cec_stop_device(); break; case HDMICEC_IOC_GETPHYADDRESS: hdmi_get_edid_cfg(&hdmi_edid_cfg); @@ -559,6 +579,7 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker); dev_info(&pdev->dev, "HDMI CEC initialized\n"); + hdmi_cec_ready = 1; goto out; err_out_class: @@ -573,7 +594,7 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) static int hdmi_cec_dev_remove(struct platform_device *pdev) { if (hdmi_cec_data.cec_state) - hdmi_stop_device(); + hdmi_cec_stop_device(); if (hdmi_cec_major > 0) { device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0)); class_destroy(hdmi_cec_class); diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 68ea29551ea37c..43a54585809d1d 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -162,6 +162,7 @@ struct mxc_hdmi { struct hdmi_data_info hdmi_data; int vic; + int edid_status; struct mxc_edid_cfg edid_cfg; u8 edid[HDMI_EDID_LEN]; bool fb_reg; @@ -171,6 +172,8 @@ struct mxc_hdmi { char *dft_mode_str; int default_bpp; u8 latest_intr_stat; + u8 plug_event; + u8 plug_mask; bool irq_enabled; spinlock_t irq_lock; bool phy_enabled; @@ -1581,6 +1584,10 @@ static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid, ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH, cfg, &fbi->monspecs); if (ret < 0) + fb_edid_add_monspecs(edid + EDID_LENGTH, &fbi->monspecs); + if (fbi->monspecs.modedb_len > 0) + hdmi->edid_cfg.hdmi_cap = false; + else return -ENOENT; } @@ -1634,15 +1641,15 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) clkdis &= ~HDMI_MC_CLKDIS_HDCPCLK_DISABLE; hdmi_writeb(clkdis, HDMI_MC_CLKDIS); } - } if (ret < 0) { dev_dbg(&hdmi->pdev->dev, "read failed\n"); return HDMI_EDID_FAIL; } - /* Save edid cfg for audio driver */ - hdmi_set_edid_cfg(&hdmi->edid_cfg); + dev_info(&hdmi->pdev->dev, "%s HDMI in %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap?"HDMI":"DVI"); + hdmi->plug_event = hdmi->edid_cfg.hdmi_cap?HDMI_IH_PHY_STAT0_HPD:HDMI_DVI_IH_STAT; + hdmi->plug_mask = hdmi->edid_cfg.hdmi_cap?HDMI_PHY_HPD:HDMI_DVI_STAT; if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) { dev_info(&hdmi->pdev->dev, "same edid\n"); @@ -1807,20 +1814,21 @@ static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi) */ mode = &hdmi->fbi->monspecs.modedb[i]; - if (!(mode->vmode & FB_VMODE_INTERLACED) && - (mxc_edid_mode_to_vic(mode) != 0)) { + if (hdmi->edid_cfg.hdmi_cap && + (mode->vmode & FB_VMODE_INTERLACED) && + (mxc_edid_mode_to_vic(mode) == 0)) + continue; - dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); - dev_dbg(&hdmi->pdev->dev, - "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n", - hdmi->fbi->monspecs.modedb[i].xres, - hdmi->fbi->monspecs.modedb[i].yres, - hdmi->fbi->monspecs.modedb[i].refresh, - hdmi->fbi->monspecs.modedb[i].vmode, - hdmi->fbi->monspecs.modedb[i].flag); + dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); + dev_dbg(&hdmi->pdev->dev, + "xres = %d, yres = %d, freq = %d, vmode = %d, flag = %d\n", + hdmi->fbi->monspecs.modedb[i].xres, + hdmi->fbi->monspecs.modedb[i].yres, + hdmi->fbi->monspecs.modedb[i].refresh, + hdmi->fbi->monspecs.modedb[i].vmode, + hdmi->fbi->monspecs.modedb[i].flag); - fb_add_videomode(mode, &hdmi->fbi->modelist); - } + fb_add_videomode(mode, &hdmi->fbi->modelist); } fb_new_modelist(hdmi->fbi); @@ -1917,18 +1925,16 @@ static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi) static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) { - int edid_status; - dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); hdmi->cable_plugin = true; /* HDMI Initialization Step C */ - edid_status = mxc_hdmi_read_edid(hdmi); + hdmi->edid_status = mxc_hdmi_read_edid(hdmi); /* Read EDID again if first EDID read failed */ - if (edid_status == HDMI_EDID_NO_MODES || - edid_status == HDMI_EDID_FAIL) { + if (hdmi->edid_status == HDMI_EDID_NO_MODES || + hdmi->edid_status == HDMI_EDID_FAIL) { int retry_status; dev_info(&hdmi->pdev->dev, "Read EDID again\n"); msleep(200); @@ -1936,11 +1942,11 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we * want NO_MODES as final result. */ if (retry_status != HDMI_EDID_SAME) - edid_status = retry_status; + hdmi->edid_status = retry_status; } /* HDMI Initialization Steps D, E, F */ - switch (edid_status) { + switch (hdmi->edid_status) { case HDMI_EDID_SUCCESS: mxc_hdmi_edid_rebuild_modelist(hdmi); break; @@ -1958,6 +1964,9 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) break; } + /* Save edid cfg for audio driver */ + hdmi_set_edid_cfg(hdmi->edid_status, &hdmi->edid_cfg); + /* Setting video mode */ mxc_hdmi_set_mode(hdmi); @@ -2004,58 +2013,44 @@ static void hotplug_worker(struct work_struct *work) struct delayed_work *delay_work = to_delayed_work(work); struct mxc_hdmi *hdmi = container_of(delay_work, struct mxc_hdmi, hotplug_work); - u32 phy_int_stat, phy_int_pol, phy_int_mask; - u8 val; + u32 hdmi_phy_stat0, hdmi_phy_pol0, hdmi_phy_mask0; unsigned long flags; char event_string[32]; char *envp[] = { event_string, NULL }; - phy_int_stat = hdmi->latest_intr_stat; - phy_int_pol = hdmi_readb(HDMI_PHY_POL0); + hdmi_phy_stat0 = hdmi_readb(HDMI_PHY_STAT0); + hdmi_phy_pol0 = hdmi_readb(HDMI_PHY_POL0); - dev_dbg(&hdmi->pdev->dev, "phy_int_stat=0x%x, phy_int_pol=0x%x\n", - phy_int_stat, phy_int_pol); + if (hdmi->latest_intr_stat & hdmi->plug_event) { + /* Make HPD intr active low to capture unplug event or + * active high to capture plugin event */ + hdmi_writeb((hdmi->plug_mask & ~hdmi_phy_pol0), HDMI_PHY_POL0); - /* check cable status */ - if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) { - /* cable connection changes */ - if (phy_int_pol & HDMI_PHY_HPD) { + /* check cable status */ + if (hdmi_phy_stat0 & hdmi->plug_mask) { /* Plugin event */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); mxc_hdmi_cable_connected(hdmi); - /* Make HPD intr active low to capture unplug event */ - val = hdmi_readb(HDMI_PHY_POL0); - val &= ~HDMI_PHY_HPD; - hdmi_writeb(val, HDMI_PHY_POL0); - - hdmi_set_cable_state(1); - sprintf(event_string, "EVENT=plugin"); kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); #ifdef CONFIG_MXC_HDMI_CEC mxc_hdmi_cec_handle(0x80); #endif - } else if (!(phy_int_pol & HDMI_PHY_HPD)) { + hdmi_set_cable_state(1); + } else { /* Plugout event */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); hdmi_set_cable_state(0); mxc_hdmi_abort_stream(); mxc_hdmi_cable_disconnected(hdmi); - /* Make HPD intr active high to capture plugin event */ - val = hdmi_readb(HDMI_PHY_POL0); - val |= HDMI_PHY_HPD; - hdmi_writeb(val, HDMI_PHY_POL0); - sprintf(event_string, "EVENT=plugout"); kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); #ifdef CONFIG_MXC_HDMI_CEC mxc_hdmi_cec_handle(0x100); #endif - - } else - dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n"); + } } /* Lock here to ensure full powerdown sequence @@ -2063,12 +2058,12 @@ static void hotplug_worker(struct work_struct *work) spin_lock_irqsave(&hdmi->irq_lock, flags); /* Re-enable HPD interrupts */ - phy_int_mask = hdmi_readb(HDMI_PHY_MASK0); - phy_int_mask &= ~HDMI_PHY_HPD; - hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0); + hdmi_phy_mask0 = hdmi_readb(HDMI_PHY_MASK0); + hdmi_phy_mask0 &= ~hdmi->plug_mask; + hdmi_writeb(hdmi_phy_mask0, HDMI_PHY_MASK0); /* Unmute interrupts */ - hdmi_writeb(~HDMI_IH_MUTE_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + hdmi_writeb(~hdmi->plug_event, HDMI_IH_MUTE_PHY_STAT0); if (hdmi_readb(HDMI_IH_FC_STAT2) & HDMI_IH_FC_STAT2_OVERFLOW_MASK) mxc_hdmi_clear_overflow(hdmi); @@ -2117,8 +2112,7 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) */ /* Capture status - used in hotplug_worker ISR */ intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0); - - if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { + if (intr_stat & hdmi->plug_event) { dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); hdmi->latest_intr_stat = intr_stat; @@ -2126,15 +2120,15 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) /* Mute interrupts until handled */ val = hdmi_readb(HDMI_IH_MUTE_PHY_STAT0); - val |= HDMI_IH_MUTE_PHY_STAT0_HPD; + val |= hdmi->plug_event; hdmi_writeb(val, HDMI_IH_MUTE_PHY_STAT0); val = hdmi_readb(HDMI_PHY_MASK0); - val |= HDMI_PHY_HPD; + val |= hdmi->plug_mask; hdmi_writeb(val, HDMI_PHY_MASK0); /* Clear Hotplug interrupts */ - hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi->plug_event, HDMI_IH_PHY_STAT0); schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20)); } @@ -2185,9 +2179,11 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) hdmi_disable_overflow_interrupts(); dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); - if (hdmi->edid_cfg.hdmi_cap) + if (hdmi->edid_cfg.hdmi_cap || !hdmi->edid_status) { + hdmi_set_dvi_mode(0); hdmi->hdmi_data.video_mode.mDVI = false; - else { + } else { + hdmi_set_dvi_mode(1); dev_dbg(&hdmi->pdev->dev, "CEA mode vic=%d work in DVI\n", hdmi->vic); hdmi->hdmi_data.video_mode.mDVI = true; } @@ -2286,13 +2282,13 @@ static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) HDMI_PHY_I2CM_CTLINT_ADDR); /* enable cable hot plug irq */ - hdmi_writeb((u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0); + hdmi_writeb(~hdmi->plug_mask, HDMI_PHY_MASK0); /* Clear Hotplug interrupts */ - hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi->plug_event, HDMI_IH_PHY_STAT0); /* Unmute interrupts */ - hdmi_writeb(~HDMI_IH_MUTE_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + hdmi_writeb(~hdmi->plug_event, HDMI_IH_MUTE_PHY_STAT0); hdmi->fb_reg = true; @@ -2336,10 +2332,17 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, hdmi->blank = *((int *)event->data); + /* Re-enable HPD interrupts */ + val = hdmi_readb(HDMI_PHY_MASK0); + val &= ~hdmi->plug_mask; + hdmi_writeb(val, HDMI_PHY_MASK0); + + /* Unmute interrupts */ + hdmi_writeb(~hdmi->plug_event, HDMI_IH_MUTE_PHY_STAT0); + if (hdmi->fb_reg && hdmi->cable_plugin) mxc_hdmi_setup(hdmi, val); hdmi_set_blank_state(1); - } else if (*((int *)event->data) != hdmi->blank) { dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_BLANK - BLANK\n"); @@ -2348,6 +2351,20 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, mxc_hdmi_phy_disable(hdmi); + if(hdmi->plug_mask == HDMI_DVI_STAT) { + u8 val; + pr_debug("In DVI Mode, disabling hotplug interrupts until unblanked\n"); + val = hdmi_readb(HDMI_IH_MUTE_PHY_STAT0); + val |= hdmi->plug_event; + hdmi_writeb(val, HDMI_IH_MUTE_PHY_STAT0); + + val = hdmi_readb(HDMI_PHY_MASK0); + val |= hdmi->plug_mask; + hdmi_writeb(val, HDMI_PHY_MASK0); + + hdmi_set_dvi_mode(1); + } + hdmi->blank = *((int *)event->data); } else dev_dbg(&hdmi->pdev->dev, @@ -2619,15 +2636,18 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, /* Default setting HDMI working in HDMI mode*/ hdmi->edid_cfg.hdmi_cap = true; + hdmi->plug_event = HDMI_DVI_IH_STAT; + hdmi->plug_mask = HDMI_DVI_STAT; + INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker); INIT_DELAYED_WORK(&hdmi->hdcp_hdp_work, hdcp_hdp_worker); /* Configure registers related to HDMI interrupt * generation before registering IRQ. */ - hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0); + hdmi_writeb(hdmi->plug_mask, HDMI_PHY_POL0); /* Clear Hotplug interrupts */ - hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi->plug_event, HDMI_IH_PHY_STAT0); hdmi->nb.notifier_call = mxc_hdmi_fb_event; ret = fb_register_client(&hdmi->nb); diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h index a675c1921e95aa..475dece3543426 100644 --- a/include/linux/mfd/mxc-hdmi-core.h +++ b/include/linux/mfd/mxc-hdmi-core.h @@ -46,8 +46,8 @@ void hdmi_set_dma_mode(unsigned int dma_running); void hdmi_init_clk_regenerator(void); void hdmi_clk_regenerator_update_pixel_clock(u32 pixclock); -void hdmi_set_edid_cfg(struct mxc_edid_cfg *cfg); -void hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg); +void hdmi_set_edid_cfg(int edid_status, struct mxc_edid_cfg *cfg); +int hdmi_get_edid_cfg(struct mxc_edid_cfg *cfg); extern int mxc_hdmi_ipu_id; extern int mxc_hdmi_disp_id; @@ -57,8 +57,12 @@ int hdmi_get_registered(void); int mxc_hdmi_abort_stream(void); int mxc_hdmi_register_audio(struct snd_pcm_substream *substream); void mxc_hdmi_unregister_audio(struct snd_pcm_substream *substream); +void hdmi_set_dvi_mode(unsigned int state); unsigned int hdmi_set_cable_state(unsigned int state); unsigned int hdmi_set_blank_state(unsigned int state); int check_hdmi_state(void); +void hdmi_cec_start_device(void); +void hdmi_cec_stop_device(void); + #endif diff --git a/include/video/mxc_hdmi.h b/include/video/mxc_hdmi.h index 6ed17492e8e9f3..7f6694253152ca 100644 --- a/include/video/mxc_hdmi.h +++ b/include/video/mxc_hdmi.h @@ -580,6 +580,10 @@ enum { HDMI_IH_MUTE_PHY_STAT0_TX_PHY_LOCK = 0x2, HDMI_IH_MUTE_PHY_STAT0_HPD = 0x1, +/* IH and IH_MUTE convenience macro RX_SENSE | HPD*/ + HDMI_DVI_IH_STAT = 0x3D, + + /* IH_AHBDMAAUD_STAT0 field values */ HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20, HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10, @@ -875,6 +879,9 @@ enum { HDMI_PHY_HPD = 0x02, HDMI_PHY_TX_PHY_LOCK = 0x01, +/* HDMI STAT convenience RX_SENSE | HPD */ + HDMI_DVI_STAT = 0xF2, + /* PHY_I2CM_SLAVE_ADDR field values */ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69, HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49, diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index 4f2ac274b85c2c..f7144e22114add 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -334,9 +334,12 @@ static void fsl_hdmi_get_playback_channels(void) static int fsl_hdmi_update_constraints(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - int ret; + int edid_status, ret; - hdmi_get_edid_cfg(&edid_cfg); + edid_status = hdmi_get_edid_cfg(&edid_cfg); + + if (edid_status && !edid_cfg.hdmi_cap) + return -1; fsl_hdmi_get_playback_rates(); ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, @@ -369,6 +372,10 @@ static int fsl_hdmi_soc_startup(struct snd_pcm_substream *substream, struct imx_hdmi *hdmi_data = snd_soc_dai_get_drvdata(dai); int ret; + ret = fsl_hdmi_update_constraints(substream); + if (ret < 0) + return ret; + clk_prepare_enable(hdmi_data->mipi_core_clk); clk_prepare_enable(hdmi_data->isfr_clk); clk_prepare_enable(hdmi_data->iahb_clk); @@ -378,10 +385,6 @@ static int fsl_hdmi_soc_startup(struct snd_pcm_substream *substream, (int)clk_get_rate(hdmi_data->isfr_clk), (int)clk_get_rate(hdmi_data->iahb_clk)); - ret = fsl_hdmi_update_constraints(substream); - if (ret < 0) - return ret; - /* Indicates the subpacket represents a flatline sample */ hdmi_audio_writeb(FC_AUDSCONF, AUD_PACKET_SAMPFIT, 0x0); From 34e45fc5b7f7ab3501f0910fd5b75af716ee832f Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 22 Aug 2014 09:12:48 +0200 Subject: [PATCH 0049/1983] video: mxc_hdmi: add userspace configuration of rgb_quant_range This adds the module parameter and sysfs node that allows a user to change the rgb_quant_range parameter between three values, default, limited, and full. --- drivers/video/mxc/mxc_hdmi.c | 70 +++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 43a54585809d1d..8b5cb0d4deaa92 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -26,6 +26,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #include #include #include @@ -138,6 +139,7 @@ struct hdmi_data_info { unsigned int pix_repet_factor; unsigned int hdcp_enable; unsigned int rgb_out_enable; + unsigned int rgb_quant_range; struct hdmi_vmode video_mode; }; @@ -207,6 +209,10 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event); static void hdmi_enable_overflow_interrupts(void); static void hdmi_disable_overflow_interrupts(void); +static char *rgb_quant_range = "default"; +module_param(rgb_quant_range, charp, S_IRUGO); +MODULE_PARM_DESC(rgb_quant_range, "RGB Quant Range (default, limited, full)"); + static struct platform_device_id imx_hdmi_devtype[] = { { .name = "hdmi-imx6DL", @@ -326,6 +332,55 @@ static DEVICE_ATTR(rgb_out_enable, S_IRUGO | S_IWUSR, mxc_hdmi_show_rgb_out_enable, mxc_hdmi_store_rgb_out_enable); +static ssize_t mxc_hdmi_show_rgb_quant_range(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + + switch (hdmi->hdmi_data.rgb_quant_range) { + case HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE: + strcpy(buf, "limited\n"); + break; + case HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE: + strcpy(buf, "full\n"); + break; + case HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT: + default: + strcpy(buf, "default\n"); + break; + }; + + return strlen(buf); +} + +static ssize_t mxc_hdmi_store_rgb_quant_range(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + int ret = count; + + if (sysfs_streq("limited", buf)) { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE; + } else if (sysfs_streq("full", buf)) { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE; + } else if (sysfs_streq("default", buf)) { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT; + } else { + ret = -EINVAL; + goto out; + } + + /* Reconfig HDMI for output RGB Quant Range change if using RGB out */ + if(hdmi->hdmi_data.rgb_out_enable) + mxc_hdmi_setup(hdmi, 0); +out: + return ret; +} + +static DEVICE_ATTR(rgb_quant_range, S_IRUGO | S_IWUSR, + mxc_hdmi_show_rgb_quant_range, + mxc_hdmi_store_rgb_quant_range); + static ssize_t mxc_hdmi_show_hdcp_enable(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1380,7 +1435,7 @@ static void hdmi_config_AVI(struct mxc_hdmi *hdmi) ********************************************/ val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry | - HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT | + hdmi->hdmi_data.rgb_quant_range | HDMI_FC_AVICONF2_SCALING_NONE; hdmi_writeb(val, HDMI_FC_AVICONF2); @@ -2659,6 +2714,14 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, /* Default HDMI working in RGB mode */ hdmi->hdmi_data.rgb_out_enable = true; + if (!strcasecmp(rgb_quant_range, "limited")) { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE; + } else if (!strcasecmp(rgb_quant_range, "full")) { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE; + } else { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT; + } + ret = devm_request_irq(&hdmi->pdev->dev, irq, mxc_hdmi_hotplug, IRQF_SHARED, dev_name(&hdmi->pdev->dev), hdmi); if (ret < 0) { @@ -2685,6 +2748,11 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, dev_warn(&hdmi->pdev->dev, "cound not create sys node for rgb out enable\n"); + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_rgb_quant_range); + if (ret < 0) + dev_warn(&hdmi->pdev->dev, + "cound not create sys node for rgb quant range\n"); + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_hdcp_enable); if (ret < 0) dev_warn(&hdmi->pdev->dev, From 47eb5c520ac6e5ff46085b9f16afbf3d58df1f04 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 6 Sep 2014 10:18:04 +0200 Subject: [PATCH 0050/1983] video: mxc_hdmi: Use color space converter to limit output color range. This patch enhances commit:9119f4982209edf12dee44240bcfaa52fba2b4d3 to change the actual RGB color range itself. The previous solution would only allow to change what is reported to the sink via AVI info frame. Since overriding the color range is only supported by devices compliant to CEA-861D (i.e. HDMI1.3), the previous method will fail on some TVs (mostly Sony Bravia). This patch implements the following behavior: mode 'full': range 0..255 indicated via AVI info frame Q1,Q0 = 1,0 mode 'limited': range 16..235 indicated via AVI info frame Q1,Q0 = 0,1 mode 'default': range 16..235 for CEA modes, range 0..255 for non-CEA Q1,Q0 = 0,0 In DVI mode, the range is always 0..255 and the color space converter is bypassed. Signed-off-by: Rudi --- drivers/video/mxc/mxc_hdmi.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 8b5cb0d4deaa92..6b361fe2c9f8a3 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -493,8 +493,10 @@ static void hdmi_video_sample(struct mxc_hdmi *hdmi) static int isColorSpaceConversion(struct mxc_hdmi *hdmi) { - return (hdmi->hdmi_data.enc_in_format != - hdmi->hdmi_data.enc_out_format); + return (hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format) || + (hdmi->hdmi_data.enc_out_format == RGB && + ((hdmi->hdmi_data.rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE) || + (hdmi->hdmi_data.rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT && hdmi->vic > 1))); } static int isColorSpaceDecimation(struct mxc_hdmi *hdmi) @@ -523,7 +525,25 @@ static void update_csc_coeffs(struct mxc_hdmi *hdmi) if (isColorSpaceConversion(hdmi)) { /* csc needed */ if (hdmi->hdmi_data.enc_out_format == RGB) { - if (hdmi->hdmi_data.colorimetry == eITU601) { + if (hdmi->hdmi_data.enc_in_format == RGB) { + csc_coeff[0][0] = 0x1b80; + csc_coeff[0][1] = 0x0000; + csc_coeff[0][2] = 0x0000; + csc_coeff[0][3] = 0x0020; + + csc_coeff[1][0] = 0x0000; + csc_coeff[1][1] = 0x1b80; + csc_coeff[1][2] = 0x0000; + csc_coeff[1][3] = 0x0020; + + csc_coeff[2][0] = 0x0000; + csc_coeff[2][1] = 0x0000; + csc_coeff[2][2] = 0x1b80; + csc_coeff[2][3] = 0x0020; + + csc_scale = 1; + coeff_selected = true; + } else if (hdmi->hdmi_data.colorimetry == eITU601) { csc_coeff[0][0] = 0x2000; csc_coeff[0][1] = 0x6926; csc_coeff[0][2] = 0x74fd; @@ -1771,7 +1791,7 @@ static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi) hdmi_writeb(clkdis, HDMI_MC_CLKDIS); /* Enable csc path */ - if (isColorSpaceConversion(hdmi)) { + if (isColorSpaceConversion(hdmi) && !hdmi->hdmi_data.video_mode.mDVI) { clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; hdmi_writeb(clkdis, HDMI_MC_CLKDIS); } From 28a0baa1e383047d6fb1cd2a469296a117855096 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 6 Sep 2014 10:40:15 +0200 Subject: [PATCH 0051/1983] video: mxc_hdmi: Use symbolic constant instead of 0. Signed-off-by: Rudi --- drivers/video/mxc/mxc_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 6b361fe2c9f8a3..80ed39c00c204d 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -702,7 +702,7 @@ static void hdmi_video_csc(struct mxc_hdmi *hdmi) { int color_depth = 0; int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; - int decimation = 0; + int decimation = HDMI_CSC_CFG_DECMODE_DISABLE; u8 val; /* YCC422 interpolation to 444 mode */ From 7ce4d3cd5da342ec282fcb07df7f1b01f4365df9 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 7 Jul 2015 11:57:32 +0200 Subject: [PATCH 0052/1983] dts: hummingboard: Add support for all hardware and FSL drivers This adds everything missing from the upstream dts files to enable all our hardware to work with FSL drivers. --- arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 108 +++++++++++++++----- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index 5c08c409e7aed5..04ad5991a6f026 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -46,9 +46,11 @@ aliases { mmc0 = &usdhc2; mmc1 = &usdhc1; + mxcfb0 = &mxcfb1; }; chosen { + bootargs = "quiet console=ttymxc0,115200 root=/dev/mmcblk0p2 rw"; stdout-path = &uart1; }; @@ -57,6 +59,7 @@ gpios = <&gpio3 5 1>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_gpio3_5>; + linux,rc-map-name = "rc-rc6-mce"; }; regulators { @@ -65,9 +68,6 @@ reg_3p3v: 3p3v { compatible = "regulator-fixed"; regulator-name = "3P3V"; - gpio = <&gpio4 30 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hummingboard_usdhc2_pwr>; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; @@ -93,6 +93,16 @@ regulator-min-microvolt = <5000000>; regulator-max-microvolt = <5000000>; }; + + reg_usdhc2_vbus: usdhc-2-vbus { + compatible = "regulator-fixed"; + regulator-name = "USDHC2-VBUS"; + gpio = <&gpio4 30 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_usdhc2_pwr>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; }; sound-sgtl5000 { @@ -105,7 +115,7 @@ model = "On-board Codec"; mux-ext-port = <5>; mux-int-port = <1>; - ssi-controller = <&ssi1>; + cpu-dai = <&ssi1>; }; sound-spdif { @@ -115,6 +125,24 @@ spdif-controller = <&spdif>; spdif-out; }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; }; &audmux { @@ -124,9 +152,31 @@ &flexcan1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; + status = "disabled"; +}; + +&hdmi_core { + ipu_id = <0>; + disp_id = <0>; status = "okay"; }; +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_hdmi>; + status = "okay"; +}; + &i2c1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_i2c1>; @@ -155,29 +205,17 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_i2c2>; status = "okay"; + + ddc: imx6_hdmi_i2c@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; }; &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; hummingboard { - pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { - fsl,pins = < - MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000 - MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000 - >; - }; - - pinctrl_hummingboard_gpio3_5: hummingboard-gpio3_5 { - fsl,pins = < - MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x1b0b1 - >; - }; - - pinctrl_hummingboard_hdmi: hummingboard-hdmi { - fsl,pins = < - MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 - >; - }; - pinctrl_hog: hoggrp { fsl,pins = < /* @@ -208,6 +246,25 @@ >; }; + pinctrl_hummingboard_flexcan1: hummingboard-flexcan1 { + fsl,pins = < + MX6QDL_PAD_SD3_CLK__FLEXCAN1_RX 0x80000000 + MX6QDL_PAD_SD3_CMD__FLEXCAN1_TX 0x80000000 + >; + }; + + pinctrl_hummingboard_gpio3_5: hummingboard-gpio3_5 { + fsl,pins = < + MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x1b0b1 + >; + }; + + pinctrl_hummingboard_hdmi: hummingboard-hdmi { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + pinctrl_hummingboard_i2c1: hummingboard-i2c1 { fsl,pins = < MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 @@ -255,7 +312,7 @@ * Similar to pinctrl_usbotg_2, but we want it * pulled down for a fixed host connection. */ - fsl,pins = ; + fsl,pins = ; }; pinctrl_hummingboard_usbotg_vbus: hummingboard-usbotg-vbus { @@ -365,7 +422,8 @@ &pinctrl_hummingboard_usdhc2_aux &pinctrl_hummingboard_usdhc2_200mhz >; - vmmc-supply = <®_3p3v>; + + vmmc-supply = <®_usdhc2_vbus>; cd-gpios = <&gpio1 4 0>; status = "okay"; }; From ad94327fcde852ceedea8a1c138d56fc9e981193 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Fri, 6 Jun 2014 22:58:37 +0300 Subject: [PATCH 0053/1983] ASoC: imx-hdmi: Add support for HBR/HD bitstreaming Enable HBR mode when the userspace sets 8 channel output with non-PCM indicated in PCM status bits (AES0 & 0x02). HBR mode allows passthrough/bitstreaming of TrueHD/DTS-HD encoded audio. ALSA needs an appropriate imx-hdmi-soc.conf for most applications to automatically set the AESx bits. Signed-off-by: Anssi Hannula --- include/video/mxc_hdmi.h | 3 ++- sound/soc/fsl/imx-hdmi-dma.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/video/mxc_hdmi.h b/include/video/mxc_hdmi.h index 7f6694253152ca..702549de7d487c 100644 --- a/include/video/mxc_hdmi.h +++ b/include/video/mxc_hdmi.h @@ -917,7 +917,8 @@ enum { /* AHB_DMA_CONF0 field values */ HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7, HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80, - HDMI_AHB_DMA_CONF0_HBR = 0x10, + HDMI_AHB_DMA_CONF0_HBR_OFFSET = 4, + HDMI_AHB_DMA_CONF0_HBR_MASK = 0x10, HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3, HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08, HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1, diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 0df685159e9c70..b2d90e7c23ccec 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -842,6 +842,15 @@ static void hdmi_dma_trigger_init(struct snd_pcm_substream *substream, struct hdmi_dma_priv *priv) { unsigned long status; + bool hbr; + + /* + * Set HBR mode (>192kHz IEC-61937 HD audio bitstreaming). + * This is done this late because userspace may alter the AESx + * parameters until the stream is finally prepared. + */ + hbr = (iec_header.B.linear_pcm != 0 && priv->channels == 8); + hdmi_audio_writeb(AHB_DMA_CONF0, HBR, !!hbr); priv->offset = 0; priv->frame_idx = 0; From 153775173f04026e37853eacef201e5f0e88c9f7 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 10 Aug 2015 20:33:27 +0200 Subject: [PATCH 0054/1983] Revert "ENGR00331799-2 ASoC: fsl_spdif: don't change the root clock rate of spdif in driver" This reverts commit e8d96ea52d83c0b3f6239ad7230e325e7cb7fa96. There is no reason to remove this functionality. It is far more functional to be able to natively support clocking to the full range of standard audio rates. --- sound/soc/fsl/fsl_spdif.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 0e7f37be2b5074..4ee0545dc75071 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1,7 +1,7 @@ /* * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver * - * Copyright (C) 2013-2014 Freescale Semiconductor, Inc. + * Copyright (C) 2013 Freescale Semiconductor, Inc. * * Based on stmp3xxx_spdif_dai.c * Vladimir Barinov @@ -388,6 +388,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, unsigned long csfs = 0; u32 stc, mask, rate; u8 clk, txclk_df, sysclk_df; + int ret; switch (sample_rate) { case 32000: @@ -429,6 +430,21 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, sysclk_df = spdif_priv->sysclk_df[rate]; + /* Don't mess up the clocks from other modules */ + if (clk != STC_TXCLK_SPDIF_ROOT) + goto clk_set_bypass; + + /* + * The S/PDIF block needs a clock of 64 * fs * txclk_df. + * So request 64 * fs * (txclk_df + 1) to get rounded. + */ + ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (txclk_df + 1)); + if (ret) { + dev_err(&pdev->dev, "failed to set tx clock rate\n"); + return ret; + } + +clk_set_bypass: dev_dbg(&pdev->dev, "expected clock rate = %d\n", (64 * sample_rate * txclk_df * sysclk_df)); dev_dbg(&pdev->dev, "actual clock rate = %ld\n", @@ -1085,7 +1101,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, { const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; bool is_sysclk = clk == spdif_priv->sysclk; - u64 rate_actual, sub; + u64 rate_ideal, rate_actual, sub; u32 sysclk_dfmin, sysclk_dfmax; u32 txclk_df, sysclk_df, arate; @@ -1095,7 +1111,11 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, for (sysclk_df = sysclk_dfmin; sysclk_df <= sysclk_dfmax; sysclk_df++) { for (txclk_df = 1; txclk_df <= 128; txclk_df++) { - rate_actual = clk_get_rate(clk); + rate_ideal = rate[index] * (txclk_df + 1) * 64; + if (round) + rate_actual = clk_round_rate(clk, rate_ideal); + else + rate_actual = clk_get_rate(clk); arate = rate_actual / 64; arate /= txclk_df * sysclk_df; From f741852fb7adc614a76de674d4da26f78c89d778 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 11 Aug 2015 07:10:13 +0200 Subject: [PATCH 0055/1983] Revert "ENGR00318063-13: ARM: imx: do not reparent during clk_set_rate" This reverts commit cde3cb071c9b45f1c46b855041a8f81e28c8654e. --- arch/arm/mach-imx/clk-busy.c | 2 +- arch/arm/mach-imx/clk-fixup-mux.c | 1 - arch/arm/mach-imx/clk.h | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-imx/clk-busy.c b/arch/arm/mach-imx/clk-busy.c index 45f17c7ca1f3f6..d8f28741fc6cd4 100644 --- a/arch/arm/mach-imx/clk-busy.c +++ b/arch/arm/mach-imx/clk-busy.c @@ -175,7 +175,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, init.name = name; init.ops = &clk_busy_mux_ops; - init.flags = CLK_SET_RATE_NO_REPARENT; + init.flags = 0; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/arch/arm/mach-imx/clk-fixup-mux.c b/arch/arm/mach-imx/clk-fixup-mux.c index caafa869f6f4e2..0d40b35c557cba 100644 --- a/arch/arm/mach-imx/clk-fixup-mux.c +++ b/arch/arm/mach-imx/clk-fixup-mux.c @@ -88,7 +88,6 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, init.name = name; init.ops = &clk_fixup_mux_ops; - init.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_PARENT_GATE; init.parent_names = parents; init.num_parents = num_parents; init.flags = 0; diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 043bd6ab805329..3ed56512013169 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -173,8 +173,8 @@ static inline struct clk *imx_clk_mux_flags(const char *name, int num_parents, unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, - flags | CLK_SET_RATE_NO_REPARENT | CLK_SET_PARENT_GATE, - reg, shift, width, 0, &imx_ccm_lock); + flags | CLK_SET_PARENT_GATE, reg, shift, width, 0, + &imx_ccm_lock); } static inline struct clk *imx_clk_mux_flags_bus(const char *name, @@ -191,7 +191,7 @@ static inline struct clk *imx_clk_mux_glitchless(const char *name, int num_parents) { return clk_register_mux(NULL, name, parents, num_parents, - CLK_SET_RATE_NO_REPARENT, reg, shift, + 0, reg, shift, width, 0, &imx_ccm_lock); } From 805eb3f1122b6bc116d84b443f00d03e87f8d25f Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 11 Aug 2015 08:45:37 +0200 Subject: [PATCH 0056/1983] Revert "ENGR00329450 ARM: imx: set CLK_SET_RATE_GATE for gate and divider clocks" This reverts commit c6ad9b4bc8bbfd68f05048a278809835c618442f. --- arch/arm/mach-imx/clk.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 3ed56512013169..21a88d0bcb94be 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -61,18 +61,16 @@ struct clk *imx_clk_gate_exclusive(const char *name, const char *parent, static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { - return clk_register_gate2(NULL, name, parent, - CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, - reg, shift, 0, &imx_ccm_lock, NULL); + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, 0, &imx_ccm_lock, NULL); } static inline struct clk *imx_clk_gate2_shared(const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned int *share_count) { - return clk_register_gate2(NULL, name, parent, - CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, - reg, shift, 0, &imx_ccm_lock, share_count); + return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, 0, &imx_ccm_lock, share_count); } static inline void imx_clk_prepare_enable(struct clk *clk) @@ -132,8 +130,7 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate) static inline struct clk *imx_clk_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width) { - return clk_register_divider(NULL, name, parent, - CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, + return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, reg, shift, width, 0, &imx_ccm_lock); } From 2fe57e3e8f4a6f78e63296bf06679ed445f6d7b5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 6 Jun 2015 16:08:22 +0200 Subject: [PATCH 0057/1983] ASoC: work around block transfer issues with imx-pcm-dma Upstream-commit: be23e812bb7b43d0b2379618a2f9ec45ffbafe58 Signed-off-by: Russell King --- sound/soc/fsl/imx-pcm-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index cb1eba73342a59..f0b63e5d5ff4d4 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -43,7 +43,7 @@ static const struct snd_pcm_hardware imx_pcm_hardware = { .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE, .period_bytes_min = 128, .period_bytes_max = 65535, /* Limited by SDMA engine */ - .periods_min = 2, + .periods_min = 4, .periods_max = 255, .fifo_size = 0, }; From a8976252e42dac14b569d3b6e8bd677eea30d7b4 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 2 Aug 2014 20:19:40 +0200 Subject: [PATCH 0058/1983] fsl_spdif: Default to 'no preemphasis' Signed-off-by: Rudi --- sound/soc/fsl/fsl_spdif.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 4ee0545dc75071..643d11e66f0dfc 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1298,8 +1298,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) spin_lock_init(&ctrl->ctl_lock); /* Init tx channel status default value */ - ctrl->ch_status[0] = IEC958_AES0_CON_NOT_COPYRIGHT | - IEC958_AES0_CON_EMPHASIS_5015; + ctrl->ch_status[0] = IEC958_AES0_CON_NOT_COPYRIGHT; ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID; ctrl->ch_status[2] = 0x00; ctrl->ch_status[3] = IEC958_AES3_CON_FS_44100 | From 2a05be411b3f8134144f36ba04fca075240cf55c Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 2 Aug 2014 20:21:28 +0200 Subject: [PATCH 0059/1983] fsl_spdif: Set validity bit according to channel status Signed-off-by: Rudi --- sound/soc/fsl/fsl_spdif.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 643d11e66f0dfc..80955b8bea6aa7 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -358,6 +358,10 @@ static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) regmap_write(regmap, REG_SPDIF_STCSCL, ch_status); dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status); + + /* Set outgoing validity off for non-audio */ + regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, + (ctrl->ch_status[0] & IEC958_AES0_NONAUDIO) ? 0 : ~0); } /* Set SPDIF PhaseConfig register for rx clock */ From f0e2cd45d0896bb17c22d23027d266aa7dc51ba6 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Fri, 6 Jun 2014 23:09:39 +0300 Subject: [PATCH 0060/1983] ASoC: fsl_spdif: Add support for sample rates 88.2kHz and 17.64kHz Add support for the sample rates 88.2kHz and 176.4Hz that are also supported by HDMI. TODO: TESTING Signed-off-by: Anssi Hannula --- sound/soc/fsl/fsl_spdif.c | 12 ++++++++++-- sound/soc/fsl/fsl_spdif.h | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 80955b8bea6aa7..83032237b33614 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -407,10 +407,18 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, rate = SPDIF_TXRATE_48000; csfs = IEC958_AES3_CON_FS_48000; break; + case 88200: + rate = SPDIF_TXRATE_88200; + csfs = IEC958_AES3_CON_FS_88200; + break; case 96000: rate = SPDIF_TXRATE_96000; csfs = IEC958_AES3_CON_FS_96000; break; + case 176400: + rate = SPDIF_TXRATE_176400; + csfs = IEC958_AES3_CON_FS_176400; + break; case 192000: rate = SPDIF_TXRATE_192000; csfs = IEC958_AES3_CON_FS_192000; @@ -1103,7 +1111,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, enum spdif_txrate index, bool round) { - const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; bool is_sysclk = clk == spdif_priv->sysclk; u64 rate_ideal, rate_actual, sub; u32 sysclk_dfmin, sysclk_dfmax; @@ -1162,7 +1170,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index) { - const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; struct platform_device *pdev = spdif_priv->pdev; struct device *dev = &pdev->dev; u64 savesub = 100000, ret; diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 00bd3514c610a9..22133ab92793e1 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -166,7 +166,9 @@ enum spdif_txrate { SPDIF_TXRATE_32000 = 0, SPDIF_TXRATE_44100, SPDIF_TXRATE_48000, + SPDIF_TXRATE_88200, SPDIF_TXRATE_96000, + SPDIF_TXRATE_176400, SPDIF_TXRATE_192000, }; #define SPDIF_TXRATE_MAX (SPDIF_TXRATE_192000 + 1) @@ -180,7 +182,9 @@ enum spdif_txrate { #define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400| \ SNDRV_PCM_RATE_192000) #define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \ From 466ad646c68ecedbecbd0732660f7549d1a7013f Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Mon, 11 Aug 2014 17:35:43 +0200 Subject: [PATCH 0061/1983] ASoC: fsl_spdif: Add support for full 6 byte channel status. This patch allows all 6 bytes ot the SPDIF channel status information to be transmitted. --- sound/soc/fsl/fsl_spdif.c | 50 +++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 83032237b33614..e712c03ef3c72f 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -58,7 +58,7 @@ struct spdif_mixer_control { spinlock_t ctl_lock; /* IEC958 channel tx status bit */ - unsigned char ch_status[4]; + unsigned char ch_status[6]; /* User bits */ unsigned char subcode[2 * SPDIF_UBITS_SIZE]; @@ -333,11 +333,11 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) return -EBUSY; } -static void spdif_set_cstatus(struct spdif_mixer_control *ctrl, - u8 mask, u8 cstatus) +static inline void spdif_set_cstatus(struct spdif_mixer_control *ctrl, + u8 byteno, u8 mask, u8 cstatus) { - ctrl->ch_status[3] &= ~mask; - ctrl->ch_status[3] |= cstatus & mask; + ctrl->ch_status[byteno] &= ~mask; + ctrl->ch_status[byteno] |= cstatus & mask; } static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) @@ -354,14 +354,16 @@ static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status); - ch_status = bitrev8(ctrl->ch_status[3]) << 16; + ch_status = bitrev8(ctrl->ch_status[3]) << 16 | + (bitrev8(ctrl->ch_status[4]) << 8) | + bitrev8(ctrl->ch_status[5]); regmap_write(regmap, REG_SPDIF_STCSCL, ch_status); dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status); - /* Set outgoing validity off for non-audio */ - regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, - (ctrl->ch_status[0] & IEC958_AES0_NONAUDIO) ? 0 : ~0); + /* Set outgoing validity (0: pcm, 1: non-audio) */ + regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_VAL_MASK, + (ctrl->ch_status[0] & IEC958_AES0_NONAUDIO) ? 0 : SCR_VAL_CLEAR); } /* Set SPDIF PhaseConfig register for rx clock */ @@ -389,7 +391,7 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; struct regmap *regmap = spdif_priv->regmap; struct platform_device *pdev = spdif_priv->pdev; - unsigned long csfs = 0; + unsigned long csfs, csofs = 0; u32 stc, mask, rate; u8 clk, txclk_df, sysclk_df; int ret; @@ -398,30 +400,37 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, case 32000: rate = SPDIF_TXRATE_32000; csfs = IEC958_AES3_CON_FS_32000; + csofs = IEC958_AES4_CON_ORIGFS_32000; break; case 44100: rate = SPDIF_TXRATE_44100; csfs = IEC958_AES3_CON_FS_44100; + csofs = IEC958_AES4_CON_ORIGFS_44100; break; case 48000: rate = SPDIF_TXRATE_48000; csfs = IEC958_AES3_CON_FS_48000; + csofs = IEC958_AES4_CON_ORIGFS_48000; break; case 88200: rate = SPDIF_TXRATE_88200; csfs = IEC958_AES3_CON_FS_88200; + csofs = IEC958_AES4_CON_ORIGFS_88200; break; case 96000: rate = SPDIF_TXRATE_96000; csfs = IEC958_AES3_CON_FS_96000; + csofs = IEC958_AES4_CON_ORIGFS_96000; break; case 176400: rate = SPDIF_TXRATE_176400; csfs = IEC958_AES3_CON_FS_176400; + csofs = IEC958_AES4_CON_ORIGFS_176400; break; case 192000: rate = SPDIF_TXRATE_192000; csfs = IEC958_AES3_CON_FS_192000; + csofs = IEC958_AES4_CON_ORIGFS_192000; break; default: dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate); @@ -463,7 +472,8 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, clk_get_rate(spdif_priv->txclk[rate])); /* set fs field in consumer channel status */ - spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); + spdif_set_cstatus(ctrl, 3, IEC958_AES3_CON_FS, csfs); + spdif_set_cstatus(ctrl, 4, IEC958_AES4_CON_ORIGFS, csofs); /* select clock source and divisor */ stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | @@ -595,7 +605,7 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream, __func__, sample_rate); return ret; } - spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK, + spdif_set_cstatus(ctrl, 3, IEC958_AES3_CON_CLOCK, IEC958_AES3_CON_CLOCK_1000PPM); spdif_write_channel_status(spdif_priv); } else { @@ -666,14 +676,13 @@ static int fsl_spdif_info(struct snd_kcontrol *kcontrol, static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue) { + int i; struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - uvalue->value.iec958.status[0] = ctrl->ch_status[0]; - uvalue->value.iec958.status[1] = ctrl->ch_status[1]; - uvalue->value.iec958.status[2] = ctrl->ch_status[2]; - uvalue->value.iec958.status[3] = ctrl->ch_status[3]; + for (i = 0; i < ARRAY_SIZE(ctrl->ch_status); i++) + uvalue->value.iec958.status[i] = ctrl->ch_status[i]; return 0; } @@ -681,14 +690,13 @@ static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol, static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue) { + int i; struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; - ctrl->ch_status[0] = uvalue->value.iec958.status[0]; - ctrl->ch_status[1] = uvalue->value.iec958.status[1]; - ctrl->ch_status[2] = uvalue->value.iec958.status[2]; - ctrl->ch_status[3] = uvalue->value.iec958.status[3]; + for (i = 0; i < ARRAY_SIZE(ctrl->ch_status); i++) + ctrl->ch_status[i] = uvalue->value.iec958.status[i]; spdif_write_channel_status(spdif_priv); @@ -1315,6 +1323,8 @@ static int fsl_spdif_probe(struct platform_device *pdev) ctrl->ch_status[2] = 0x00; ctrl->ch_status[3] = IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM; + ctrl->ch_status[4] = IEC958_AES4_CON_ORIGFS_44100; + ctrl->ch_status[5] = IEC958_AES5_CON_CGMSA_COPYFREELY; spdif_priv->dpll_locked = false; From c621f931640dd6acbf8559ec2dd6dd7cb57295e5 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 10:18:59 +0200 Subject: [PATCH 0062/1983] ASoC: imx-hdmi-dma: Cleanup parity bit calculation Inline the odd_ones() function and use it in all places where possible. Signed-off-by: Rudi --- sound/soc/fsl/imx-hdmi-dma.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index b2d90e7c23ccec..8b434c1b7f609b 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -221,7 +221,7 @@ static void hdmi_mask(int mask) hdmi_writeb(regval, HDMI_AHB_DMA_MASK); } -int odd_ones(unsigned a) +static inline int odd_ones(unsigned a) { a ^= a >> 8; a ^= a >> 4; @@ -301,51 +301,32 @@ static void init_table(int channels) static void hdmi_dma_copy_16_c_lut(u16 *src, u32 *dst, int samples, u8 *lookup_table) { - u32 sample, head, p; + u32 sample, head; int i; for (i = 0; i < samples; i++) { /* get source sample */ sample = *src++; - /* xor every bit */ - p = sample ^ (sample >> 8); - p ^= (p >> 4); - p ^= (p >> 2); - p ^= (p >> 1); - p &= 1; /* only want last bit */ - p <<= 3; /* bit p */ + /* get packet header and p-bit */ + head = *lookup_table++ ^ (odd_ones(sample) << 3); - /* get packet header */ - head = *lookup_table++; - - /* fix head */ - head ^= p; - - /* store */ + /* store sample and header */ *dst++ = (head << 24) | (sample << 8); } } static void hdmi_dma_copy_16_c_fast(u16 *src, u32 *dst, int samples) { - u32 sample, p; + u32 sample; int i; for (i = 0; i < samples; i++) { /* get source sample */ sample = *src++; - /* xor every bit */ - p = sample ^ (sample >> 8); - p ^= (p >> 4); - p ^= (p >> 2); - p ^= (p >> 1); - p &= 1; /* only want last bit */ - p <<= 3; /* bit p */ - - /* store */ - *dst++ = (p << 24) | (sample << 8); + /* store sample and p-bit */ + *dst++ = (odd_ones(sample) << (3+24)) | (sample << 8); } } From ce2eb635a7d0c08ca855e45aff2ba92fe2e26135 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 10:27:37 +0200 Subject: [PATCH 0063/1983] ASoC: imx-hdmi-dma: Set validity bit in HDMI according to channel status. Signed-off-by: Rudi --- sound/soc/fsl/imx-hdmi-dma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 8b434c1b7f609b..e147e393479b13 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -249,6 +249,9 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, else subframe.B.c = 0; + /* fill v (validity) */ + subframe.B.v = iec_header.B.linear_pcm; + subframe.B.p = odd_ones(pcm_data); subframe.B.p ^= subframe.B.c; subframe.B.p ^= subframe.B.u; From f8a5f8b37c799a78c3cc9c3e55f3b894ff1571a6 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 10:41:47 +0200 Subject: [PATCH 0064/1983] ASoC: fsl_hdmi/imx-hdmi-dma: Enable/fix 24bit audio output. Since SNDRV_PCM_FORMAT_S24 uses 4 bytes, the constraint has to be set to 32. Also we need to extend the parity calculation to full 32 bit. Signed-off-by: Rudi --- sound/soc/fsl/fsl_hdmi.c | 2 +- sound/soc/fsl/imx-hdmi-dma.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index f7144e22114add..94638ac0e3a8cf 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -301,7 +301,7 @@ static void fsl_hdmi_get_playback_sample_size(void) playback_sample_size[i++] = 16; if (edid_cfg.sample_sizes & 0x4) - playback_sample_size[i++] = 24; + playback_sample_size[i++] = 32; playback_constraint_bits.list = playback_sample_size; playback_constraint_bits.count = i; diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index e147e393479b13..726e63ddfd4129 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -223,6 +223,7 @@ static void hdmi_mask(int mask) static inline int odd_ones(unsigned a) { + a ^= a >> 16; a ^= a >> 8; a ^= a >> 4; a ^= a >> 2; From d554d0ec4903e1c95a1b498833d874582b8cbbd1 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 10:55:35 +0200 Subject: [PATCH 0065/1983] ASoC: imx-hdmi-dma: Add support for mmap emulation when using 24bit samples. Signed-off-by: Rudi --- sound/soc/fsl/imx-hdmi-dma.c | 61 +++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 726e63ddfd4129..e2b95cc0201d5a 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -334,32 +334,77 @@ static void hdmi_dma_copy_16_c_fast(u16 *src, u32 *dst, int samples) } } -static void hdmi_dma_copy_16(u16 *src, u32 *dst, int framecnt, int channelcnt) +static void hdmi_dma_copy_24_c_lut(u32 *src, u32 *dst, int samples, + u8 *lookup_table) +{ + u32 sample, head; + int i; + + for (i = 0; i < samples; i++) { + /* get source sample */ + sample = *src++ & 0x00ffffff; + + /* get packet header and p-bit */ + head = *lookup_table++ ^ (odd_ones(sample) << 3); + + /* store sample and header */ + *dst++ = (head << 24) | sample; + } +} + +static void hdmi_dma_copy_24_c_fast(u32 *src, u32 *dst, int samples) +{ + u32 sample; + int i; + + for (i = 0; i < samples; i++) { + /* get source sample */ + sample = *src++ & 0x00ffffff; + + /* store sample and p-bit */ + *dst++ = (odd_ones(sample) << (3+24)) | sample; + } +} + +static void hdmi_mmap_copy(u8 *src, int samplesize, u32 *dst, int framecnt, int channelcnt) { /* split input frames into 192-frame each */ int count_in_192 = (framecnt + 191) / 192; int i; + typedef void (*fn_copy_lut)(u8 *src, u32 *dst, int samples, u8 *lookup_table); + typedef void (*fn_copy_fast)(u8 *src, u32 *dst, int samples); + fn_copy_lut copy_lut; + fn_copy_fast copy_fast; + + if (samplesize == 4) { + copy_lut = (fn_copy_lut)hdmi_dma_copy_24_c_lut; + copy_fast = (fn_copy_fast)hdmi_dma_copy_24_c_fast; + } else { + copy_lut = (fn_copy_lut)hdmi_dma_copy_16_c_lut; + copy_fast = (fn_copy_fast)hdmi_dma_copy_16_c_fast; + } + for (i = 0; i < count_in_192; i++) { int count, samples; /* handles frame index [0, 48) */ count = (framecnt < 48) ? framecnt : 48; samples = count * channelcnt; - hdmi_dma_copy_16_c_lut(src, dst, samples, g_packet_head_table); + copy_lut(src, dst, samples, g_packet_head_table); framecnt -= count; if (framecnt == 0) break; - src += samples; + src += samples * samplesize; dst += samples; /* handles frame index [48, 192) */ count = (framecnt < 192 - 48) ? framecnt : 192 - 48; samples = count * channelcnt; - hdmi_dma_copy_16_c_fast(src, dst, samples); + copy_fast(src, dst, samples); framecnt -= count; - src += samples; + src += samples * samplesize; dst += samples; } } @@ -411,7 +456,6 @@ static void hdmi_dma_mmap_copy(struct snd_pcm_substream *substream, struct hdmi_dma_priv *priv = runtime->private_data; struct device *dev = rtd->platform->dev; u32 framecount, *dst; - u16 *src16; framecount = count / (priv->sample_align * priv->channels); @@ -420,9 +464,10 @@ static void hdmi_dma_mmap_copy(struct snd_pcm_substream *substream, switch (priv->format) { case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S24_LE: /* dma_buffer is the mmapped buffer we are copying pcm from. */ - src16 = (u16 *)(runtime->dma_area + offset); - hdmi_dma_copy_16(src16, dst, framecount, priv->channels); + hdmi_mmap_copy(runtime->dma_area + offset, + priv->sample_align, dst, framecount, priv->channels); break; default: dev_err(dev, "unsupported sample format %s\n", From 3c2d47bfa8815b88163adc3deefc4bd62639b7c2 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 11:01:27 +0200 Subject: [PATCH 0066/1983] ASoC: imx-hdmi-dma: Remove unnecessary double condition check. Signed-off-by: Rudi --- sound/soc/fsl/imx-hdmi-dma.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index e2b95cc0201d5a..c39a538bc69f35 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -486,16 +486,15 @@ static void hdmi_dma_data_copy(struct snd_pcm_substream *substream, return; appl_bytes = runtime->status->hw_ptr * (runtime->frame_bits / 8); - if (type == 'p') - appl_bytes += 2 * priv->period_bytes; - offset = appl_bytes % priv->buffer_bytes; switch (type) { case 'p': + offset = (appl_bytes + 2 * priv->period_bytes) % priv->buffer_bytes; count = priv->period_bytes; space_to_end = priv->period_bytes; break; case 'b': + offset = appl_bytes % priv->buffer_bytes; count = priv->buffer_bytes; space_to_end = priv->buffer_bytes - offset; From 996ba514d6a87054c99969c66efa40b2048b769a Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 11:03:51 +0200 Subject: [PATCH 0067/1983] ASoC: imx-hdmi-dma: Fix md optimize subframe assembly. Do not include unused bits of pcm_data in parity calculation and avoid make better use of odd_ones(). Signed-off-by: Rudi --- sound/soc/fsl/imx-hdmi-dma.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index c39a538bc69f35..6eff00e52688fb 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -239,31 +239,30 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, union hdmi_audio_dma_data_t subframe; subframe.U = 0; - iec_header.B.channel = subframe_idx; - - /* fill b (start-of-block) */ - subframe.B.b = (priv->frame_idx == 0) ? 1 : 0; /* fill c (channel status) */ - if (priv->frame_idx < 42) - subframe.B.c = (iec_header.U >> priv->frame_idx) & 0x1; - else - subframe.B.c = 0; - + if (priv->frame_idx < 42) { + iec_header.B.channel = + (iec_header.B.linear_pcm == 0) ? subframe_idx : 0; + subframe.B.c = iec_header.U >> priv->frame_idx; + } + /* fill v (validity) */ subframe.B.v = iec_header.B.linear_pcm; - subframe.B.p = odd_ones(pcm_data); - subframe.B.p ^= subframe.B.c; - subframe.B.p ^= subframe.B.u; - subframe.B.p ^= subframe.B.v; - /* fill data */ if (priv->sample_bits == 16) subframe.B.data = pcm_data << 8; else subframe.B.data = pcm_data; + /* fill p (parity) Note: Do not include b ! */ + subframe.B.p = odd_ones(subframe.U); + + /* fill b (start-of-block) */ + if (priv->frame_idx == 0) + subframe.B.b = 1; + return subframe.U; } From a93b2c4eeb0cde9f6b17819e2e73f418c37dad7f Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 3 Aug 2014 11:19:05 +0200 Subject: [PATCH 0068/1983] ASoC: imx-hdmi-dma: Fix channel mapping ALSA <-> CEA Map default ALSA channel allocation (see snd_pcm_std_chmaps[] in pcm_lib.c) to HDMI/CEA-861D speaker locations. This patch only implements a single, fixed mapping. In order to support all features, we need to add this: https://www.kernel.org/doc/Documentation/sound/alsa/Channel-Mapping-API.txt Signed-off-by: Rudi --- sound/soc/fsl/fsl_hdmi.c | 2 +- sound/soc/fsl/imx-hdmi-dma.c | 126 ++++++++++++++++++++++++++--------- 2 files changed, 96 insertions(+), 32 deletions(-) diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index 94638ac0e3a8cf..9300b6e42710bc 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -136,13 +136,13 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { /* 2.1 */ { .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL },}, /* Dolby Surround */ + { .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL },}, /* Prefer FL/FR/RL/RR over FL/FR/LFE/FC */ { .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL },}, { .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL },}, { .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL },}, { .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL },}, { .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL },}, { .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL },}, - { .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL },}, { .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL },}, { .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL },}, /* surround51 */ diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 6eff00e52688fb..5d091ba4b23d1c 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -89,6 +89,26 @@ void hdmi_dma_copy_24_neon_fast(unsigned int *src, unsigned int *dst, static void hdmi_dma_irq_enable(struct hdmi_dma_priv *priv); static void hdmi_dma_irq_disable(struct hdmi_dma_priv *priv); +/* channel remapping for hdmi_dma_copy_xxxx() */ +static u8 g_channel_remap_table[24]; + +/* default mapping tables */ +static const u8 channel_maps_alsa_cea[5][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 0CH: no remapping */ + { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 2CH: no remapping */ + { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 4CH: no remapping */ + { 0, 1, 4, 5, 3, 2, 6, 7 }, /* 6CH: ALSA5.1 to CEA */ + { 0, 1, 6, 7, 3, 2, 4, 5 } /* 8CH: ALSA7.1 to CEA */ +}; + +static const u8 channel_maps_cea_alsa[5][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 0CH: no remapping */ + { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 2CH: no remapping */ + { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 4CH: no remapping */ + { 0, 1, 5, 4, 2, 3, 6, 7 }, /* 6CH: CEA to ALSA5.1 */ + { 0, 1, 5, 4, 6, 7, 2, 3 } /* 8CH: CEA to ALSA7.1 */ +}; + union hdmi_audio_header_t iec_header; EXPORT_SYMBOL(iec_header); @@ -269,7 +289,7 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, static void init_table(int channels) { unsigned char *p = g_packet_head_table; - int i, ch = 0; + int i, map_sel, ch = 0; for (i = 0; i < 48; i++) { int b = 0; @@ -286,6 +306,12 @@ static void init_table(int channels) *p++ = (b << 4) | (c << 2) | (c << 3); } } + + map_sel = channels / 2; + for (i = 0; i < 24; i++) { + g_channel_remap_table[i] = (i / channels) * channels + + channel_maps_cea_alsa[map_sel][i % channels]; + } } /* @@ -305,31 +331,41 @@ static void hdmi_dma_copy_16_c_lut(u16 *src, u32 *dst, int samples, u8 *lookup_table) { u32 sample, head; - int i; + int i = 0; - for (i = 0; i < samples; i++) { + while (samples--) { /* get source sample */ - sample = *src++; + sample = src[g_channel_remap_table[i]]; /* get packet header and p-bit */ head = *lookup_table++ ^ (odd_ones(sample) << 3); /* store sample and header */ *dst++ = (head << 24) | (sample << 8); + + if (++i == 24) { + src += 24; + i = 0; + } } } static void hdmi_dma_copy_16_c_fast(u16 *src, u32 *dst, int samples) { u32 sample; - int i; + int i = 0; - for (i = 0; i < samples; i++) { + while (samples--) { /* get source sample */ - sample = *src++; + sample = src[g_channel_remap_table[i]]; /* store sample and p-bit */ *dst++ = (odd_ones(sample) << (3+24)) | (sample << 8); + + if (++i == 24) { + src += 24; + i = 0; + } } } @@ -337,31 +373,41 @@ static void hdmi_dma_copy_24_c_lut(u32 *src, u32 *dst, int samples, u8 *lookup_table) { u32 sample, head; - int i; + int i = 0; - for (i = 0; i < samples; i++) { + while (samples--) { /* get source sample */ - sample = *src++ & 0x00ffffff; + sample = src[g_channel_remap_table[i]] & 0x00ffffff; /* get packet header and p-bit */ head = *lookup_table++ ^ (odd_ones(sample) << 3); /* store sample and header */ *dst++ = (head << 24) | sample; + + if (++i == 24) { + src += 24; + i = 0; + } } } static void hdmi_dma_copy_24_c_fast(u32 *src, u32 *dst, int samples) { u32 sample; - int i; + int i = 0; - for (i = 0; i < samples; i++) { + while (samples--) { /* get source sample */ - sample = *src++ & 0x00ffffff; + sample = src[g_channel_remap_table[i]] & 0x00ffffff; /* store sample and p-bit */ *dst++ = (odd_ones(sample) << (3+24)) | sample; + + if (++i == 24) { + src += 24; + i = 0; + } } } @@ -587,8 +633,8 @@ static int hdmi_dma_set_thrsld_incrtype(struct device *dev, int channels) static int hdmi_dma_configure_dma(struct device *dev, int channels) { - u8 i, val = 0; int ret; + static u8 chan_enable[] = { 0x00, 0x03, 0x33, 0x3f, 0xff }; if (channels <= 0 || channels > 8 || channels % 2 != 0) { dev_err(dev, "unsupported channel number: %d\n", channels); @@ -601,10 +647,7 @@ static int hdmi_dma_configure_dma(struct device *dev, int channels) if (ret) return ret; - for (i = 0; i < channels; i += 2) - val |= 0x3 << i; - - hdmi_writeb(val, HDMI_AHB_DMA_CONF1); + hdmi_writeb(chan_enable[channels / 2], HDMI_AHB_DMA_CONF1); return 0; } @@ -703,24 +746,45 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, struct hdmi_dma_priv *priv = runtime->private_data; unsigned int count = frames_to_bytes(runtime, frames); unsigned int pos_bytes = frames_to_bytes(runtime, pos); - u32 *hw_buf; - int subframe_idx; - u32 pcm_data; - + int channel_no, pcm_idx, subframe_no, bits_left, sample_bits, map_sel; + u32 pcm_data[8], pcm_temp, *hw_buf, sample_block; + /* Adding frame info to pcm data from userspace and copy to hw_buffer */ hw_buf = (u32 *)(priv->hw_buffer.area + (pos_bytes * priv->buffer_ratio)); - while (count > 0) { - for (subframe_idx = 1 ; subframe_idx <= priv->channels ; subframe_idx++) { - if (copy_from_user(&pcm_data, buf, priv->sample_align)) - return -EFAULT; + sample_bits = priv->sample_align * 8; + sample_block = priv->sample_align * priv->channels; + map_sel = (iec_header.B.linear_pcm == 0) ? (priv->channels / 2) : 0; - buf += priv->sample_align; - count -= priv->sample_align; + while (count > 0) { + if (copy_from_user(pcm_data, buf, sample_block)) + return -EFAULT; + + buf += sample_block; + count -= sample_block; + + channel_no = pcm_idx = 0; + do { + pcm_temp = pcm_data[pcm_idx++]; + bits_left = 32; + for (;;) { + /* re-map channels */ + subframe_no = channel_maps_alsa_cea[map_sel][channel_no]; + + /* Save the header info to the audio dma buffer */ + hw_buf[subframe_no] = hdmi_dma_add_frame_info( + priv, pcm_temp, subframe_no + 1); + channel_no++; + + if (bits_left <= sample_bits) + break; + + bits_left -= sample_bits; + pcm_temp >>= sample_bits; + } + } while (channel_no < priv->channels); - /* Save the header info to the audio dma buffer */ - *hw_buf++ = hdmi_dma_add_frame_info(priv, pcm_data, subframe_idx); - } + hw_buf += priv->channels; priv->frame_idx++; if (priv->frame_idx == 192) From 3989f227845206f665100eb4eb6cbabb46060fd8 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 9 Aug 2014 13:13:37 +0200 Subject: [PATCH 0069/1983] ASoC: imx-hdmi-dma: Whitespace cleanup --- sound/soc/fsl/imx-hdmi-dma.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 5d091ba4b23d1c..e048a6515e3d93 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -93,7 +93,7 @@ static void hdmi_dma_irq_disable(struct hdmi_dma_priv *priv); static u8 g_channel_remap_table[24]; /* default mapping tables */ -static const u8 channel_maps_alsa_cea[5][8] = { +static const u8 channel_maps_alsa_cea[5][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 0CH: no remapping */ { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 2CH: no remapping */ { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 4CH: no remapping */ @@ -101,7 +101,7 @@ static const u8 channel_maps_alsa_cea[5][8] = { { 0, 1, 6, 7, 3, 2, 4, 5 } /* 8CH: ALSA7.1 to CEA */ }; -static const u8 channel_maps_cea_alsa[5][8] = { +static const u8 channel_maps_cea_alsa[5][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 0CH: no remapping */ { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 2CH: no remapping */ { 0, 1, 2, 3, 4, 5, 6, 7 }, /* 4CH: no remapping */ @@ -262,11 +262,11 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, /* fill c (channel status) */ if (priv->frame_idx < 42) { - iec_header.B.channel = + iec_header.B.channel = (iec_header.B.linear_pcm == 0) ? subframe_idx : 0; subframe.B.c = iec_header.U >> priv->frame_idx; } - + /* fill v (validity) */ subframe.B.v = iec_header.B.linear_pcm; @@ -309,7 +309,7 @@ static void init_table(int channels) map_sel = channels / 2; for (i = 0; i < 24; i++) { - g_channel_remap_table[i] = (i / channels) * channels + + g_channel_remap_table[i] = (i / channels) * channels + channel_maps_cea_alsa[map_sel][i % channels]; } } @@ -748,7 +748,7 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, unsigned int pos_bytes = frames_to_bytes(runtime, pos); int channel_no, pcm_idx, subframe_no, bits_left, sample_bits, map_sel; u32 pcm_data[8], pcm_temp, *hw_buf, sample_block; - + /* Adding frame info to pcm data from userspace and copy to hw_buffer */ hw_buf = (u32 *)(priv->hw_buffer.area + (pos_bytes * priv->buffer_ratio)); From f097e179902bf62dd103ae560f252eb7308f829f Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 9 Aug 2014 20:17:34 +0200 Subject: [PATCH 0070/1983] ASoC: imx-hdmi-dma: Fix subframe assembly / channel status in HBR mode. High bitrate mode is signalled to the driver as passthrough with 8 channels. However, it is infact a two channel mode. This patch corrects the handling of priv->frame_idx to create proper channel status information. --- sound/soc/fsl/imx-hdmi-dma.c | 48 +++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index e048a6515e3d93..58b00aadcef2ed 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -263,7 +263,7 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, /* fill c (channel status) */ if (priv->frame_idx < 42) { iec_header.B.channel = - (iec_header.B.linear_pcm == 0) ? subframe_idx : 0; + (iec_header.B.linear_pcm == 0) ? (subframe_idx + 1) : 0; subframe.B.c = iec_header.U >> priv->frame_idx; } @@ -272,9 +272,8 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, /* fill data */ if (priv->sample_bits == 16) - subframe.B.data = pcm_data << 8; - else - subframe.B.data = pcm_data; + pcm_data <<= 8; + subframe.B.data = pcm_data; /* fill p (parity) Note: Do not include b ! */ subframe.B.p = odd_ones(subframe.U); @@ -289,7 +288,7 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, static void init_table(int channels) { unsigned char *p = g_packet_head_table; - int i, map_sel, ch = 0; + int i, map_sel, ch; for (i = 0; i < 48; i++) { int b = 0; @@ -746,15 +745,22 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, struct hdmi_dma_priv *priv = runtime->private_data; unsigned int count = frames_to_bytes(runtime, frames); unsigned int pos_bytes = frames_to_bytes(runtime, pos); - int channel_no, pcm_idx, subframe_no, bits_left, sample_bits, map_sel; - u32 pcm_data[8], pcm_temp, *hw_buf, sample_block; + int channel_no, pcm_idx, subframe_idx, bits_left, sample_bits, map_sel; + u32 pcm_data[8], pcm_temp, *hw_buf, sample_block, inc_mask; /* Adding frame info to pcm data from userspace and copy to hw_buffer */ hw_buf = (u32 *)(priv->hw_buffer.area + (pos_bytes * priv->buffer_ratio)); sample_bits = priv->sample_align * 8; sample_block = priv->sample_align * priv->channels; - map_sel = (iec_header.B.linear_pcm == 0) ? (priv->channels / 2) : 0; + + if (iec_header.B.linear_pcm == 0) { + map_sel = priv->channels / 2; + inc_mask = 1 << (priv->channels - 1); + } else { + map_sel = 0; + inc_mask = 0xaa; + } while (count > 0) { if (copy_from_user(pcm_data, buf, sample_block)) @@ -769,11 +775,17 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, bits_left = 32; for (;;) { /* re-map channels */ - subframe_no = channel_maps_alsa_cea[map_sel][channel_no]; + subframe_idx = channel_maps_alsa_cea[map_sel][channel_no]; /* Save the header info to the audio dma buffer */ - hw_buf[subframe_no] = hdmi_dma_add_frame_info( - priv, pcm_temp, subframe_no + 1); + hw_buf[subframe_idx] = hdmi_dma_add_frame_info( + priv, pcm_temp, subframe_idx); + + if (inc_mask & (1 << channel_no)) { + if (++priv->frame_idx == 192) + priv->frame_idx = 0; + } + channel_no++; if (bits_left <= sample_bits) @@ -785,10 +797,6 @@ static int hdmi_dma_copy(struct snd_pcm_substream *substream, int channel, } while (channel_no < priv->channels); hw_buf += priv->channels; - - priv->frame_idx++; - if (priv->frame_idx == 192) - priv->frame_idx = 0; } return 0; @@ -944,6 +952,16 @@ static void hdmi_dma_trigger_init(struct snd_pcm_substream *substream, hbr = (iec_header.B.linear_pcm != 0 && priv->channels == 8); hdmi_audio_writeb(AHB_DMA_CONF0, HBR, !!hbr); + /* + * Override AES3 - parameter: This is a temporary hack for + * callers that provide incorrect information when opening + * the device. 0x09 (i.e. 768K) is the only acceptable value. + */ + if (hbr) { + iec_header.B.sample_freq = 0x09; + iec_header.B.org_sample_freq = 0x00; + } + priv->offset = 0; priv->frame_idx = 0; From e65455e747a420cd9eea761b59a8f9ac19044ba6 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 10 Aug 2014 09:30:29 +0200 Subject: [PATCH 0071/1983] ASoC: fsl_hdmi: Allow access to full channel status info (6 bytes). --- sound/soc/fsl/fsl_hdmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index 9300b6e42710bc..13f358e4b45255 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -437,7 +437,7 @@ static int fsl_hdmi_iec_get(struct snd_kcontrol *kcontrol, { int i; - for (i = 0 ; i < 4 ; i++) + for (i = 0 ; i < 6 ; i++) uvalue->value.iec958.status[i] = iec_header.status[i]; return 0; @@ -452,7 +452,7 @@ static int fsl_hdmi_iec_put(struct snd_kcontrol *kcontrol, if (uvalue->value.iec958.status[0] & IEC958_AES0_PROFESSIONAL) return -EPERM; - for (i = 0 ; i < 4 ; i++) { + for (i = 0 ; i < 6 ; i++) { iec_header.status[i] = uvalue->value.iec958.status[i]; pr_debug("%s status[%d]=0x%02x\n", __func__, i, iec_header.status[i]); } From 0a7ea53eead3a54a1490bed145592ee5ceb6b7b0 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 10 Aug 2014 09:34:26 +0200 Subject: [PATCH 0072/1983] ASoC: imx-hdmi-dma: Do not modify global iec_header during subframe assembly. To make the iec958 control return consistent data, the field B.channel in iec_header should not be rewritten when building subframes. --- sound/soc/fsl/imx-hdmi-dma.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 58b00aadcef2ed..61fe89da73b9df 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -257,18 +257,24 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, u32 pcm_data, int subframe_idx) { union hdmi_audio_dma_data_t subframe; + union hdmi_audio_header_t tmp_header; subframe.U = 0; - /* fill c (channel status) */ if (priv->frame_idx < 42) { - iec_header.B.channel = - (iec_header.B.linear_pcm == 0) ? (subframe_idx + 1) : 0; - subframe.B.c = iec_header.U >> priv->frame_idx; - } + tmp_header = iec_header; + + /* fill v (validity) */ + subframe.B.v = tmp_header.B.linear_pcm; - /* fill v (validity) */ - subframe.B.v = iec_header.B.linear_pcm; + /* fill c (channel status) */ + if (tmp_header.B.linear_pcm == 0) + tmp_header.B.channel = subframe_idx + 1; + subframe.B.c = tmp_header.U >> priv->frame_idx; + } else { + /* fill v (validity), c is always zero */ + subframe.B.v = iec_header.B.linear_pcm; + } /* fill data */ if (priv->sample_bits == 16) @@ -287,8 +293,9 @@ static u32 hdmi_dma_add_frame_info(struct hdmi_dma_priv *priv, static void init_table(int channels) { - unsigned char *p = g_packet_head_table; int i, map_sel, ch; + unsigned char *p = g_packet_head_table; + union hdmi_audio_header_t tmp_header = iec_header; for (i = 0; i < 48; i++) { int b = 0; @@ -298,8 +305,8 @@ static void init_table(int channels) for (ch = 0; ch < channels; ch++) { int c = 0; if (i < 42) { - iec_header.B.channel = ch+1; - c = (iec_header.U >> i) & 0x1; + tmp_header.B.channel = ch + 1; + c = (tmp_header.U >> i) & 0x1; } /* preset bit p as c */ *p++ = (b << 4) | (c << 2) | (c << 3); From a7c2ab5c9dab49c78ea5b6d24cdd26c2cff80c93 Mon Sep 17 00:00:00 2001 From: Rudi Date: Tue, 12 Aug 2014 22:16:53 +0200 Subject: [PATCH 0073/1983] ASoC: imx-hdmi-dma: Optimize SNDRV_PCM_TRIGGER_xxx handling. Do not reset frame_idx on SNDRV_PCM_TRIGGER_xxx events. This speeds up recovering from underrun conditions in non-PCM mode. Also don't do a full dma re-initialisation on SNDRV_PCM_TRIGGER_PAUSE_RELEASE. --- sound/soc/fsl/imx-hdmi-dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 61fe89da73b9df..0e05b583a13467 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -941,6 +941,7 @@ static int hdmi_dma_hw_params(struct snd_pcm_substream *substream, init_table(priv->channels); priv->appl_bytes = 0; + priv->frame_idx = 0; return 0; } @@ -970,7 +971,6 @@ static void hdmi_dma_trigger_init(struct snd_pcm_substream *substream, } priv->offset = 0; - priv->frame_idx = 0; /* Copy data by buffer_bytes */ hdmi_dma_data_copy(substream, priv, 'b'); @@ -1015,13 +1015,13 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!check_hdmi_state()) return 0; hdmi_dma_trigger_init(substream, priv); dumpregs(dev); + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: priv->tx_active = true; hdmi_audio_writeb(AHB_DMA_START, START, 0x1); hdmi_dma_irq_set(false); From 75f9a724e10e3bb8457825712b88e1c0eb1d602a Mon Sep 17 00:00:00 2001 From: Rudi Date: Tue, 19 Aug 2014 20:46:36 +0200 Subject: [PATCH 0074/1983] ASoC: imx-hdmi-dma: Increase audio buffer size to enable HD and high rate multichannel playback. --- sound/soc/fsl/imx-hdmi-dma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/fsl/imx-hdmi-dma.c b/sound/soc/fsl/imx-hdmi-dma.c index 0e05b583a13467..bd05654ae326d4 100644 --- a/sound/soc/fsl/imx-hdmi-dma.c +++ b/sound/soc/fsl/imx-hdmi-dma.c @@ -152,8 +152,8 @@ EXPORT_SYMBOL(iec_header); * is necessary for 6 ch. */ #define HDMI_DMA_PERIOD_BYTES (12288) -#define HDMI_DMA_BUF_SIZE (128 * 1024) -#define HDMI_PCM_BUF_SIZE (128 * 1024) +#define HDMI_DMA_BUF_SIZE (1280 * 1024) +#define HDMI_PCM_BUF_SIZE (1280 * 1024) #define hdmi_audio_debug(dev, reg) \ dev_dbg(dev, #reg ": 0x%02x\n", hdmi_readb(reg)) @@ -1070,7 +1070,7 @@ static struct snd_pcm_hardware snd_imx_hardware = { .period_bytes_min = HDMI_DMA_PERIOD_BYTES / 2, .period_bytes_max = HDMI_DMA_PERIOD_BYTES / 2, .periods_min = 8, - .periods_max = 8, + .periods_max = HDMI_DMA_BUF_SIZE / HDMI_DMA_PERIOD_BYTES, .fifo_size = 0, }; @@ -1251,6 +1251,7 @@ static int imx_soc_platform_probe(struct platform_device *pdev) case 0x0a: snd_imx_hardware.period_bytes_max = HDMI_DMA_PERIOD_BYTES / 4; snd_imx_hardware.period_bytes_min = HDMI_DMA_PERIOD_BYTES / 4; + snd_imx_hardware.periods_max = HDMI_DMA_BUF_SIZE / (HDMI_DMA_PERIOD_BYTES / 2); break; default: break; From a83fb5b118055a292589da0c1c7d8fe1ea7956bd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Jan 2014 14:55:59 +0100 Subject: [PATCH 0075/1983] cfg80211: make connect ie param const Upstream-commit: 4b5800fec6173765207abded99df3d692ed55691 This required liberally sprinkling 'const' over brcmfmac and mwifiex but seems like a useful thing to do since the pointer can't really be written. Signed-off-by: Johannes Berg --- .../net/wireless/brcm80211/brcmfmac/fwil.c | 5 +- .../net/wireless/brcm80211/brcmfmac/fwil.h | 2 +- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 46 +++++++++---------- .../wireless/brcm80211/brcmfmac/wl_cfg80211.h | 3 +- drivers/net/wireless/mwifiex/main.h | 2 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 2 +- include/net/cfg80211.h | 2 +- 7 files changed, 32 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 22adbe311d206d..59a5af5bf994d8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -124,7 +124,8 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) } static u32 -brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) +brcmf_create_iovar(char *name, const char *data, u32 datalen, + char *buf, u32 buflen) { u32 len; @@ -144,7 +145,7 @@ brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) s32 -brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 77eae86e55c233..a30be683f4a15e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -83,7 +83,7 @@ s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); -s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len); s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, u32 len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index d7718a5fa2f032..3d25c18340c57d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -351,13 +351,11 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, * triples, returning a pointer to the substring whose first element * matches tag */ -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key) { - struct brcmf_tlv *elt; - int totlen; - - elt = (struct brcmf_tlv *)buf; - totlen = buflen; + const struct brcmf_tlv *elt = buf; + int totlen = buflen; /* find tagged parameter */ while (totlen >= TLV_HDR_LEN) { @@ -378,8 +376,8 @@ struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) * not update the tlvs buffer pointer/length. */ static bool -brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, - u8 *oui, u32 oui_len, u8 type) +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) { /* If the contents match the OUI and the type */ if (ie[TLV_LEN_OFF] >= oui_len + 1 && @@ -401,12 +399,12 @@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, } static struct brcmf_vs_tlv * -brcmf_find_wpaie(u8 *parse, u32 len) +brcmf_find_wpaie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, + if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) return (struct brcmf_vs_tlv *)ie; } @@ -414,9 +412,9 @@ brcmf_find_wpaie(u8 *parse, u32 len) } static struct brcmf_vs_tlv * -brcmf_find_wpsie(u8 *parse, u32 len) +brcmf_find_wpsie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, @@ -1562,9 +1560,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; - void *ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; u32 ie_len; struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; @@ -1591,7 +1589,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ie_len = wpa_ie->len + TLV_HDR_LEN; } else { /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len, + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, + sme->ie_len, WLAN_EID_RSN); if (rsn_ie) { ie = rsn_ie; @@ -2455,7 +2454,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); struct brcmf_bss_info_le *bi; struct brcmf_ssid *ssid; - struct brcmf_tlv *tim; + const struct brcmf_tlv *tim; u16 beacon_interval; u8 dtim_period; size_t ie_len; @@ -3220,8 +3219,9 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) } static s32 -brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie) +brcmf_configure_wpaie(struct net_device *ndev, + const struct brcmf_vs_tlv *wpa_ie, + bool is_rsn_ie) { struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ @@ -3707,11 +3707,11 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, s32 ie_offset; struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *ssid_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; struct brcmf_join_params join_params; enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 2dc6a074e8ede1..254feed2860e96 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -491,7 +491,8 @@ void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, const u8 *vndr_ie_buf, u32 vndr_ie_len); s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key); +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key); u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch); u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index d8ad554ce39f56..29d27d9b5ebe50 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1078,7 +1078,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, const u8 *key, int key_len, u8 key_index, const u8 *mac_addr, int disable); -int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); +int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); int mwifiex_get_ver_ext(struct mwifiex_private *priv); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index c5cb2ed19ec2e2..0bec94351f3684 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -1391,7 +1391,7 @@ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, * with requisite parameters and calls the IOCTL handler. */ int -mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) +mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) { struct mwifiex_ds_misc_gen_ie gen_ie; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 37123eb1f09394..a2f4e4bbfc33d0 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1729,7 +1729,7 @@ struct cfg80211_connect_params { u8 *ssid; size_t ssid_len; enum nl80211_auth_type auth_type; - u8 *ie; + const u8 *ie; size_t ie_len; bool privacy; enum nl80211_mfp mfp; From 1ec78fb0b30a1b987203cec3a9acac0cc18a7221 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Wed, 29 Jan 2014 17:53:43 +0100 Subject: [PATCH 0076/1983] cfg80211: fix channel configuration in IBSS join Upstream-commit: fe94f3a4ffaa20c7470038c69ffc8e545ef5f90a When receiving an IBSS_JOINED event select the BSS object based on the {bssid, channel} couple rather than the bssid only. With the current approach if another cell having the same BSSID (but using a different channel) exists then cfg80211 picks up the wrong BSS object. The result is a mismatching channel configuration between cfg80211 and the driver, that can lead to any sort of problem. The issue can be triggered by having an IBSS sitting on given channel and then asking the driver to create a new cell using the same BSSID but with a different frequency. By passing the channel to cfg80211_get_bss() we can solve this ambiguity and retrieve/create the correct BSS object. All the users of cfg80211_ibss_joined() have been changed accordingly. Moreover WARN when cfg80211_ibss_joined() gets a NULL channel as argument and remove a bogus call of the same function in ath6kl (it does not make sense to call cfg80211_ibss_joined() with a zero BSSID on ibss-leave). Cc: Kalle Valo Cc: Arend van Spriel Cc: Bing Zhao Cc: Jussi Kivilinna Cc: libertas-dev@lists.infradead.org Acked-by: Kalle Valo Signed-off-by: Antonio Quartulli [minor code cleanup in ath6kl] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 8 ++----- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 +++- drivers/net/wireless/libertas/cfg.c | 3 ++- drivers/net/wireless/mwifiex/cfg80211.c | 3 ++- drivers/net/wireless/rndis_wlan.c | 4 +++- include/net/cfg80211.h | 4 +++- net/mac80211/ibss.c | 2 +- net/wireless/core.h | 4 +++- net/wireless/ibss.c | 17 +++++++++----- net/wireless/trace.h | 23 +++++++++++++++---- net/wireless/util.c | 3 ++- 11 files changed, 50 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 56d9bed4b0694e..ea33b691f66145 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -790,7 +790,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, if (nw_type & ADHOC_NETWORK) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", nw_type & ADHOC_CREATOR ? "creator" : "joiner"); - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(vif->ndev, bssid, chan, GFP_KERNEL); cfg80211_put_bss(ar->wiphy, bss); return; } @@ -862,13 +862,9 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, } if (vif->nw_type & ADHOC_NETWORK) { - if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { + if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in ibss mode\n", __func__); - return; - } - memset(bssid, 0, ETH_ALEN); - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); return; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 3d25c18340c57d..1a80bf19cb8908 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4658,6 +4658,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct net_device *ndev = ifp->ndev; struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan; s32 err = 0; if (ifp->vif->mode == WL_MODE_AP) { @@ -4665,9 +4666,10 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else if (brcmf_is_linkup(e)) { brcmf_dbg(CONN, "Linkup\n"); if (brcmf_is_ibssmode(ifp->vif)) { + chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); memcpy(profile->bssid, e->addr, ETH_ALEN); wl_inform_ibss(cfg, ndev, e->addr); - cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL); + cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); set_bit(BRCMF_VIF_STATUS_CONNECTED, diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index cb6d189bc3e60f..54e344aed6e05d 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1766,7 +1766,8 @@ static void lbs_join_post(struct lbs_private *priv, memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); priv->wdev->ssid_len = params->ssid_len; - cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, + GFP_KERNEL); /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ priv->connect_status = LBS_CONNECTED; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 8bfc07cd330e74..dcd0e0de47ba99 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1881,7 +1881,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, params->privacy); done: if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 5028557aa18adb..2e89a865a67d10 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2835,7 +2835,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, GFP_KERNEL); } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) - cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL); + cfg80211_ibss_joined(usbdev->net, bssid, + get_current_channel(usbdev, NULL), + GFP_KERNEL); kfree(info); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a2f4e4bbfc33d0..866703292ef379 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3888,6 +3888,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, * * @dev: network device * @bssid: the BSSID of the IBSS joined + * @channel: the channel of the IBSS joined * @gfp: allocation flags * * This function notifies cfg80211 that the device joined an IBSS or @@ -3897,7 +3898,8 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, * with the locally generated beacon -- this guarantees that there is * always a scan result for this IBSS. cfg80211 will handle the rest. */ -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp); +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel, gfp_t gfp); /** * cfg80211_notify_new_candidate - notify cfg80211 of a new mesh peer candidate diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 3f076b9c9308aa..82fbf329f737b4 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -386,7 +386,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, presp->head_len, 0, GFP_KERNEL); cfg80211_put_bss(local->hw.wiphy, bss); netif_carrier_on(sdata->dev); - cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL); + cfg80211_ibss_joined(sdata->dev, ifibss->bssid, chan, GFP_KERNEL); } static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, diff --git a/net/wireless/core.h b/net/wireless/core.h index f1d193b557b69a..c408300210b6ca 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -211,6 +211,7 @@ struct cfg80211_event { } dc; struct { u8 bssid[ETH_ALEN]; + struct ieee80211_channel *channel; } ij; }; }; @@ -258,7 +259,8 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel); int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index f911c5f9f903d8..e37e39c29dfb82 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -14,7 +14,8 @@ #include "rdev-ops.h" -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) +void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; @@ -28,8 +29,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) if (!wdev->ssid_len) return; - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, + bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); if (WARN_ON(!bss)) @@ -54,21 +54,26 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) #endif } -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) +void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, + struct ieee80211_channel *channel, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; - trace_cfg80211_ibss_joined(dev, bssid); + trace_cfg80211_ibss_joined(dev, bssid, channel); + + if (WARN_ON(!channel)) + return; ev = kzalloc(sizeof(*ev), gfp); if (!ev) return; ev->type = EVENT_IBSS_JOINED; - memcpy(ev->cr.bssid, bssid, ETH_ALEN); + memcpy(ev->ij.bssid, bssid, ETH_ALEN); + ev->ij.channel = channel; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index b89eb3990f0a7e..815a3b7e206bb0 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2279,11 +2279,6 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt, TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr)) ); -DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined, - TP_PROTO(struct net_device *netdev, const u8 *addr), - TP_ARGS(netdev, addr) -); - DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame, TP_PROTO(struct net_device *netdev, const u8 *addr), TP_ARGS(netdev, addr) @@ -2294,6 +2289,24 @@ DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame, TP_ARGS(netdev, addr) ); +TRACE_EVENT(cfg80211_ibss_joined, + TP_PROTO(struct net_device *netdev, const u8 *bssid, + struct ieee80211_channel *channel), + TP_ARGS(netdev, bssid, channel), + TP_STRUCT__entry( + NETDEV_ENTRY + MAC_ENTRY(bssid) + CHAN_ENTRY + ), + TP_fast_assign( + NETDEV_ASSIGN; + MAC_ASSIGN(bssid, bssid); + CHAN_ASSIGN(channel); + ), + TP_printk(NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", " CHAN_PR_FMT, + NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) +); + TRACE_EVENT(cfg80211_probe_status, TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie, bool acked), diff --git a/net/wireless/util.c b/net/wireless/util.c index d39c37104ae2f1..7526a4d8aa1697 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -820,7 +820,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->dc.reason, true); break; case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid, + ev->ij.channel); break; } wdev_unlock(wdev); From 4c20818d5d65a79650ccf6ece4a5601c23e01e18 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 29 Jan 2014 15:32:11 +0100 Subject: [PATCH 0077/1983] brcmfmac: expand sta info to report dtim and beacon period. Upstream-commit: 9ee66d1bb631eb13a967b4888e412ea4d6dc55e5 Expand the get_station command to also report dtim period and beacon interval when the device is connected. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 1a80bf19cb8908..616b37824d33f3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2163,6 +2163,8 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, s32 err = 0; u8 *bssid = profile->bssid; struct brcmf_sta_info_le sta_info_le; + u32 beacon_period; + u32 dtim_period; brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac); if (!check_vif_up(ifp->vif)) @@ -2217,6 +2219,30 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, sinfo->signal = rssi; brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD, + &beacon_period); + if (err) { + brcmf_err("Could not get beacon period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.beacon_interval = + beacon_period; + brcmf_dbg(CONN, "Beacon peroid %d\n", + beacon_period); + } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD, + &dtim_period); + if (err) { + brcmf_err("Could not get DTIM period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.dtim_period = dtim_period; + brcmf_dbg(CONN, "DTIM peroid %d\n", + dtim_period); + } + sinfo->filled |= STATION_INFO_BSS_PARAM; } } else err = -EPERM; From a517dc0810ecc99d064e55ad78a4cb7d2c201aba Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 29 Jan 2014 15:32:12 +0100 Subject: [PATCH 0078/1983] brcmfmac: add owner info to sdio_driver structure Upstream-commit: 0b0acd8ce0d9fcf94ac0d2609ca25a6d99e3864b To link module attribute with sdio device driver attribute in sysfs. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index fa35b23bbaa736..9eb6f786b3b486 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1115,11 +1115,12 @@ static struct sdio_driver brcmf_sdmmc_driver = { .remove = brcmf_ops_sdio_remove, .name = BRCMFMAC_SDIO_PDATA_NAME, .id_table = brcmf_sdmmc_ids, -#ifdef CONFIG_PM_SLEEP .drv = { + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP .pm = &brcmf_sdio_pm_ops, - }, #endif /* CONFIG_PM_SLEEP */ + }, }; static int brcmf_sdio_pd_probe(struct platform_device *pdev) From 0dc40423555106f152b7ba4cb9c1e46dcc96fb63 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Wed, 29 Jan 2014 15:32:13 +0100 Subject: [PATCH 0079/1983] brcmfmac: enable firmware console logging functionality Upstream-commit: 0801e6c5d8224a27abb7afc920676782efdd5922 Address of rte console was not initialized and so console logging functionality didn't kick in. This patch fixes it and makes console polling interval a debugfs variable. Reviewed-by: Franky Lin Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 154 ++++++++++-------- 1 file changed, 88 insertions(+), 66 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ddaa9efd053df3..8882018beda911 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -952,6 +952,86 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } +#ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ + return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + shaddr = bus->ci->rambase + bus->ramsize - 4; + + /* + * Read last word in socram to determine + * address of sdpcm_shared structure + */ + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); + sdio_release_host(bus->sdiodev->func[1]); + if (rv < 0) + return rv; + + addr = le32_to_cpu(addr_le); + + brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_err("invalid sdpcm_shared address 0x%08X\n", + addr); + return -EINVAL; + } + + /* Read hndrte_shared structure */ + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + return rv; + + /* Endianness */ + sh->flags = le32_to_cpu(sh_le.flags); + sh->trap_addr = le32_to_cpu(sh_le.trap_addr); + sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); + sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); + sh->assert_line = le32_to_cpu(sh_le.assert_line); + sh->console_addr = le32_to_cpu(sh_le.console_addr); + sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { + brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", + SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK); + return -EPROTO; + } + + return 0; +} + +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ + struct sdpcm_shared sh; + + if (brcmf_sdio_readshared(bus, &sh) == 0) + bus->console_addr = sh.console_addr; +} +#else +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) { u32 intstatus = 0; @@ -995,6 +1075,12 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) else brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", bus->sdpcm_ver); + + /* + * Retrieve console state address now that firmware should have + * updated it. + */ + brcmf_sdio_get_console_addr(bus); } /* @@ -2811,72 +2897,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #ifdef DEBUG -static inline bool brcmf_sdio_valid_shared_address(u32 addr) -{ - return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); -} - -static int brcmf_sdio_readshared(struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr; - int rv; - u32 shaddr = 0; - struct sdpcm_shared_le sh_le; - __le32 addr_le; - - shaddr = bus->ci->rambase + bus->ramsize - 4; - - /* - * Read last word in socram to determine - * address of sdpcm_shared structure - */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); - if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); - - /* - * Check if addr is valid. - * NVRAM length at the end of memory should have been overwritten. - */ - if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; - } - - /* Read hndrte_shared structure */ - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); - if (rv < 0) - return rv; - - /* Endianness */ - sh->flags = le32_to_cpu(sh_le.flags); - sh->trap_addr = le32_to_cpu(sh_le.trap_addr); - sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); - sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); - sh->assert_line = le32_to_cpu(sh_le.assert_line); - sh->console_addr = le32_to_cpu(sh_le.console_addr); - sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - - if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { - brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", - SDPCM_SHARED_VERSION, - sh->flags & SDPCM_SHARED_VERSION_MASK); - return -EPROTO; - } - - return 0; -} - static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) @@ -3106,6 +3126,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) debugfs_create_file("forensics", S_IRUGO, dentry, bus, &brcmf_sdio_forensic_ops); brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); + debugfs_create_u32("console_interval", 0644, dentry, + &bus->console_interval); } #else static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) From bb5a85f21da445c49ec515fbf39a5460a10332cb Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:14 +0100 Subject: [PATCH 0080/1983] brcmfmac: move SDIO specific functions Upstream-commit: 65d80d0b80960f9bc35d1067a1a297efc60cc014 The chip related functions will be made agnostic of the host interface. However, some functions in the source file are rather SDIO specific so better move it to the SDIO specific source file. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 173 +++++++++++++++++- .../wireless/brcm80211/brcmfmac/sdio_chip.c | 172 +---------------- .../wireless/brcm80211/brcmfmac/sdio_chip.h | 3 - .../wireless/brcm80211/brcmfmac/sdio_host.h | 1 + 4 files changed, 174 insertions(+), 175 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 8882018beda911..a214837430956f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -493,6 +493,52 @@ enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_SUB, }; +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { + u8 strength; /* Pad Drive Strength in mA */ + u8 sel; /* Chip-specific select value */ +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { + {16, 0x7}, + {12, 0x5}, + {8, 0x3}, + {4, 0x1} +}; + #define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" #define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" #define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" @@ -3741,6 +3787,131 @@ static void brcmf_sdio_dataworker(struct work_struct *work) } } +static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 drivestrength) +{ + const struct sdiod_drive_str *str_tab = NULL; + u32 str_mask; + u32 str_shift; + char chn[8]; + u32 base = ci->c_inf[0].base; + u32 i; + u32 drivestrength_sel = 0; + u32 cc_data_temp; + u32 addr; + + if (!(ci->c_inf[0].caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + str_tab = sdiod_drvstr_tab1_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + str_tab = sdiod_drvstr_tab6_1v8; + str_mask = 0x00001800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): + /* note: 43143 does not support tristate */ + i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; + if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { + str_tab = sdiod_drvstr_tab2_3v3; + str_mask = 0x00000007; + str_shift = 0; + } else + brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", + brcmf_sdio_chip_name(ci->chip, chn, 8), + drivestrength); + break; + case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + str_tab = sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + brcmf_sdio_chip_name(ci->chip, chn, 8), + ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + addr = CORE_CC_REG(base, chipcontrol_addr); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + + brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", + str_tab[i].strength, drivestrength, cc_data_temp); + } +} + +int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev) +{ + int err = 0; + u8 clkval, clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (err) { + brcmf_err("error writing for HT off\n"); + return err; + } + + /* If register supported, wait for ALPAvail and then force ALP */ + /* This may take up to 15 milliseconds */ + clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); + + if ((clkval & ~SBSDIO_AVBITS) != clkset) { + brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", + clkset, clkval); + return -EACCES; + } + + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + static bool brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { @@ -3791,7 +3962,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) drivestrength = bus->sdiodev->pdata->drive_strength; else drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); /* Get info on the SOCRAM cores... */ bus->ramsize = bus->ci->ramsize; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index 2de2736eb332db..64e5ef84858418 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -73,50 +73,6 @@ #define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 #define D11_BCMA_IOCTL_PHYRESET 0x0008 -#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) -/* SDIO Pad drive strength to select value mappings */ -struct sdiod_drive_str { - u8 strength; /* Pad Drive Strength in mA */ - u8 sel; /* Chip-specific select value */ -}; -/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { - {32, 0x6}, - {26, 0x7}, - {22, 0x4}, - {16, 0x5}, - {12, 0x2}, - {8, 0x3}, - {4, 0x0}, - {0, 0x1} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ -static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { - {6, 0x7}, - {5, 0x6}, - {4, 0x5}, - {3, 0x4}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ -static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { - {3, 0x3}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} }; - -/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { - {16, 0x7}, - {12, 0x5}, - {8, 0x3}, - {4, 0x1} -}; - u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid) { @@ -662,51 +618,6 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, return brcmf_sdio_chip_cichk(ci); } -static int -brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) -{ - int err = 0; - u8 clkval, clkset; - - /* Try forcing SDIO core to do ALPAvail request only */ - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - if (err) { - brcmf_err("error writing for HT off\n"); - return err; - } - - /* If register supported, wait for ALPAvail and then force ALP */ - /* This may take up to 15 milliseconds */ - clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); - - if ((clkval & ~SBSDIO_AVBITS) != clkset) { - brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", - clkset, clkval); - return -EACCES; - } - - SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL)), - !SBSDIO_ALPAV(clkval)), - PMU_MAX_TRANSITION_DLY); - if (!SBSDIO_ALPAV(clkval)) { - brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", - clkval); - return -EBUSY; - } - - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - udelay(65); - - /* Also, disable the extra SDIO pull-ups */ - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); - - return 0; -} - static void brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci) @@ -755,7 +666,7 @@ int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, if (!ci) return -ENOMEM; - ret = brcmf_sdio_chip_buscoreprep(sdiodev); + ret = brcmf_sdio_buscoreprep(sdiodev); if (ret != 0) goto err; @@ -787,87 +698,6 @@ brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr) *ci_ptr = NULL; } -static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - -void -brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 drivestrength) -{ - const struct sdiod_drive_str *str_tab = NULL; - u32 str_mask; - u32 str_shift; - char chn[8]; - u32 base = ci->c_inf[0].base; - u32 i; - u32 drivestrength_sel = 0; - u32 cc_data_temp; - u32 addr; - - if (!(ci->c_inf[0].caps & CC_CAP_PMU)) - return; - - switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): - str_tab = sdiod_drvstr_tab1_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): - str_tab = sdiod_drvstr_tab6_1v8; - str_mask = 0x00001800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): - /* note: 43143 does not support tristate */ - i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; - if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { - str_tab = sdiod_drvstr_tab2_3v3; - str_mask = 0x00000007; - str_shift = 0; - } else - brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - drivestrength); - break; - case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): - str_tab = sdiod_drive_strength_tab5_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - default: - brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - ci->chiprev, ci->pmurev); - break; - } - - if (str_tab != NULL) { - for (i = 0; str_tab[i].strength != 0; i++) { - if (drivestrength >= str_tab[i].strength) { - drivestrength_sel = str_tab[i].sel; - break; - } - } - addr = CORE_CC_REG(base, chipcontrol_addr); - brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); - cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); - cc_data_temp &= ~str_mask; - drivestrength_sel <<= str_shift; - cc_data_temp |= drivestrength_sel; - brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); - - brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", - str_tab[i].strength, drivestrength, cc_data_temp); - } -} - static void brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index fb0614329ede71..2bc00c5d1a7fba 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -219,9 +219,6 @@ struct sdpcmd_regs { int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip **ci_ptr); void brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr); -void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, - u32 drivestrength); u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid); void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 092e9c8249926a..396c06cea8efdf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -239,5 +239,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev); #endif /* _BRCM_SDH_H_ */ From 8ffd68a9db30b3ca5f68f5b44a5e434db5b3e6b9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:15 +0100 Subject: [PATCH 0081/1983] brcmfmac: rename sdio_chip.[ch] Upstream-commit: 20c9c9bc144c8a7df04259fa98fd0597b2f85cc2 Just renaming the file. This file will contain chip related functions that are independent of the host interface type. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 4 +- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 - .../net/wireless/brcm80211/brcmfmac/chip.c | 802 ++++++++++++++++++ .../brcmfmac/{sdio_chip.h => chip.h} | 0 .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- 5 files changed, 805 insertions(+), 4 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/chip.c rename drivers/net/wireless/brcm80211/brcmfmac/{sdio_chip.h => chip.h} (100%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 57cddee03252b0..1d2ceac3a221bd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -24,6 +24,7 @@ ccflags-y += -D__CHECK_ENDIAN__ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ wl_cfg80211.o \ + chip.o \ fwil.o \ fweh.o \ fwsignal.o \ @@ -36,8 +37,7 @@ brcmfmac-objs += \ btcoex.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ - bcmsdh.o \ - sdio_chip.o + bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o brcmfmac-$(CONFIG_BRCMDBG) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9eb6f786b3b486..5711fd6f551a85 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -43,7 +43,6 @@ #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" -#include "sdio_chip.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c new file mode 100644 index 00000000000000..37fd44a3483a84 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -0,0 +1,802 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* ***** SDIO interface chip backplane handle functions ***** */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "dhd_dbg.h" +#include "sdio_host.h" +#include "chip.h" + +/* chip core base & ramsize */ +/* bcm4329 */ +/* SDIO device core, ID 0x829 */ +#define BCM4329_CORE_BUS_BASE 0x18011000 +/* internal memory core, ID 0x80e */ +#define BCM4329_CORE_SOCRAM_BASE 0x18003000 +/* ARM Cortex M3 core, ID 0x82a */ +#define BCM4329_CORE_ARM_BASE 0x18002000 +#define BCM4329_RAMSIZE 0x48000 + +/* bcm43143 */ +/* SDIO device core */ +#define BCM43143_CORE_BUS_BASE 0x18002000 +/* internal memory core */ +#define BCM43143_CORE_SOCRAM_BASE 0x18004000 +/* ARM Cortex M3 core, ID 0x82a */ +#define BCM43143_CORE_ARM_BASE 0x18003000 +#define BCM43143_RAMSIZE 0x70000 + +/* All D11 cores, ID 0x812 */ +#define BCM43xx_CORE_D11_BASE 0x18001000 + +#define SBCOREREV(sbidh) \ + ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ + ((sbidh) & SSB_IDHIGH_RCLO)) + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 + +/* EROM CompIdentB */ +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 + +/* ARM CR4 core specific control flag bits */ +#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 + +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + +u8 +brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid) +{ + u8 idx; + + for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) + if (coreid == ci->c_inf[idx].id) + return idx; + + return BRCMF_MAX_CORENUM; +} + +static u32 +brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid) +{ + u32 regdata; + u8 idx; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbidhigh), + NULL); + return SBCOREREV(regdata); +} + +static u32 +brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid) +{ + u8 idx; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + + return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; +} + +static bool +brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid) +{ + u32 regdata; + u8 idx; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return false; + + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); + regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | + SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); + return SSB_TMSLOW_CLOCK == regdata; +} + +static bool +brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid) +{ + u32 regdata; + u8 idx; + bool ret; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return false; + + regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + NULL); + ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; + + regdata = brcmf_sdiod_regrl(sdiodev, + ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + NULL); + ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); + + return ret; +} + +static void +brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits) +{ + u32 regdata, base; + u8 idx; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + base = ci->c_inf[idx].base; + + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); + if (regdata & SSB_TMSLOW_RESET) + return; + + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); + if ((regdata & SSB_TMSLOW_CLOCK) != 0) { + /* + * set target reject and spin until busy is clear + * (preserve core-specific bits) + */ + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatelow), NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), + regdata | SSB_TMSLOW_REJECT, NULL); + + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatelow), NULL); + udelay(1); + SPINWAIT((brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatehigh), + NULL) & + SSB_TMSHIGH_BUSY), 100000); + + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatehigh), + NULL); + if (regdata & SSB_TMSHIGH_BUSY) + brcmf_err("core state still busy\n"); + + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), + NULL); + if (regdata & SSB_IDLOW_INITIATOR) { + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL); + regdata |= SSB_IMSTATE_REJECT; + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), + regdata, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL); + udelay(1); + SPINWAIT((brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL) & + SSB_IMSTATE_BUSY), 100000); + } + + /* set reset and reject while enabling the clocks */ + regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), + regdata, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatelow), NULL); + udelay(10); + + /* clear the initiator reject bit */ + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), + NULL); + if (regdata & SSB_IDLOW_INITIATOR) { + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL); + regdata &= ~SSB_IMSTATE_REJECT; + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), + regdata, NULL); + } + } + + /* leave reset and reject asserted */ + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); + udelay(1); +} + +static void +brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits) +{ + u8 idx; + u32 regdata; + u32 wrapbase; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return; + + wrapbase = ci->c_inf[idx].wrapbase; + + /* if core is already in reset, just return */ + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL); + if ((regdata & BCMA_RESET_CTL_RESET) != 0) + return; + + /* configure reset */ + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | + BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); + + /* put in reset */ + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, + BCMA_RESET_CTL_RESET, NULL); + usleep_range(10, 20); + + /* wait till reset is 1 */ + SPINWAIT(brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) != + BCMA_RESET_CTL_RESET, 300); + + /* post reset configure */ + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | + BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); +} + +static void +brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits, u32 post_resetbits) +{ + u32 regdata; + u8 idx; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return; + + /* + * Must do the disable sequence first to work for + * arbitrary current core state. + */ + brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, pre_resetbits, + in_resetbits); + + /* + * Now do the initialization sequence. + * set reset while enabling the clock and + * forcing them on throughout the core + */ + brcmf_sdiod_regwl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, + NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); + udelay(1); + + /* clear any serror */ + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), + NULL); + if (regdata & SSB_TMSHIGH_SERR) + brcmf_sdiod_regwl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), + 0, NULL); + + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbimstate), + NULL); + if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) + brcmf_sdiod_regwl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbimstate), + regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), + NULL); + + /* clear reset and allow it to propagate throughout the core */ + brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); + udelay(1); + + /* leave clock enabled */ + brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + SSB_TMSLOW_CLOCK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); + udelay(1); +} + +static void +brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, + u32 in_resetbits, u32 post_resetbits) +{ + u8 idx; + u32 regdata; + u32 wrapbase; + + idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return; + + wrapbase = ci->c_inf[idx].wrapbase; + + /* must disable first to work for arbitrary current core state */ + brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, pre_resetbits, + in_resetbits); + + while (brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) & + BCMA_RESET_CTL_RESET) { + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, 0, NULL); + usleep_range(40, 60); + } + + brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, post_resetbits | + BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); +} + +#ifdef DEBUG +/* safety check for chipinfo */ +static int brcmf_sdio_chip_cichk(struct brcmf_chip *ci) +{ + u8 core_idx; + + /* check RAM core presence for ARM CM3 core */ + core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); + if (BRCMF_MAX_CORENUM != core_idx) { + core_idx = brcmf_sdio_chip_getinfidx(ci, + BCMA_CORE_INTERNAL_MEM); + if (BRCMF_MAX_CORENUM == core_idx) { + brcmf_err("RAM core not provided with ARM CM3 core\n"); + return -ENODEV; + } + } + + /* check RAM base for ARM CR4 core */ + core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); + if (BRCMF_MAX_CORENUM != core_idx) { + if (ci->rambase == 0) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -ENOMEM; + } + } + + return 0; +} +#else /* DEBUG */ +static inline int brcmf_sdio_chip_cichk(struct brcmf_chip *ci) +{ + return 0; +} +#endif + +static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) +{ + u32 regdata; + u32 socitype; + + /* Get CC core rev + * Chipid is assume to be at offset 0 from SI_ENUM_BASE + * For different chiptypes or old sdio hosts w/o chipcommon, + * other ways of recognition should be added here. + */ + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_CC_REG(SI_ENUM_BASE, chipid), + NULL); + ci->chip = regdata & CID_ID_MASK; + ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + ci->chiprev >= 2) + ci->chip = BCM4339_CHIP_ID; + socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + + brcmf_dbg(INFO, "found %s chip: id=0x%x, rev=%d\n", + socitype == SOCI_SB ? "SB" : "AXI", ci->chip, ci->chiprev); + + if (socitype == SOCI_SB) { + if (ci->chip != BCM4329_CHIP_ID) { + brcmf_err("SB chip is not supported\n"); + return -ENODEV; + } + ci->iscoreup = brcmf_sdio_sb_iscoreup; + ci->corerev = brcmf_sdio_sb_corerev; + ci->coredisable = brcmf_sdio_sb_coredisable; + ci->resetcore = brcmf_sdio_sb_resetcore; + + ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; + ci->c_inf[0].base = SI_ENUM_BASE; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->ramsize = BCM4329_RAMSIZE; + } else if (socitype == SOCI_AI) { + ci->iscoreup = brcmf_sdio_ai_iscoreup; + ci->corerev = brcmf_sdio_ai_corerev; + ci->coredisable = brcmf_sdio_ai_coredisable; + ci->resetcore = brcmf_sdio_ai_resetcore; + + ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; + ci->c_inf[0].base = SI_ENUM_BASE; + + /* Address of cores for new chips should be added here */ + switch (ci->chip) { + case BCM43143_CHIP_ID: + ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000; + ci->c_inf[0].cib = 0x2b000000; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = BCM43143_CORE_BUS_BASE; + ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000; + ci->c_inf[1].cib = 0x18000000; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE; + ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000; + ci->c_inf[2].cib = 0x14000000; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; + ci->c_inf[3].cib = 0x07000000; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = BCM43143_RAMSIZE; + break; + case BCM43241_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2a084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0e004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x14080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x07004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x90000; + break; + case BCM4330_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x27004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x07004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x0d080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x03004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x48000; + break; + case BCM4334_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x29004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0d004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x13080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x07004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x80000; + break; + case BCM4335_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2b084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18005000; + ci->c_inf[1].wrapbase = 0x18105000; + ci->c_inf[1].cib = 0x0f004211; + ci->c_inf[2].id = BCMA_CORE_ARM_CR4; + ci->c_inf[2].base = 0x18002000; + ci->c_inf[2].wrapbase = 0x18102000; + ci->c_inf[2].cib = 0x01084411; + ci->c_inf[3].id = BCMA_CORE_80211; + ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; + ci->ramsize = 0xc0000; + ci->rambase = 0x180000; + break; + case BCM43362_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x27004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0a004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x08080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x03004211; + ci->c_inf[4].id = BCMA_CORE_80211; + ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; + ci->ramsize = 0x3C000; + break; + case BCM4339_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2e084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18005000; + ci->c_inf[1].wrapbase = 0x18105000; + ci->c_inf[1].cib = 0x15004211; + ci->c_inf[2].id = BCMA_CORE_ARM_CR4; + ci->c_inf[2].base = 0x18002000; + ci->c_inf[2].wrapbase = 0x18102000; + ci->c_inf[2].cib = 0x04084411; + ci->c_inf[3].id = BCMA_CORE_80211; + ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; + ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; + ci->ramsize = 0xc0000; + ci->rambase = 0x180000; + break; + default: + brcmf_err("AXI chip is not supported\n"); + return -ENODEV; + } + } else { + brcmf_err("chip backplane type %u is not supported\n", + socitype); + return -ENODEV; + } + + return brcmf_sdio_chip_cichk(ci); +} + +static void +brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) +{ + u32 base = ci->c_inf[0].base; + + /* get chipcommon rev */ + ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); + + /* get chipcommon capabilites */ + ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev, + CORE_CC_REG(base, capabilities), + NULL); + + /* get pmu caps & rev */ + if (ci->c_inf[0].caps & CC_CAP_PMU) { + ci->pmucaps = + brcmf_sdiod_regrl(sdiodev, + CORE_CC_REG(base, pmucapabilities), + NULL); + ci->pmurev = ci->pmucaps & PCAP_REV_MASK; + } + + ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); + + brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", + ci->c_inf[0].rev, ci->pmurev, + ci->c_inf[1].rev, ci->c_inf[1].id); + + /* + * Make sure any on-chip ARM is off (in case strapping is wrong), + * or downloaded code was already running. + */ + ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); +} + +int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip **ci_ptr) +{ + int ret; + struct brcmf_chip *ci; + + brcmf_dbg(TRACE, "Enter\n"); + + ci = kzalloc(sizeof(*ci), GFP_ATOMIC); + if (!ci) + return -ENOMEM; + + ret = brcmf_sdio_buscoreprep(sdiodev); + if (ret != 0) + goto err; + + ret = brcmf_sdio_chip_recognition(sdiodev, ci); + if (ret != 0) + goto err; + + brcmf_sdio_chip_buscoresetup(sdiodev, ci); + + brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), + 0, NULL); + brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), + 0, NULL); + + *ci_ptr = ci; + return 0; + +err: + kfree(ci); + return ret; +} + +void +brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr) +{ + brcmf_dbg(TRACE, "Enter\n"); + + kfree(*ci_ptr); + *ci_ptr = NULL; +} + +static void +brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) +{ + ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); + ci->resetcore(sdiodev, ci, BCMA_CORE_80211, + D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); + ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0); +} + +static bool brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) +{ + u8 core_idx; + u32 reg_addr; + + if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) { + brcmf_err("SOCRAM core is down after reset?\n"); + return false; + } + + /* clear all interrupts */ + core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); + reg_addr = ci->c_inf[core_idx].base; + reg_addr += offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0, 0); + + return true; +} + +static inline void +brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) +{ + u8 idx; + u32 regdata; + u32 wrapbase; + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); + + if (idx == BRCMF_MAX_CORENUM) + return; + + wrapbase = ci->c_inf[idx].wrapbase; + regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); + regdata &= ARMCR4_BCMA_IOCTL_CPUHALT; + ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, regdata, + ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); + ci->resetcore(sdiodev, ci, BCMA_CORE_80211, + D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 rstvec) +{ + u8 core_idx; + u32 reg_addr; + + /* clear all interrupts */ + core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); + reg_addr = ci->c_inf[core_idx].base; + reg_addr += offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); + + /* restore ARM */ + ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT, + 0, 0); + + return true; +} + +void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci) +{ + u8 arm_core_idx; + + arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); + if (BRCMF_MAX_CORENUM != arm_core_idx) { + brcmf_sdio_chip_cm3_enterdl(sdiodev, ci); + return; + } + + brcmf_sdio_chip_cr4_enterdl(sdiodev, ci); +} + +bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 rstvec) +{ + u8 arm_core_idx; + + arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); + if (BRCMF_MAX_CORENUM != arm_core_idx) + return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci); + + return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, rstvec); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h similarity index 100% rename from drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h rename to drivers/net/wireless/brcm80211/brcmfmac/chip.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a214837430956f..ce53cb527eef74 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -40,7 +40,7 @@ #include #include #include "sdio_host.h" -#include "sdio_chip.h" +#include "chip.h" #include "nvram.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ From f628fda833f6926436deb82987eacbd059018a4c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 29 Jan 2014 15:32:16 +0100 Subject: [PATCH 0082/1983] brcmfmac: fix sdio sending of large buffers. Upstream-commit: 79c868e5ada93601c8107a6d08fbb1e0d9348b94 the function brcmf_sdiod_ramrw is supposed to be able to send large blobs of data. However inside the loop the skb->len field did not correctly get reset each round. As a result only small blobs could be sent. This patch fixes this problem. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 5711fd6f551a85..07e7d252025784 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -826,7 +826,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, } if (!write) memcpy(data, pkt->data, dsize); - skb_trim(pkt, dsize); + skb_trim(pkt, 0); /* Adjust for next transfer (if any) */ size -= dsize; From 2af52f36427a70b0fcb788bcb4c1e7b6f8b4e9d8 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 29 Jan 2014 15:32:17 +0100 Subject: [PATCH 0083/1983] brcmfmac: simplify sdio code download routine. Upstream-commit: f9951c13349ac1553ab1e0962dc3f877e4841037 brcmf_sdio_download_code_file is using a loop to send small blobs of data. This is unnecessarily complex and was simplified with this patch. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ce53cb527eef74..9dea0427c0306d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3292,32 +3292,17 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, const struct firmware *fw) { int err; - int offset; - int address; - int len; brcmf_dbg(TRACE, "Enter\n"); - err = 0; - offset = 0; - address = bus->ci->rambase; - while (offset < fw->size) { - len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK : - fw->size - offset; - err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, - (u8 *)&fw->data[offset], len); - if (err) { - brcmf_err("error %d on writing %d membytes at 0x%08x\n", - err, len, address); - return err; - } - offset += len; - address += len; - } - if (!err) - if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, - (u8 *)fw->data, fw->size)) - err = -EIO; + err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, + (u8 *)fw->data, fw->size); + if (err) + brcmf_err("error %d on writing %d membytes at 0x%08x\n", + err, (int)fw->size, bus->ci->rambase); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; return err; } From 0e228325e01824f52f7f020e90cfe14c63f441a6 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 29 Jan 2014 15:32:18 +0100 Subject: [PATCH 0084/1983] brcmfmac: on sdio remove first detach bus then stop worker. Upstream-commit: e0c180ecf181aa157df28313c82d3b7449b5df65 Currently the function sdio_remove will first destroy the datawork workqueue and then detach the bus. This can create the situation where work gets added on non-existing work queue resulting in panic. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 9dea0427c0306d..b4f79a82acd71b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4202,14 +4202,14 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) /* De-register interrupt handler */ brcmf_sdiod_intr_unregister(bus->sdiodev); - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); - if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); } + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + if (bus->ci) { if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { sdio_claim_host(bus->sdiodev->func[1]); From eb7cbcdf96755fbca88c9a1564ed5fe67f43553b Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:19 +0100 Subject: [PATCH 0085/1983] brcmfmac: make chip related functions host interface independent Upstream-commit: cb7cf7be9eba76f3cd6258906074c72084570c84 This patch make several chip related functions host interface independent by defining callback interface struct brcmf_buscore_ops. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/chip.c | 1150 +++++++++-------- .../net/wireless/brcm80211/brcmfmac/chip.h | 259 +--- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 200 +-- .../wireless/brcm80211/brcmfmac/sdio_host.h | 90 +- 4 files changed, 900 insertions(+), 799 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 37fd44a3483a84..151a67110ecfc5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Broadcom Corporation + * Copyright (c) 2014 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,25 +13,36 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* ***** SDIO interface chip backplane handle functions ***** */ - -#include -#include -#include -#include -#include +#include +#include +#include #include #include +#include -#include +#include +#include #include -#include #include -#include +#include #include "dhd_dbg.h" -#include "sdio_host.h" #include "chip.h" +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 + +/* EROM CompIdentB */ +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 + +/* ARM CR4 core specific control flag bits */ +#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 + +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + /* chip core base & ramsize */ /* bcm4329 */ /* SDIO device core, ID 0x829 */ @@ -51,20 +62,58 @@ #define BCM43143_CORE_ARM_BASE 0x18003000 #define BCM43143_RAMSIZE 0x70000 -/* All D11 cores, ID 0x812 */ -#define BCM43xx_CORE_D11_BASE 0x18001000 - +#define CORE_SB(base, field) \ + (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) #define SBCOREREV(sbidh) \ ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ ((sbidh) & SSB_IDHIGH_RCLO)) -/* SOC Interconnect types (aka chip types) */ -#define SOCI_SB 0 -#define SOCI_AI 1 - -/* EROM CompIdentB */ -#define CIB_REV_MASK 0xff000000 -#define CIB_REV_SHIFT 24 +struct sbconfig { + u32 PAD[2]; + u32 sbipsflag; /* initiator port ocp slave flag */ + u32 PAD[3]; + u32 sbtpsflag; /* target port ocp slave flag */ + u32 PAD[11]; + u32 sbtmerrloga; /* (sonics >= 2.3) */ + u32 PAD; + u32 sbtmerrlog; /* (sonics >= 2.3) */ + u32 PAD[3]; + u32 sbadmatch3; /* address match3 */ + u32 PAD; + u32 sbadmatch2; /* address match2 */ + u32 PAD; + u32 sbadmatch1; /* address match1 */ + u32 PAD[7]; + u32 sbimstate; /* initiator agent state */ + u32 sbintvec; /* interrupt mask */ + u32 sbtmstatelow; /* target state */ + u32 sbtmstatehigh; /* target state */ + u32 sbbwa0; /* bandwidth allocation table0 */ + u32 PAD; + u32 sbimconfiglow; /* initiator configuration */ + u32 sbimconfighigh; /* initiator configuration */ + u32 sbadmatch0; /* address match0 */ + u32 PAD; + u32 sbtmconfiglow; /* target configuration */ + u32 sbtmconfighigh; /* target configuration */ + u32 sbbconfig; /* broadcast configuration */ + u32 PAD; + u32 sbbstate; /* broadcast state */ + u32 PAD[3]; + u32 sbactcnfg; /* activate configuration */ + u32 PAD[3]; + u32 sbflagst; /* current sbflags */ + u32 PAD[3]; + u32 sbidlow; /* identification */ + u32 sbidhigh; /* identification */ +}; + +struct brcmf_core_priv { + struct brcmf_core pub; + u32 wrapbase; + struct list_head list; + struct brcmf_chip_priv *chip; +}; /* ARM CR4 core specific control flag bits */ #define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 @@ -73,350 +122,350 @@ #define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 #define D11_BCMA_IOCTL_PHYRESET 0x0008 -u8 -brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid) -{ - u8 idx; - - for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) - if (coreid == ci->c_inf[idx].id) - return idx; - - return BRCMF_MAX_CORENUM; -} - -static u32 -brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) +struct brcmf_chip_priv { + struct brcmf_chip pub; + const struct brcmf_buscore_ops *ops; + void *ctx; + /* assured first core is chipcommon, second core is buscore */ + struct list_head cores; + u16 num_cores; + + bool (*iscoreup)(struct brcmf_core_priv *core); + void (*coredisable)(struct brcmf_core_priv *core, u32 prereset, + u32 reset); + void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset, + u32 postreset); +}; + +static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci, + struct brcmf_core *core) { u32 regdata; - u8 idx; - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbidhigh), - NULL); - return SBCOREREV(regdata); + regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh)); + core->rev = SBCOREREV(regdata); } -static u32 -brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) -{ - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; -} - -static bool -brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) +static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core) { + struct brcmf_chip_priv *ci; u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return false; + u32 address; - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + ci = core->chip; + address = CORE_SB(core->pub.base, sbtmstatelow); + regdata = ci->ops->read32(ci->ctx, address); regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); return SSB_TMSLOW_CLOCK == regdata; } -static bool -brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid) +static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core) { + struct brcmf_chip_priv *ci; u32 regdata; - u8 idx; bool ret; - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return false; - - regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); + ci = core->chip; + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; - regdata = brcmf_sdiod_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); return ret; } -static void -brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits) +static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) { - u32 regdata, base; - u8 idx; + struct brcmf_chip_priv *ci; + u32 val, base; - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - base = ci->c_inf[idx].base; - - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); - if (regdata & SSB_TMSLOW_RESET) + ci = core->chip; + base = core->pub.base; + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if (val & SSB_TMSLOW_RESET) return; - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); - if ((regdata & SSB_TMSLOW_CLOCK) != 0) { + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if ((val & SSB_TMSLOW_CLOCK) != 0) { /* * set target reject and spin until busy is clear * (preserve core-specific bits) */ - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatelow), NULL); - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata | SSB_TMSLOW_REJECT, NULL); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + val | SSB_TMSLOW_REJECT); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatelow), NULL); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); udelay(1); - SPINWAIT((brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL) & - SSB_TMSHIGH_BUSY), 100000); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL); - if (regdata & SSB_TMSHIGH_BUSY) + SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)) + & SSB_TMSHIGH_BUSY), 100000); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (val & SSB_TMSHIGH_BUSY) brcmf_err("core state still busy\n"); - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); - if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - regdata |= SSB_IMSTATE_REJECT; - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val |= SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); udelay(1); - SPINWAIT((brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL) & + SPINWAIT((ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)) & SSB_IMSTATE_BUSY), 100000); } /* set reset and reject while enabling the clocks */ - regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | - SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbtmstatelow), NULL); + val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); udelay(10); /* clear the initiator reject bit */ - regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); - if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - regdata &= ~SSB_IMSTATE_REJECT; - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val &= ~SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); } } /* leave reset and reject asserted */ - brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); udelay(1); } -static void -brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits) +static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) { - u8 idx; + struct brcmf_chip_priv *ci; u32 regdata; - u32 wrapbase; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return; - wrapbase = ci->c_inf[idx].wrapbase; + ci = core->chip; /* if core is already in reset, just return */ - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL); + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); if ((regdata & BCMA_RESET_CTL_RESET) != 0) return; /* configure reset */ - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | - BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); /* put in reset */ - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, - BCMA_RESET_CTL_RESET, NULL); + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, + BCMA_RESET_CTL_RESET); usleep_range(10, 20); /* wait till reset is 1 */ - SPINWAIT(brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) != + SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) != BCMA_RESET_CTL_RESET, 300); - /* post reset configure */ - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, pre_resetbits | - BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); + /* in-reset configure */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); } -static void -brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits, u32 post_resetbits) +static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) { + struct brcmf_chip_priv *ci; u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return; + u32 base; + ci = core->chip; + base = core->pub.base; /* * Must do the disable sequence first to work for * arbitrary current core state. */ - brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, pre_resetbits, - in_resetbits); + brcmf_chip_sb_coredisable(core, 0, 0); /* * Now do the initialization sequence. * set reset while enabling the clock and * forcing them on throughout the core */ - brcmf_sdiod_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, - NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_RESET); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); udelay(1); /* clear any serror */ - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - NULL); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); if (regdata & SSB_TMSHIGH_SERR) - brcmf_sdiod_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - 0, NULL); - - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - NULL); - if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) - brcmf_sdiod_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), - NULL); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0); + + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate)); + if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { + regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); + ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata); + } /* clear reset and allow it to propagate throughout the core */ - brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); udelay(1); /* leave clock enabled */ - brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); udelay(1); } -static void -brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits, u32 post_resetbits) +static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) { - u8 idx; - u32 regdata; - u32 wrapbase; + struct brcmf_chip_priv *ci; + int count; - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - if (idx == BRCMF_MAX_CORENUM) - return; - - wrapbase = ci->c_inf[idx].wrapbase; + ci = core->chip; /* must disable first to work for arbitrary current core state */ - brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, pre_resetbits, - in_resetbits); + brcmf_chip_ai_coredisable(core, prereset, reset); - while (brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_RESET_CTL, NULL) & + count = 0; + while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET) { - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_RESET_CTL, 0, NULL); + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0); + count++; + if (count > 50) + break; usleep_range(40, 60); } - brcmf_sdiod_regwl(sdiodev, wrapbase + BCMA_IOCTL, post_resetbits | - BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + postreset | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static char *brcmf_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, + u16 coreid, u32 base, + u32 wrapbase) +{ + struct brcmf_core_priv *core; + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) + return ERR_PTR(-ENOMEM); + + core->pub.id = coreid; + core->pub.base = base; + core->chip = ci; + core->wrapbase = wrapbase; + + list_add_tail(&core->list, &ci->cores); + return &core->pub; } #ifdef DEBUG /* safety check for chipinfo */ -static int brcmf_sdio_chip_cichk(struct brcmf_chip *ci) +static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) { - u8 core_idx; - - /* check RAM core presence for ARM CM3 core */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); - if (BRCMF_MAX_CORENUM != core_idx) { - core_idx = brcmf_sdio_chip_getinfidx(ci, - BCMA_CORE_INTERNAL_MEM); - if (BRCMF_MAX_CORENUM == core_idx) { - brcmf_err("RAM core not provided with ARM CM3 core\n"); - return -ENODEV; + struct brcmf_core_priv *core; + bool need_socram = false; + bool has_socram = false; + int idx = 1; + + list_for_each_entry(core, &ci->cores, list) { + brcmf_dbg(INFO, " [%-2d] core 0x%x rev %-2d base 0x%08x\n", + idx++, core->pub.id, core->pub.rev, core->pub.base); + + switch (core->pub.id) { + case BCMA_CORE_ARM_CM3: + need_socram = true; + break; + case BCMA_CORE_INTERNAL_MEM: + has_socram = true; + break; + case BCMA_CORE_ARM_CR4: + if (ci->pub.rambase == 0) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -ENOMEM; + } + break; + default: + break; } } - /* check RAM base for ARM CR4 core */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); - if (BRCMF_MAX_CORENUM != core_idx) { - if (ci->rambase == 0) { - brcmf_err("RAM base not provided with ARM CR4 core\n"); - return -ENOMEM; - } + /* check RAM core presence for ARM CM3 core */ + if (need_socram && !has_socram) { + brcmf_err("RAM core not provided with ARM CM3 core\n"); + return -ENODEV; } - return 0; } #else /* DEBUG */ -static inline int brcmf_sdio_chip_cichk(struct brcmf_chip *ci) +static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) { return 0; } #endif -static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) +static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) { + switch (ci->pub.chip) { + case BCM4329_CHIP_ID: + ci->pub.ramsize = BCM4329_RAMSIZE; + break; + case BCM43143_CHIP_ID: + ci->pub.ramsize = BCM43143_RAMSIZE; + break; + case BCM43241_CHIP_ID: + ci->pub.ramsize = 0x90000; + break; + case BCM4330_CHIP_ID: + ci->pub.ramsize = 0x48000; + break; + case BCM4334_CHIP_ID: + ci->pub.ramsize = 0x80000; + break; + case BCM4335_CHIP_ID: + ci->pub.ramsize = 0xc0000; + ci->pub.rambase = 0x180000; + break; + case BCM43362_CHIP_ID: + ci->pub.ramsize = 0x3c000; + break; + case BCM4339_CHIP_ID: + ci->pub.ramsize = 0xc0000; + ci->pub.rambase = 0x180000; + break; + default: + brcmf_err("unknown chip: %s\n", ci->pub.name); + break; + } +} + +static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; u32 regdata; u32 socitype; @@ -425,184 +474,131 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, * For different chiptypes or old sdio hosts w/o chipcommon, * other ways of recognition should be added here. */ - regdata = brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(SI_ENUM_BASE, chipid), - NULL); - ci->chip = regdata & CID_ID_MASK; - ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; - if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && - ci->chiprev >= 2) - ci->chip = BCM4339_CHIP_ID; + regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid)); + ci->pub.chip = regdata & CID_ID_MASK; + ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; - brcmf_dbg(INFO, "found %s chip: id=0x%x, rev=%d\n", - socitype == SOCI_SB ? "SB" : "AXI", ci->chip, ci->chiprev); + brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name)); + brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n", + socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name, + ci->pub.chiprev); if (socitype == SOCI_SB) { - if (ci->chip != BCM4329_CHIP_ID) { + if (ci->pub.chip != BCM4329_CHIP_ID) { brcmf_err("SB chip is not supported\n"); return -ENODEV; } - ci->iscoreup = brcmf_sdio_sb_iscoreup; - ci->corerev = brcmf_sdio_sb_corerev; - ci->coredisable = brcmf_sdio_sb_coredisable; - ci->resetcore = brcmf_sdio_sb_resetcore; - - ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = SI_ENUM_BASE; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->ramsize = BCM4329_RAMSIZE; + ci->iscoreup = brcmf_chip_sb_iscoreup; + ci->coredisable = brcmf_chip_sb_coredisable; + ci->resetcore = brcmf_chip_sb_resetcore; + + core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, + SI_ENUM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + BCM4329_CORE_BUS_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + BCM4329_CORE_SOCRAM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + BCM4329_CORE_ARM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); } else if (socitype == SOCI_AI) { - ci->iscoreup = brcmf_sdio_ai_iscoreup; - ci->corerev = brcmf_sdio_ai_corerev; - ci->coredisable = brcmf_sdio_ai_coredisable; - ci->resetcore = brcmf_sdio_ai_resetcore; + ci->iscoreup = brcmf_chip_ai_iscoreup; + ci->coredisable = brcmf_chip_ai_coredisable; + ci->resetcore = brcmf_chip_ai_resetcore; - ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = SI_ENUM_BASE; + core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, + SI_ENUM_BASE, + SI_ENUM_BASE + 0x100000); /* Address of cores for new chips should be added here */ - switch (ci->chip) { + switch (ci->pub.chip) { case BCM43143_CHIP_ID: - ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000; - ci->c_inf[0].cib = 0x2b000000; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = BCM43143_CORE_BUS_BASE; - ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000; - ci->c_inf[1].cib = 0x18000000; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE; - ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000; - ci->c_inf[2].cib = 0x14000000; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->c_inf[3].cib = 0x07000000; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = BCM43143_RAMSIZE; + core->rev = 43; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + BCM43143_CORE_BUS_BASE, + BCM43143_CORE_BUS_BASE + + 0x100000); + core->rev = 24; + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + BCM43143_CORE_SOCRAM_BASE, + BCM43143_CORE_SOCRAM_BASE + + 0x100000); + core->rev = 20; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + BCM43143_CORE_ARM_BASE, + BCM43143_CORE_ARM_BASE + + 0x100000); + core->rev = 7; break; case BCM43241_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2a084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0e004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x14080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x90000; + core->rev = 42; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + 0x18002000, 0x18102000); + core->rev = 14; + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + 0x18004000, 0x18104000); + core->rev = 20; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + 0x18003000, 0x18103000); + core->rev = 7; break; case BCM4330_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x07004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x0d080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x48000; + core->rev = 39; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + 0x18002000, 0x18102000); + core->rev = 7; + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + 0x18004000, 0x18104000); + core->rev = 13; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + 0x18003000, 0x18103000); + core->rev = 3; break; case BCM4334_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x29004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0d004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x13080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x80000; + core->rev = 41; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + 0x18002000, 0x18102000); + core->rev = 13; + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + 0x18004000, 0x18104000); + core->rev = 19; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + 0x18003000, 0x18103000); + core->rev = 7; break; case BCM4335_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2b084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18005000; - ci->c_inf[1].wrapbase = 0x18105000; - ci->c_inf[1].cib = 0x0f004211; - ci->c_inf[2].id = BCMA_CORE_ARM_CR4; - ci->c_inf[2].base = 0x18002000; - ci->c_inf[2].wrapbase = 0x18102000; - ci->c_inf[2].cib = 0x01084411; - ci->c_inf[3].id = BCMA_CORE_80211; - ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; + core->rev = 43; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + 0x18005000, 0x18105000); + core->rev = 15; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CR4, + 0x18002000, 0x18102000); + core->rev = 1; break; case BCM43362_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0a004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x08080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->c_inf[4].id = BCMA_CORE_80211; - ci->c_inf[4].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[4].wrapbase = ci->c_inf[4].base + 0x00100000; - ci->ramsize = 0x3C000; + core->rev = 39; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + 0x18002000, 0x18102000); + core->rev = 10; + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + 0x18004000, 0x18104000); + core->rev = 8; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + 0x18003000, 0x18103000); + core->rev = 3; break; case BCM4339_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2e084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18005000; - ci->c_inf[1].wrapbase = 0x18105000; - ci->c_inf[1].cib = 0x15004211; - ci->c_inf[2].id = BCMA_CORE_ARM_CR4; - ci->c_inf[2].base = 0x18002000; - ci->c_inf[2].wrapbase = 0x18102000; - ci->c_inf[2].cib = 0x04084411; - ci->c_inf[3].id = BCMA_CORE_80211; - ci->c_inf[3].base = BCM43xx_CORE_D11_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; + core->rev = 46; + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + 0x18005000, 0x18105000); + core->rev = 21; + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CR4, + 0x18002000, 0x18102000); + core->rev = 4; break; default: brcmf_err("AXI chip is not supported\n"); @@ -614,189 +610,321 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, return -ENODEV; } - return brcmf_sdio_chip_cichk(ci); + /* add 802.11 core for all chips on same backplane address */ + core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0x18101000); + + brcmf_chip_get_raminfo(ci); + + return brcmf_chip_cores_check(ci); } -static void -brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) +static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) { - u32 base = ci->c_inf[0].base; + struct brcmf_core *core; + struct brcmf_core_priv *cr4; + u32 val; - /* get chipcommon rev */ - ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); + + core = brcmf_chip_get_core(&chip->pub, id); + if (!core) + return; + + switch (id) { + case BCMA_CORE_ARM_CM3: + brcmf_chip_coredisable(core, 0, 0); + break; + case BCMA_CORE_ARM_CR4: + cr4 = container_of(core, struct brcmf_core_priv, pub); + + /* clear all IOCTL bits except HALT bit */ + val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL); + val &= ARMCR4_BCMA_IOCTL_CPUHALT; + brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, + ARMCR4_BCMA_IOCTL_CPUHALT); + break; + default: + brcmf_err("unknown id: %u\n", id); + break; + } +} + +static int brcmf_chip_setup(struct brcmf_chip_priv *chip) +{ + struct brcmf_chip *pub; + struct brcmf_core_priv *cc; + struct brcmf_core_priv *bus; + u32 base; + u32 val; + int ret = 0; + + pub = &chip->pub; + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + base = cc->pub.base; /* get chipcommon capabilites */ - ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(base, capabilities), - NULL); + pub->cc_caps = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, capabilities)); /* get pmu caps & rev */ - if (ci->c_inf[0].caps & CC_CAP_PMU) { - ci->pmucaps = - brcmf_sdiod_regrl(sdiodev, - CORE_CC_REG(base, pmucapabilities), - NULL); - ci->pmurev = ci->pmucaps & PCAP_REV_MASK; + if (pub->cc_caps & CC_CAP_PMU) { + val = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, pmucapabilities)); + pub->pmurev = val & PCAP_REV_MASK; + pub->pmucaps = val; } - ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); + bus = list_next_entry(cc, list); brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", - ci->c_inf[0].rev, ci->pmurev, - ci->c_inf[1].rev, ci->c_inf[1].id); + cc->pub.rev, pub->pmurev, bus->pub.rev, bus->pub.id); + + /* execute bus core specific setup */ + if (chip->ops->setup) + ret = chip->ops->setup(chip->ctx, pub); /* * Make sure any on-chip ARM is off (in case strapping is wrong), * or downloaded code was already running. */ - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + return ret; } -int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip **ci_ptr) +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops) { - int ret; - struct brcmf_chip *ci; + struct brcmf_chip_priv *chip; + int err = 0; + + if (WARN_ON(!ops->read32)) + err = -EINVAL; + if (WARN_ON(!ops->write32)) + err = -EINVAL; + if (WARN_ON(!ops->prepare)) + err = -EINVAL; + if (WARN_ON(!ops->exit_dl)) + err = -EINVAL; + if (err < 0) + return ERR_PTR(-EINVAL); + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&chip->cores); + chip->num_cores = 0; + chip->ops = ops; + chip->ctx = ctx; + + err = ops->prepare(ctx); + if (err < 0) + goto fail; + + err = brcmf_chip_recognition(chip); + if (err < 0) + goto fail; + + err = brcmf_chip_setup(chip); + if (err < 0) + goto fail; + + return &chip->pub; + +fail: + brcmf_chip_detach(&chip->pub); + return ERR_PTR(err); +} - brcmf_dbg(TRACE, "Enter\n"); +void brcmf_chip_detach(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + struct brcmf_core_priv *tmp; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry_safe(core, tmp, &chip->cores, list) { + list_del(&core->list); + kfree(core); + } + kfree(chip); +} - ci = kzalloc(sizeof(*ci), GFP_ATOMIC); - if (!ci) - return -ENOMEM; +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; - ret = brcmf_sdio_buscoreprep(sdiodev); - if (ret != 0) - goto err; + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry(core, &chip->cores, list) + if (core->pub.id == coreid) + return &core->pub; - ret = brcmf_sdio_chip_recognition(sdiodev, ci); - if (ret != 0) - goto err; + return NULL; +} - brcmf_sdio_chip_buscoresetup(sdiodev, ci); +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *cc; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON)) + return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON); + return &cc->pub; +} - brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), - 0, NULL); - brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), - 0, NULL); +bool brcmf_chip_iscoreup(struct brcmf_core *pub) +{ + struct brcmf_core_priv *core; - *ci_ptr = ci; - return 0; + core = container_of(pub, struct brcmf_core_priv, pub); + return core->chip->iscoreup(core); +} -err: - kfree(ci); - return ret; +void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->coredisable(core, prereset, reset); } -void -brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr) +void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, + u32 postreset) { - brcmf_dbg(TRACE, "Enter\n"); + struct brcmf_core_priv *core; - kfree(*ci_ptr); - *ci_ptr = NULL; + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->resetcore(core, prereset, reset, postreset); } static void -brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) +brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) { - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0); - ci->resetcore(sdiodev, ci, BCMA_CORE_80211, - D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); - ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0, 0, 0); + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); } -static bool brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) +static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) { - u8 core_idx; - u32 reg_addr; + struct brcmf_core *core; - if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) { + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + if (!brcmf_chip_iscoreup(core)) { brcmf_err("SOCRAM core is down after reset?\n"); return false; } - /* clear all interrupts */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); - reg_addr = ci->c_inf[core_idx].base; - reg_addr += offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + chip->ops->exit_dl(chip->ctx, &chip->pub, 0); - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0, 0, 0); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); + brcmf_chip_resetcore(core, 0, 0, 0); return true; } static inline void -brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) +brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) { - u8 idx; - u32 regdata; - u32 wrapbase; - idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); + struct brcmf_core *core; - if (idx == BRCMF_MAX_CORENUM) - return; + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); - wrapbase = ci->c_inf[idx].wrapbase; - regdata = brcmf_sdiod_regrl(sdiodev, wrapbase + BCMA_IOCTL, NULL); - regdata &= ARMCR4_BCMA_IOCTL_CPUHALT; - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, regdata, - ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); - ci->resetcore(sdiodev, ci, BCMA_CORE_80211, - D11_BCMA_IOCTL_PHYRESET | D11_BCMA_IOCTL_PHYCLOCKEN, - D11_BCMA_IOCTL_PHYCLOCKEN, D11_BCMA_IOCTL_PHYCLOCKEN); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); } -static bool brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 rstvec) +static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) { - u8 core_idx; - u32 reg_addr; + struct brcmf_core *core; - /* clear all interrupts */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); - reg_addr = ci->c_inf[core_idx].base; - reg_addr += offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - /* Write reset vector to address 0 */ - brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, - sizeof(rstvec)); + chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec); /* restore ARM */ - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, ARMCR4_BCMA_IOCTL_CPUHALT, - 0, 0); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); return true; } -void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci) +void brcmf_chip_enter_download(struct brcmf_chip *pub) { - u8 arm_core_idx; + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); - arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); - if (BRCMF_MAX_CORENUM != arm_core_idx) { - brcmf_sdio_chip_cm3_enterdl(sdiodev, ci); + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) { + brcmf_chip_cm3_enterdl(chip); return; } - brcmf_sdio_chip_cr4_enterdl(sdiodev, ci); + brcmf_chip_cr4_enterdl(chip); +} + +bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) + return brcmf_chip_cm3_exitdl(chip); + + return brcmf_chip_cr4_exitdl(chip, rstvec); } -bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 rstvec) +bool brcmf_chip_sr_capable(struct brcmf_chip *pub) { - u8 arm_core_idx; + u32 base, addr, reg, pmu_cc3_mask = ~0; + struct brcmf_chip_priv *chip; - arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); - if (BRCMF_MAX_CORENUM != arm_core_idx) - return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci); + brcmf_dbg(TRACE, "Enter\n"); + + /* old chips with PMU version less than 17 don't support save restore */ + if (pub->pmurev < 17) + return false; - return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, rstvec); + base = brcmf_chip_get_chipcommon(pub)->base; + chip = container_of(pub, struct brcmf_chip_priv, pub); + + switch (pub->chip) { + case BCM43241_CHIP_ID: + case BCM4335_CHIP_ID: + case BCM4339_CHIP_ID: + /* read PMU chipcontrol register 3 */ + addr = CORE_CC_REG(base, chipcontrol_addr); + chip->ops->write32(chip->ctx, addr, 3); + addr = CORE_CC_REG(base, chipcontrol_data); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & pmu_cc3_mask) != 0; + default: + addr = CORE_CC_REG(base, pmucapabilities_ext); + reg = chip->ops->read32(chip->ctx, addr); + if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) + return false; + + addr = CORE_CC_REG(base, retention_ctl); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | + PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; + } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h index 2bc00c5d1a7fba..c32908da90c853 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Broadcom Corporation + * Copyright (c) 2014 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,216 +13,79 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef BRCMF_CHIP_H +#define BRCMF_CHIP_H -#ifndef _BRCMFMAC_SDIO_CHIP_H_ -#define _BRCMFMAC_SDIO_CHIP_H_ +#include -/* - * Core reg address translation. - * Both macro's returns a 32 bits byte address on the backplane bus. - */ #define CORE_CC_REG(base, field) \ (base + offsetof(struct chipcregs, field)) -#define CORE_BUS_REG(base, field) \ - (base + offsetof(struct sdpcmd_regs, field)) -#define CORE_SB(base, field) \ - (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) - -/* SDIO function 1 register CHIPCLKCSR */ -/* Force ALP request to backplane */ -#define SBSDIO_FORCE_ALP 0x01 -/* Force HT request to backplane */ -#define SBSDIO_FORCE_HT 0x02 -/* Force ILP request to backplane */ -#define SBSDIO_FORCE_ILP 0x04 -/* Make ALP ready (power up xtal) */ -#define SBSDIO_ALP_AVAIL_REQ 0x08 -/* Make HT ready (power up PLL) */ -#define SBSDIO_HT_AVAIL_REQ 0x10 -/* Squelch clock requests from HW */ -#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 -/* Status: ALP is ready */ -#define SBSDIO_ALP_AVAIL 0x40 -/* Status: HT is ready */ -#define SBSDIO_HT_AVAIL 0x80 -#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) -#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) -#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) -#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) -#define SBSDIO_CLKAV(regval, alponly) \ - (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) - -#define BRCMF_MAX_CORENUM 6 - -struct brcmf_core { - u16 id; - u16 rev; - u32 base; - u32 wrapbase; - u32 caps; - u32 cib; -}; +/** + * struct brcmf_chip - chip level information. + * + * @chip: chip identifier. + * @chiprev: chip revision. + * @cc_caps: chipcommon core capabilities. + * @pmucaps: PMU capabilities. + * @pmurev: PMU revision. + * @rambase: RAM base address (only applicable for ARM CR4 chips). + * @ramsize: amount of RAM on chip. + * @name: string representation of the chip identifier. + */ struct brcmf_chip { u32 chip; u32 chiprev; - /* core info */ - /* always put chipcommon core at 0, bus core at 1 */ - struct brcmf_core c_inf[BRCMF_MAX_CORENUM]; - u32 pmurev; + u32 cc_caps; u32 pmucaps; - u32 ramsize; + u32 pmurev; u32 rambase; - u32 rst_vec; /* reset vertor for ARM CR4 core */ - - bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci, - u16 coreid); - u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct brcmf_chip *ci, - u16 coreid); - void (*coredisable)(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits); - void (*resetcore)(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u16 coreid, u32 pre_resetbits, - u32 in_resetbits, u32 post_resetbits); + u32 ramsize; + char name[8]; }; -struct sbconfig { - u32 PAD[2]; - u32 sbipsflag; /* initiator port ocp slave flag */ - u32 PAD[3]; - u32 sbtpsflag; /* target port ocp slave flag */ - u32 PAD[11]; - u32 sbtmerrloga; /* (sonics >= 2.3) */ - u32 PAD; - u32 sbtmerrlog; /* (sonics >= 2.3) */ - u32 PAD[3]; - u32 sbadmatch3; /* address match3 */ - u32 PAD; - u32 sbadmatch2; /* address match2 */ - u32 PAD; - u32 sbadmatch1; /* address match1 */ - u32 PAD[7]; - u32 sbimstate; /* initiator agent state */ - u32 sbintvec; /* interrupt mask */ - u32 sbtmstatelow; /* target state */ - u32 sbtmstatehigh; /* target state */ - u32 sbbwa0; /* bandwidth allocation table0 */ - u32 PAD; - u32 sbimconfiglow; /* initiator configuration */ - u32 sbimconfighigh; /* initiator configuration */ - u32 sbadmatch0; /* address match0 */ - u32 PAD; - u32 sbtmconfiglow; /* target configuration */ - u32 sbtmconfighigh; /* target configuration */ - u32 sbbconfig; /* broadcast configuration */ - u32 PAD; - u32 sbbstate; /* broadcast state */ - u32 PAD[3]; - u32 sbactcnfg; /* activate configuration */ - u32 PAD[3]; - u32 sbflagst; /* current sbflags */ - u32 PAD[3]; - u32 sbidlow; /* identification */ - u32 sbidhigh; /* identification */ +/** + * struct brcmf_core - core related information. + * + * @id: core identifier. + * @rev: core revision. + * @base: base address of core register space. + */ +struct brcmf_core { + u16 id; + u16 rev; + u32 base; }; -/* sdio core registers */ -struct sdpcmd_regs { - u32 corecontrol; /* 0x00, rev8 */ - u32 corestatus; /* rev8 */ - u32 PAD[1]; - u32 biststatus; /* rev8 */ - - /* PCMCIA access */ - u16 pcmciamesportaladdr; /* 0x010, rev8 */ - u16 PAD[1]; - u16 pcmciamesportalmask; /* rev8 */ - u16 PAD[1]; - u16 pcmciawrframebc; /* rev8 */ - u16 PAD[1]; - u16 pcmciaunderflowtimer; /* rev8 */ - u16 PAD[1]; - - /* interrupt */ - u32 intstatus; /* 0x020, rev8 */ - u32 hostintmask; /* rev8 */ - u32 intmask; /* rev8 */ - u32 sbintstatus; /* rev8 */ - u32 sbintmask; /* rev8 */ - u32 funcintmask; /* rev4 */ - u32 PAD[2]; - u32 tosbmailbox; /* 0x040, rev8 */ - u32 tohostmailbox; /* rev8 */ - u32 tosbmailboxdata; /* rev8 */ - u32 tohostmailboxdata; /* rev8 */ - - /* synchronized access to registers in SDIO clock domain */ - u32 sdioaccess; /* 0x050, rev8 */ - u32 PAD[3]; - - /* PCMCIA frame control */ - u8 pcmciaframectrl; /* 0x060, rev8 */ - u8 PAD[3]; - u8 pcmciawatermark; /* rev8 */ - u8 PAD[155]; - - /* interrupt batching control */ - u32 intrcvlazy; /* 0x100, rev8 */ - u32 PAD[3]; - - /* counters */ - u32 cmd52rd; /* 0x110, rev8 */ - u32 cmd52wr; /* rev8 */ - u32 cmd53rd; /* rev8 */ - u32 cmd53wr; /* rev8 */ - u32 abort; /* rev8 */ - u32 datacrcerror; /* rev8 */ - u32 rdoutofsync; /* rev8 */ - u32 wroutofsync; /* rev8 */ - u32 writebusy; /* rev8 */ - u32 readwait; /* rev8 */ - u32 readterm; /* rev8 */ - u32 writeterm; /* rev8 */ - u32 PAD[40]; - u32 clockctlstatus; /* rev8 */ - u32 PAD[7]; - - u32 PAD[128]; /* DMA engines */ - - /* SDIO/PCMCIA CIS region */ - char cis[512]; /* 0x400-0x5ff, rev6 */ - - /* PCMCIA function control registers */ - char pcmciafcr[256]; /* 0x600-6ff, rev6 */ - u16 PAD[55]; - - /* PCMCIA backplane access */ - u16 backplanecsr; /* 0x76E, rev6 */ - u16 backplaneaddr0; /* rev6 */ - u16 backplaneaddr1; /* rev6 */ - u16 backplaneaddr2; /* rev6 */ - u16 backplaneaddr3; /* rev6 */ - u16 backplanedata0; /* rev6 */ - u16 backplanedata1; /* rev6 */ - u16 backplanedata2; /* rev6 */ - u16 backplanedata3; /* rev6 */ - u16 PAD[31]; - - /* sprom "size" & "blank" info */ - u16 spromstatus; /* 0x7BE, rev2 */ - u32 PAD[464]; - - u16 PAD[0x80]; +/** + * struct brcmf_buscore_ops - buscore specific callbacks. + * + * @read32: read 32-bit value over bus. + * @write32: write 32-bit value over bus. + * @prepare: prepare bus for core configuration. + * @setup: bus-specific core setup. + * @exit_dl: exit download state. + * The callback should use the provided @rstvec when non-zero. + */ +struct brcmf_buscore_ops { + u32 (*read32)(void *ctx, u32 addr); + void (*write32)(void *ctx, u32 addr, u32 value); + int (*prepare)(void *ctx); + int (*setup)(void *ctx, struct brcmf_chip *chip); + void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec); }; -int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip **ci_ptr); -void brcmf_sdio_chip_detach(struct brcmf_chip **ci_ptr); -u8 brcmf_sdio_chip_getinfidx(struct brcmf_chip *ci, u16 coreid); -void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci); -bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 rstvec); - -#endif /* _BRCMFMAC_SDIO_CHIP_H_ */ +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops); +void brcmf_chip_detach(struct brcmf_chip *chip); +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); +bool brcmf_chip_iscoreup(struct brcmf_core *core); +void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); +void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, + u32 postreset); +void brcmf_chip_enter_download(struct brcmf_chip *ci); +bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec); +bool brcmf_chip_sr_capable(struct brcmf_chip *pub); + +#endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index b4f79a82acd71b..52b0b6fa1ed41c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,33 @@ struct rte_console { /* manfid tuple length, include tuple, link bytes */ #define SBSDIO_CIS_MANFID_TUPLE_LEN 6 +#define CORE_BUS_REG(base, field) \ + (base + offsetof(struct sdpcmd_regs, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP 0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT 0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 +/* Status: ALP is ready */ +#define SBSDIO_ALP_AVAIL 0x40 +/* Status: HT is ready */ +#define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ + (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) + /* intstatus */ #define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ #define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ @@ -664,27 +692,24 @@ static bool data_ok(struct brcmf_sdio *bus) * Reads a register in the SDIO hardware block. This block occupies a series of * adresses on the 32 bit backplane bus. */ -static int -r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - *regvar = brcmf_sdiod_regrl(bus->sdiodev, - bus->ci->c_inf[idx].base + offset, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); return ret; } -static int -w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - brcmf_sdiod_regwl(bus->sdiodev, - bus->ci->c_inf[idx].base + reg_offset, - regval, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); return ret; } @@ -2426,14 +2451,13 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { - u8 idx; + struct brcmf_core *buscore; u32 addr; unsigned long val; int n, ret; - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - addr = bus->ci->c_inf[idx].base + - offsetof(struct sdpcmd_regs, intstatus); + buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; @@ -3345,7 +3369,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - brcmf_sdio_chip_enter_download(bus->sdiodev, bus->ci); + brcmf_chip_enter_download(bus->ci); fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); if (fw == NULL) { @@ -3377,7 +3401,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) } /* Take arm out of reset */ - if (!brcmf_sdio_chip_exit_download(bus->sdiodev, bus->ci, rstvec)) { + if (!brcmf_chip_exit_download(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } @@ -3392,40 +3416,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) return bcmerror; } -static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus) -{ - u32 addr, reg, pmu_cc3_mask = ~0; - int err; - - brcmf_dbg(TRACE, "Enter\n"); - - /* old chips with PMU version less than 17 don't support save restore */ - if (bus->ci->pmurev < 17) - return false; - - switch (bus->ci->chip) { - case BCM43241_CHIP_ID: - case BCM4335_CHIP_ID: - case BCM4339_CHIP_ID: - /* read PMU chipcontrol register 3 */ - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); - brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL); - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); - return (reg & pmu_cc3_mask) != 0; - default: - addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucapabilities_ext); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, &err); - if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) - return false; - - addr = CORE_CC_REG(bus->ci->c_inf[0].base, retention_ctl); - reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); - return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | - PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; - } -} - static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) { int err = 0; @@ -3477,7 +3467,7 @@ static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); /* KSO bit added in SDIO core rev 12 */ - if (bus->ci->c_inf[1].rev < 12) + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) return 0; val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); @@ -3508,15 +3498,13 @@ static int brcmf_sdio_bus_preinit(struct device *dev) struct brcmf_sdio *bus = sdiodev->bus; uint pad_size; u32 value; - u8 idx; int err; /* the commands below use the terms tx and rx from * a device perspective, ie. bus:txglom affects the * bus transfers from device to host. */ - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - if (bus->ci->c_inf[idx].rev < 12) { + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { /* for sdio core rev < 12, disable txgloming */ value = 0; err = brcmf_iovar_data_set(dev, "bus:txglom", &value, @@ -3623,7 +3611,7 @@ static int brcmf_sdio_bus_init(struct device *dev) ret = -ENODEV; } - if (brcmf_sdio_sr_capable(bus)) { + if (brcmf_chip_sr_capable(bus->ci)) { brcmf_sdio_sr_init(bus); } else { /* Restore previous clock setting */ @@ -3772,30 +3760,20 @@ static void brcmf_sdio_dataworker(struct work_struct *work) } } -static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - static void brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 drivestrength) + struct brcmf_chip *ci, u32 drivestrength) { const struct sdiod_drive_str *str_tab = NULL; u32 str_mask; u32 str_shift; - char chn[8]; - u32 base = ci->c_inf[0].base; + u32 base; u32 i; u32 drivestrength_sel = 0; u32 cc_data_temp; u32 addr; - if (!(ci->c_inf[0].caps & CC_CAP_PMU)) + if (!(ci->cc_caps & CC_CAP_PMU)) return; switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { @@ -3818,8 +3796,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, str_shift = 0; } else brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - drivestrength); + ci->name, drivestrength); break; case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): str_tab = sdiod_drive_strength_tab5_1v8; @@ -3828,8 +3805,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, break; default: brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - ci->chiprev, ci->pmurev); + ci->name, ci->chiprev, ci->pmurev); break; } @@ -3840,6 +3816,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, break; } } + base = brcmf_chip_get_chipcommon(ci)->base; addr = CORE_CC_REG(base, chipcontrol_addr); brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); @@ -3853,8 +3830,9 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } } -int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev) +static int brcmf_sdio_buscoreprep(void *ctx) { + struct brcmf_sdio_dev *sdiodev = ctx; int err = 0; u8 clkval, clkset; @@ -3897,6 +3875,55 @@ int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev) return 0; } +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + struct brcmf_core *core; + u32 reg_addr; + + /* clear all interrupts */ + core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); + reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + if (rstvec) + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + u32 val, rev; + + val = brcmf_sdiod_regrl(sdiodev, addr, NULL); + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { + rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; + if (rev >= 2) { + val &= ~CID_ID_MASK; + val |= BCM4339_CHIP_ID; + } + } + return val; +} + +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + + brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { + .prepare = brcmf_sdio_buscoreprep, + .exit_dl = brcmf_sdio_buscore_exitdl, + .read32 = brcmf_sdio_buscore_read32, + .write32 = brcmf_sdio_buscore_write32, +}; + static bool brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { @@ -3912,7 +3939,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); /* - * Force PLL off until brcmf_sdio_chip_attach() + * Force PLL off until brcmf_chip_attach() * programs PLL control regs */ @@ -3933,8 +3960,10 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) */ brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); - if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) { - brcmf_err("brcmf_sdio_chip_attach failed!\n"); + bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + if (IS_ERR(bus->ci)) { + brcmf_err("brcmf_chip_attach failed!\n"); + bus->ci = NULL; goto fail; } @@ -3970,24 +3999,18 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, + reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, pmucontrol); - reg_val = brcmf_sdiod_regrl(bus->sdiodev, - reg_addr, - &err); + reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); if (err) goto fail; reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); - brcmf_sdiod_regwl(bus->sdiodev, - reg_addr, - reg_val, - &err); + brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); if (err) goto fail; - sdio_release_host(bus->sdiodev->func[1]); brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); @@ -4220,12 +4243,11 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) * all necessary cores. */ msleep(20); - brcmf_sdio_chip_enter_download(bus->sdiodev, - bus->ci); + brcmf_chip_enter_download(bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); } - brcmf_sdio_chip_detach(&bus->ci); + brcmf_chip_detach(bus->ci); } kfree(bus->rxbuf); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 396c06cea8efdf..5e53eb1b2ffaf6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -182,6 +182,95 @@ struct brcmf_sdio_dev { uint max_segment_size; }; +/* sdio core registers */ +struct sdpcmd_regs { + u32 corecontrol; /* 0x00, rev8 */ + u32 corestatus; /* rev8 */ + u32 PAD[1]; + u32 biststatus; /* rev8 */ + + /* PCMCIA access */ + u16 pcmciamesportaladdr; /* 0x010, rev8 */ + u16 PAD[1]; + u16 pcmciamesportalmask; /* rev8 */ + u16 PAD[1]; + u16 pcmciawrframebc; /* rev8 */ + u16 PAD[1]; + u16 pcmciaunderflowtimer; /* rev8 */ + u16 PAD[1]; + + /* interrupt */ + u32 intstatus; /* 0x020, rev8 */ + u32 hostintmask; /* rev8 */ + u32 intmask; /* rev8 */ + u32 sbintstatus; /* rev8 */ + u32 sbintmask; /* rev8 */ + u32 funcintmask; /* rev4 */ + u32 PAD[2]; + u32 tosbmailbox; /* 0x040, rev8 */ + u32 tohostmailbox; /* rev8 */ + u32 tosbmailboxdata; /* rev8 */ + u32 tohostmailboxdata; /* rev8 */ + + /* synchronized access to registers in SDIO clock domain */ + u32 sdioaccess; /* 0x050, rev8 */ + u32 PAD[3]; + + /* PCMCIA frame control */ + u8 pcmciaframectrl; /* 0x060, rev8 */ + u8 PAD[3]; + u8 pcmciawatermark; /* rev8 */ + u8 PAD[155]; + + /* interrupt batching control */ + u32 intrcvlazy; /* 0x100, rev8 */ + u32 PAD[3]; + + /* counters */ + u32 cmd52rd; /* 0x110, rev8 */ + u32 cmd52wr; /* rev8 */ + u32 cmd53rd; /* rev8 */ + u32 cmd53wr; /* rev8 */ + u32 abort; /* rev8 */ + u32 datacrcerror; /* rev8 */ + u32 rdoutofsync; /* rev8 */ + u32 wroutofsync; /* rev8 */ + u32 writebusy; /* rev8 */ + u32 readwait; /* rev8 */ + u32 readterm; /* rev8 */ + u32 writeterm; /* rev8 */ + u32 PAD[40]; + u32 clockctlstatus; /* rev8 */ + u32 PAD[7]; + + u32 PAD[128]; /* DMA engines */ + + /* SDIO/PCMCIA CIS region */ + char cis[512]; /* 0x400-0x5ff, rev6 */ + + /* PCMCIA function control registers */ + char pcmciafcr[256]; /* 0x600-6ff, rev6 */ + u16 PAD[55]; + + /* PCMCIA backplane access */ + u16 backplanecsr; /* 0x76E, rev6 */ + u16 backplaneaddr0; /* rev6 */ + u16 backplaneaddr1; /* rev6 */ + u16 backplaneaddr2; /* rev6 */ + u16 backplaneaddr3; /* rev6 */ + u16 backplanedata0; /* rev6 */ + u16 backplanedata1; /* rev6 */ + u16 backplanedata2; /* rev6 */ + u16 backplanedata3; /* rev6 */ + u16 PAD[31]; + + /* sprom "size" & "blank" info */ + u16 spromstatus; /* 0x7BE, rev2 */ + u32 PAD[464]; + + u16 PAD[0x80]; +}; + /* Register/deregister interrupt handler. */ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); @@ -239,6 +328,5 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); -int brcmf_sdio_buscoreprep(struct brcmf_sdio_dev *sdiodev); #endif /* _BRCM_SDH_H_ */ From e3fb07f3476cddf7ecae6a86e373a4e9c015c4e3 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:20 +0100 Subject: [PATCH 0086/1983] brcmfmac: remove TRACE level debug message from brcmf_sdio_bus_sleep() Upstream-commit: 82030d6df3856896e6a7c912347fe547eaf61738 The function brcmf_sdio_bus_sleep() function is called rather frequently, which fills the log when TRACE level is enabled. Reduced the level to SDIO. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 52b0b6fa1ed41c..9f547db7f03318 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -970,8 +970,8 @@ static int brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) { int err = 0; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(SDIO, "request %s currently %s\n", + + brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); From fd129b6c649c03bcfb1d02af7e76331e33e6dc92 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:21 +0100 Subject: [PATCH 0087/1983] brcmfmac: remove unintended error logging Upstream-commit: c5a9f3c1931902ee161b264dd3bdee3ead03095b In brcmf_contstruct_reginfo() some error logging was added by: commit f7c51a1a72f50870f80001ddf528a6f7f992bc16 Author: Arend van Spriel Date: Wed Dec 11 16:21:21 2013 +0100 brcmfmac: correct reporting HT40 support in wiphy htcap This logging was not intended to be delivered and adds a lot of messages in the log. The patch removes this logging statement. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 616b37824d33f3..6dc718bf3be3a2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5192,9 +5192,6 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, ieee80211_channel_to_frequency(ch.chnum, band); band_chan_arr[index].hw_value = ch.chnum; - brcmf_err("channel %d: f=%d bw=%d sb=%d\n", - ch.chnum, band_chan_arr[index].center_freq, - ch.bw, ch.sb); if (ch.bw == BRCMU_CHAN_BW_40) { /* assuming the order is HT20, HT40 Upper, * HT40 lower from chanspecs From f86e3dd7b213debc382879f62c98428fc266af14 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:22 +0100 Subject: [PATCH 0088/1983] brcmfmac: get chip core information from the device Upstream-commit: 4aa2c47cd60fa2ed8652f78d35286b639d00120c Instead of instantiating core info structs based upon the chip identifier it is now done parsing information provided on the device. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/chip.c | 307 ++++++++++++------ 1 file changed, 203 insertions(+), 104 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 151a67110ecfc5..724a40fd6711ad 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -32,6 +32,55 @@ #define SOCI_SB 0 #define SOCI_AI 1 +/* PL-368 DMP definitions */ +#define DMP_DESC_TYPE_MSK 0x0000000F +#define DMP_DESC_EMPTY 0x00000000 +#define DMP_DESC_VALID 0x00000001 +#define DMP_DESC_COMPONENT 0x00000001 +#define DMP_DESC_MASTER_PORT 0x00000003 +#define DMP_DESC_ADDRESS 0x00000005 +#define DMP_DESC_ADDRSIZE_GT32 0x00000008 +#define DMP_DESC_EOT 0x0000000F + +#define DMP_COMP_DESIGNER 0xFFF00000 +#define DMP_COMP_DESIGNER_S 20 +#define DMP_COMP_PARTNUM 0x000FFF00 +#define DMP_COMP_PARTNUM_S 8 +#define DMP_COMP_CLASS 0x000000F0 +#define DMP_COMP_CLASS_S 4 +#define DMP_COMP_REVISION 0xFF000000 +#define DMP_COMP_REVISION_S 24 +#define DMP_COMP_NUM_SWRAP 0x00F80000 +#define DMP_COMP_NUM_SWRAP_S 19 +#define DMP_COMP_NUM_MWRAP 0x0007C000 +#define DMP_COMP_NUM_MWRAP_S 14 +#define DMP_COMP_NUM_SPORT 0x00003E00 +#define DMP_COMP_NUM_SPORT_S 9 +#define DMP_COMP_NUM_MPORT 0x000001F0 +#define DMP_COMP_NUM_MPORT_S 4 + +#define DMP_MASTER_PORT_UID 0x0000FF00 +#define DMP_MASTER_PORT_UID_S 8 +#define DMP_MASTER_PORT_NUM 0x000000F0 +#define DMP_MASTER_PORT_NUM_S 4 + +#define DMP_SLAVE_ADDR_BASE 0xFFFFF000 +#define DMP_SLAVE_ADDR_BASE_S 12 +#define DMP_SLAVE_PORT_NUM 0x00000F00 +#define DMP_SLAVE_PORT_NUM_S 8 +#define DMP_SLAVE_TYPE 0x000000C0 +#define DMP_SLAVE_TYPE_S 6 +#define DMP_SLAVE_TYPE_SLAVE 0 +#define DMP_SLAVE_TYPE_BRIDGE 1 +#define DMP_SLAVE_TYPE_SWRAP 2 +#define DMP_SLAVE_TYPE_MWRAP 3 +#define DMP_SLAVE_SIZE_TYPE 0x00000030 +#define DMP_SLAVE_SIZE_TYPE_S 4 +#define DMP_SLAVE_SIZE_4K 0 +#define DMP_SLAVE_SIZE_8K 1 +#define DMP_SLAVE_SIZE_16K 2 +#define DMP_SLAVE_SIZE_DESC 3 + /* EROM CompIdentB */ #define CIB_REV_MASK 0xff000000 #define CIB_REV_SHIFT 24 @@ -393,8 +442,9 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) int idx = 1; list_for_each_entry(core, &ci->cores, list) { - brcmf_dbg(INFO, " [%-2d] core 0x%x rev %-2d base 0x%08x\n", - idx++, core->pub.id, core->pub.rev, core->pub.base); + brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n", + idx++, core->pub.id, core->pub.rev, core->pub.base, + core->wrapbase); switch (core->pub.id) { case BCMA_CORE_ARM_CM3: @@ -463,6 +513,151 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) } } +static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, + u8 *type) +{ + u32 val; + + /* read next descriptor */ + val = ci->ops->read32(ci->ctx, *eromaddr); + *eromaddr += 4; + + if (!type) + return val; + + /* determine descriptor type */ + *type = (val & DMP_DESC_TYPE_MSK); + if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS) + *type = DMP_DESC_ADDRESS; + + return val; +} + +static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, + u32 *regbase, u32 *wrapbase) +{ + u8 desc; + u32 val; + u8 mpnum = 0; + u8 stype, sztype, wraptype; + + *regbase = 0; + *wrapbase = 0; + + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + if (desc == DMP_DESC_MASTER_PORT) { + mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; + wraptype = DMP_SLAVE_TYPE_MWRAP; + } else if (desc == DMP_DESC_ADDRESS) { + /* revert erom address */ + *eromaddr -= 4; + wraptype = DMP_SLAVE_TYPE_SWRAP; + } else { + *eromaddr -= 4; + return -EILSEQ; + } + + do { + /* locate address descriptor */ + do { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + /* unexpected table end */ + if (desc == DMP_DESC_EOT) { + *eromaddr -= 4; + return -EFAULT; + } + } while (desc != DMP_DESC_ADDRESS); + + /* skip upper 32-bit address descriptor */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + + sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S; + + /* next size descriptor can be skipped */ + if (sztype == DMP_SLAVE_SIZE_DESC) { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + /* skip upper size descriptor if present */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + } + + /* only look for 4K register regions */ + if (sztype != DMP_SLAVE_SIZE_4K) + continue; + + stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S; + + /* only regular slave and wrapper */ + if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE) + *regbase = val & DMP_SLAVE_ADDR_BASE; + if (*wrapbase == 0 && stype == wraptype) + *wrapbase = val & DMP_SLAVE_ADDR_BASE; + } while (*regbase == 0 || *wrapbase == 0); + + return 0; +} + +static +int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 eromaddr; + u8 desc_type = 0; + u32 val; + u16 id; + u8 nmp, nsp, nmw, nsw, rev; + u32 base, wrap; + int err; + + eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr)); + + while (desc_type != DMP_DESC_EOT) { + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (!(val & DMP_DESC_VALID)) + continue; + + if (desc_type == DMP_DESC_EMPTY) + continue; + + /* need a component descriptor */ + if (desc_type != DMP_DESC_COMPONENT) + continue; + + id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S; + + /* next descriptor must be component as well */ + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT)) + return -EFAULT; + + /* only look at cores with master port(s) */ + nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; + nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; + nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; + nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; + rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; + + /* need core with ports */ + if (nmw + nsw == 0) + continue; + + /* try to obtain register address info */ + err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap); + if (err) + continue; + + /* finally a core to be added */ + core = brcmf_chip_add_core(ci, id, base, wrap); + if (IS_ERR(core)) + return PTR_ERR(core); + + core->rev = rev; + } + + return 0; +} + static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) { struct brcmf_core *core; @@ -505,114 +700,21 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, BCM4329_CORE_ARM_BASE, 0); brcmf_chip_sb_corerev(ci, core); + + core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0); + brcmf_chip_sb_corerev(ci, core); } else if (socitype == SOCI_AI) { ci->iscoreup = brcmf_chip_ai_iscoreup; ci->coredisable = brcmf_chip_ai_coredisable; ci->resetcore = brcmf_chip_ai_resetcore; - core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, - SI_ENUM_BASE, - SI_ENUM_BASE + 0x100000); - - /* Address of cores for new chips should be added here */ - switch (ci->pub.chip) { - case BCM43143_CHIP_ID: - core->rev = 43; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - BCM43143_CORE_BUS_BASE, - BCM43143_CORE_BUS_BASE + - 0x100000); - core->rev = 24; - core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, - BCM43143_CORE_SOCRAM_BASE, - BCM43143_CORE_SOCRAM_BASE + - 0x100000); - core->rev = 20; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, - BCM43143_CORE_ARM_BASE, - BCM43143_CORE_ARM_BASE + - 0x100000); - core->rev = 7; - break; - case BCM43241_CHIP_ID: - core->rev = 42; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - 0x18002000, 0x18102000); - core->rev = 14; - core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, - 0x18004000, 0x18104000); - core->rev = 20; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, - 0x18003000, 0x18103000); - core->rev = 7; - break; - case BCM4330_CHIP_ID: - core->rev = 39; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - 0x18002000, 0x18102000); - core->rev = 7; - core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, - 0x18004000, 0x18104000); - core->rev = 13; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, - 0x18003000, 0x18103000); - core->rev = 3; - break; - case BCM4334_CHIP_ID: - core->rev = 41; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - 0x18002000, 0x18102000); - core->rev = 13; - core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, - 0x18004000, 0x18104000); - core->rev = 19; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, - 0x18003000, 0x18103000); - core->rev = 7; - break; - case BCM4335_CHIP_ID: - core->rev = 43; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - 0x18005000, 0x18105000); - core->rev = 15; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CR4, - 0x18002000, 0x18102000); - core->rev = 1; - break; - case BCM43362_CHIP_ID: - core->rev = 39; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - 0x18002000, 0x18102000); - core->rev = 10; - core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, - 0x18004000, 0x18104000); - core->rev = 8; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, - 0x18003000, 0x18103000); - core->rev = 3; - break; - case BCM4339_CHIP_ID: - core->rev = 46; - core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, - 0x18005000, 0x18105000); - core->rev = 21; - core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CR4, - 0x18002000, 0x18102000); - core->rev = 4; - break; - default: - brcmf_err("AXI chip is not supported\n"); - return -ENODEV; - } + brcmf_chip_dmp_erom_scan(ci); } else { brcmf_err("chip backplane type %u is not supported\n", socitype); return -ENODEV; } - /* add 802.11 core for all chips on same backplane address */ - core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0x18101000); - brcmf_chip_get_raminfo(ci); return brcmf_chip_cores_check(ci); @@ -652,7 +754,6 @@ static int brcmf_chip_setup(struct brcmf_chip_priv *chip) { struct brcmf_chip *pub; struct brcmf_core_priv *cc; - struct brcmf_core_priv *bus; u32 base; u32 val; int ret = 0; @@ -673,10 +774,8 @@ static int brcmf_chip_setup(struct brcmf_chip_priv *chip) pub->pmucaps = val; } - bus = list_next_entry(cc, list); - - brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", - cc->pub.rev, pub->pmurev, bus->pub.rev, bus->pub.id); + brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n", + cc->pub.rev, pub->pmurev, pub->pmucaps); /* execute bus core specific setup */ if (chip->ops->setup) From 9d30dc6829188ca0b5f88057138cbf047ce414b2 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Wed, 29 Jan 2014 15:32:23 +0100 Subject: [PATCH 0089/1983] brcmfmac: correct setting of WEP broadcast/unicast keys Upstream-commit: 787eb033f9950788f7fc520e8532b85a86d3ca02 The brcmf_add_keyext() is for setting per-station key for cipher algorithms such as WPA1/WPA2 and should not be used to set WEP broadcast/unicast keys. This patch fixes connect failure problem with AP using 802.1x-WEP. Reviewed-by: Hante Meuleman Reviewed-by: Arend van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 6dc718bf3be3a2..a54db918574773 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1980,7 +1980,9 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (mac_addr) { + if (mac_addr && + (params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { brcmf_dbg(TRACE, "Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } From b72142e9e576ae30612b4caf3b8d82b962fdc002 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Jan 2014 15:32:24 +0100 Subject: [PATCH 0090/1983] brcmfmac: CR4 takes precedence over CM3 in brcmf_chip_enter_download() Upstream-commit: 2da5cb297918f4f2321a8e4e8fe25a6b472c69fc In the enter and exit download sequence the chip core info was checked for presence of CM3 ARM core. If found it would enter download state for the CM3. However, on devices that have a CM3 and CR4 this is not correct and the CR4 should be used to enter download state. This patch changes the ARM core lookup giving CR4 precedence. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 724a40fd6711ad..a07b95ef9e7033 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -967,13 +967,13 @@ void brcmf_chip_enter_download(struct brcmf_chip *pub) brcmf_dbg(TRACE, "Enter\n"); chip = container_of(pub, struct brcmf_chip_priv, pub); - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) { - brcmf_chip_cm3_enterdl(chip); + brcmf_chip_cr4_enterdl(chip); return; } - brcmf_chip_cr4_enterdl(chip); + brcmf_chip_cm3_enterdl(chip); } bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) @@ -984,11 +984,11 @@ bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) brcmf_dbg(TRACE, "Enter\n"); chip = container_of(pub, struct brcmf_chip_priv, pub); - arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) - return brcmf_chip_cm3_exitdl(chip); + return brcmf_chip_cr4_exitdl(chip, rstvec); - return brcmf_chip_cr4_exitdl(chip, rstvec); + return brcmf_chip_cm3_exitdl(chip); } bool brcmf_chip_sr_capable(struct brcmf_chip *pub) From f8a56c90882c88a2545ba5485249297669a0276e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:26 +0100 Subject: [PATCH 0091/1983] brcmfmac: Make firmeware roaming a module param. Upstream-commit: 68ca395f94e932a2d9a775f2c103c5bce257e795 Internal firmware roaming is enabled by default. This patch makes it possible to disable internal firmware roaming by specifying roamoff=1 as module param. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 38 ++++++++++++------- .../wireless/brcm80211/brcmfmac/wl_cfg80211.h | 1 - 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index a54db918574773..474df2ce6e3ba5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -251,6 +252,10 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +static int brcmf_roamoff; +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); +MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); + /* Quarter dBm units to mW * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 * Table is offset so the last entry is largest mW value that fits in @@ -4444,7 +4449,9 @@ static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) u32 event = e->event_code; u16 flags = e->flags; - if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) { + if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DISASSOC_IND) || + ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { brcmf_dbg(CONN, "Processing link down\n"); return true; } @@ -4688,6 +4695,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct ieee80211_channel *chan; s32 err = 0; + u16 reason; if (ifp->vif->mode == WL_MODE_AP) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); @@ -4709,9 +4717,15 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) - cfg80211_disconnected(ndev, 0, NULL, 0, + &ifp->vif->sme_state)) { + reason = 0; + if (((e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DISASSOC_IND)) && + (e->reason != WLAN_REASON_UNSPECIFIED)) + reason = e->reason; + cfg80211_disconnected(ndev, reason, NULL, 0, GFP_KERNEL); + } } brcmf_link_down(ifp->vif); brcmf_init_prof(ndev_to_prof(ndev)); @@ -4905,11 +4919,8 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) cfg->scan_request = NULL; cfg->pwr_save = true; - cfg->roam_on = true; /* roam on & off switch. - we enable roam per default */ - cfg->active_scan = true; /* we do active scan for - specific scan per default */ - cfg->dongle_up = false; /* dongle is not up yet */ + cfg->active_scan = true; /* we do active scan per default */ + cfg->dongle_up = false; /* dongle is not up yet */ err = brcmf_init_priv_mem(cfg); if (err) return err; @@ -5029,7 +5040,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) } static s32 -brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) +brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { s32 err = 0; __le32 roamtrigger[2]; @@ -5039,7 +5050,7 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) * Setup timeout if Beacons are lost and roam is * off to report link down */ - if (roamvar) { + if (brcmf_roamoff) { err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); if (err) { brcmf_err("bcn_timeout error (%d)\n", err); @@ -5051,8 +5062,9 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) * Enable/Disable built-in roaming to allow supplicant * to take care of roaming */ - brcmf_dbg(INFO, "Internal Roaming = %s\n", roamvar ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar); + brcmf_dbg(INFO, "Internal Roaming = %s\n", + brcmf_roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); if (err) { brcmf_err("roam_off error (%d)\n", err); goto dongle_rom_out; @@ -5408,7 +5420,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "power save set to %s\n", (power_mode ? "enabled" : "disabled")); - err = brcmf_dongle_roam(ifp, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT); + err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 254feed2860e96..5715bb0708cf39 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -402,7 +402,6 @@ struct brcmf_cfg80211_info { bool ibss_starter; bool pwr_save; bool dongle_up; - bool roam_on; bool scan_tried; u8 *dcmd_buf; u8 *extra_buf; From 0fc3c24d4fa781a64df4751a8f708c070c87c9f7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Feb 2014 20:30:27 +0100 Subject: [PATCH 0092/1983] brcmfmac: fix use of skb control buffer in SDIO driver part Upstream-commit: 5aa9f0ea18f3d5ec329a619b0bc54e214e02bc33 The SDIO driver has a 16-bit field defined in the skbuff control buffer. However, it is accessed as a u32 overwriting other control info. Another issue is that the field is not initialized for networking packets, but the control buffer content is unspecified as other networking layers can use it. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 9f547db7f03318..dc244c815b5e7f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2112,7 +2112,7 @@ static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, memcpy(pkt_pad->data, pkt->data + pkt->len - tail_chop, tail_chop); - *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; skb_trim(pkt, pkt->len - tail_chop); skb_trim(pkt_pad, tail_pad + tail_chop); __skb_queue_after(pktq, pkt, pkt_pad); @@ -2160,7 +2160,7 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, * already properly aligned and does not * need an sdpcm header. */ - if (*(u32 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) continue; /* align packet data pointer */ @@ -2224,11 +2224,11 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) u8 *hdr; u32 dat_offset; u16 tail_pad; - u32 dummy_flags, chop_len; + u16 dummy_flags, chop_len; struct sk_buff *pkt_next, *tmp, *pkt_prev; skb_queue_walk_safe(pktq, pkt_next, tmp) { - dummy_flags = *(u32 *)(pkt_next->cb); + dummy_flags = *(u16 *)(pkt_next->cb); if (dummy_flags & ALIGN_SKB_FLAG) { chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; if (chop_len) { @@ -2710,6 +2710,8 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) /* Priority based enq */ spin_lock_irqsave(&bus->txqlock, flags); + /* reset bus_flags in packet cb */ + *(u16 *)(pkt->cb) = 0; if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); From df54ab359061a575745feac34fc095a9f58dfc1d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Feb 2014 20:30:28 +0100 Subject: [PATCH 0093/1983] brcmfmac: remove unused variable data_len from brcmf_sdio_bus_txdata() Upstream-commit: 44ff5660e5cf5ccb739e77a19d2ecf1144cf9e36 The local variable data_len is assigned but never used so get rid of it. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index dc244c815b5e7f..2c9e01af35f189 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2687,15 +2687,13 @@ static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; - uint datalen, prec; + uint prec; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; ulong flags; - brcmf_dbg(TRACE, "Enter\n"); - - datalen = pkt->len; + brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); /* Add space for the header */ skb_push(pkt, bus->tx_hdrlen); From 7b3930a0b2c369db12902e7756984ff6f7144478 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:29 +0100 Subject: [PATCH 0094/1983] brcmfmac: Correct header debug dump for sdio tx hdrs. Upstream-commit: 47ab4cd893f565a1093f316ec95babfa181e722a Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 2c9e01af35f189..e7bcbb69b64a9d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2194,10 +2194,10 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, if (BRCMF_BYTES_ON() && ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) - brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, + brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, "Tx Frame:\n"); else if (BRCMF_HDRS_ON()) - brcmf_dbg_hex_dump(true, pkt_next, + brcmf_dbg_hex_dump(true, pkt_next->data, head_pad + bus->tx_hdrlen, "Tx Header:\n"); } From c2fe1b0ed2bc747aba9b4c2b4217a51a2e6df90a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:30 +0100 Subject: [PATCH 0095/1983] brcmfmac: de-init driver layers in correct order. Upstream-commit: 05c2c7671393edf4fd3870db4de14eb6fff276d5 First clean up fw signalling, before cleaning up the bus and proto layer. Old order can cause oops in some circumstances. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index d4d966beb840b8..7d28cd3850925a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -1040,12 +1040,12 @@ void brcmf_detach(struct device *dev) brcmf_cfg80211_detach(drvr->config); + brcmf_fws_deinit(drvr); + brcmf_bus_detach(drvr); brcmf_proto_detach(drvr); - brcmf_fws_deinit(drvr); - brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; kfree(drvr); From e8abc4402eca66b445ada49b422ec519eece5014 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:31 +0100 Subject: [PATCH 0096/1983] brcmfmac: Minimize SDIO dpc scheduling. Upstream-commit: 71abdc00d54bb48ad2df4f9710ac661cf3aed30d SDIO dpc scheduling is done (repeated) when counter is set. This counter gets decreased when dpc is finished. It is more efficient to set counter to 0 before the dpc is actullay run. This will minimize the frequency with which dpc is executed. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index e7bcbb69b64a9d..ab7b1b64c91c4a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3755,8 +3755,8 @@ static void brcmf_sdio_dataworker(struct work_struct *work) datawork); while (atomic_read(&bus->dpc_tskcnt)) { + atomic_set(&bus->dpc_tskcnt, 0); brcmf_sdio_dpc(bus); - atomic_dec(&bus->dpc_tskcnt); } } From 76ad0cfe354d8f81b8c6bd0601e87e61cd69d469 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:32 +0100 Subject: [PATCH 0097/1983] brcmfmac: Remove immediate sleep support from SDIO. Upstream-commit: 6f6c195b95136e0b2972b3c5213f6cd876c4fa8f Immediate sleep support is an aggressive power saving option that has not been enabled in brcmfmac and is removed to simplify code. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index ab7b1b64c91c4a..2d94ebe1482169 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -304,7 +304,6 @@ struct rte_console { /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -#define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */ #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle */ @@ -2663,16 +2662,6 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) data_ok(bus)) || PKT_AVAILABLE()) { atomic_inc(&bus->dpc_tskcnt); } - - /* If we're done for now, turn off clock request. */ - if ((bus->clkstate != CLK_PENDING) - && bus->idletime == BRCMF_IDLE_IMMEDIATE) { - bus->activity = false; - brcmf_dbg(SDIO, "idle state\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } } static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) @@ -2949,15 +2938,6 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } while (ret < 0 && retries++ < TXRETRIES); } - if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && - atomic_read(&bus->dpc_tskcnt) == 0) { - bus->activity = false; - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_dbg(INFO, "idle\n"); - brcmf_sdio_clkctl(bus, CLK_NONE, true); - sdio_release_host(bus->sdiodev->func[1]); - } - if (ret) bus->sdcnt.tx_ctlerrs++; else From d6827162470b5d2459504e8c442cb4b79cd3107c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:33 +0100 Subject: [PATCH 0098/1983] brcmfmac: Small cleanup of redundant code. Upstream-commit: b6a8cf2cd8f1d17c850db0f591e3d349d417b40a In time some of the code got redundant, without being noticed. This patch does not change any functionality, just removes redundant code. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 2d94ebe1482169..226883f5d6862c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -769,8 +769,6 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) return err; } -#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND) - #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) /* Turn backplane clock on or off */ @@ -869,7 +867,6 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } #endif /* defined (DEBUG) */ - bus->activity = true; } else { clkreq = 0; @@ -2342,7 +2339,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) cnt += i; /* In poll mode, need to check for other events */ - if (!bus->intr && cnt) { + if (!bus->intr) { /* Check device status, signal pending interrupt */ sdio_claim_host(bus->sdiodev->func[1]); ret = r_sdreg32(bus, &intstatus, @@ -2486,9 +2483,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; unsigned long intstatus; - uint rxlimit = bus->rxbound; /* Rx frames to read before resched */ uint txlimit = bus->txbound; /* Tx frames to send before resched */ - uint framecnt = 0; /* Temporary counter of tx/rx frames */ + uint framecnt; /* Temporary counter of tx/rx frames */ int err = 0, n; brcmf_dbg(TRACE, "Enter\n"); @@ -2586,11 +2582,10 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) intstatus &= ~I_HMB_FRAME_IND; /* On frame indication, read available frames */ - if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { - framecnt = brcmf_sdio_readframes(bus, rxlimit); + if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { + brcmf_sdio_readframes(bus, bus->rxbound); if (!bus->rxpending) intstatus &= ~I_HMB_FRAME_IND; - rxlimit -= min(framecnt, rxlimit); } /* Keep still-pending events for next scheduling */ @@ -2648,8 +2643,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) && data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; - framecnt = brcmf_sdio_sendfromq(bus, framecnt); - txlimit -= framecnt; + brcmf_sdio_sendfromq(bus, framecnt); } if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { @@ -2659,7 +2653,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus)) || PKT_AVAILABLE()) { + data_ok(bus))) { atomic_inc(&bus->dpc_tskcnt); } } From 27076af57f48ccde365cf3daec1fd788c3981a9a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 26 Feb 2014 13:32:32 +0100 Subject: [PATCH 0099/1983] brcmfmac: Use atomic functions for intstatus update. Upstream-commit: c98db0bec72ac7ef127119c1ed962d6f56802b12 The intstatus in sdio code can be updated from different threads. To protect intstatus access, atomic functions are used. The loop was replaced using atomic_set_mask(). Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 226883f5d6862c..aff889c9151f14 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2450,7 +2450,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) struct brcmf_core *buscore; u32 addr; unsigned long val; - int n, ret; + int ret; buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); @@ -2458,7 +2458,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - val = 0; + return ret; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2467,13 +2467,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - } - - if (ret) { - atomic_set(&bus->intstatus, 0); - } else if (val) { - for_each_set_bit(n, &val, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); + atomic_set_mask(val, &bus->intstatus); } return ret; @@ -2485,7 +2479,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) unsigned long intstatus; uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0, n; + int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -2589,10 +2583,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } /* Keep still-pending events for next scheduling */ - if (intstatus) { - for_each_set_bit(n, &intstatus, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); - } + if (intstatus) + atomic_set_mask(intstatus, &bus->intstatus); brcmf_sdio_clrintr(bus); From 4e911e083e148d119c6c5f16af0b2a3f06c3c318 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 25 Feb 2014 20:30:35 +0100 Subject: [PATCH 0100/1983] brcmfmac: Put frame sdio tx error handling in sub function. Upstream-commit: 81c7883c46fddd53b7a98c3659ffae21189ae4ab Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 112 ++++++------------ 1 file changed, 33 insertions(+), 79 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index aff889c9151f14..a58648cfa36b0a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -1236,6 +1236,28 @@ static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) bus->cur_read.len = 0; } +static void brcmf_sdio_txfail(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + u8 i, hi, lo; + + /* On failure, abort the command and terminate the frame */ + brcmf_err("sdio error, abort command and terminate frame\n"); + bus->sdcnt.tx_sderrs++; + + brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); + bus->sdcnt.f1regdata++; + + for (i = 0; i < 3; i++) { + hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->sdcnt.f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } +} + /* return total length of buffer chain */ static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) { @@ -2253,7 +2275,6 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, uint chan) { int ret; - int i; struct sk_buff *pkt_next, *tmp; brcmf_dbg(TRACE, "Enter\n"); @@ -2266,28 +2287,9 @@ static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); bus->sdcnt.f2txdata++; - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; + if (ret < 0) + brcmf_sdio_txfail(bus); - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } - } sdio_release_host(bus->sdiodev->func[1]); done: @@ -2589,42 +2591,17 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - int i; + (bus->clkstate == CLK_AVAIL)) { sdio_claim_host(bus->sdiodev->func[1]); err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, (u32)bus->ctrl_frame_len); - if (err < 0) { - /* On failure, abort the command and - terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - err); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, &err); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, - &err); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, - &err); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } - - } else { + if (err < 0) + brcmf_sdio_txfail(bus); + else bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - } + sdio_release_host(bus->sdiodev->func[1]); bus->ctrl_frame_stat = false; brcmf_sdio_wait_event_wakeup(bus); @@ -2794,38 +2771,15 @@ static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) { - int i; int ret; bus->ctrl_frame_stat = false; ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if (hi == 0 && lo == 0) - break; - } - return ret; - } - - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; return ret; } From 7deefb310de16ad582c380a9124eae39e175b73f Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Tue, 25 Feb 2014 20:30:36 +0100 Subject: [PATCH 0101/1983] brcmfmac: Correct mcs index report Upstream-commit: 4aca7a185aeba4472e7a798e5d834d7702ebbeca There is a mismatch between the mcs index(0-7) reported to cfg80211 and the actual mcs index(0-15) in use. This patch resolves the mismatch by setting mcs info with the number of chains read from FW. Reviewed-by: Arend Van Spriel Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 474df2ce6e3ba5..00bd1e16c3ce71 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5306,6 +5306,8 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) u32 band_list[3]; u32 nmode; u32 bw_cap[2] = { 0, 0 }; + u32 rxchain; + u32 nchain; s8 phy; s32 err; u32 nband; @@ -5342,6 +5344,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode, bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]); + err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); + if (err) { + brcmf_err("rxchain error (%d)\n", err); + nchain = 1; + } else { + for (nchain = 0; rxchain; nchain++) + rxchain = rxchain & (rxchain - 1); + } + brcmf_dbg(INFO, "nchain=%d\n", nchain); + err = brcmf_construct_reginfo(cfg, bw_cap); if (err) { brcmf_err("brcmf_construct_reginfo failed (%d)\n", err); @@ -5370,10 +5382,7 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) band->ht_cap.ht_supported = true; band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - /* An HT shall support all EQM rates for one spatial - * stream - */ - band->ht_cap.mcs.rx_mask[0] = 0xff; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; bands[band->band] = band; } From e2e9b64886f54bf68655afa4ddfad09ac6a8c9b2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 27 Feb 2014 19:25:00 +0100 Subject: [PATCH 0102/1983] brcmfmac: use pre-allocated scatter-gather table for txglomming Upstream-commit: af1fa210f4fc6e304b859b386a3c8a266b1110ab Instead of allocating a scatter-gather table for every transmit reuse a pre-allocated table. The transmit path will be faster by taking out this allocation. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 47 +++++++++++++++---- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 8 +--- .../wireless/brcm80211/brcmfmac/sdio_host.h | 2 + 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 07e7d252025784..9eea7d4dd5015e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -53,6 +53,12 @@ /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 +#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ +#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ + +static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; +module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); +MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) { @@ -487,7 +493,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, struct mmc_request mmc_req; struct mmc_command mmc_cmd; struct mmc_data mmc_dat; - struct sg_table st; struct scatterlist *sgl; int ret = 0; @@ -532,16 +537,11 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, pkt_offset = 0; pkt_next = target_list->next; - if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) { - ret = -ENOMEM; - goto exit; - } - memset(&mmc_req, 0, sizeof(struct mmc_request)); memset(&mmc_cmd, 0, sizeof(struct mmc_command)); memset(&mmc_dat, 0, sizeof(struct mmc_data)); - mmc_dat.sg = st.sgl; + mmc_dat.sg = sdiodev->sgtable.sgl; mmc_dat.blksz = func_blk_sz; mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; mmc_cmd.opcode = SD_IO_RW_EXTENDED; @@ -557,7 +557,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, while (seg_sz) { req_sz = 0; sg_cnt = 0; - sgl = st.sgl; + sgl = sdiodev->sgtable.sgl; /* prep sg table */ while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; @@ -639,7 +639,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, } exit: - sg_free_table(&st); + sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents); while ((pkt_next = __skb_dequeue(&local_list)) != NULL) brcmu_pkt_buf_free_skb(pkt_next); @@ -863,6 +863,29 @@ int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) return 0; } +static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) +{ + uint nents; + int err; + + if (!sdiodev->sg_support) + return; + + nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); + nents += (nents >> 4) + 1; + + WARN_ON(nents > sdiodev->max_segment_count); + + brcmf_dbg(TRACE, "nents=%d\n", nents); + err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL); + if (err < 0) { + brcmf_err("allocation failed: disable scatter-gather"); + sdiodev->sg_support = false; + } + + sdiodev->txglomsz = brcmf_sdiod_txglomsz; +} + static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) { if (sdiodev->bus) { @@ -880,6 +903,7 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) sdio_disable_func(sdiodev->func[1]); sdio_release_host(sdiodev->func[1]); + sg_free_table(&sdiodev->sgtable); sdiodev->sbwad = 0; return 0; @@ -935,6 +959,11 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) SG_MAX_SINGLE_ALLOC); sdiodev->max_segment_size = host->max_seg_size; + /* allocate scatter-gather table. sg support + * will be disabled upon allocation failure. + */ + brcmf_sdiod_sgtable_alloc(sdiodev); + /* try to attach to the target device */ sdiodev->bus = brcmf_sdio_probe(sdiodev); if (!sdiodev->bus) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a58648cfa36b0a..71fa47dbe0f66d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -113,8 +113,6 @@ struct rte_console { #define BRCMF_TXBOUND 20 /* Default for max tx frames in one scheduling */ -#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ - #define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ #define MEMBLOCK 2048 /* Block size used for downloading @@ -510,10 +508,6 @@ static const uint max_roundup = 512; #define ALIGNMENT 4 -static int brcmf_sdio_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; -module_param_named(txglomsz, brcmf_sdio_txglomsz, int, 0); -MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); - enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_NORMAL, BRCMF_SDIO_FT_SUPER, @@ -2322,7 +2316,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) __skb_queue_head_init(&pktq); if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, - brcmf_sdio_txglomsz); + bus->sdiodev->txglomsz); pkt_num = min_t(u32, pkt_num, brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); spin_lock_bh(&bus->txqlock); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 5e53eb1b2ffaf6..3deab7959a0d9a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -180,6 +180,8 @@ struct brcmf_sdio_dev { uint max_request_size; ushort max_segment_count; uint max_segment_size; + uint txglomsz; + struct sg_table sgtable; }; /* sdio core registers */ From e265cefd7834bff26a640260d7617b397a1986ec Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Feb 2014 20:30:38 +0100 Subject: [PATCH 0103/1983] brcmfmac: reset suspend flag upon sdio suspend failure Upstream-commit: 3e3831c4fdc53aabf3a56419ef6d96a841c52435 The suspend callback first sets the suspend flag used in the driver but after that the actual suspend is done, which may fail. Reset the flag upon suspend failure. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9eea7d4dd5015e..4a6508e7e3a105 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1101,9 +1101,7 @@ static int brcmf_ops_sdio_suspend(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; int ret = 0; - brcmf_dbg(SDIO, "\n"); - - atomic_set(&sdiodev->suspend, true); + brcmf_dbg(SDIO, "Enter\n"); sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { @@ -1111,9 +1109,12 @@ static int brcmf_ops_sdio_suspend(struct device *dev) return -EINVAL; } + atomic_set(&sdiodev->suspend, true); + ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); if (ret) { brcmf_err("Failed to set pm_flags\n"); + atomic_set(&sdiodev->suspend, false); return ret; } @@ -1127,6 +1128,7 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + brcmf_dbg(SDIO, "Enter\n"); brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); atomic_set(&sdiodev->suspend, false); return 0; From 1050f5170d1f78361f1ac21cc4484abba56aff03 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Mar 2014 10:16:11 +0100 Subject: [PATCH 0104/1983] Revert "brcmfmac: Use atomic functions for intstatus update." Upstream-commit: 561e722201e41e304936b8a2aaa282c46ad4f393 This reverts commit c98db0bec72ac7ef127119c1ed962d6f56802b12. The function atomic_set_mask() is not architecture independent so it can not be used in the driver as is. Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 71fa47dbe0f66d..b3bcda158553f2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2446,7 +2446,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) struct brcmf_core *buscore; u32 addr; unsigned long val; - int ret; + int n, ret; buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); @@ -2454,7 +2454,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - return ret; + val = 0; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2463,7 +2463,13 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - atomic_set_mask(val, &bus->intstatus); + } + + if (ret) { + atomic_set(&bus->intstatus, 0); + } else if (val) { + for_each_set_bit(n, &val, 32) + set_bit(n, (unsigned long *)&bus->intstatus.counter); } return ret; @@ -2475,7 +2481,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) unsigned long intstatus; uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0; + int err = 0, n; brcmf_dbg(TRACE, "Enter\n"); @@ -2579,8 +2585,10 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } /* Keep still-pending events for next scheduling */ - if (intstatus) - atomic_set_mask(intstatus, &bus->intstatus); + if (intstatus) { + for_each_set_bit(n, &intstatus, 32) + set_bit(n, (unsigned long *)&bus->intstatus.counter); + } brcmf_sdio_clrintr(bus); From a890153886ae34081f4c902f68a8f15433f049a2 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Mar 2014 10:16:12 +0100 Subject: [PATCH 0105/1983] brcmfmac: Use atomic functions for intstatus update. Upstream-commit: 5cbb9c285bdcc14ee381120dab7e23a81060f1c0 The intstatus in sdio code can be updated from different threads. To protect intstatus access, atomic functions are used. One of them is set_bit, but this function is not guaranteed atomic on all platforms. The loop was replaced with local created OR function. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index b3bcda158553f2..964d09e7b8d6b6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2441,12 +2441,21 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) } } +static void atomic_orr(int val, atomic_t *v) +{ + int old_val; + + old_val = atomic_read(v); + while (atomic_cmpxchg(v, old_val, val | old_val) != old_val) + old_val = atomic_read(v); +} + static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { struct brcmf_core *buscore; u32 addr; unsigned long val; - int n, ret; + int ret; buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); @@ -2454,7 +2463,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - val = 0; + return ret; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); @@ -2463,13 +2472,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - } - - if (ret) { - atomic_set(&bus->intstatus, 0); - } else if (val) { - for_each_set_bit(n, &val, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); + atomic_orr(val, &bus->intstatus); } return ret; @@ -2481,7 +2484,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) unsigned long intstatus; uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0, n; + int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -2585,10 +2588,8 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } /* Keep still-pending events for next scheduling */ - if (intstatus) { - for_each_set_bit(n, &intstatus, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); - } + if (intstatus) + atomic_orr(intstatus, &bus->intstatus); brcmf_sdio_clrintr(bus); From e0d2001be58aef39445b5d9147f12a4e93b9f713 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 13 Mar 2014 23:21:08 +0100 Subject: [PATCH 0106/1983] brcmfmac: Make probe function __init Upstream-commit: c2d23c709c4ca2ddee4256689ac608ee50ec955a One of the benefits of platform_driver_probe() is that you can make the probe function __init. Signed-off-by: Jean Delvare Cc: Hante Meuleman Cc: Arend van Spriel Cc: John W. Linville Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 4a6508e7e3a105..d737cf78469a64 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1153,7 +1153,7 @@ static struct sdio_driver brcmf_sdmmc_driver = { }, }; -static int brcmf_sdio_pd_probe(struct platform_device *pdev) +static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) { brcmf_dbg(SDIO, "Enter\n"); From 774071e8af721800708cacdbc770d745d74bc504 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 15 Mar 2014 17:18:17 +0100 Subject: [PATCH 0107/1983] brcmfmac: add BCM4354 SDIO interface support Upstream-commit: a797ca1eadeef7f4fdba2ab5d143d56cc3ec5da3 BCM4354 is an a/b/g/n/ac 2x2 WiFi chip. This patch adds support for it through SDIO interface. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend Van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/chip.c | 5 +++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 7 ++++++- drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 1 + include/linux/mmc/sdio_ids.h | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index d737cf78469a64..6e8718bf69209c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -988,6 +988,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335_4339)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)}, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index a07b95ef9e7033..df130ef53d1c40 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -504,6 +504,7 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) ci->pub.ramsize = 0x3c000; break; case BCM4339_CHIP_ID: + case BCM4354_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; @@ -1006,6 +1007,10 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) chip = container_of(pub, struct brcmf_chip_priv, pub); switch (pub->chip) { + case BCM4354_CHIP_ID: + /* explicitly check SR engine enable bit */ + pmu_cc3_mask = BIT(2); + /* fall-through */ case BCM43241_CHIP_ID: case BCM4335_CHIP_ID: case BCM4339_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 964d09e7b8d6b6..7512bca93d9668 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -578,6 +578,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43143_NVRAM_NAME); @@ -597,6 +599,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); struct brcmf_firmware_names { u32 chipid; @@ -622,7 +626,8 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) } + { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 6fa5d4863782ea..d816270db3be56 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -43,5 +43,6 @@ #define BCM4335_CHIP_ID 0x4335 #define BCM43362_CHIP_ID 43362 #define BCM4339_CHIP_ID 0x4339 +#define BCM4354_CHIP_ID 0x4354 #endif /* _BRCM_HW_IDS_H_ */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index d8836623f36afc..0f01fe065424d0 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -31,6 +31,7 @@ #define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 #define SDIO_DEVICE_ID_BROADCOM_43362 43362 +#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_VENDOR_ID_INTEL 0x0089 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 From c24592bfef52278f8ede96d19fd250d323f75118 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 15 Mar 2014 17:18:18 +0100 Subject: [PATCH 0108/1983] brcmfmac: Protect tx seq number for data and control Upstream-commit: fed7ec44e7ef647c1b1584164fe172963731f26d SDIO tx uses a sequence number which is common for data and control. This requires that access to this sequence number is protected. A mutex was used to achieve this, but it also required the reordering of code for tx control. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 214 +++++++++--------- 1 file changed, 108 insertions(+), 106 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 7512bca93d9668..83b3747eda15b5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -458,10 +458,11 @@ struct brcmf_sdio { bool alp_only; /* Don't use HT clock (ALP only) */ u8 *ctrl_frame_buf; - u32 ctrl_frame_len; + u16 ctrl_frame_len; bool ctrl_frame_stat; - spinlock_t txqlock; + spinlock_t txq_lock; /* protect bus->txq */ + struct semaphore tx_seq_lock; /* protect bus->tx_seq */ wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; @@ -2318,13 +2319,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { pkt_num = 1; - __skb_queue_head_init(&pktq); + if (down_interruptible(&bus->tx_seq_lock)) + return cnt; if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, bus->sdiodev->txglomsz); pkt_num = min_t(u32, pkt_num, brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); - spin_lock_bh(&bus->txqlock); + __skb_queue_head_init(&pktq); + spin_lock_bh(&bus->txq_lock); for (i = 0; i < pkt_num; i++) { pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); @@ -2332,11 +2335,15 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) break; __skb_queue_tail(&pktq, pkt); } - spin_unlock_bh(&bus->txqlock); - if (i == 0) + spin_unlock_bh(&bus->txq_lock); + if (i == 0) { + up(&bus->tx_seq_lock); break; + } ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + up(&bus->tx_seq_lock); + cnt += i; /* In poll mode, need to check for other events */ @@ -2365,6 +2372,68 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) return cnt; } +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ + u8 doff; + u16 pad; + uint retries = 0; + struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Back the pointer to make room for bus header */ + frame -= bus->tx_hdrlen; + len += bus->tx_hdrlen; + + /* Add alignment padding (optional for ctl frames) */ + doff = ((unsigned long)frame % bus->head_align); + if (doff) { + frame -= doff; + len += doff; + memset(frame + bus->tx_hdrlen, 0, doff); + } + + /* Round send length to next SDIO block */ + pad = 0; + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + pad = bus->blocksize - (len % bus->blocksize); + if ((pad > bus->roundup) || (pad >= bus->blocksize)) + pad = 0; + } else if (len % bus->head_align) { + pad = bus->head_align - (len % bus->head_align); + } + len += pad; + + hd_info.len = len - pad; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff + bus->tx_hdrlen; + hd_info.seq_num = bus->tx_seq; + hd_info.lastfrm = true; + hd_info.tail_pad = pad; + brcmf_sdio_hdpack(bus, frame, &hd_info); + + if (bus->txglom) + brcmf_sdio_update_hwhdr(frame, len); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + frame, len, "Tx Frame:\n"); + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && + BRCMF_HDRS_ON(), + frame, min_t(u16, len, 16), "TxHdr:\n"); + + do { + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + } while (ret < 0 && retries++ < TXRETRIES); + + return ret; +} + static void brcmf_sdio_bus_stop(struct device *dev) { u32 local_hostintmask; @@ -2598,26 +2667,23 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); - if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, - (u32)bus->ctrl_frame_len); - - if (err < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && + (down_interruptible(&bus->tx_seq_lock) == 0)) { + if (data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); - sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + up(&bus->tx_seq_lock); } /* Send queued frames (limit 1 if rx may still be pending) */ - else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit - && data_ok(bus)) { + if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && + data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; brcmf_sdio_sendfromq(bus, framecnt); @@ -2651,7 +2717,6 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - ulong flags; brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); @@ -2667,7 +2732,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->sdcnt.fcqueued++; /* Priority based enq */ - spin_lock_irqsave(&bus->txqlock, flags); + spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ *(u16 *)(pkt->cb) = 0; if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { @@ -2682,7 +2747,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } - spin_unlock_irqrestore(&bus->txqlock, flags); + spin_unlock_bh(&bus->txq_lock); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) @@ -2777,87 +2842,27 @@ static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) } #endif /* DEBUG */ -static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - int ret; - - bus->ctrl_frame_stat = false; - ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - - if (ret < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - - return ret; -} - static int brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { - u8 *frame; - u16 len, pad; - uint retries = 0; - u8 doff = 0; - int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret = -1; brcmf_dbg(TRACE, "Enter\n"); - /* Back the pointer to make a room for bus header */ - frame = msg - bus->tx_hdrlen; - len = (msglen += bus->tx_hdrlen); - - /* Add alignment padding (optional for ctl frames) */ - doff = ((unsigned long)frame % bus->head_align); - if (doff) { - frame -= doff; - len += doff; - msglen += doff; - memset(frame, 0, doff + bus->tx_hdrlen); - } - /* precondition: doff < bus->head_align */ - doff += bus->tx_hdrlen; - - /* Round send length to next SDIO block */ - pad = 0; - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - pad = bus->blocksize - (len % bus->blocksize); - if ((pad > bus->roundup) || (pad >= bus->blocksize)) - pad = 0; - } else if (len % bus->head_align) { - pad = bus->head_align - (len % bus->head_align); - } - len += pad; - - /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - - /* Make sure backplane clock is on */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - sdio_release_host(bus->sdiodev->func[1]); - - hd_info.len = (u16)msglen; - hd_info.channel = SDPCM_CONTROL_CHANNEL; - hd_info.dat_offset = doff; - hd_info.seq_num = bus->tx_seq; - hd_info.lastfrm = true; - hd_info.tail_pad = pad; - brcmf_sdio_hdpack(bus, frame, &hd_info); - - if (bus->txglom) - brcmf_sdio_update_hwhdr(frame, len); + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; if (!data_ok(bus)) { brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", bus->tx_max, bus->tx_seq); - bus->ctrl_frame_stat = true; + up(&bus->tx_seq_lock); /* Send from dpc */ - bus->ctrl_frame_buf = frame; - bus->ctrl_frame_len = len; + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, @@ -2868,22 +2873,18 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) ret = 0; } else { brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); + bus->ctrl_frame_stat = false; + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; ret = -1; } } - if (ret == -1) { - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - do { - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdio_tx_frame(bus, frame, len); - sdio_release_host(bus->sdiodev->func[1]); - } while (ret < 0 && retries++ < TXRETRIES); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); + sdio_release_host(bus->sdiodev->func[1]); + up(&bus->tx_seq_lock); } if (ret) @@ -4054,7 +4055,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) } spin_lock_init(&bus->rxctl_lock); - spin_lock_init(&bus->txqlock); + spin_lock_init(&bus->txq_lock); + sema_init(&bus->tx_seq_lock, 1); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); From b019ef74a8b93414329f0256e624e799b33eaeb0 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 15 Mar 2014 17:18:19 +0100 Subject: [PATCH 0109/1983] brcmfmac: Improve scanning settings for connect. Upstream-commit: 63dd99e699f2d5e70bf3b3b28fa59fb2c8c640c7 When connecting without specifying the channel it can take quite some time to connect. This is because not all parameters for the scan operation are set to optimized values. This patch changes these parameters resulting in a much faster connect in certain situations. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 00bd1e16c3ce71..adbd5b7331470c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1682,22 +1682,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); - /*increase dwell time to receive probe response or detect Beacon - * from target AP at a noisy air only during connect command - */ - ext_join_params->scan_le.active_time = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); - ext_join_params->scan_le.passive_time = - cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* Set up join scan parameters */ ext_join_params->scan_le.scan_type = -1; - /* to sync with presence period of VSDB GO. - * Send probe request more frequently. Probe request will be stopped - * when it gets probe response from target AP/GO. - */ - ext_join_params->scan_le.nprobes = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / - BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); ext_join_params->scan_le.home_time = cpu_to_le32(-1); if (sme->bssid) @@ -1710,6 +1697,25 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->assoc_le.chanspec_list[0] = cpu_to_le16(chanspec); + /* Increase dwell time to receive probe response or detect + * beacon from target AP at a noisy air only during connect + * command. + */ + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* To sync with presence period of VSDB GO send probe request + * more frequently. Probe request will be stopped when it gets + * probe response from target AP/GO. + */ + ext_join_params->scan_le.nprobes = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / + BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); + } else { + ext_join_params->scan_le.active_time = cpu_to_le32(-1); + ext_join_params->scan_le.passive_time = cpu_to_le32(-1); + ext_join_params->scan_le.nprobes = cpu_to_le32(-1); } err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, From fbcffd0f581a51d83efaed8cc666f9df09927d91 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 15 Mar 2014 17:18:20 +0100 Subject: [PATCH 0110/1983] brcmfmac: assure active clock request upon entering SLEEP state Upstream-commit: 8a385ba542dd6d20e3067d486f5f75ff71baa4db When the SDIO driver goes in low power state it must assure that a clock request in ChipCLKCSR is set. Otherwise waking up the device can fail. The register is read and if necessary the ALP clock will be requested. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 83b3747eda15b5..4a345cac8d4eee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -175,6 +175,7 @@ struct rte_console { #define SBSDIO_ALP_AVAIL 0x40 /* Status: HT is ready */ #define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_CSR_MASK 0x1F #define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) #define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) #define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) @@ -720,16 +721,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) int err = 0; int try_cnt = 0; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter: on=%d\n", on); wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */ brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); - if (err) { - brcmf_err("SDIO_AOS KSO write error: %d\n", err); - return err; - } if (on) { /* device WAKEUP through KSO: @@ -759,13 +756,19 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) &err); if (((rd_val & bmask) == cmp_val) && !err) break; - brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n", - try_cnt, MAX_KSO_ATTEMPTS, err); + udelay(KSO_WAIT_US); brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); } while (try_cnt++ < MAX_KSO_ATTEMPTS); + if (try_cnt > 2) + brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, + rd_val, err); + + if (try_cnt > MAX_KSO_ATTEMPTS) + brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); + return err; } @@ -966,6 +969,7 @@ static int brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) { int err = 0; + u8 clkcsr; brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), @@ -984,8 +988,20 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) - return -EBUSY; + data_ok(bus))) { + err = -EBUSY; + goto done; + } + + clkcsr = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if ((clkcsr & SBSDIO_CSR_MASK) == 0) { + brcmf_dbg(SDIO, "no clock, set ALP\n"); + brcmf_sdiod_regwb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + } err = brcmf_sdio_kso_control(bus, false); /* disable watchdog */ if (!err) @@ -1002,7 +1018,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_err("error while changing bus sleep state %d\n", err); - return err; + goto done; } } @@ -1014,7 +1030,8 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; } From e3234a87ec48d5a1d1bb83bd8dc7329fc023dd80 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 15 Mar 2014 17:18:21 +0100 Subject: [PATCH 0111/1983] brcmfmac: remove mode field from brcmf_cfg80211_vif structure Upstream-commit: 967fe2c82dd8f8d16e873ebdf2328ec4d3258930 The nl80211 iftype was converted to a mode value and stored in brcmf_cfg80211_vif structure. The value was used only within brcmfmac driver to decide execution of conditional code. Better use wireless_dev::iftype for that as the wdev is contained in the brcmf_cfg80211_vif structure. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 6 +- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 60 ++++++------------- .../wireless/brcm80211/brcmfmac/wl_cfg80211.h | 17 ------ 3 files changed, 21 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index fc4f98b275d7db..f3445ac627e48d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -797,7 +797,8 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, /* SOCIAL CHANNELS 1, 6, 11 */ search_state = WL_P2P_DISC_ST_SEARCH; brcmf_dbg(INFO, "P2P SEARCH PHASE START\n"); - } else if (dev != NULL && vif->mode == WL_MODE_AP) { + } else if (dev != NULL && + vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) { /* If you are already a GO, then do SEARCH only */ brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n"); search_state = WL_P2P_DISC_ST_SEARCH; @@ -2256,7 +2257,6 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct brcmf_cfg80211_vif *vif; enum brcmf_fil_p2p_if_types iftype; - enum wl_mode mode; int err; if (brcmf_cfg80211_vif_event_armed(cfg)) @@ -2267,11 +2267,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, switch (type) { case NL80211_IFTYPE_P2P_CLIENT: iftype = BRCMF_FIL_P2P_IF_CLIENT; - mode = WL_MODE_BSS; break; case NL80211_IFTYPE_P2P_GO: iftype = BRCMF_FIL_P2P_IF_GO; - mode = WL_MODE_AP; break; case NL80211_IFTYPE_P2P_DEVICE: return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index adbd5b7331470c..9f75afb3baa0e5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -494,6 +494,19 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) return err; } +static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) +{ + enum nl80211_iftype iftype; + + iftype = vif->wdev.iftype; + return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; +} + +static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) +{ + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; +} + static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, enum nl80211_iftype type, @@ -654,7 +667,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, type); return -EOPNOTSUPP; case NL80211_IFTYPE_ADHOC: - vif->mode = WL_MODE_IBSS; infra = 0; break; case NL80211_IFTYPE_STATION: @@ -670,12 +682,10 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, */ return 0; } - vif->mode = WL_MODE_BSS; infra = 1; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - vif->mode = WL_MODE_AP; ap = 1; break; default: @@ -699,7 +709,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, err = -EAGAIN; goto done; } - brcmf_dbg(INFO, "IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ? + brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? "Adhoc" : "Infra"); } ndev->ieee80211_ptr->iftype = type; @@ -1923,7 +1933,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "Setting the key index %d\n", key.index); memcpy(key.data, params->key, key.len); - if ((ifp->vif->mode != WL_MODE_AP) && + if (!brcmf_is_apmode(ifp->vif) && (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); @@ -2022,7 +2032,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: - if (ifp->vif->mode != WL_MODE_AP) { + if (!brcmf_is_apmode(ifp->vif)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); @@ -2183,7 +2193,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (ifp->vif->mode == WL_MODE_AP) { + if (brcmf_is_apmode(ifp->vif)) { memcpy(&sta_info_le, mac, ETH_ALEN); err = brcmf_fil_iovar_data_get(ifp, "sta_info", &sta_info_le, @@ -2200,7 +2210,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, } brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", sinfo->inactive_time, sinfo->connected_time); - } else if (ifp->vif->mode == WL_MODE_BSS) { + } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) { if (memcmp(mac, bssid, ETH_ALEN)) { brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n", mac, bssid); @@ -2482,11 +2492,6 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, return err; } -static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) -{ - return vif->mode == WL_MODE_IBSS; -} - static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { @@ -4259,32 +4264,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode) }; -static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type) -{ - switch (type) { - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return -ENOTSUPP; - case NL80211_IFTYPE_ADHOC: - return WL_MODE_IBSS; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - return WL_MODE_BSS; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - return WL_MODE_AP; - case NL80211_IFTYPE_P2P_DEVICE: - return WL_MODE_P2P; - case NL80211_IFTYPE_UNSPECIFIED: - default: - break; - } - - return -EINVAL; -} - static void brcmf_wiphy_pno_params(struct wiphy *wiphy) { /* scheduled scan settings */ @@ -4409,7 +4388,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->wdev.wiphy = cfg->wiphy; vif->wdev.iftype = type; - vif->mode = brcmf_nl80211_iftype_to_mode(type); vif->pm_block = pm_block; vif->roam_off = -1; @@ -4703,7 +4681,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, s32 err = 0; u16 reason; - if (ifp->vif->mode == WL_MODE_AP) { + if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(e)) { brcmf_dbg(CONN, "Linkup\n"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 5715bb0708cf39..283c525a44f759 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -89,21 +89,6 @@ enum brcmf_scan_status { BRCMF_SCAN_STATUS_SUPPRESS, }; -/** - * enum wl_mode - driver mode of virtual interface. - * - * @WL_MODE_BSS: connects to BSS. - * @WL_MODE_IBSS: operate as ad-hoc. - * @WL_MODE_AP: operate as access-point. - * @WL_MODE_P2P: provide P2P discovery. - */ -enum wl_mode { - WL_MODE_BSS, - WL_MODE_IBSS, - WL_MODE_AP, - WL_MODE_P2P -}; - /* dongle configuration */ struct brcmf_cfg80211_conf { u32 frag_threshold; @@ -193,7 +178,6 @@ struct vif_saved_ie { * @ifp: lower layer interface pointer * @wdev: wireless device. * @profile: profile information. - * @mode: operating mode. * @roam_off: roaming state. * @sme_state: SME state using enum brcmf_vif_status bits. * @pm_block: power-management blocked. @@ -204,7 +188,6 @@ struct brcmf_cfg80211_vif { struct brcmf_if *ifp; struct wireless_dev wdev; struct brcmf_cfg80211_profile profile; - s32 mode; s32 roam_off; unsigned long sme_state; bool pm_block; From ca2fb49b6497d98a5e866a60bad454b392614e7c Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Thu, 20 Mar 2014 10:18:00 +0100 Subject: [PATCH 0112/1983] brcmfmac: Enable 40MHz bandwidth in 2GHz band and OBSS scanning operations Upstream-commit: d23536796011cbeeb93fc866446800c52deb5603 This patch enables 40MHz bandwidth in 2GHz band after checking whether cfg80211 allows it or not, and enables OBSS scanning operations to to support 20/40 BSS coexistence. Reviewed-by: Arend Van Spriel Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/fwil_types.h | 10 +++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index af17a5bc8b83bd..614e4888504fae 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -48,6 +48,11 @@ #define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ +/* OBSS Coex Auto/On/Off */ +#define BRCMF_OBSS_COEX_AUTO (-1) +#define BRCMF_OBSS_COEX_OFF 0 +#define BRCMF_OBSS_COEX_ON 1 + enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_CLIENT, BRCMF_FIL_P2P_IF_GO, @@ -87,6 +92,11 @@ struct brcmf_fil_bss_enable_le { __le32 enable; }; +struct brcmf_fil_bwcap_le { + __le32 band; + __le32 bw_cap; +}; + /** * struct tdls_iovar - common structure for tdls iovars. * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 9f75afb3baa0e5..428b4cb94da1e1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -191,6 +191,7 @@ static struct ieee80211_supported_band __wl_band_2ghz = { .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size, + .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true}, }; static struct ieee80211_supported_band __wl_band_5ghz_a = { @@ -4929,6 +4930,19 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } +static int brcmf_set_bwcap(struct brcmf_if *ifp, u32 band, u32 bw_cap) +{ + struct brcmf_fil_bwcap_le band_bwcap; + int err; + + band_bwcap.band = cpu_to_le32(band); + band_bwcap.bw_cap = cpu_to_le32(bw_cap); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + + return err; +} + struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { @@ -4986,6 +5000,18 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto cfg80211_p2p_attach_out; } + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + err = brcmf_set_bwcap(ifp, WLC_BAND_2G, WLC_BW_CAP_40MHZ); + if (!err) { + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + } + } + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); From 27ef0587b9908282fee54e3cebb20f1c956e04f1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Mar 2014 10:18:01 +0100 Subject: [PATCH 0113/1983] brcmfmac: reinit watchdog completion after handling watchdog Upstream-commit: 58e9df462fd70a1862378beb46b312f1f6bca94f The watchdog thread waits on completion that is set from a timer. As the completion is count based this could mean that on a busy system the watchdog is handled multiple times with a very short interval. This is not the intended behaviour. After handling the watchdog it should wait for the next timer expiry. This is accomplished by reinitializing the completion. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 4a345cac8d4eee..13c89a0c4ba7ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3996,6 +3996,7 @@ brcmf_sdio_watchdog_thread(void *data) brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); } else break; } From ebf0c1c7b57aa3df282f3d8dba0de9c383cb08a7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Mar 2014 10:18:02 +0100 Subject: [PATCH 0114/1983] brcmfmac: only show error message when brcmf_sdiod_regrw_helper() fails Upstream-commit: 4d1a4f16c96d3f4cf6afd92ca3ffb4d2c24875e1 In the function brcmf_sdiod_request_data() an error message is logged, but the calling function retries it. This patch will only log an error message when retry limit is reached. The low-level error is still logged by a SDIO debug message. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 6e8718bf69209c..a16e644e7c0882 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -269,26 +269,17 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, break; } - if (ret) { - /* - * SleepCSR register access can fail when - * waking up the device so reduce this noise - * in the logs. - */ - if (addr != SBSDIO_FUNC1_SLEEPCSR) - brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - else - brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", - write ? "write" : "read", fn, addr, ret); - } + if (ret) + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", fn, addr, ret); + return ret; } static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { - u8 func_num; + u8 func; s32 retry = 0; int ret; @@ -302,9 +293,9 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, * The rest: function 1 silicon backplane core registers */ if ((addr & ~REG_F0_REG_MASK) == 0) - func_num = SDIO_FUNC_0; + func = SDIO_FUNC_0; else - func_num = SDIO_FUNC_1; + func = SDIO_FUNC_1; do { if (!write) @@ -312,16 +303,26 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, /* for retry wait for 1 ms till bus get settled down */ if (retry) usleep_range(1000, 2000); - ret = brcmf_sdiod_request_data(sdiodev, func_num, addr, regsz, + ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz, data, write); } while (ret != 0 && ret != -ENOMEDIUM && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret == -ENOMEDIUM) brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); - else if (ret != 0) - brcmf_err("failed with %d\n", ret); - + else if (ret != 0) { + /* + * SleepCSR register access can fail when + * waking up the device so reduce this noise + * in the logs. + */ + if (addr != SBSDIO_FUNC1_SLEEPCSR) + brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + else + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + } return ret; } From b2ee610be4c441caeedec4b5d9dc9c7d066ee3a8 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Mar 2014 10:18:03 +0100 Subject: [PATCH 0115/1983] brcmfmac: fallback to mimo_bw_cap for older firmwares Upstream-commit: 67b3bd4e65f0854aca70e0134d59b1daede49504 In order to support the driver behaviour introduced by: commit d0575a5a703978c43e25128421158c78534ba100 Author: Daniel Kim Date: Wed Mar 12 18:12:14 2014 -0700 brcmfmac: Enable 40MHz bandwidth in 2GHz band and OBSS scanning in devices that do not support bwcap firmware command a fallback is added. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 428b4cb94da1e1..e0e649aab8dbc5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4930,16 +4930,27 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } -static int brcmf_set_bwcap(struct brcmf_if *ifp, u32 band, u32 bw_cap) +static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) { struct brcmf_fil_bwcap_le band_bwcap; + u32 val; int err; - band_bwcap.band = cpu_to_le32(band); - band_bwcap.bw_cap = cpu_to_le32(bw_cap); - err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, - sizeof(band_bwcap)); + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_40MHZ_BIT); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } return err; } @@ -5005,11 +5016,10 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, */ if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - err = brcmf_set_bwcap(ifp, WLC_BAND_2G, WLC_BW_CAP_40MHZ); - if (!err) { + err = brcmf_enable_bw40_2g(ifp); + if (!err) err = brcmf_fil_iovar_int_set(ifp, "obss_coex", BRCMF_OBSS_COEX_AUTO); - } } err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); From 3e9b7b52c18f3d1bd361e3828a3757afee9675e0 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Tue, 25 Mar 2014 21:45:09 +0100 Subject: [PATCH 0116/1983] brcmfmac: use mfp if required from user-space Upstream-commit: 87b7e9e2f6fe7e6b401443b3dd4b64815bcaa15c The struct cfg80211_connect_params indicate whether the connection should use management frame protection (mfp). If required set the MFP_CAPABLE flag in the firmware command. This is supported from user-space by wpa_supplicant since v2.1. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 15 ++++++++++----- .../net/wireless/brcm80211/include/brcmu_wifi.h | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index e0e649aab8dbc5..afb3d15e38ff03 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1354,13 +1354,14 @@ static s32 brcmf_set_auth_type(struct net_device *ndev, } static s32 -brcmf_set_set_cipher(struct net_device *ndev, - struct cfg80211_connect_params *sme) +brcmf_set_wsec_mode(struct net_device *ndev, + struct cfg80211_connect_params *sme, bool mfp) { struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); struct brcmf_cfg80211_security *sec; s32 pval = 0; s32 gval = 0; + s32 wsec; s32 err = 0; if (sme->crypto.n_ciphers_pairwise) { @@ -1412,7 +1413,12 @@ brcmf_set_set_cipher(struct net_device *ndev, if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && sme->privacy) pval = AES_ENABLED; - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", pval | gval); + + if (mfp) + wsec = pval | gval | MFP_CAPABLE; + else + wsec = pval | gval; + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); if (err) { brcmf_err("error (%d)\n", err); return err; @@ -1582,7 +1588,6 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, u32 ie_len; struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -1651,7 +1656,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = brcmf_set_set_cipher(ndev, sme); + err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); if (err) { brcmf_err("wl_set_set_cipher failed (%d)\n", err); goto done; diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h index 7ca2aa1035b210..74419d4bd12377 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h @@ -217,6 +217,9 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WSEC_SWFLAG 0x0008 /* to go into transition mode without setting wep */ #define SES_OW_ENABLED 0x0040 +/* MFP */ +#define MFP_CAPABLE 0x0200 +#define MFP_REQUIRED 0x0400 /* WPA authentication mode bitvec */ #define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ From 4a90aaeb1e25782c40f49c654c0c00c18a832a5f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Apr 2014 12:20:55 +0200 Subject: [PATCH 0117/1983] brcmfmac: Fix brcmf_chip_ai_coredisable not applying reset bits to BCMA_IOCTL Upstream-commit: ffa216bb5eecfce0f01b0b2a95d5c320dde90005 brcmfmac has been broken on my cubietruck with a BCM43362: brcmfmac: brcmf_chip_recognition: found AXI chip: BCM43362, rev=1 brcmfmac: brcmf_c_preinit_dcmds: Firmware version = wl0: Apr 22 2013 14:50:00 version 5.90.195.89.6 FWID 01-b30a427d since commit 53036261033: "brcmfmac: update core reset and disable routines". The problem is that since this commit brcmf_chip_ai_resetcore no longer sets BCMA_IOCTL itself before bringing the core out of reset, instead relying on brcmf_chip_ai_coredisable to do so. But brcmf_chip_ai_coredisable is a nop of the chip is already in reset. This patch modifies brcmf_chip_ai_coredisable to always set BCMA_IOCTL even if the core is already in reset. This fixes brcmfmac hanging in firmware loading on my board. Cc: stable@vger.kernel.org # v3.14 Signed-off-by: Hans de Goede Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index df130ef53d1c40..c7c9f15c0fe081 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -303,10 +303,10 @@ static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, ci = core->chip; - /* if core is already in reset, just return */ + /* if core is already in reset, skip reset */ regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); if ((regdata & BCMA_RESET_CTL_RESET) != 0) - return; + goto in_reset_configure; /* configure reset */ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, @@ -322,6 +322,7 @@ static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) != BCMA_RESET_CTL_RESET, 300); +in_reset_configure: /* in-reset configure */ ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); From 0b63eafd5a418d9417fa1069958b2ab201232f69 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Fri, 9 May 2014 12:37:05 +0200 Subject: [PATCH 0118/1983] brcmfmac: Fix iovar 'bw_cap' set command failure Upstream-commit: 03e5da151b8e390f2e28c4edf8fbbb6ca6d7a7ed Fix iovar 'bw_cap' set command failure introduced by commit ff3b0fba6f25555ef59c55d138a467d0f81d82d7 Author: Arend van Spriel Date: Sat Mar 15 12:00:57 2014 +0100 brcmfmac: fallback to mimo_bw_cap for older firmwares This resulted in disabling 20MHz operation in the firmware. Reviewed-by: Arend Van Spriel Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index afb3d15e38ff03..be1985296bdc75 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4948,7 +4948,7 @@ static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) if (!err) { /* only set 2G bandwidth using bw_cap command */ band_bwcap.band = cpu_to_le32(WLC_BAND_2G); - band_bwcap.bw_cap = cpu_to_le32(WLC_BW_40MHZ_BIT); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, sizeof(band_bwcap)); } else { From 6b30ae164dacafc69575ae6dcbc2f0a95b93d1f2 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Mon, 12 May 2014 10:47:26 +0200 Subject: [PATCH 0119/1983] brcmfmac: Give priority to 5GHz band in selecting target BSS Upstream-commit: e09cc63dc368f26f3636afbbb38aba9c9b99b3a5 When a BSS provides both 2.4GHz and 5GHz bands, in many cases it makes sense to choose 5GHz. Typically a 5GHz channel is less crowded and has less interference and therefore its performance will be better than a crowded 2.4 GHz channel. This patch configures 'join_pref' to induce firmware to preferably select 5GHz BSS. Reviewed-by: Arend Van Spriel Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_common.c | 18 ++++++++++++++ .../wireless/brcm80211/brcmfmac/fwil_types.h | 24 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 6a8983a1fb9c34..ed3e32ce8c23ee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -32,6 +32,9 @@ #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 #define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" +/* boost value for RSSI_DELTA in preferred join selection */ +#define BRCMF_JOIN_PREF_RSSI_BOOST 8 + bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec) @@ -246,6 +249,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; u8 buf[BRCMF_DCMD_SMLEN]; + struct brcmf_join_pref_params join_pref_params[2]; char *ptr; s32 err; @@ -298,6 +302,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ + join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; + join_pref_params[0].len = 2; + join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; + join_pref_params[0].band = WLC_BAND_5G; + join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; + join_pref_params[1].len = 2; + join_pref_params[1].rssi_gain = 0; + join_pref_params[1].band = 0; + err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, + sizeof(join_pref_params)); + if (err) + brcmf_err("Set join_pref error (%d)\n", err); + /* Setup event_msgs, enable E_IF */ err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 614e4888504fae..2bc68a2137fccb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -53,6 +53,14 @@ #define BRCMF_OBSS_COEX_OFF 0 #define BRCMF_OBSS_COEX_ON 1 +/* join preference types for join_pref iovar */ +enum brcmf_join_pref_types { + BRCMF_JOIN_PREF_RSSI = 1, + BRCMF_JOIN_PREF_WPA, + BRCMF_JOIN_PREF_BAND, + BRCMF_JOIN_PREF_RSSI_DELTA, +}; + enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_CLIENT, BRCMF_FIL_P2P_IF_GO, @@ -282,6 +290,22 @@ struct brcmf_assoc_params_le { __le16 chanspec_list[1]; }; +/** + * struct join_pref params - parameters for preferred join selection. + * + * @type: preference type (see enum brcmf_join_pref_types). + * @len: length of bytes following (currently always 2). + * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA). + * @band: band to which selection preference applies. + * This is used if @type is BAND or RSSI_DELTA. + */ +struct brcmf_join_pref_params { + u8 type; + u8 len; + u8 rssi_gain; + u8 band; +}; + /* used for join with or without a specific bssid and channel list */ struct brcmf_join_params { struct brcmf_ssid_le ssid_le; From bec9f5a359925fff17033d5e7421ea76538e0a99 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Mon, 12 May 2014 10:47:27 +0200 Subject: [PATCH 0120/1983] brcmfmac: Report the support of firmware roaming Upstream-commit: 604bf237cb3aa3e15d26254981ae7bc7968f05e5 Currently firmware roaming support is not reported to cfg80211. This patch reports the support of firmware based roaming when it is enabled. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Reviewed-by: Arend Van Spriel Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index be1985296bdc75..a91514ad57b370 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4364,6 +4364,8 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); From 858f6f8584b8474de4c7414c676aa1536529cded Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 12 May 2014 10:47:28 +0200 Subject: [PATCH 0121/1983] brcmfmac: Move out hdrpull from tx_finalize. Upstream-commit: 53e30ea42070bcb5cef0c9a2c59af8f9ce758f78 In tx_finalize the hdrpull is performed. For the new protocol msgbuf this is complex, because it does not use protocol headers in front of payload anymore and therefor can not determine interface index in the hdr pulll operation. Move out the hdrpull operation from tx_finalize to make msgbuf implementation easier. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 2 +- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 27 +++++----- .../wireless/brcm80211/brcmfmac/fwsignal.c | 50 +++++++++---------- 3 files changed, 37 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 939d6b13292248..16f9ab2568a808 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -186,7 +186,7 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); u32 brcmf_get_chip_info(struct brcmf_if *ifp); -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success); /* Sets dongle media info (drv_version, mac address). */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 7d28cd3850925a..6056efd02fcde5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -538,31 +538,26 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) brcmf_netif_rx(ifp, skb); } -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success) { struct brcmf_if *ifp; struct ethhdr *eh; - u8 ifidx; u16 type; - int res; - - res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp); ifp = drvr->iflist[ifidx]; if (!ifp) goto done; - if (res == 0) { - eh = (struct ethhdr *)(txp->data); - type = ntohs(eh->h_proto); + eh = (struct ethhdr *)(txp->data); + type = ntohs(eh->h_proto); - if (type == ETH_P_PAE) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } + if (type == ETH_P_PAE) { + atomic_dec(&ifp->pend_8021x_cnt); + if (waitqueue_active(&ifp->pend_8021x_wait)) + wake_up(&ifp->pend_8021x_wait); } + if (!success) ifp->stats.tx_errors++; done: @@ -573,13 +568,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; + u8 ifidx; /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(drvr->fws)) { if (!success) brcmf_fws_bustxfail(drvr->fws, txp); } else { - brcmf_txfinalize(drvr, txp, success); + if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp)) + brcmu_pkt_buf_free_skb(txp); + else + brcmf_txfinalize(drvr, txp, ifidx, success); } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index c3e7d76dbf35f5..bfe7c9aab65ca2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1369,13 +1369,12 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u32 genbit, - u16 seq) + struct sk_buff *skb, u8 ifidx, + u32 genbit, u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u32 hslot; int ret; - u8 ifidx; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); @@ -1389,29 +1388,21 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->generation = genbit; - ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (ret == 0) { - brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); - brcmf_skbcb(skb)->htod_seq = seq; - if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { - brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); - brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); - } else { - brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); - } - ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, - skb); + brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); + brcmf_skbcb(skb)->htod_seq = seq; + if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); + brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); + } else { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); } + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); if (ret != 0) { - /* suppress q is full or hdrpull failed, drop this packet */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, - true); + /* suppress q is full drop this packet */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); } else { - /* - * Mark suppressed to avoid a double free during - * wlfc cleanup - */ + /* Mark suppressed to avoid a double free during wlfc cleanup */ brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); } @@ -1428,6 +1419,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, struct sk_buff *skb; struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; + u8 ifidx; brcmf_dbg(DATA, "flags %d\n", flags); @@ -1476,12 +1468,15 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } brcmf_fws_macdesc_return_req_credit(skb); + if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) { + brcmu_pkt_buf_free_skb(skb); + return -EINVAL; + } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit, - seq); - + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx, + genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, true); + brcmf_txfinalize(fws->drvr, skb, ifidx, true); return 0; } @@ -1982,7 +1977,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) - brcmf_txfinalize(drvr, skb, false); + brcmf_txfinalize(drvr, skb, ifidx, + false); if (fws->bus_flow_blocked) break; } From b48ba0646986bef0a47e4e71acdd1977956bbefa Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 12 May 2014 10:47:29 +0200 Subject: [PATCH 0122/1983] brcmfmac: Move handling 802.1x frames to dhd_linux. Upstream-commit: 55929443c80af533e1ff03328d5377cf51591b38 Tracking and handling of 802.1x frames is done in two modules, it is more logical and clear to move this to dhd_linux module. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 5 ++++- .../net/wireless/brcm80211/brcmfmac/fwsignal.c | 15 +++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 6056efd02fcde5..4cacc3d85212dc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -190,7 +190,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - struct ethhdr *eh; + struct ethhdr *eh = (struct ethhdr *)(skb->data); brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); @@ -236,6 +236,9 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } + if (eh->h_proto == htons(ETH_P_PAE)) + atomic_inc(&ifp->pend_8021x_cnt); + ret = brcmf_fws_process_skb(ifp, skb); done: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index bfe7c9aab65ca2..b58a97aa659a64 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1863,7 +1863,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) struct ethhdr *eh = (struct ethhdr *)(skb->data); int fifo = BRCMF_FWS_FIFO_BCMC; bool multicast = is_multicast_ether_addr(eh->h_dest); - bool pae = eh->h_proto == htons(ETH_P_PAE); + int rc = 0; brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); /* determine the priority */ @@ -1871,8 +1871,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) skb->priority = cfg80211_classify8021d(skb, NULL); drvr->tx_multicast += !!multicast; - if (pae) - atomic_inc(&ifp->pend_8021x_cnt); /* set control buffer information */ skcb->if_flags = 0; @@ -1894,15 +1892,12 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_schedule_deq(fws); } else { brcmf_err("drop skb: no hanger slot\n"); - if (pae) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } - brcmu_pkt_buf_free_skb(skb); + brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + rc = -ENOMEM; } brcmf_fws_unlock(fws); - return 0; + + return rc; } void brcmf_fws_reset_interface(struct brcmf_if *ifp) From e07caec9bca197ac264d70c88f26261ba89e35c9 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Mon, 12 May 2014 10:47:30 +0200 Subject: [PATCH 0123/1983] brcmfmac: Make FWS queueing configurable. Upstream-commit: 9cd18359d31edd576b0aead131adff8b5f555425 FWS is always queuing frames and using a worker for de-queueing, this is not always efficient for all bus layer. For example SDIO has an internal queue and worker making the queueing of FWS unnecessary. Make it possible to bypass the worker if fws mode is none using a bus interface configuration. For USB bus layer this configuration is set true to have fws provide queueing regardless the fws mode. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 1 + .../net/wireless/brcm80211/brcmfmac/fwsignal.c | 15 +++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/usb.c | 1 + 3 files changed, 17 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index c4535616064e83..c5dcd82e884bf7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -99,6 +99,7 @@ struct brcmf_bus { unsigned long tx_realloc; u32 chip; u32 chiprev; + bool always_use_fws_queue; struct brcmf_bus_ops *ops; }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index b58a97aa659a64..699908de314a94 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -476,6 +476,7 @@ struct brcmf_fws_info { bool bus_flow_blocked; bool creditmap_received; u8 mode; + bool avoid_queueing; }; /* @@ -1872,6 +1873,13 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) drvr->tx_multicast += !!multicast; + if (fws->avoid_queueing) { + rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); + if (rc < 0) + brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + return rc; + } + /* set control buffer information */ skcb->if_flags = 0; skcb->state = BRCMF_FWS_SKBSTATE_NEW; @@ -2030,6 +2038,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr) fws->drvr = drvr; fws->fcmode = fcmode; + if ((drvr->bus_if->always_use_fws_queue == false) && + (fcmode == BRCMF_FWS_FCMODE_NONE)) { + fws->avoid_queueing = true; + brcmf_dbg(INFO, "FWS queueing will be avoided\n"); + return 0; + } + fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); if (fws->fws_wq == NULL) { brcmf_err("workqueue creation failed\n"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 24f65cd538595a..3ce0e7cfd02715 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1254,6 +1254,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; bus->proto_type = BRCMF_PROTO_BCDC; + bus->always_use_fws_queue = true; /* Attach to the common driver interface */ ret = brcmf_attach(dev); From ccd45626a8e5ea24347745fb00116dce178cff1a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:31 +0200 Subject: [PATCH 0124/1983] brcmfmac: enhance nvram processing Upstream-commit: 3e99b08ab53c039e440f015ff872490fbad30d78 The driver serializes the nvram firmware file before sending it to the device. This patch enhances this to assure serialized data is properly formatted and provide warnings on syntax failures. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/nvram.c | 220 ++++++++++++++---- 1 file changed, 176 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c index d5ef86db631b96..5c450d11dbc917 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/nvram.c @@ -18,72 +18,205 @@ #include #include +#include "dhd_dbg.h" #include "nvram.h" -/* brcmf_nvram_strip :Takes a buffer of "=\n" lines read from a file +enum nvram_parser_state { + IDLE, + KEY, + VALUE, + COMMENT, + END +}; + +/** + * struct nvram_parser - internal info for parser. + * + * @state: current parser state. + * @fwnv: input buffer being parsed. + * @nvram: output buffer with parse result. + * @nvram_len: lenght of parse result. + * @line: current line. + * @column: current column in line. + * @pos: byte offset in input buffer. + * @entry: start position of key,value entry. + */ +struct nvram_parser { + enum nvram_parser_state state; + const struct firmware *fwnv; + u8 *nvram; + u32 nvram_len; + u32 line; + u32 column; + u32 pos; + u32 entry; +}; + +static bool is_nvram_char(char c) +{ + /* comment marker excluded */ + if (c == '#') + return false; + + /* key and value may have any other readable character */ + return (c > 0x20 && c < 0x7f); +} + +static bool is_whitespace(char c) +{ + return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); +} + +static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) +{ + char c; + + c = nvp->fwnv->data[nvp->pos]; + if (c == '\n') + return COMMENT; + if (is_whitespace(c)) + goto proceed; + if (c == '#') + return COMMENT; + if (is_nvram_char(c)) { + nvp->entry = nvp->pos; + return KEY; + } + brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n", + nvp->line, nvp->column); +proceed: + nvp->column++; + nvp->pos++; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) +{ + enum nvram_parser_state st = nvp->state; + char c; + + c = nvp->fwnv->data[nvp->pos]; + if (c == '=') { + st = VALUE; + } else if (!is_nvram_char(c)) { + brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", + nvp->line, nvp->column); + return COMMENT; + } + + nvp->column++; + nvp->pos++; + return st; +} + +static enum nvram_parser_state +brcmf_nvram_handle_value(struct nvram_parser *nvp) +{ + char c; + char *skv; + char *ekv; + u32 cplen; + + c = nvp->fwnv->data[nvp->pos]; + if (!is_nvram_char(c)) { + /* key,value pair complete */ + ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; + skv = (u8 *)&nvp->fwnv->data[nvp->entry]; + cplen = ekv - skv; + /* copy to output buffer */ + memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); + nvp->nvram_len += cplen; + nvp->nvram[nvp->nvram_len] = '\0'; + nvp->nvram_len++; + return IDLE; + } + nvp->pos++; + nvp->column++; + return VALUE; +} + +static enum nvram_parser_state +brcmf_nvram_handle_comment(struct nvram_parser *nvp) +{ + char *eol, *sol; + + sol = (char *)&nvp->fwnv->data[nvp->pos]; + eol = strchr(sol, '\n'); + if (eol == NULL) + return END; + + /* eat all moving to next line */ + nvp->line++; + nvp->column = 1; + nvp->pos += (eol - sol) + 1; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) +{ + /* final state */ + return END; +} + +static enum nvram_parser_state +(*nv_parser_states[])(struct nvram_parser *nvp) = { + brcmf_nvram_handle_idle, + brcmf_nvram_handle_key, + brcmf_nvram_handle_value, + brcmf_nvram_handle_comment, + brcmf_nvram_handle_end +}; + +static int brcmf_init_nvram_parser(struct nvram_parser *nvp, + const struct firmware *nv) +{ + memset(nvp, 0, sizeof(*nvp)); + nvp->fwnv = nv; + /* Alloc for extra 0 byte + roundup by 4 + length field */ + nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvp->nvram) + return -ENOMEM; + + nvp->line = 1; + nvp->column = 1; + return 0; +} + +/* brcmf_nvram_strip :Takes a buffer of "=\n" lines read from a fil * and ending in a NUL. Removes carriage returns, empty lines, comment lines, * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. * End of buffer is completed with token identifying length of buffer. */ void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length) { - u8 *nvram; - u32 i; - u32 len; - u32 column; - u8 val; - bool comment; + struct nvram_parser nvp; + u32 pad; u32 token; __le32 token_le; - /* Alloc for extra 0 byte + roundup by 4 + length field */ - nvram = kmalloc(nv->size + 1 + 3 + sizeof(token_le), GFP_KERNEL); - if (!nvram) + if (brcmf_init_nvram_parser(&nvp, nv) < 0) return NULL; - len = 0; - column = 0; - comment = false; - for (i = 0; i < nv->size; i++) { - val = nv->data[i]; - if (val == 0) + while (nvp.pos < nv->size) { + nvp.state = nv_parser_states[nvp.state](&nvp); + if (nvp.state == END) break; - if (val == '\r') - continue; - if (comment && (val != '\n')) - continue; - comment = false; - if (val == '#') { - comment = true; - continue; - } - if (val == '\n') { - if (column == 0) - continue; - nvram[len] = 0; - len++; - column = 0; - continue; - } - nvram[len] = val; - len++; - column++; } - column = len; - *new_length = roundup(len + 1, 4); - while (column != *new_length) { - nvram[column] = 0; - column++; + pad = nvp.nvram_len; + *new_length = roundup(nvp.nvram_len + 1, 4); + while (pad != *new_length) { + nvp.nvram[pad] = 0; + pad++; } token = *new_length / 4; token = (~token << 16) | (token & 0x0000FFFF); token_le = cpu_to_le32(token); - memcpy(&nvram[*new_length], &token_le, sizeof(token_le)); + memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); *new_length += sizeof(token_le); - return nvram; + return nvp.nvram; } void brcmf_nvram_free(void *nvram) @@ -91,4 +224,3 @@ void brcmf_nvram_free(void *nvram) kfree(nvram); } - From 49e3b246a05ded073b4b084b82cfe4d932396509 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Mon, 12 May 2014 10:47:32 +0200 Subject: [PATCH 0125/1983] brcmfmac: Fix reconnect failure after beacon timeout Upstream-commit: b8d2e8783991d5aa693fd0b4bc8dd11422a41950 The DISASSOC command needs to be sent to firmware when a connection loss is detected by firmware (e.g., beacon timeout). Otherwise the next connect request fails due to a lingering LINK(down) event from firmware. This patch resolves the issue by using brcmf_link_down() handler, instead of the incomplete duplicated codes. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index a91514ad57b370..e9d7413522b386 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4687,7 +4687,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct ieee80211_channel *chan; s32 err = 0; - u16 reason; if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); @@ -4708,16 +4707,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); - if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) { - reason = 0; - if (((e->event_code == BRCMF_E_DEAUTH_IND) || - (e->event_code == BRCMF_E_DISASSOC_IND)) && - (e->reason != WLAN_REASON_UNSPECIFIED)) - reason = e->reason; - cfg80211_disconnected(ndev, reason, NULL, 0, - GFP_KERNEL); - } } brcmf_link_down(ifp->vif); brcmf_init_prof(ndev_to_prof(ndev)); From 3920137a09b27f2fb8441fdba51a19a19e1e3449 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:33 +0200 Subject: [PATCH 0126/1983] brcmfmac: remove usage of cfg80211_get_chandef_type() Upstream-commit: a9a56878a7db16f9749030385b00604189a8bc7e In the .start_ap callback cfg80211_get_chandef_type() was used to provide debug log info. However, this causes a warning when the chandef contains VHT channel with 80MHz bandwidth. Avoid the warning by just printing the channel and bandwidth instead. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index e9d7413522b386..c4c563e5c8b0e9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3766,10 +3766,9 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; - brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n", - cfg80211_get_chandef_type(&settings->chandef), - settings->beacon_interval, - settings->dtim_period); + brcmf_dbg(TRACE, "channel=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", + settings->chandef.chan->hw_value, settings->chandef.width, + settings->beacon_interval, settings->dtim_period); brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); From 01c1d229a292d380932d2ad4ebdc956809847f3f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:35 +0200 Subject: [PATCH 0127/1983] brcmfmac: provide VHT capability information to user-space Upstream-commit: 18d6c535cec18b8fc900fee9ce020ca0dd1d2da7 Although brcmfmac support several 11ac devices it did not advertise VHT related information to cfg80211. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 73 +++++++++++++++---- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index c4c563e5c8b0e9..6bdbabb63d1929 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5313,13 +5313,63 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) } } +static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + band->ht_cap.ht_supported = true; + if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) +{ + u16 mcs_map; + int i; + + for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) + mcs_map = (mcs_map << 2) | supp; + + return cpu_to_le16(mcs_map); +} + +static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + __le16 mcs_map; + + /* not allowed in 2.4G band */ + if (band->band == IEEE80211_BAND_2GHZ) + return; + + band->vht_cap.vht_supported = true; + /* 80MHz is mandatory */ + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { + band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; + } + /* all support 256-QAM */ + mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); + band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; + band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; +} + static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct wiphy *wiphy; s32 phy_list; u32 band_list[3]; - u32 nmode; + u32 nmode = 0; + u32 vhtmode = 0; u32 bw_cap[2] = { 0, 0 }; u32 rxchain; u32 nchain; @@ -5350,14 +5400,16 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", band_list[0], band_list[1], band_list[2]); + (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); if (err) { brcmf_err("nmode error (%d)\n", err); } else { brcmf_get_bwcap(ifp, bw_cap); } - brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode, - bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]); + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], + bw_cap[IEEE80211_BAND_5GHZ]); err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); if (err) { @@ -5388,17 +5440,10 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) else continue; - if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - band->ht_cap.ht_supported = true; - band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); - band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (nmode) + brcmf_update_ht_cap(band, bw_cap, nchain); + if (vhtmode) + brcmf_update_vht_cap(band, bw_cap, nchain); bands[band->band] = band; } From 3e5fc586551c3a87fc0814ecef29a5beebaf5183 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:36 +0200 Subject: [PATCH 0128/1983] brcmfmac: enable 80Mhz in 5G custom regulatory rules Upstream-commit: c555ecde6c003bbe800e52b9d4b7201b1f1c4dec By default allow 80Mhz in custom regulatory rules of the 5G band so the channels will not be flagged with N0_80MHZ. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 6bdbabb63d1929..bfe2c932f92499 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -221,9 +221,9 @@ static const struct ieee80211_regdomain brcmf_regdom = { */ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), /* IEEE 802.11a, channel 36..64 */ - REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } + REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } }; static const u32 __wl_cipher_suites[] = { From bec7ed0b14760b9ec038305f9674e2f3fcdabe22 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:37 +0200 Subject: [PATCH 0129/1983] brcmfmac: get rid of brcmf_cfg80211_set_channel() function Upstream-commit: 06c01585ee94e96286c4e6510a4a0ec32c3df729 The function does not provide any additional functionality and is used only once so just get rid of it. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index bfe2c932f92499..77c9309c271bac 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3733,23 +3733,6 @@ brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, return err; } -static s32 -brcmf_cfg80211_set_channel(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, - struct ieee80211_channel *channel) -{ - u16 chanspec; - s32 err; - - brcmf_dbg(TRACE, "band=%d, center_freq=%d\n", channel->band, - channel->center_freq); - - chanspec = channel_to_chanspec(&cfg->d11inf, channel); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - - return err; -} - static s32 brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_ap_settings *settings) @@ -3765,9 +3748,11 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_join_params join_params; enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; + u16 chanspec; - brcmf_dbg(TRACE, "channel=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", - settings->chandef.chan->hw_value, settings->chandef.width, + brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", + settings->chandef.chan->hw_value, + settings->chandef.center_freq1, settings->chandef.width, settings->beacon_interval, settings->dtim_period); brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, @@ -3825,9 +3810,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - err = brcmf_cfg80211_set_channel(cfg, ifp, settings->chandef.chan); + chanspec = channel_to_chanspec(&cfg->d11inf, settings->chandef.chan); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); if (err < 0) { - brcmf_err("Set Channel failed, %d\n", err); + brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); goto exit; } From 2649dc1734d213c0a596e40045af46ab13f05567 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:38 +0200 Subject: [PATCH 0130/1983] brcmfmac: handle 80MHz chanspecs in construct_reg_info() function Upstream-commit: ee942ecc42af9af0f0398861f636019f9726755b The device is queried about the usability of channels, but it did not take 80MHz channels into consideration. This patch adds processing those chanspecs and clear the NO_80MHZ flag for those control/primary channels. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 77c9309c271bac..7aeb52b7260b7f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5191,6 +5191,9 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; + if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_80) + continue; update = false; for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { if (band_chan_arr[j].hw_value == ch.chnum) { @@ -5207,10 +5210,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, ieee80211_channel_to_frequency(ch.chnum, band); band_chan_arr[index].hw_value = ch.chnum; - if (ch.bw == BRCMU_CHAN_BW_40) { - /* assuming the order is HT20, HT40 Upper, - * HT40 lower from chanspecs - */ + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_80MHZ; + } else if (ch.bw == BRCMU_CHAN_BW_40) { ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; if (ch.sb == BRCMU_CHAN_SB_U) { @@ -5231,8 +5237,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, IEEE80211_CHAN_NO_HT40MINUS; } } else { + /* disable other bandwidths for now as mentioned + * order assure they are enabled for subsequent + * chanspecs. + */ band_chan_arr[index].flags = - IEEE80211_CHAN_NO_HT40; + IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; ch.bw = BRCMU_CHAN_BW_20; cfg->d11inf.encchspec(&ch); channel = ch.chspec; From 4a224e829ac29f544b27942edd9525f25f65726d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Mon, 12 May 2014 10:47:39 +0200 Subject: [PATCH 0131/1983] brcmfmac: determine chanspec from struct cfg80211_chan_def info Upstream-commit: 600a897d811d0a0033212a0cefcbc9d06d1bc57d The struct cfg80211_chan_def contains additional info to derive the bandwidth and side-band information of the chanspec. This patch adds chandef_to_chanspec() function used in IBSS join and starting AP operation. Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 7aeb52b7260b7f..70bc2542061a0d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -341,6 +341,60 @@ static u8 brcmf_mw_to_qdbm(u16 mw) return qdbm; } +u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, + struct cfg80211_chan_def *ch) +{ + struct brcmu_chan ch_inf; + s32 primary_offset; + + brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", + ch->chan->center_freq, ch->center_freq1, ch->width); + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); + primary_offset = ch->center_freq1 - ch->chan->center_freq; + switch (ch->width) { + case NL80211_CHAN_WIDTH_20: + ch_inf.bw = BRCMU_CHAN_BW_20; + WARN_ON(primary_offset != 0); + break; + case NL80211_CHAN_WIDTH_40: + ch_inf.bw = BRCMU_CHAN_BW_40; + if (primary_offset < 0) + ch_inf.sb = BRCMU_CHAN_SB_U; + else + ch_inf.sb = BRCMU_CHAN_SB_L; + break; + case NL80211_CHAN_WIDTH_80: + ch_inf.bw = BRCMU_CHAN_BW_80; + if (primary_offset < 0) { + if (primary_offset < -CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_UU; + else + ch_inf.sb = BRCMU_CHAN_SB_UL; + } else { + if (primary_offset > CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_LL; + else + ch_inf.sb = BRCMU_CHAN_SB_LU; + } + break; + default: + WARN_ON_ONCE(1); + } + switch (ch->chan->band) { + case IEEE80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case IEEE80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; + default: + WARN_ON_ONCE(1); + } + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch) { @@ -1236,8 +1290,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, params->chandef.chan->center_freq); if (params->channel_fixed) { /* adding chanspec */ - chanspec = channel_to_chanspec(&cfg->d11inf, - params->chandef.chan); + chanspec = chandef_to_chanspec(&cfg->d11inf, + ¶ms->chandef); join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); join_params.params_le.chanspec_num = cpu_to_le32(1); @@ -3810,7 +3864,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - chanspec = channel_to_chanspec(&cfg->d11inf, settings->chandef.chan); + chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); if (err < 0) { brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); From da7d35dfe91ab71d672be904d3f0478345114efd Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:13 +0200 Subject: [PATCH 0132/1983] brcmfmac: reduce log level for invalid scheduled scan request Upstream-commit: 181f2d177ed1ed91633e6f39c599dedaa05844e8 When a regular scan does not return any networks user-space does request a scheduled scan without any matchset or ssid. This can not be handled by the firmware so we return -EINVAL. However, as this request is done let us not add an error message to the log. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 70bc2542061a0d..5d1a7fa0bf0373 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3178,7 +3178,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } if (!request->n_ssids || !request->n_match_sets) { - brcmf_err("Invalid sched scan req!! n_ssids:%d\n", + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", request->n_ssids); return -EINVAL; } From 47779ae052df5571dfdf0e199bf999954287743a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:14 +0200 Subject: [PATCH 0133/1983] brcmfmac: restore mpc before passing scan status to cfg80211 Upstream-commit: 0f0fe990e329583bf403f0c36d4e310d79e75b87 Before informing cfg80211 about the scan status the device should be put back in mpc state. If done after user-space may initiate another (scheduled) scan and fail because scan is still busy as shown in logging below: [ 3301.367376] brcmfmac: brcmf_fweh_event_worker event ESCAN_RESULT (69) [ 3301.377305] brcmfmac: brcmf_fweh_event_worker version 2 flags 0 status 0 [ 3301.384993] brcmutil: event payload, len=12 [ 3301.389208] 00000000: 0c 00 00 00 6d 00 00 00 34 12 00 00 [ 3301.389214] brcmfmac: brcmf_sdio_kso_control Enter: on=0 [ 3301.402196] brcmfmac: brcmf_inform_bss scanned AP count (0) [ 3301.407808] brcmfmac: brcmf_notify_escan_complete Enter [ 3301.413064] brcmfmac: brcmf_notify_escan_complete ESCAN Completed scan: Done [ 3301.420137] brcmfmac: brcmf_sdio_bus_txctl Enter [ 3301.420368] brcmfmac: brcmf_cfg80211_sched_scan_start Enter [ 3301.420370] brcmfmac: brcmf_cfg80211_sched_scan_start: Scanning already: status (1) [ 3301.440190] brcmfmac: brcmf_sdio_kso_control Enter: on=1 [ 3301.448695] brcmfmac: brcmf_sdio_tx_ctrlframe Enter [ 3301.453662] brcmfmac: brcmf_sdio_bus_rxctl Enter [ 3301.458326] brcmfmac: brcmf_sdio_isr Enter [ 3301.462523] brcmfmac: brcmf_sdio_dpc Enter [ 3301.466632] brcmfmac: brcmf_sdio_readframes Enter [ 3301.471431] brcmfmac: brcmf_sdio_read_control Enter [ 3301.476340] brcmfmac: brcmf_set_mpc MPC : 1 Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 5d1a7fa0bf0373..a181aa04be124e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -640,6 +640,9 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, if (err) brcmf_err("Scan abort failed\n"); } + + brcmf_set_mpc(ifp, 1); + /* * e-scan can be initiated by scheduled scan * which takes precedence. @@ -649,12 +652,10 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, cfg->sched_escan = false; if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); - brcmf_set_mpc(ifp, 1); } else if (scan_request) { brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, aborted); - brcmf_set_mpc(ifp, 1); } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); From cb77bce184036f4049a0f999048167a27dfd3863 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:15 +0200 Subject: [PATCH 0134/1983] brcmfmac: make chandef_to_chanspec() function static Upstream-commit: 5a394eba4bc7ec8741f59fcede6bd3d20ae6488c The function chandef_to_chanspec() was added by brcmfmac: determine chanspec from struct cfg80211_chan_def info The struct cfg80211_chan_def contains additional info to derive the bandwidth and side-band information of the chanspec. This patch adds chandef_to_chanspec() function used in IBSS join and starting AP operation. However, it introduced a sparse warning because the function is only called from within the source file wl_cfg80211.c. Reported-by: Fengguang Wu Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index a181aa04be124e..4d0166a9611e76 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -341,8 +341,8 @@ static u8 brcmf_mw_to_qdbm(u16 mw) return qdbm; } -u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, - struct cfg80211_chan_def *ch) +static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, + struct cfg80211_chan_def *ch) { struct brcmu_chan ch_inf; s32 primary_offset; From 91083e452ab44e675ad303b8aed3f6aa5cf00221 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:16 +0200 Subject: [PATCH 0135/1983] brcmfmac: remove .init() callback for internal bus interface Upstream-commit: f33d7a914125423e357693f972512fa9c94fd3aa The .init() callback was the first function called by the common bus function brcmf_bus_start(). Given that it is not really necessary and the bus layer can call it before calling the brcmf_bus_start() function. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 6 ------ drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 7 ------- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 5 ++++- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 7 ++++++- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index c5dcd82e884bf7..7735328fff21e6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -63,7 +63,6 @@ struct brcmf_bus_dcmd { */ struct brcmf_bus_ops { int (*preinit)(struct device *dev); - int (*init)(struct device *dev); void (*stop)(struct device *dev); int (*txdata)(struct device *dev, struct sk_buff *skb); int (*txctl)(struct device *dev, unsigned char *msg, uint len); @@ -114,11 +113,6 @@ static inline int brcmf_bus_preinit(struct brcmf_bus *bus) return bus->ops->preinit(bus->dev); } -static inline int brcmf_bus_init(struct brcmf_bus *bus) -{ - return bus->ops->init(bus->dev); -} - static inline void brcmf_bus_stop(struct brcmf_bus *bus) { bus->ops->stop(bus->dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 4cacc3d85212dc..09dd8c13d84483 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -916,13 +916,6 @@ int brcmf_bus_start(struct device *dev) brcmf_dbg(TRACE, "\n"); - /* Bring up the bus */ - ret = brcmf_bus_init(bus_if); - if (ret != 0) { - brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret); - return ret; - } - /* add primary networking interface */ ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); if (IS_ERR(ifp)) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 13c89a0c4ba7ec..494371ccf0f7b2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4020,7 +4020,6 @@ brcmf_sdio_watchdog(unsigned long data) static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .stop = brcmf_sdio_bus_stop, .preinit = brcmf_sdio_bus_preinit, - .init = brcmf_sdio_bus_init, .txdata = brcmf_sdio_bus_txdata, .txctl = brcmf_sdio_bus_txctl, .rxctl = brcmf_sdio_bus_rxctl, @@ -4150,6 +4149,10 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); + ret = brcmf_sdio_bus_init(sdiodev->dev); + if (ret) + goto fail; + /* if firmware path present try to download and bring up bus */ ret = brcmf_bus_start(bus->sdiodev->dev); if (ret != 0) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 3ce0e7cfd02715..1c846c54db23fa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1222,7 +1222,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, static struct brcmf_bus_ops brcmf_usb_bus_ops = { .txdata = brcmf_usb_tx, - .init = brcmf_usb_up, .stop = brcmf_usb_down, .txctl = brcmf_usb_tx_ctlpkt, .rxctl = brcmf_usb_rx_ctlpkt, @@ -1263,6 +1262,12 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) goto fail; } + ret = brcmf_usb_up(dev); + if (ret) { + brcmf_detach(dev); + goto fail; + } + ret = brcmf_bus_start(dev); if (ret) { brcmf_err("dongle is not responding\n"); From 8d3bae04776b9f72c50f8ca794c542ce8f2dac89 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:17 +0200 Subject: [PATCH 0136/1983] brcmfmac: rename nvram.[ch] for upcoming firmware handling functions Upstream-commit: dabedab98317e8915eefd8485921fb571b26cdd1 The firmware processing will be modified to use asynchronous request firmware api. In preparation this patch is simple rename of source and header file to which the functionality will be added. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 6 +++--- .../brcm80211/brcmfmac/{nvram.c => firmware.c} | 6 +++--- .../brcm80211/brcmfmac/{nvram.h => firmware.h} | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{nvram.c => firmware.c} (97%) rename drivers/net/wireless/brcm80211/brcmfmac/{nvram.h => firmware.h} (79%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 1d2ceac3a221bd..98e67c18f27647 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -33,7 +33,7 @@ brcmfmac-objs += \ bcdc.o \ dhd_common.o \ dhd_linux.o \ - nvram.o \ + firmware.o \ btcoex.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 494371ccf0f7b2..010e1bd521f01b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -42,7 +42,7 @@ #include #include "sdio_host.h" #include "chip.h" -#include "nvram.h" +#include "firmware.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ @@ -3287,7 +3287,7 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, brcmf_dbg(TRACE, "Enter\n"); - vars = brcmf_nvram_strip(nv, &varsz); + vars = brcmf_fw_nvram_strip(nv, &varsz); if (vars == NULL) return -EINVAL; @@ -3300,7 +3300,7 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) err = -EIO; - brcmf_nvram_free(vars); + brcmf_fw_nvram_free(vars); return err; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c similarity index 97% rename from drivers/net/wireless/brcm80211/brcmfmac/nvram.c rename to drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 5c450d11dbc917..ff5b86733d5a7f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/nvram.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -19,7 +19,7 @@ #include #include "dhd_dbg.h" -#include "nvram.h" +#include "firmware.h" enum nvram_parser_state { IDLE, @@ -187,7 +187,7 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. * End of buffer is completed with token identifying length of buffer. */ -void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length) +void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) { struct nvram_parser nvp; u32 pad; @@ -219,7 +219,7 @@ void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length) return nvp.nvram; } -void brcmf_nvram_free(void *nvram) +void brcmf_fw_nvram_free(void *nvram) { kfree(nvram); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/nvram.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h similarity index 79% rename from drivers/net/wireless/brcm80211/brcmfmac/nvram.h rename to drivers/net/wireless/brcm80211/brcmfmac/firmware.h index d454580928c9ab..127633bc242d52 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/nvram.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -13,12 +13,12 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef BRCMFMAC_NVRAM_H -#define BRCMFMAC_NVRAM_H +#ifndef BRCMFMAC_FIRMWARE_H +#define BRCMFMAC_FIRMWARE_H -void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length); -void brcmf_nvram_free(void *nvram); +void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); +void brcmf_fw_nvram_free(void *nvram); -#endif /* BRCMFMAC_NVRAM_H */ +#endif /* BRCMFMAC_FIRMWARE_H */ From b21fc610c617b4d4bb0f03b143cc8ee5621f0ca5 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:18 +0200 Subject: [PATCH 0137/1983] brcmfmac: call brcmf_detach() unconditional in sdio .remove() callback Upstream-commit: 4faf28b7b471b97be35f29dade653d271c1951cc The function brcmf_detach() checks whether it needs to do his stuff or can return immediately. No need to have the same check in the calling code. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 010e1bd521f01b..efb8c4febe6563 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4176,9 +4176,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) /* De-register interrupt handler */ brcmf_sdiod_intr_unregister(bus->sdiodev); - if (bus->sdiodev->bus_if->drvr) { - brcmf_detach(bus->sdiodev->dev); - } + brcmf_detach(bus->sdiodev->dev); cancel_work_sync(&bus->datawork); if (bus->brcmf_wq) From d99a562f2e22a95dab4bdecf751faa9a315dfa94 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:19 +0200 Subject: [PATCH 0138/1983] brcmfmac: rework usb callback operations Upstream-commit: 4dd7de1f9e935373c64b5943055987170a0ef7b8 The resume callbacks do partly the same a the probe callback so put common code in separate function for use in the callbacks. This also fixes suspend/resume regression introduced by brcmfmac: remove .init() callback for internal bus interface The .init() callback was the first function called by the common bus function brcmf_bus_start(). Given that it is not really necessary and the bus layer can call it before calling the brcmf_bus_start() function. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 1c846c54db23fa..3c3ffff57a0e0d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1227,6 +1227,31 @@ static struct brcmf_bus_ops brcmf_usb_bus_ops = { .rxctl = brcmf_usb_rx_ctlpkt, }; +static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(devinfo->dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + return ret; + } + + ret = brcmf_usb_up(devinfo->dev); + if (ret) + goto fail; + + ret = brcmf_bus_start(devinfo->dev); + if (ret) + goto fail; + + return 0; +fail: + brcmf_detach(devinfo->dev); + return ret; +} + static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) { struct brcmf_bus *bus = NULL; @@ -1255,23 +1280,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; - /* Attach to the common driver interface */ - ret = brcmf_attach(dev); - if (ret) { - brcmf_err("brcmf_attach failed\n"); - goto fail; - } - - ret = brcmf_usb_up(dev); - if (ret) { - brcmf_detach(dev); - goto fail; - } - - ret = brcmf_bus_start(dev); + ret = brcmf_usb_bus_setup(devinfo); if (ret) { brcmf_err("dongle is not responding\n"); - brcmf_detach(dev); goto fail; } @@ -1461,10 +1472,7 @@ static int brcmf_usb_resume(struct usb_interface *intf) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(USB, "Enter\n"); - if (!brcmf_attach(devinfo->dev)) - return brcmf_bus_start(&usb->dev); - - return 0; + return brcmf_usb_bus_setup(devinfo); } static int brcmf_usb_reset_resume(struct usb_interface *intf) @@ -1475,7 +1483,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) brcmf_dbg(USB, "Enter\n"); if (!brcmf_usb_fw_download(devinfo)) - return brcmf_usb_resume(intf); + return brcmf_usb_bus_setup(devinfo); return -EIO; } From 21c64c0738f5d0e95123c778c86150f3d242bc5a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 27 May 2014 12:56:20 +0200 Subject: [PATCH 0139/1983] brcmfmac: Add log of superspeed device detection to USB probe. Upstream-commit: de389a533b88de18b98d5f007996aff6f2885180 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 3c3ffff57a0e0d..8d679403518f18 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1417,7 +1417,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; - if (usb->speed == USB_SPEED_HIGH) + if (usb->speed == USB_SPEED_SUPER) + brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); + else if (usb->speed == USB_SPEED_HIGH) brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); else brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); From be1631cd88b6f92ab2e6c3d00a1414649b8d1411 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:21 +0200 Subject: [PATCH 0140/1983] brcmfmac: introduce asynchronous firmware loading Upstream-commit: c1416e77a6166766c847f03f21cca18d8ac54dd3 The driver needs firmware to be loaded to the device, which is done through the firmware class API. The synchronous call request_firmware() need root filesystem to be mounted and/or user-mode helper. These may not be avaliable on the moment it is called. Instead use request_firmware_nowait(). Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/firmware.c | 106 ++++++++++++++++++ .../wireless/brcm80211/brcmfmac/firmware.h | 15 ++- 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index ff5b86733d5a7f..b1525591f2e746 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -16,6 +16,7 @@ #include #include +#include #include #include "dhd_dbg.h" @@ -224,3 +225,108 @@ void brcmf_fw_nvram_free(void *nvram) kfree(nvram); } +struct brcmf_fw { + struct device *dev; + u16 flags; + const struct firmware *code; + const char *nvram_name; + void (*done)(struct device *dev, const struct firmware *fw, + void *nvram_image, u32 nvram_len); +}; + +static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + u32 nvram_length = 0; + void *nvram = NULL; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + + if (fw) { + nvram = brcmf_fw_nvram_strip(fw, &nvram_length); + release_firmware(fw); + if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + } + + fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); + kfree(fwctx); + return; + +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + if (fwctx->code) + release_firmware(fwctx->code); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + int ret; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw) + goto fail; + + /* only requested code so done here */ + if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + fwctx->code = fw; + ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, + fwctx->dev, GFP_KERNEL, fwctx, + brcmf_fw_request_nvram_done); + + if (!ret) + return; + + /* when nvram is optional call .done() callback here */ + if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + + /* failed nvram request */ + release_firmware(fw); +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + struct brcmf_fw *fwctx; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); + if (!fw_cb || !code) + return -EINVAL; + + if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) + return -EINVAL; + + fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); + if (!fwctx) + return -ENOMEM; + + fwctx->dev = dev; + fwctx->flags = flags; + fwctx->done = fw_cb; + if (flags & BRCMF_FW_REQUEST_NVRAM) + fwctx->nvram_name = nvram; + + return request_firmware_nowait(THIS_MODULE, true, code, dev, + GFP_KERNEL, fwctx, + brcmf_fw_request_code_done); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 127633bc242d52..25b64bd9760999 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -16,9 +16,22 @@ #ifndef BRCMFMAC_FIRMWARE_H #define BRCMFMAC_FIRMWARE_H +#define BRCMF_FW_REQUEST 0x000F +#define BRCMF_FW_REQUEST_NVRAM 0x0001 +#define BRCMF_FW_REQ_FLAGS 0x00F0 +#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); void brcmf_fw_nvram_free(void *nvram); - +/* + * Request firmware(s) asynchronously. When the asynchronous request + * fails it will not use the callback, but call device_release_driver() + * instead which will call the driver .remove() callback. + */ +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)); #endif /* BRCMFMAC_FIRMWARE_H */ From 5e554d85a19ecab6f3690198a1cfa9131704511d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:22 +0200 Subject: [PATCH 0141/1983] brcmfmac: use asynchronous firmware request in SDIO Upstream-commit: bd0e1b1d380efe713a612c91417b948e7ebea58d This patch adds use of asynchronous firmware request to the driver SDIO layer. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 271 ++++++++---------- 1 file changed, 124 insertions(+), 147 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index efb8c4febe6563..506ef0d4a52b95 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -632,43 +632,28 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; - -static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus, - enum brcmf_firmware_type type) +static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, + enum brcmf_firmware_type type) { - const struct firmware *fw; - const char *name; - int err, i; + int i; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { - if (brcmf_fwname_data[i].chipid == bus->ci->chip && - brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) { + if (brcmf_fwname_data[i].chipid == ci->chip && + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { switch (type) { case BRCMF_FIRMWARE_BIN: - name = brcmf_fwname_data[i].bin; - break; + return brcmf_fwname_data[i].bin; case BRCMF_FIRMWARE_NVRAM: - name = brcmf_fwname_data[i].nv; - break; + return brcmf_fwname_data[i].nv; default: brcmf_err("invalid firmware type (%d)\n", type); return NULL; } - goto found; } } brcmf_err("Unknown chipid %d [%d]\n", - bus->ci->chip, bus->ci->chiprev); + ci->chip, ci->chiprev); return NULL; - -found: - err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev); - if ((err) || (!fw)) { - brcmf_err("fail to request firmware %s (%d)\n", name, err); - return NULL; - } - - return fw; } static void pkt_align(struct sk_buff *p, int len, int align) @@ -3278,20 +3263,13 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, } static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, - const struct firmware *nv) + void *vars, u32 varsz) { - void *vars; - u32 varsz; int address; int err; brcmf_dbg(TRACE, "Enter\n"); - vars = brcmf_fw_nvram_strip(nv, &varsz); - - if (vars == NULL) - return -EINVAL; - address = bus->ci->ramsize - varsz + bus->ci->rambase; err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); if (err) @@ -3300,15 +3278,14 @@ static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) err = -EIO; - brcmf_fw_nvram_free(vars); - return err; } -static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, + const struct firmware *fw, + void *nvram, u32 nvlen) { int bcmerror = -EFAULT; - const struct firmware *fw; u32 rstvec; sdio_claim_host(bus->sdiodev->func[1]); @@ -3317,12 +3294,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) /* Keep arm in reset */ brcmf_chip_enter_download(bus->ci); - fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); - if (fw == NULL) { - bcmerror = -ENOENT; - goto err; - } - rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); @@ -3330,17 +3301,12 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) release_firmware(fw); if (bcmerror) { brcmf_err("dongle image file download failed\n"); + brcmf_fw_nvram_free(nvram); goto err; } - fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); - if (fw == NULL) { - bcmerror = -ENOENT; - goto err; - } - - bcmerror = brcmf_sdio_download_nvram(bus, fw); - release_firmware(fw); + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); + brcmf_fw_nvram_free(nvram); if (bcmerror) { brcmf_err("dongle nvram file download failed\n"); goto err; @@ -3490,97 +3456,6 @@ static int brcmf_sdio_bus_preinit(struct device *dev) return err; } -static int brcmf_sdio_bus_init(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - int err, ret = 0; - u8 saveclk; - - brcmf_dbg(TRACE, "Enter\n"); - - /* try to download image and nvram to the dongle */ - if (bus_if->state == BRCMF_BUS_DOWN) { - bus->alp_only = true; - err = brcmf_sdio_download_firmware(bus); - if (err) - return err; - bus->alp_only = false; - } - - if (!bus->sdiodev->bus_if->drvr) - return 0; - - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - - sdio_claim_host(bus->sdiodev->func[1]); - - /* Make sure backplane clock is on, needed to generate F2 interrupt */ - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - if (bus->clkstate != CLK_AVAIL) - goto exit; - - /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (!err) { - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); - } - if (err) { - brcmf_err("Failed to force clock for F2: err %d\n", err); - goto exit; - } - - /* Enable function 2 (frame transfers) */ - w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, - offsetof(struct sdpcmd_regs, tosbmailboxdata)); - err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]); - - - brcmf_dbg(INFO, "enable F2: err=%d\n", err); - - /* If F2 successfully enabled, set core and enable interrupts */ - if (!err) { - /* Set up the interrupt mask and enable interrupts */ - bus->hostintmask = HOSTINTMASK; - w_sdreg32(bus, bus->hostintmask, - offsetof(struct sdpcmd_regs, hostintmask)); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); - } else { - /* Disable F2 again */ - sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); - ret = -ENODEV; - } - - if (brcmf_chip_sr_capable(bus->ci)) { - brcmf_sdio_sr_init(bus); - } else { - /* Restore previous clock setting */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - saveclk, &err); - } - - if (ret == 0) { - ret = brcmf_sdiod_intr_register(bus->sdiodev); - if (ret != 0) - brcmf_err("intr register failed:%d\n", ret); - } - - /* If we didn't come up, turn off backplane clock */ - if (ret != 0) - brcmf_sdio_clkctl(bus, CLK_NONE, false); - -exit: - sdio_release_host(bus->sdiodev->func[1]); - - return ret; -} - void brcmf_sdio_isr(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -4026,6 +3901,108 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .gettxq = brcmf_sdio_bus_gettxq, }; +static void brcmf_sdio_firmware_callback(struct device *dev, + const struct firmware *code, + void *nvram, u32 nvram_len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + int err = 0; + u8 saveclk; + + brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); + + /* try to download image and nvram to the dongle */ + if (bus_if->state == BRCMF_BUS_DOWN) { + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; + } + + if (!bus_if->drvr) + return; + + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + + sdio_claim_host(sdiodev->func[1]); + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + if (bus->clkstate != CLK_AVAIL) + goto release; + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + brcmf_err("Failed to force clock for F2: err %d\n", err); + goto release; + } + + /* Enable function 2 (frame transfers) */ + w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, + offsetof(struct sdpcmd_regs, tosbmailboxdata)); + err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); + + + brcmf_dbg(INFO, "enable F2: err=%d\n", err); + + /* If F2 successfully enabled, set core and enable interrupts */ + if (!err) { + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + w_sdreg32(bus, bus->hostintmask, + offsetof(struct sdpcmd_regs, hostintmask)); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); + } else { + /* Disable F2 again */ + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + goto release; + } + + if (brcmf_chip_sr_capable(bus->ci)) { + brcmf_sdio_sr_init(bus); + } else { + /* Restore previous clock setting */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + saveclk, &err); + } + + if (err == 0) { + err = brcmf_sdiod_intr_register(sdiodev); + if (err != 0) + brcmf_err("intr register failed:%d\n", err); + } + + /* If we didn't come up, turn off backplane clock */ + if (err != 0) + brcmf_sdio_clkctl(bus, CLK_NONE, false); + + sdio_release_host(sdiodev->func[1]); + + err = brcmf_bus_start(dev); + if (err != 0) { + brcmf_err("dongle is not responding\n"); + goto fail; + } + return; + +release: + sdio_release_host(sdiodev->func[1]); +fail: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); + device_release_driver(dev); +} + struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { int ret; @@ -4149,14 +4126,14 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); - ret = brcmf_sdio_bus_init(sdiodev->dev); - if (ret) - goto fail; - - /* if firmware path present try to download and bring up bus */ - ret = brcmf_bus_start(bus->sdiodev->dev); + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, + brcmf_sdio_get_fwname(bus->ci, + BRCMF_FIRMWARE_BIN), + brcmf_sdio_get_fwname(bus->ci, + BRCMF_FIRMWARE_NVRAM), + brcmf_sdio_firmware_callback); if (ret != 0) { - brcmf_err("dongle is not responding\n"); + brcmf_err("async firmware request failed: %d\n", ret); goto fail; } From bc90e0aea0460f7af48525862d946c380113eab5 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:23 +0200 Subject: [PATCH 0142/1983] brcmfmac: use asynchronous firmware request in USB Upstream-commit: 5b8045d484d0ef77d6aa9444023220c5671fa3fe This patch adds use of asynchronous firmware request to the driver USB layer. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 98 +++++++++++++------ 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 8d679403518f18..21335e2ca0e6e9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -25,6 +25,7 @@ #include #include +#include "firmware.h" #include "usb_rdl.h" #include "usb.h" @@ -87,7 +88,7 @@ struct brcmf_usbdev_info { struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; - u8 *image; /* buffer for combine fw and nvram */ + const u8 *image; /* buffer for combine fw and nvram */ int image_len; struct usb_device *usbdev; @@ -1021,7 +1022,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) } err = brcmf_usb_dlstart(devinfo, - devinfo->image, devinfo->image_len); + (u8 *)devinfo->image, devinfo->image_len); if (err == 0) err = brcmf_usb_dlrun(devinfo); return err; @@ -1080,7 +1081,22 @@ static int check_file(const u8 *headers) return -1; } -static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) +static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) +{ + switch (devinfo->bus_pub.devid) { + case 43143: + return BRCMF_USB_43143_FW_NAME; + case 43235: + case 43236: + case 43238: + return BRCMF_USB_43236_FW_NAME; + case 43242: + return BRCMF_USB_43242_FW_NAME; + default: + return NULL; + } +} +static __used int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) { s8 *fwname; const struct firmware *fw; @@ -1202,16 +1218,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, goto error; } - if (!brcmf_usb_dlneeded(devinfo)) - return &devinfo->bus_pub; - - brcmf_dbg(USB, "Start fw downloading\n"); - if (brcmf_usb_get_fw(devinfo)) - goto error; - - if (brcmf_usb_fw_download(devinfo)) - goto error; - return &devinfo->bus_pub; error: @@ -1252,12 +1258,47 @@ static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) return ret; } +static void brcmf_usb_probe_phase2(struct device *dev, + const struct firmware *fw, + void *nvram, u32 nvlen) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_usbdev_info *devinfo; + int ret; + + brcmf_dbg(USB, "Start fw downloading\n"); + ret = check_file(fw->data); + if (ret < 0) { + brcmf_err("invalid firmware\n"); + release_firmware(fw); + goto error; + } + + devinfo = bus->bus_priv.usb->devinfo; + devinfo->image = fw->data; + devinfo->image_len = fw->size; + + ret = brcmf_usb_fw_download(devinfo); + release_firmware(fw); + if (ret) + goto error; + + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto error; + + return; +error: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); + device_release_driver(dev); +} + static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) { struct brcmf_bus *bus = NULL; struct brcmf_usbdev *bus_pub = NULL; - int ret; struct device *dev = devinfo->dev; + int ret; brcmf_dbg(USB, "Enter\n"); bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); @@ -1280,13 +1321,16 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; - ret = brcmf_usb_bus_setup(devinfo); - if (ret) { - brcmf_err("dongle is not responding\n"); - goto fail; + if (!brcmf_usb_dlneeded(devinfo)) { + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto fail; } - + /* request firmware here */ + brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); return 0; + fail: /* Release resources in reverse order */ kfree(bus); @@ -1409,12 +1453,8 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) } /* Allocate interrupt URB and data buffer */ - /* RNDIS says 8-byte intr, our old drivers used 4-byte */ - if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16)) - devinfo->intr_size = 8; - else - devinfo->intr_size = 4; - + devinfo->intr_size = + le16_to_cpu(IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize); devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; if (usb->speed == USB_SPEED_SUPER) @@ -1481,13 +1521,11 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - brcmf_dbg(USB, "Enter\n"); - if (!brcmf_usb_fw_download(devinfo)) - return brcmf_usb_bus_setup(devinfo); - - return -EIO; + return brcmf_fw_get_firmwares(&usb->dev, 0, + brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); } #define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c From 217a32de631e8853678a4514ba54b2bf694255cc Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:24 +0200 Subject: [PATCH 0143/1983] brcmfmac: make brcmf_fw_nvram_strip() static Upstream-commit: 71ded72a2bcd8920e85122f54c230684d6f77119 The function brcmf_fw_nvram_strip() is no longer called so it does not need to be exposed. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/firmware.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index b1525591f2e746..7b7d237c1ddb27 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -188,7 +188,7 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. * End of buffer is completed with token identifying length of buffer. */ -void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) { struct nvram_parser nvp; u32 pad; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 25b64bd9760999..6431bfd7afffc6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -21,7 +21,6 @@ #define BRCMF_FW_REQ_FLAGS 0x00F0 #define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 -void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length); void brcmf_fw_nvram_free(void *nvram); /* * Request firmware(s) asynchronously. When the asynchronous request From 04127a004be5eeb6843cf0c25fe7136c731ada8e Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Tue, 27 May 2014 12:56:26 +0200 Subject: [PATCH 0144/1983] brcmfmac: Increase max buffer size for receiving control message from dongle Upstream-commit: 7dd3abc14f94bc1226aaa5075c73928d6b1d544c The max buffer size for receiving control message from dongle needs to be increased considering possible block padding. Otherwise some big control message can't be received due to buffer overrun check. Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 506ef0d4a52b95..8fa0dbbbda72b6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4086,8 +4086,13 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) goto fail; } + /* Query the F2 block size, set roundup accordingly */ + bus->blocksize = bus->sdiodev->func[2]->cur_blksize; + bus->roundup = min(max_roundup, bus->blocksize); + /* Allocate buffers */ if (bus->sdiodev->bus_if->maxctl) { + bus->sdiodev->bus_if->maxctl += bus->roundup; bus->rxblen = roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), ALIGNMENT) + bus->head_align; @@ -4115,10 +4120,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->idletime = BRCMF_IDLE_INTERVAL; bus->idleclock = BRCMF_IDLE_ACTIVE; - /* Query the F2 block size, set roundup accordingly */ - bus->blocksize = bus->sdiodev->func[2]->cur_blksize; - bus->roundup = min(max_roundup, bus->blocksize); - /* SR state */ bus->sleeping = false; bus->sr_enabled = false; From 00ca46311f65b347eb3bb61dfa9e417144ce123f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 27 May 2014 12:56:27 +0200 Subject: [PATCH 0145/1983] brcmfmac: Remove interrupt endpoint usage from USB driver. Upstream-commit: c40edfc042ecae11437b2cb260f1eb17730f8a0f The USB bus driver always configured an USB intr EP urb. The driver did not use the result at all and with newer firmware it is causing continues errors on this EP. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 78 +------------------ 1 file changed, 1 insertion(+), 77 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 21335e2ca0e6e9..fa440ba1b30070 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -64,11 +64,6 @@ struct brcmf_usb_image { }; static struct list_head fw_image_list; -struct intr_transfer_buf { - u32 notification; - u32 reserved; -}; - struct brcmf_usbdev_info { struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ spinlock_t qlock; @@ -76,7 +71,7 @@ struct brcmf_usbdev_info { struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; - uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; + uint rx_pipe, tx_pipe, rx_pipe2; int rx_low_watermark; int tx_low_watermark; @@ -105,10 +100,6 @@ struct brcmf_usbdev_info { ulong ctl_op; struct urb *bulk_urb; /* used for FW download */ - struct urb *intr_urb; /* URB for interrupt endpoint */ - int intr_size; /* Size of interrupt message */ - int interval; /* Interrupt polling interval */ - struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */ }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, @@ -532,39 +523,6 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) } } -static void -brcmf_usb_intr_complete(struct urb *urb) -{ - struct brcmf_usbdev_info *devinfo = - (struct brcmf_usbdev_info *)urb->context; - int err; - - brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); - - if (devinfo == NULL) - return; - - if (unlikely(urb->status)) { - if (urb->status == -ENOENT || - urb->status == -ESHUTDOWN || - urb->status == -ENODEV) { - brcmf_usb_state_change(devinfo, - BRCMFMAC_USB_STATE_DOWN); - } - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) { - brcmf_err("intr cb when DBUS down, ignoring\n"); - return; - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { - err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); - if (err) - brcmf_err("usb_submit_urb, err=%d\n", err); - } -} - static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); @@ -620,7 +578,6 @@ static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); u16 ifnum; - int ret; brcmf_dbg(USB, "Enter\n"); if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) @@ -629,23 +586,6 @@ static int brcmf_usb_up(struct device *dev) /* Success, indicate devinfo is fully up */ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP); - if (devinfo->intr_urb) { - usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev, - devinfo->intr_pipe, - &devinfo->intr, - devinfo->intr_size, - (usb_complete_t)brcmf_usb_intr_complete, - devinfo, - devinfo->interval); - - ret = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); - if (ret) { - brcmf_err("USB_SUBMIT_URB failed with status %d\n", - ret); - return -EINVAL; - } - } - if (devinfo->ctl_urb) { devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); @@ -682,8 +622,6 @@ static void brcmf_usb_down(struct device *dev) return; brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); - if (devinfo->intr_urb) - usb_kill_urb(devinfo->intr_urb); if (devinfo->ctl_urb) usb_kill_urb(devinfo->ctl_urb); @@ -1037,7 +975,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) brcmf_usb_free_q(&devinfo->rx_freeq, false); brcmf_usb_free_q(&devinfo->tx_freeq, false); - usb_free_urb(devinfo->intr_urb); usb_free_urb(devinfo->ctl_urb); usb_free_urb(devinfo->bulk_urb); @@ -1202,11 +1139,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, goto error; devinfo->tx_freecount = ntxq; - devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!devinfo->intr_urb) { - brcmf_err("usb_alloc_urb (intr) failed\n"); - goto error; - } devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->ctl_urb) { brcmf_err("usb_alloc_urb (ctl) failed\n"); @@ -1418,9 +1350,6 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto fail; } - endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num); - devinfo->rx_pipe = 0; devinfo->rx_pipe2 = 0; devinfo->tx_pipe = 0; @@ -1452,11 +1381,6 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) } } - /* Allocate interrupt URB and data buffer */ - devinfo->intr_size = - le16_to_cpu(IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize); - devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; - if (usb->speed == USB_SPEED_SUPER) brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); else if (usb->speed == USB_SPEED_HIGH) From fb0a55a7a8dfee89c987c7ba70243ca061db9dbf Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 27 May 2014 12:56:28 +0200 Subject: [PATCH 0146/1983] brcmfmac: remove firmware list from USB driver Upstream-commit: 52f98a57d8c162feac17adac8b5e31707d2d42fc The USB driver was using a list for firmware info that was used in suspend/resume scenario. Now that brcmfmac is using the asynchronous firmware request this is no longer needed. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 76 ------------------- 1 file changed, 76 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index fa440ba1b30070..6db51a666f619a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -62,7 +62,6 @@ struct brcmf_usb_image { u8 *image; int image_len; }; -static struct list_head fw_image_list; struct brcmf_usbdev_info { struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ @@ -1033,69 +1032,6 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) return NULL; } } -static __used int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) -{ - s8 *fwname; - const struct firmware *fw; - struct brcmf_usb_image *fw_image; - int err; - - brcmf_dbg(USB, "Enter\n"); - switch (devinfo->bus_pub.devid) { - case 43143: - fwname = BRCMF_USB_43143_FW_NAME; - break; - case 43235: - case 43236: - case 43238: - fwname = BRCMF_USB_43236_FW_NAME; - break; - case 43242: - fwname = BRCMF_USB_43242_FW_NAME; - break; - default: - return -EINVAL; - break; - } - brcmf_dbg(USB, "Loading FW %s\n", fwname); - list_for_each_entry(fw_image, &fw_image_list, list) { - if (fw_image->fwname == fwname) { - devinfo->image = fw_image->image; - devinfo->image_len = fw_image->image_len; - return 0; - } - } - /* fw image not yet loaded. Load it now and add to list */ - err = request_firmware(&fw, fwname, devinfo->dev); - if (!fw) { - brcmf_err("fail to request firmware %s\n", fwname); - return err; - } - if (check_file(fw->data) < 0) { - brcmf_err("invalid firmware %s\n", fwname); - return -EINVAL; - } - - fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC); - if (!fw_image) - return -ENOMEM; - INIT_LIST_HEAD(&fw_image->list); - list_add_tail(&fw_image->list, &fw_image_list); - fw_image->fwname = fwname; - fw_image->image = vmalloc(fw->size); - if (!fw_image->image) - return -ENOMEM; - - memcpy(fw_image->image, fw->data, fw->size); - fw_image->image_len = fw->size; - - release_firmware(fw); - - devinfo->image = fw_image->image; - devinfo->image_len = fw_image->image_len; - - return 0; -} static @@ -1484,16 +1420,6 @@ static struct usb_driver brcmf_usbdrvr = { .disable_hub_initiated_lpm = 1, }; -static void brcmf_release_fw(struct list_head *q) -{ - struct brcmf_usb_image *fw_image, *next; - - list_for_each_entry_safe(fw_image, next, q, list) { - vfree(fw_image->image); - list_del_init(&fw_image->list); - } -} - static int brcmf_usb_reset_device(struct device *dev, void *notused) { /* device past is the usb interface so we @@ -1512,12 +1438,10 @@ void brcmf_usb_exit(void) ret = driver_for_each_device(drv, NULL, NULL, brcmf_usb_reset_device); usb_deregister(&brcmf_usbdrvr); - brcmf_release_fw(&fw_image_list); } void brcmf_usb_register(void) { brcmf_dbg(USB, "Enter\n"); - INIT_LIST_HEAD(&fw_image_list); usb_register(&brcmf_usbdrvr); } From 2dddd162b159a2831ed0af522a10c3eda6c7f461 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 31 May 2014 10:14:06 -0300 Subject: [PATCH 0147/1983] net: wireless: Remove useless return variables Upstream-commit: 12f32370068d539bdc2fc9cf22c37dbdc2bae158 This patch remove variables that are initialized with a constant, are never updated, and are only used as parameter of return. Return the constant instead of using a variable. wl_cfg80211.c verified by compilation only. phy/phy_cmn.c unverified. The coccinelle script that find and fixes this issue is: // @@ type T; constant C; identifier ret; @@ - T ret = C; ... when != ret when strict return - ret + C ; // Signed-off-by: Peter Senna Tschudin Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 18 ++++++------------ .../wireless/brcm80211/brcmsmac/phy/phy_cmn.c | 6 +----- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 4d0166a9611e76..886740b81a76ef 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1331,7 +1331,6 @@ static s32 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -1341,7 +1340,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) brcmf_dbg(TRACE, "Exit\n"); - return err; + return 0; } static s32 brcmf_set_wpa_version(struct net_device *ndev, @@ -2388,7 +2387,6 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, struct cfg80211_bss *bss; struct ieee80211_supported_band *band; struct brcmu_chan ch; - s32 err = 0; u16 channel; u32 freq; u16 notify_capability; @@ -2438,7 +2436,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, cfg80211_put_bss(wiphy, bss); - return err; + return 0; } static struct brcmf_bss_info_le * @@ -2690,7 +2688,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; s32 status; - s32 err = 0; struct brcmf_escan_result_le *escan_result_le; struct brcmf_bss_info_le *bss_info_le; struct brcmf_bss_info_le *bss = NULL; @@ -2781,7 +2778,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, status); } exit: - return err; + return 0; } static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) @@ -3507,7 +3504,6 @@ static s32 brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, struct parsed_vndr_ies *vndr_ies) { - s32 err = 0; struct brcmf_vs_tlv *vndrie; struct brcmf_tlv *ie; struct parsed_vndr_ie_info *parsed_info; @@ -3560,7 +3556,7 @@ brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + TLV_HDR_LEN); } - return err; + return 0; } static u32 @@ -4650,7 +4646,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -4676,7 +4671,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, completed ? "succeeded" : "failed"); } brcmf_dbg(TRACE, "Exit\n"); - return err; + return 0; } static s32 @@ -4768,7 +4763,6 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 err = 0; u32 event = e->event_code; u32 status = e->status; @@ -4779,7 +4773,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp, brcmf_bss_connect_done(cfg, ifp->ndev, e, true); } - return err; + return 0; } static s32 diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c index b0fd807f2b2b6b..57ecc05802e965 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c @@ -1538,11 +1538,7 @@ static s8 wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band, u8 rate) { - s8 offset = 0; - - if (!pi->user_txpwr_at_rfport) - return offset; - return offset; + return 0; } void wlc_phy_txpower_recalc_target(struct brcms_phy *pi) From f446ff9ab934a179fecd06e431ad04c4f3722f6a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 20 Jun 2014 22:19:25 +0200 Subject: [PATCH 0148/1983] brcmfmac: assign chip id and rev in bus interface after brcmf_usb_dlneeded Upstream-commit: c6bff5449fa78e1d524bb5b67b8ab82faf835bff The function brcmf_usb_dlneeded() queries the device to obtain the chip id and revision. So assigning these in bus interface before the call resulted in chip id and revision being zero. This was introduced by: commit 5b8045d484d0ef77d6aa9444023220c5671fa3fe Author: Arend van Spriel Date: Tue May 27 12:56:23 2014 +0200 brcmfmac: use asynchronous firmware request in USB Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 6db51a666f619a..d06fcb05adf251 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1184,8 +1184,6 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->bus_priv.usb = bus_pub; dev_set_drvdata(dev, bus); bus->ops = &brcmf_usb_bus_ops; - bus->chip = bus_pub->devid; - bus->chiprev = bus_pub->chiprev; bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; @@ -1194,6 +1192,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) if (ret) goto fail; } + bus->chip = bus_pub->devid; + bus->chiprev = bus_pub->chiprev; + /* request firmware here */ brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, brcmf_usb_probe_phase2); From cb4d65670fbf2e9d394af210717520d818359be7 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Sun, 22 Jun 2014 20:50:40 +0200 Subject: [PATCH 0149/1983] net/wireless/brcm80211/brcmfmac: Make return type and name reflect actual semantics Upstream-commit: e843bb199ba58ce5d1364d4c82fcf6975f08eec2 Applying ++ to a bool is equivalent to setting it true, regardless of its initial value (bools are not uint1_t). Hence the function wl_get_vif_state_all can only ever return true/false. The only in-tree caller uses its return value as a boolean. So update its return type, and since the list traversal and bit testing have no side effects, just return true immediately. Its return value tells if any vif is in the specified state, so also rename it to brcmf_get_vif_state_any. Reviewed-by: Arend van Spriel Signed-off-by: Rasmus Villemoes Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 7 +++---- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index f3445ac627e48d..588fdbdb325539 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -708,7 +708,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans, active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS; else if (num_chans == AF_PEER_SEARCH_CNT) active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS; - else if (wl_get_vif_state_all(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED)) + else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED)) active = -1; else active = P2PAPI_SCAN_DWELL_TIME_MS; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 886740b81a76ef..8d8534ac4e91e8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5619,16 +5619,15 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) return wdev->iftype; } -u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state) +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state) { struct brcmf_cfg80211_vif *vif; - bool result = 0; list_for_each_entry(vif, &cfg->vif_list, list) { if (test_bit(state, &vif->sme_state)) - result++; + return true; } - return result; + return false; } static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 283c525a44f759..f9fb10998e7960 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -477,7 +477,7 @@ const struct brcmf_tlv * brcmf_parse_tlvs(const void *buf, int buflen, uint key); u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch); -u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state); +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state); void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *vif); bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); From 5a801c20910a831ec5cad81c8c2707d3685104d9 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 21 Jun 2014 12:11:12 +0200 Subject: [PATCH 0150/1983] brcmfmac: Add 43569 USB support. Upstream-commit: b6fd7fd23ef2fe32a6cf64c02211e48d79766b58 Added usb device id for the new device 43569 to the list of supported devices. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index d06fcb05adf251..370b31897e256d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -55,6 +55,7 @@ #define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" #define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" #define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" +#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" struct brcmf_usb_image { struct list_head list; @@ -928,6 +929,8 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev) return (chiprev == 3); case 43242: return true; + case 43569: + return true; default: break; } @@ -1028,6 +1031,8 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) return BRCMF_USB_43236_FW_NAME; case 43242: return BRCMF_USB_43242_FW_NAME; + case 43569: + return BRCMF_USB_43569_FW_NAME; default: return NULL; } @@ -1230,7 +1235,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) u8 endpoint_num; struct brcmf_usbdev_info *devinfo; - brcmf_dbg(USB, "Enter\n"); + brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC); if (devinfo == NULL) @@ -1393,12 +1398,14 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) #define BRCMF_USB_DEVICE_ID_43143 0xbd1e #define BRCMF_USB_DEVICE_ID_43236 0xbd17 #define BRCMF_USB_DEVICE_ID_43242 0xbd1f +#define BRCMF_USB_DEVICE_ID_43569 0xbd27 #define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc static struct usb_device_id brcmf_usb_devid_table[] = { { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) }, { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) }, { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) }, + { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) }, /* special entry for device with firmware loaded and running */ { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) }, { } @@ -1408,6 +1415,7 @@ MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); +MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME); static struct usb_driver brcmf_usbdrvr = { .name = KBUILD_MODNAME, From 59bcf23c9ea1108a5e84b8b6893ea48c1c3e5c99 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 21 Jun 2014 12:11:13 +0200 Subject: [PATCH 0151/1983] brcmfmac: Add USB device 43566 to supported devices. Upstream-commit: 457cfabb99461b9f8d6bf32396f9c14445b7155a Add the USB 43566 device to the supported devices list. The 43566 is a WiFi-only variant of the 43569. It uses the same FW as 43569. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 370b31897e256d..c0f98e058e3dca 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -929,6 +929,7 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev) return (chiprev == 3); case 43242: return true; + case 43566: case 43569: return true; default: @@ -1031,6 +1032,7 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) return BRCMF_USB_43236_FW_NAME; case 43242: return BRCMF_USB_43242_FW_NAME; + case 43566: case 43569: return BRCMF_USB_43569_FW_NAME; default: From 05fc67160cf701e94e1a92ca09362d3073f63ccf Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 21 Jun 2014 12:11:14 +0200 Subject: [PATCH 0152/1983] brcmfmac: clear ht info during attach phase Upstream-commit: d83f8face594340551b2c57ae95acfe70ae444f8 After updating 2G bandwidth capability clear ht info. This will be properly set upon calling brcmf_update_wiphy_bands(). Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 8d8534ac4e91e8..708d2621e8e9cb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5051,6 +5051,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, err = brcmf_fil_iovar_int_set(ifp, "obss_coex", BRCMF_OBSS_COEX_AUTO); } + /* clear for now and rely on update later */ + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { From 5fab1fbb4d1ab5b874698f3ef6eadf25bf3bf8ac Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 21 Jun 2014 12:11:15 +0200 Subject: [PATCH 0153/1983] brcmfmac: Change USB probe routine to support Composite USB Upstream-commit: 51c7f5eddd0f2e8eb6b49a3025b04148cf3b3912 Some of the USB devices also have Bluetooth inside. These devices can with specific firmware result in a composite USB device. This change will update the driver such that it will also accept the correct interface of composite devices. It is backward compatible with old non-composite USB fw. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 204 ++++++++---------- 1 file changed, 87 insertions(+), 117 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index c0f98e058e3dca..b732a99e402cb8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -29,33 +29,24 @@ #include "usb_rdl.h" #include "usb.h" -#define IOCTL_RESP_TIMEOUT 2000 +#define IOCTL_RESP_TIMEOUT 2000 #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ #define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 #define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ -#define BRCMF_USB_NRXQ 50 -#define BRCMF_USB_NTXQ 50 +#define BRCMF_USB_NRXQ 50 +#define BRCMF_USB_NTXQ 50 -#define CONFIGDESC(usb) (&((usb)->actconfig)->desc) -#define IFPTR(usb, idx) ((usb)->actconfig->interface[(idx)]) -#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0]) -#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc -#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[(ep)]).desc +#define BRCMF_USB_CBCTL_WRITE 0 +#define BRCMF_USB_CBCTL_READ 1 +#define BRCMF_USB_MAX_PKT_SIZE 1600 -#define CONTROL_IF 0 -#define BULK_IF 0 - -#define BRCMF_USB_CBCTL_WRITE 0 -#define BRCMF_USB_CBCTL_READ 1 -#define BRCMF_USB_MAX_PKT_SIZE 1600 - -#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" -#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" -#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" -#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" +#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin" +#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" +#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" +#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" struct brcmf_usb_image { struct list_head list; @@ -71,7 +62,7 @@ struct brcmf_usbdev_info { struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; - uint rx_pipe, tx_pipe, rx_pipe2; + uint rx_pipe, tx_pipe; int rx_low_watermark; int tx_low_watermark; @@ -98,6 +89,7 @@ struct brcmf_usbdev_info { int ctl_completed; wait_queue_head_t ioctl_resp_wait; ulong ctl_op; + u8 ifnum; struct urb *bulk_urb; /* used for FW download */ }; @@ -577,7 +569,6 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - u16 ifnum; brcmf_dbg(USB, "Enter\n"); if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) @@ -590,21 +581,19 @@ static int brcmf_usb_up(struct device *dev) devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); - ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber; - /* CTL Write */ devinfo->ctl_write.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_write.bRequest = 0; devinfo->ctl_write.wValue = cpu_to_le16(0); - devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum); + devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum); /* CTL Read */ devinfo->ctl_read.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; devinfo->ctl_read.bRequest = 1; devinfo->ctl_read.wValue = cpu_to_le16(0); - devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum); + devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum); } brcmf_usb_rx_fill_all(devinfo); return 0; @@ -643,19 +632,19 @@ brcmf_usb_sync_complete(struct urb *urb) brcmf_usb_ioctl_resp_wake(devinfo); } -static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, - void *buffer, int buflen) +static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, + void *buffer, int buflen) { - int ret = 0; + int ret; char *tmpbuf; u16 size; if ((!devinfo) || (devinfo->ctl_urb == NULL)) - return false; + return -EINVAL; tmpbuf = kmalloc(buflen, GFP_ATOMIC); if (!tmpbuf) - return false; + return -ENOMEM; size = buflen; devinfo->ctl_urb->transfer_buffer_length = size; @@ -676,14 +665,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); if (ret < 0) { brcmf_err("usb_submit_urb failed %d\n", ret); - kfree(tmpbuf); - return false; + goto finalize; } - ret = brcmf_usb_ioctl_resp_wait(devinfo); - memcpy(buffer, tmpbuf, buflen); - kfree(tmpbuf); + if (!brcmf_usb_ioctl_resp_wait(devinfo)) + ret = -ETIMEDOUT; + else + memcpy(buffer, tmpbuf, buflen); +finalize: + kfree(tmpbuf); return ret; } @@ -725,6 +716,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) { struct bootrom_id_le id; u32 loop_cnt; + int err; brcmf_dbg(USB, "Enter\n"); @@ -733,7 +725,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT); loop_cnt++; id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ - brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); + err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); + if ((err) && (err != -ETIMEDOUT)) + return err; if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) break; } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT); @@ -795,8 +789,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) } /* 1) Prepare USB boot loader for runtime image */ - brcmf_usb_dl_cmd(devinfo, DL_START, &state, - sizeof(struct rdl_state_le)); + brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); rdlstate = le32_to_cpu(state.state); rdlbytes = le32_to_cpu(state.bytes); @@ -840,10 +833,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) dlpos += sendlen; sent += sendlen; } - if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, - sizeof(struct rdl_state_le))) { - brcmf_err("DL_GETSTATE Failed xxxx\n"); - err = -EINVAL; + err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, + sizeof(state)); + if (err) { + brcmf_err("DL_GETSTATE Failed\n"); goto fail; } @@ -899,13 +892,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) return -EINVAL; /* Check we are runnable */ - brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, - sizeof(struct rdl_state_le)); + state.state = 0; + brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state)); /* Start the image */ if (state.state == cpu_to_le32(DL_RUNNABLE)) { - if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state, - sizeof(struct rdl_state_le))) + if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state))) return -ENODEV; if (brcmf_usb_resetcfg(devinfo)) return -ENODEV; @@ -1229,13 +1221,13 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) static int brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - int ep; - struct usb_endpoint_descriptor *endpoint; - int ret = 0; struct usb_device *usb = interface_to_usbdev(intf); - int num_of_eps; - u8 endpoint_num; struct brcmf_usbdev_info *devinfo; + struct usb_interface_descriptor *desc; + struct usb_endpoint_descriptor *endpoint; + int ret = 0; + u32 num_of_eps; + u8 endpoint_num, ep; brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct); @@ -1245,92 +1237,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->usbdev = usb; devinfo->dev = &usb->dev; - usb_set_intfdata(intf, devinfo); /* Check that the device supports only one configuration */ if (usb->descriptor.bNumConfigurations != 1) { - ret = -1; - goto fail; - } - - if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) { - ret = -1; - goto fail; - } - - /* - * Only the BDC interface configuration is supported: - * Device class: USB_CLASS_VENDOR_SPEC - * if0 class: USB_CLASS_VENDOR_SPEC - * if0/ep0: control - * if0/ep1: bulk in - * if0/ep2: bulk out (ok if swapped with bulk in) - */ - if (CONFIGDESC(usb)->bNumInterfaces != 1) { - ret = -1; + brcmf_err("Number of configurations: %d not supported\n", + usb->descriptor.bNumConfigurations); + ret = -ENODEV; goto fail; } - /* Check interface */ - if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC || - IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 || - IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) { - brcmf_err("invalid control interface: class %d, subclass %d, proto %d\n", - IFDESC(usb, CONTROL_IF).bInterfaceClass, - IFDESC(usb, CONTROL_IF).bInterfaceSubClass, - IFDESC(usb, CONTROL_IF).bInterfaceProtocol); - ret = -1; + if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) && + (usb->descriptor.bDeviceClass != USB_CLASS_MISC) && + (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) { + brcmf_err("Device class: 0x%x not supported\n", + usb->descriptor.bDeviceClass); + ret = -ENODEV; goto fail; } - /* Check control endpoint */ - endpoint = &IFEPDESC(usb, CONTROL_IF, 0); - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - != USB_ENDPOINT_XFER_INT) { - brcmf_err("invalid control endpoint %d\n", - endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - ret = -1; + desc = &intf->altsetting[0].desc; + if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + (desc->bInterfaceSubClass != 2) || + (desc->bInterfaceProtocol != 0xff)) { + brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n", + desc->bInterfaceNumber, desc->bInterfaceClass, + desc->bInterfaceSubClass, desc->bInterfaceProtocol); + ret = -ENODEV; goto fail; } - devinfo->rx_pipe = 0; - devinfo->rx_pipe2 = 0; - devinfo->tx_pipe = 0; - num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1; - - /* Check data endpoints and get pipes */ - for (ep = 1; ep <= num_of_eps; ep++) { - endpoint = &IFEPDESC(usb, BULK_IF, ep); - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != - USB_ENDPOINT_XFER_BULK) { - brcmf_err("invalid data endpoint %d\n", ep); - ret = -1; - goto fail; - } - - endpoint_num = endpoint->bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - == USB_DIR_IN) { - if (!devinfo->rx_pipe) { + num_of_eps = desc->bNumEndpoints; + for (ep = 0; ep < num_of_eps; ep++) { + endpoint = &intf->altsetting[0].endpoint[ep].desc; + endpoint_num = usb_endpoint_num(endpoint); + if (!usb_endpoint_xfer_bulk(endpoint)) + continue; + if (usb_endpoint_dir_in(endpoint)) { + if (!devinfo->rx_pipe) devinfo->rx_pipe = usb_rcvbulkpipe(usb, endpoint_num); - } else { - devinfo->rx_pipe2 = - usb_rcvbulkpipe(usb, endpoint_num); - } } else { - devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num); + if (!devinfo->tx_pipe) + devinfo->tx_pipe = + usb_sndbulkpipe(usb, endpoint_num); } } + if (devinfo->rx_pipe == 0) { + brcmf_err("No RX (in) Bulk EP found\n"); + ret = -ENODEV; + goto fail; + } + if (devinfo->tx_pipe == 0) { + brcmf_err("No TX (out) Bulk EP found\n"); + ret = -ENODEV; + goto fail; + } + + devinfo->ifnum = desc->bInterfaceNumber; if (usb->speed == USB_SPEED_SUPER) - brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n"); else if (usb->speed == USB_SPEED_HIGH) - brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n"); else - brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n"); ret = brcmf_usb_probe_cb(devinfo); if (ret) @@ -1340,11 +1311,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) return 0; fail: - brcmf_err("failed with errno %d\n", ret); kfree(devinfo); usb_set_intfdata(intf, NULL); return ret; - } static void @@ -1389,6 +1358,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + brcmf_dbg(USB, "Enter\n"); return brcmf_fw_get_firmwares(&usb->dev, 0, From 7595246d4060c586c671c8a3c7b4cc7aef3c76c0 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 21 Jun 2014 12:11:16 +0200 Subject: [PATCH 0154/1983] brcmfmac: replace cfg80211 testmode with vendor command Upstream-commit: 1bacb0487d0ed914a9fd08fa7c24fa6c4b27cacc Passing a pointer from user space and using it directly in driver is not a preferable behavior. Switch to cfg80211 vendor mode for dongle command for better cross platform compatibility. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 3 +- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 10 -- .../net/wireless/brcm80211/brcmfmac/vendor.c | 115 ++++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/vendor.h | 64 ++++++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 37 ++---- 5 files changed, 188 insertions(+), 41 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/vendor.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/vendor.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 98e67c18f27647..4cffb2ee367381 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -34,7 +34,8 @@ brcmfmac-objs += \ dhd_common.o \ dhd_linux.o \ firmware.o \ - btcoex.o + btcoex.o \ + vendor.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ bcmsdh.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 16f9ab2568a808..a8998eb60d2216 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -49,16 +49,6 @@ */ #define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 -/* Bus independent dongle command */ -struct brcmf_dcmd { - uint cmd; /* common dongle cmd definition */ - void *buf; /* pointer to user buffer */ - uint len; /* length of user buffer */ - u8 set; /* get or set request (optional) */ - uint used; /* bytes read or written (optional) */ - uint needed; /* bytes needed (optional) */ -}; - /** * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c new file mode 100644 index 00000000000000..5960d827508c9d --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include "fwil_types.h" +#include "dhd.h" +#include "p2p.h" +#include "dhd_dbg.h" +#include "wl_cfg80211.h" +#include "vendor.h" +#include "fwil.h" + +static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; + struct sk_buff *reply; + int ret, payload, ret_len; + void *dcmd_buf = NULL, *wr_pointer; + u16 msglen, maxmsglen = PAGE_SIZE - 0x100; + + brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, + cmdhdr->len); + + len -= sizeof(struct brcmf_vndr_dcmd_hdr); + ret_len = cmdhdr->len; + if (ret_len > 0 || len > 0) { + if (len > BRCMF_DCMD_MAXLEN) { + brcmf_err("oversize input buffer %d\n", len); + len = BRCMF_DCMD_MAXLEN; + } + if (ret_len > BRCMF_DCMD_MAXLEN) { + brcmf_err("oversize return buffer %d\n", ret_len); + ret_len = BRCMF_DCMD_MAXLEN; + } + payload = max(ret_len, len) + 1; + dcmd_buf = vzalloc(payload); + if (NULL == dcmd_buf) + return -ENOMEM; + + memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len); + *(char *)(dcmd_buf + len) = '\0'; + } + + if (cmdhdr->set) + ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd, + dcmd_buf, ret_len); + else + ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd, + dcmd_buf, ret_len); + if (ret != 0) + goto exit; + + wr_pointer = dcmd_buf; + while (ret_len > 0) { + msglen = ret_len > maxmsglen ? maxmsglen : ret_len; + ret_len -= msglen; + payload = msglen + sizeof(msglen); + reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload); + if (NULL == reply) { + ret = -ENOMEM; + break; + } + + if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) || + nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) { + kfree_skb(reply); + ret = -ENOBUFS; + break; + } + + ret = cfg80211_vendor_cmd_reply(reply); + if (ret) + break; + + wr_pointer += msglen; + } + +exit: + vfree(dcmd_buf); + + return ret; +} + +const struct wiphy_vendor_command brcmf_vendor_cmds[] = { + { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_CMDS_DCMD + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler + }, +}; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h new file mode 100644 index 00000000000000..061b7bfa2e1cf5 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _vendor_h_ +#define _vendor_h_ + +#define BROADCOM_OUI 0x001018 + +enum brcmf_vndr_cmds { + BRCMF_VNDR_CMDS_UNSPEC, + BRCMF_VNDR_CMDS_DCMD, + BRCMF_VNDR_CMDS_LAST +}; + +/** + * enum brcmf_nlattrs - nl80211 message attributes + * + * @BRCMF_NLATTR_LEN: message body length + * @BRCMF_NLATTR_DATA: message body + */ +enum brcmf_nlattrs { + BRCMF_NLATTR_UNSPEC, + + BRCMF_NLATTR_LEN, + BRCMF_NLATTR_DATA, + + __BRCMF_NLATTR_AFTER_LAST, + BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 +}; + +/** + * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd + * support + * + * @cmd: common dongle cmd definition + * @len: length of expecting return buffer + * @offset: offset of data buffer + * @set: get or set request(optional) + * @magic: magic number for verification + */ +struct brcmf_vndr_dcmd_hdr { + uint cmd; + int len; + uint offset; + uint set; + uint magic; +}; + +extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; + +#endif /* _vendor_h_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 708d2621e8e9cb..4c8a5b1fa04727 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ #include "btcoex.h" #include "wl_cfg80211.h" #include "fwil.h" +#include "vendor.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -3257,35 +3259,6 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, return 0; } -#ifdef CONFIG_NL80211_TESTMODE -static int brcmf_cfg80211_testmode(struct wiphy *wiphy, - struct wireless_dev *wdev, - void *data, int len) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_dcmd *dcmd = data; - struct sk_buff *reply; - int ret; - - brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set, - dcmd->buf, dcmd->len); - - if (dcmd->set) - ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd, - dcmd->buf, dcmd->len); - else - ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd, - dcmd->buf, dcmd->len); - if (ret == 0) { - reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd)); - nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd); - ret = cfg80211_testmode_reply(reply); - } - return ret; -} -#endif - static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) { s32 err; @@ -4303,7 +4276,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { .crit_proto_start = brcmf_cfg80211_crit_proto_start, .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, .tdls_oper = brcmf_cfg80211_tdls_oper, - CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode) }; static void brcmf_wiphy_pno_params(struct wiphy *wiphy) @@ -4408,6 +4380,11 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) brcmf_dbg(INFO, "Registering custom regulatory\n"); wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* vendor commands/events support */ + wiphy->vendor_commands = brcmf_vendor_cmds; + wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + err = wiphy_register(wiphy); if (err < 0) { brcmf_err("Could not register wiphy device (%d)\n", err); From b556bf33a4abaf1e53be5770f1638b581a8aa337 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 21 Jun 2014 12:11:17 +0200 Subject: [PATCH 0155/1983] brcmfmac: correct logging levels in btcoex source Upstream-commit: 2bb443d9ade2d4557bffdb40447fdfbdadb6071c All log messages were set to TRACE level, which is intended for function entry and exit. Using INFO instead in other places. Also reducing an error message that always popped up upon module unload. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/btcoex.c | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index 0cb591b050b3c7..a29ac4977b3a12 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -157,7 +157,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, */ /* save current */ - brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n"); + brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n"); brcmf_btcoex_params_read(ifp, 50, &btci->reg50); brcmf_btcoex_params_read(ifp, 51, &btci->reg51); brcmf_btcoex_params_read(ifp, 64, &btci->reg64); @@ -165,7 +165,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, brcmf_btcoex_params_read(ifp, 71, &btci->reg71); btci->saved_regs_part2 = true; - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", btci->reg50, btci->reg51, btci->reg64, btci->reg65, btci->reg71); @@ -179,21 +179,21 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, } else if (btci->saved_regs_part2) { /* restore previously saved bt params */ - brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n"); + brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n"); brcmf_btcoex_params_write(ifp, 50, btci->reg50); brcmf_btcoex_params_write(ifp, 51, btci->reg51); brcmf_btcoex_params_write(ifp, 64, btci->reg64); brcmf_btcoex_params_write(ifp, 65, btci->reg65); brcmf_btcoex_params_write(ifp, 71, btci->reg71); - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", btci->reg50, btci->reg51, btci->reg64, btci->reg65, btci->reg71); btci->saved_regs_part2 = false; } else { - brcmf_err("attempted to restore not saved BTCOEX params\n"); + brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n"); } } @@ -219,14 +219,14 @@ static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp) break; } - brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27); + brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27); if ((param27 & 0x6) == 2) { /* count both sco & esco */ sco_id_cnt++; } if (sco_id_cnt > 2) { - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "sco/esco detected, pkt id_cnt:%d samples:%d\n", sco_id_cnt, i); res = true; @@ -250,7 +250,7 @@ static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci) brcmf_btcoex_params_read(ifp, 41, &btci->reg41); brcmf_btcoex_params_read(ifp, 68, &btci->reg68); btci->saved_regs_part1 = true; - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n", btci->reg66, btci->reg41, btci->reg68); @@ -270,7 +270,7 @@ static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci) brcmf_btcoex_params_write(ifp, 66, btci->reg66); brcmf_btcoex_params_write(ifp, 41, btci->reg41); brcmf_btcoex_params_write(ifp, 68, btci->reg68); - brcmf_dbg(TRACE, + brcmf_dbg(INFO, "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n", btci->reg66, btci->reg41, btci->reg68); @@ -307,7 +307,7 @@ static void brcmf_btcoex_handler(struct work_struct *work) /* DHCP started provide OPPORTUNITY window to get DHCP address */ - brcmf_dbg(TRACE, "DHCP started\n"); + brcmf_dbg(INFO, "DHCP started\n"); btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN; if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) { mod_timer(&btci->timer, btci->timer.expires); @@ -322,12 +322,12 @@ static void brcmf_btcoex_handler(struct work_struct *work) case BRCMF_BT_DHCP_OPPR_WIN: if (btci->dhcp_done) { - brcmf_dbg(TRACE, "DHCP done before T1 expiration\n"); + brcmf_dbg(INFO, "DHCP done before T1 expiration\n"); goto idle; } /* DHCP is not over yet, start lowering BT priority */ - brcmf_dbg(TRACE, "DHCP T1:%d expired\n", + brcmf_dbg(INFO, "DHCP T1:%d expired\n", BRCMF_BTCOEX_OPPR_WIN_TIME); brcmf_btcoex_boost_wifi(btci, true); @@ -339,9 +339,9 @@ static void brcmf_btcoex_handler(struct work_struct *work) case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT: if (btci->dhcp_done) - brcmf_dbg(TRACE, "DHCP done before T2 expiration\n"); + brcmf_dbg(INFO, "DHCP done before T2 expiration\n"); else - brcmf_dbg(TRACE, "DHCP T2:%d expired\n", + brcmf_dbg(INFO, "DHCP T2:%d expired\n", BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT); goto idle; @@ -440,13 +440,13 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci) /* Stop any bt timer because DHCP session is done */ btci->dhcp_done = true; if (btci->timer_on) { - brcmf_dbg(TRACE, "disable BT DHCP Timer\n"); + brcmf_dbg(INFO, "disable BT DHCP Timer\n"); btci->timer_on = false; del_timer_sync(&btci->timer); /* schedule worker if transition to IDLE is needed */ if (btci->bt_state != BRCMF_BT_DHCP_IDLE) { - brcmf_dbg(TRACE, "bt_state:%d\n", + brcmf_dbg(INFO, "bt_state:%d\n", btci->bt_state); schedule_work(&btci->work); } @@ -472,7 +472,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, switch (mode) { case BRCMF_BTCOEX_DISABLED: - brcmf_dbg(TRACE, "DHCP session starts\n"); + brcmf_dbg(INFO, "DHCP session starts\n"); if (btci->bt_state != BRCMF_BT_DHCP_IDLE) return -EBUSY; /* Start BT timer only for SCO connection */ @@ -484,14 +484,14 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, break; case BRCMF_BTCOEX_ENABLED: - brcmf_dbg(TRACE, "DHCP session ends\n"); + brcmf_dbg(INFO, "DHCP session ends\n"); if (btci->bt_state != BRCMF_BT_DHCP_IDLE && vif == btci->vif) { brcmf_btcoex_dhcp_end(btci); } break; default: - brcmf_dbg(TRACE, "Unknown mode, ignored\n"); + brcmf_dbg(INFO, "Unknown mode, ignored\n"); } return 0; } From a99596d625d74507b33675ca3d41cd0e31f623bf Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Sat, 21 Jun 2014 12:11:18 +0200 Subject: [PATCH 0156/1983] brcmfmac: Don't control mpc setting during scan operation Upstream-commit: 5e787f7588ff69d3bcd546a64bf70e58a4e98fc5 Instead of controlling mpc setting during scan operation, initialize mpc setting and then let firmware take care of it. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Daniel Kim [arend@broadcom.com: keep mpc setting for bcm4329] Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 7 +++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 16 +++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index ed3e32ce8c23ee..d991f8e3d9ece1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -282,6 +282,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) ptr = strrchr(buf, ' ') + 1; strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); + /* set mpc */ + err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); + if (err) { + brcmf_err("failed setting mpc\n"); + goto done; + } + /* * Setup timeout if Beacons are lost and roam is off to report * link down diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 4c8a5b1fa04727..31206fe78bf2ed 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -590,6 +590,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, } } +static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) +{ + if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329) + brcmf_set_mpc(ifp, mpc); +} + void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) { s32 err = 0; @@ -643,7 +649,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, brcmf_err("Scan abort failed\n"); } - brcmf_set_mpc(ifp, 1); + brcmf_scan_config_mpc(ifp, 1); /* * e-scan can be initiated by scheduled scan @@ -922,7 +928,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, brcmf_err("error (%d)\n", err); return err; } - brcmf_set_mpc(ifp, 0); + brcmf_scan_config_mpc(ifp, 0); results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; results->version = 0; results->count = 0; @@ -930,7 +936,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); if (err) - brcmf_set_mpc(ifp, 1); + brcmf_scan_config_mpc(ifp, 1); return err; } @@ -1021,7 +1027,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); goto scan_out; } - brcmf_set_mpc(ifp, 0); + brcmf_scan_config_mpc(ifp, 0); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &sr->ssid_le, sizeof(sr->ssid_le)); if (err) { @@ -1031,7 +1037,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, else brcmf_err("WLC_SCAN error (%d)\n", err); - brcmf_set_mpc(ifp, 1); + brcmf_scan_config_mpc(ifp, 1); goto scan_out; } } From 76e0e6322803091f175233557752250709950fb2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 21 Jun 2014 12:11:19 +0200 Subject: [PATCH 0157/1983] brcmfmac: reduce log level in fwil if firmware returns error Upstream-commit: c94b7e6cb223986a1eaf7bd4c49f2ab758752649 The users of the fwil put an error message in the log so there is no need to do the same in the lower level functions in fwil when the firmware on the device returns an error. Some errors can be ignored for the driver to function and this will avoid driver users to point at the low-level error message as potential bug. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 59a5af5bf994d8..ded328f80cd123 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -54,7 +54,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) if (err >= 0) err = 0; else - brcmf_err("Failed err=%d\n", err); + brcmf_dbg(FIL, "Failed err=%d\n", err); return err; } From 8d15d027a0a950c995d44feaa4ff1663e32338a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 5 Jul 2014 01:10:41 +0200 Subject: [PATCH 0158/1983] bcma: add driver for PCIe Gen 2 core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream-commit: 5c4d5e816c2cc86e17d09677b649be47fbc30e51 New Broadcom PCIe devices (802.11ac ones?) use Gen2 and have to be initialized differently. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/Makefile | 1 + drivers/bcma/driver_pcie2.c | 175 +++++++++++++++++++++++++ drivers/bcma/main.c | 8 ++ include/linux/bcma/bcma.h | 2 + include/linux/bcma/bcma_driver_pcie2.h | 158 ++++++++++++++++++++++ 5 files changed, 344 insertions(+) create mode 100644 drivers/bcma/driver_pcie2.c create mode 100644 include/linux/bcma/bcma_driver_pcie2.h diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile index 734b32f09c0a21..91290f7f61b8c2 100644 --- a/drivers/bcma/Makefile +++ b/drivers/bcma/Makefile @@ -3,6 +3,7 @@ bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o bcma-y += driver_pci.o +bcma-y += driver_pcie2.o bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c new file mode 100644 index 00000000000000..e4be537b0c6699 --- /dev/null +++ b/drivers/bcma/driver_pcie2.c @@ -0,0 +1,175 @@ +/* + * Broadcom specific AMBA + * PCIe Gen 2 Core + * + * Copyright 2014, Broadcom Corporation + * Copyright 2014, Rafał Miłecki + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" +#include + +/************************************************** + * R/W ops. + **************************************************/ + +#if 0 +static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR); + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); +} +#endif + +static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr, + u32 val) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val); +} + +/************************************************** + * Init. + **************************************************/ + +static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2, + bool enable) +{ + u32 val; + + /* restore back to default */ + val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); + val |= PCIE2_CLKC_DLYPERST; + val &= ~PCIE2_CLKC_DISSPROMLD; + if (enable) { + val &= ~PCIE2_CLKC_DLYPERST; + val |= PCIE2_CLKC_DISSPROMLD; + } + pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val); + /* flush */ + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); +} + +static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2) +{ + /* LTR0 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c); + /* LTR1 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864); + /* LTR2 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003); +} + +static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + u32 devstsctr2; + + if (core_rev < 2 || core_rev == 10 || core_rev > 13) + return; + + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); + if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) { + /* force the right LTR values */ + bcma_core_pcie2_set_ltr_vals(pcie2); + + /* TODO: + si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */ + + /* enable the LTR */ + devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2); + + /* set the LTR state to be active */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_ACTIVE); + usleep_range(1000, 2000); + + /* set the LTR state to be sleep */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_SLEEP); + usleep_range(1000, 2000); + } +} + +static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + bool pciewar160, pciewar162; + + pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11; + pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 || + core_rev == 9 || core_rev == 11; + + if (!pciewar160 && !pciewar162) + return; + +/* TODO */ +#if 0 + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL, + PCIE_DISABLE_L1CLK_GATING); +#if 0 + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIEGEN2_COE_PVT_TL_CTRL_0); + pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, + ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT)); +#endif +#endif +} + +static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP); + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f); +} + +static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0); +} + +static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc; + u8 core_rev = pcie2->core->id.rev; + u32 alp_khz, pm_value; + + if (core_rev <= 13) { + alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000; + pm_value = (1000000 * 2) / alp_khz; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_PVT_REG_PM_CLK_PERIOD); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value); + } +} + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo; + u32 tmp; + + tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54)); + if ((tmp & 0xe) >> 1 == 2) + bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17); + + /* TODO: Do we need pcie_reqsize? */ + + if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3) + bcma_core_pcie2_war_delay_perst_enab(pcie2, true); + bcma_core_pcie2_hw_ltr_war(pcie2); + pciedev_crwlpciegen2(pcie2); + pciedev_reg_pm_clk_period(pcie2); + pciedev_crwlpciegen2_180(pcie2); + pciedev_crwlpciegen2_182(pcie2); +} diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 34ea4c588d36bd..0ff8d58831ef30 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus) case BCMA_CORE_CHIPCOMMON: case BCMA_CORE_PCI: case BCMA_CORE_PCIE: + case BCMA_CORE_PCIE2: case BCMA_CORE_MIPS_74K: case BCMA_CORE_4706_MAC_GBIT_COMMON: continue; @@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus) bcma_core_pci_init(&bus->drv_pci[1]); } + /* Init PCIe Gen 2 core */ + core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0); + if (core) { + bus->drv_pcie2.core = core; + bcma_core_pcie2_init(&bus->drv_pcie2); + } + /* Init GBIT MAC COMMON core */ core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); if (core) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 0b3bb16c705a2e..452286a38b2b9f 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include /* SPROM sharing */ @@ -333,6 +334,7 @@ struct bcma_bus { struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci[2]; + struct bcma_drv_pcie2 drv_pcie2; struct bcma_drv_mips drv_mips; struct bcma_drv_gmac_cmn drv_gmac_cmn; diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h new file mode 100644 index 00000000000000..5988b05781c336 --- /dev/null +++ b/include/linux/bcma/bcma_driver_pcie2.h @@ -0,0 +1,158 @@ +#ifndef LINUX_BCMA_DRIVER_PCIE2_H_ +#define LINUX_BCMA_DRIVER_PCIE2_H_ + +#define BCMA_CORE_PCIE2_CLK_CONTROL 0x0000 +#define PCIE2_CLKC_RST_OE 0x0001 /* When set, drives PCI_RESET out to pin */ +#define PCIE2_CLKC_RST 0x0002 /* Value driven out to pin */ +#define PCIE2_CLKC_SPERST 0x0004 /* SurvivePeRst */ +#define PCIE2_CLKC_DISABLE_L1CLK_GATING 0x0010 +#define PCIE2_CLKC_DLYPERST 0x0100 /* Delay PeRst to CoE Core */ +#define PCIE2_CLKC_DISSPROMLD 0x0200 /* DisableSpromLoadOnPerst */ +#define PCIE2_CLKC_WAKE_MODE_L2 0x1000 /* Wake on L2 */ +#define BCMA_CORE_PCIE2_RC_PM_CONTROL 0x0004 +#define BCMA_CORE_PCIE2_RC_PM_STATUS 0x0008 +#define BCMA_CORE_PCIE2_EP_PM_CONTROL 0x000C +#define BCMA_CORE_PCIE2_EP_PM_STATUS 0x0010 +#define BCMA_CORE_PCIE2_EP_LTR_CONTROL 0x0014 +#define BCMA_CORE_PCIE2_EP_LTR_STATUS 0x0018 +#define BCMA_CORE_PCIE2_EP_OBFF_STATUS 0x001C +#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS 0x0020 +#define BCMA_CORE_PCIE2_RC_AXI_CONFIG 0x0100 +#define BCMA_CORE_PCIE2_EP_AXI_CONFIG 0x0104 +#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0 0x0108 +#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0 0x010C +#define BCMA_CORE_PCIE2_CONFIGINDADDR 0x0120 +#define BCMA_CORE_PCIE2_CONFIGINDDATA 0x0124 +#define BCMA_CORE_PCIE2_MDIOCONTROL 0x0128 +#define BCMA_CORE_PCIE2_MDIOWRDATA 0x012C +#define BCMA_CORE_PCIE2_MDIORDDATA 0x0130 +#define BCMA_CORE_PCIE2_DATAINTF 0x0180 +#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0 0x0188 +#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0 0x018c +#define BCMA_CORE_PCIE2_H2D_INTSTAT_0 0x0190 +#define BCMA_CORE_PCIE2_H2D_INTMASK_0 0x0194 +#define BCMA_CORE_PCIE2_D2H_INTSTAT_0 0x0198 +#define BCMA_CORE_PCIE2_D2H_INTMASK_0 0x019c +#define BCMA_CORE_PCIE2_LTR_STATE 0x01A0 /* Latency Tolerance Reporting */ +#define PCIE2_LTR_ACTIVE 2 +#define PCIE2_LTR_ACTIVE_IDLE 1 +#define PCIE2_LTR_SLEEP 0 +#define PCIE2_LTR_FINAL_MASK 0x300 +#define PCIE2_LTR_FINAL_SHIFT 8 +#define BCMA_CORE_PCIE2_PWR_INT_STATUS 0x01A4 +#define BCMA_CORE_PCIE2_PWR_INT_MASK 0x01A8 +#define BCMA_CORE_PCIE2_CFG_ADDR 0x01F8 +#define BCMA_CORE_PCIE2_CFG_DATA 0x01FC +#define BCMA_CORE_PCIE2_SYS_EQ_PAGE 0x0200 +#define BCMA_CORE_PCIE2_SYS_MSI_PAGE 0x0204 +#define BCMA_CORE_PCIE2_SYS_MSI_INTREN 0x0208 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0 0x0210 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1 0x0214 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2 0x0218 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3 0x021C +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4 0x0220 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5 0x0224 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0 0x0250 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0 0x0254 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1 0x0258 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1 0x025C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2 0x0260 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2 0x0264 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3 0x0268 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3 0x026C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4 0x0270 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4 0x0274 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5 0x0278 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5 0x027C +#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN 0x0330 +#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR 0x0334 +#define BCMA_CORE_PCIE2_SYS_MSI_REQ 0x0340 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN 0x0344 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR 0x0348 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR0 0x0350 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR1 0x0354 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR2 0x0358 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR3 0x035C +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0 0x0360 +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1 0x0364 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0 0x0370 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1 0x0374 +#define BCMA_CORE_PCIE2_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0 0x0C00 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1 0x0C04 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2 0x0C08 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3 0x0C0C +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4 0x0C10 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5 0x0C14 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6 0x0C18 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7 0x0C1C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0 0x0C20 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1 0x0C24 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2 0x0C28 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3 0x0C2C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4 0x0C30 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5 0x0C34 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6 0x0C38 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7 0x0C3C +#define BCMA_CORE_PCIE2_FUNC0_IMAP1 0x0C80 +#define BCMA_CORE_PCIE2_FUNC1_IMAP1 0x0C88 +#define BCMA_CORE_PCIE2_FUNC0_IMAP2 0x0CC0 +#define BCMA_CORE_PCIE2_FUNC1_IMAP2 0x0CC8 +#define BCMA_CORE_PCIE2_IARR0_LOWER 0x0D00 +#define BCMA_CORE_PCIE2_IARR0_UPPER 0x0D04 +#define BCMA_CORE_PCIE2_IARR1_LOWER 0x0D08 +#define BCMA_CORE_PCIE2_IARR1_UPPER 0x0D0C +#define BCMA_CORE_PCIE2_IARR2_LOWER 0x0D10 +#define BCMA_CORE_PCIE2_IARR2_UPPER 0x0D14 +#define BCMA_CORE_PCIE2_OARR0 0x0D20 +#define BCMA_CORE_PCIE2_OARR1 0x0D28 +#define BCMA_CORE_PCIE2_OARR2 0x0D30 +#define BCMA_CORE_PCIE2_OMAP0_LOWER 0x0D40 +#define BCMA_CORE_PCIE2_OMAP0_UPPER 0x0D44 +#define BCMA_CORE_PCIE2_OMAP1_LOWER 0x0D48 +#define BCMA_CORE_PCIE2_OMAP1_UPPER 0x0D4C +#define BCMA_CORE_PCIE2_OMAP2_LOWER 0x0D50 +#define BCMA_CORE_PCIE2_OMAP2_UPPER 0x0D54 +#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE 0x0D58 +#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE 0x0D5C +#define BCMA_CORE_PCIE2_MEM_CONTROL 0x0F00 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0 0x0F04 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1 0x0F08 +#define BCMA_CORE_PCIE2_LINK_STATUS 0x0F0C +#define BCMA_CORE_PCIE2_STRAP_STATUS 0x0F10 +#define BCMA_CORE_PCIE2_RESET_STATUS 0x0F14 +#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN 0x0F18 +#define BCMA_CORE_PCIE2_MISC_INTR_EN 0x0F1C +#define BCMA_CORE_PCIE2_TX_DEBUG_CFG 0x0F20 +#define BCMA_CORE_PCIE2_MISC_CONFIG 0x0F24 +#define BCMA_CORE_PCIE2_MISC_STATUS 0x0F28 +#define BCMA_CORE_PCIE2_INTR_EN 0x0F30 +#define BCMA_CORE_PCIE2_INTR_CLEAR 0x0F34 +#define BCMA_CORE_PCIE2_INTR_STATUS 0x0F38 + +/* PCIE gen2 config regs */ +#define PCIE2_INTSTATUS 0x090 +#define PCIE2_INTMASK 0x094 +#define PCIE2_SBMBX 0x098 + +#define PCIE2_PMCR_REFUP 0x1814 /* Trefup time */ + +#define PCIE2_CAP_DEVSTSCTRL2_OFFSET 0xD4 +#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB 0x400 +#define PCIE2_PVT_REG_PM_CLK_PERIOD 0x184c + +struct bcma_drv_pcie2 { + struct bcma_device *core; +}; + +#define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset) +#define pcie2_read32(pcie2, offset) bcma_read32((pcie2)->core, offset) +#define pcie2_write16(pcie2, offset, val) bcma_write16((pcie2)->core, offset, val) +#define pcie2_write32(pcie2, offset, val) bcma_write32((pcie2)->core, offset, val) + +#define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set) +#define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask) + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); + +#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */ From 019f0567b16adfa7d3925f005aa3962f0b752f65 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 12 Jul 2014 08:49:34 +0200 Subject: [PATCH 0159/1983] brcmfmac: Cleanup used device IDs. Upstream-commit: 5779ae6a5559252adabc5b5e807381dc7a16f1ff This patch cleans up used broadcom IDs, device IDs for all the bus layers and uses consistent naming for all IDs. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 23 +++---- .../net/wireless/brcm80211/brcmfmac/chip.c | 28 ++++----- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 32 +++++----- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 49 +++++++-------- .../wireless/brcm80211/include/brcm_hw_ids.h | 60 +++++++++++++------ 5 files changed, 107 insertions(+), 85 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index a16e644e7c0882..f467cafe3e8f08 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -979,18 +978,20 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) return ret; } +#define BRCMF_SDIO_DEVICE(dev_id) \ + {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)} + /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, - SDIO_DEVICE_ID_BROADCOM_4335_4339)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)}, - { /* end: all zeroes */ }, + BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), + { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index c7c9f15c0fe081..96800db0536b57 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -482,30 +482,30 @@ static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) { switch (ci->pub.chip) { - case BCM4329_CHIP_ID: + case BRCM_CC_4329_CHIP_ID: ci->pub.ramsize = BCM4329_RAMSIZE; break; - case BCM43143_CHIP_ID: + case BRCM_CC_43143_CHIP_ID: ci->pub.ramsize = BCM43143_RAMSIZE; break; - case BCM43241_CHIP_ID: + case BRCM_CC_43241_CHIP_ID: ci->pub.ramsize = 0x90000; break; - case BCM4330_CHIP_ID: + case BRCM_CC_4330_CHIP_ID: ci->pub.ramsize = 0x48000; break; - case BCM4334_CHIP_ID: + case BRCM_CC_4334_CHIP_ID: ci->pub.ramsize = 0x80000; break; - case BCM4335_CHIP_ID: + case BRCM_CC_4335_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; - case BCM43362_CHIP_ID: + case BRCM_CC_43362_CHIP_ID: ci->pub.ramsize = 0x3c000; break; - case BCM4339_CHIP_ID: - case BCM4354_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; @@ -682,7 +682,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) ci->pub.chiprev); if (socitype == SOCI_SB) { - if (ci->pub.chip != BCM4329_CHIP_ID) { + if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) { brcmf_err("SB chip is not supported\n"); return -ENODEV; } @@ -1008,13 +1008,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) chip = container_of(pub, struct brcmf_chip_priv, pub); switch (pub->chip) { - case BCM4354_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: /* explicitly check SR engine enable bit */ pmu_cc3_mask = BIT(2); /* fall-through */ - case BCM43241_CHIP_ID: - case BCM4335_CHIP_ID: - case BCM4339_CHIP_ID: + case BRCM_CC_43241_CHIP_ID: + case BRCM_CC_4335_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: /* read PMU chipcontrol register 3 */ addr = CORE_CC_REG(base, chipcontrol_addr); chip->ops->write32(chip->ctx, addr, 3); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 8fa0dbbbda72b6..bf9bc2d8923019 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -620,16 +620,16 @@ enum brcmf_firmware_type { name ## _FIRMWARE_NAME, name ## _NVRAM_NAME static const struct brcmf_firmware_names brcmf_fwname_data[] = { - { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, - { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, - { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, - { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, - { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, - { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, - { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, - { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, - { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } + { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, + { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, + { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, + { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, + { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, + { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, + { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, @@ -3598,17 +3598,17 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, return; switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12): str_tab = sdiod_drvstr_tab1_1v8; str_mask = 0x00003800; str_shift = 11; break; - case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17): str_tab = sdiod_drvstr_tab6_1v8; str_mask = 0x00001800; str_shift = 11; break; - case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): + case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17): /* note: 43143 does not support tristate */ i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { @@ -3619,7 +3619,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", ci->name, drivestrength); break; - case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13): str_tab = sdiod_drive_strength_tab5_1v8; str_mask = 0x00003800; str_shift = 11; @@ -3720,12 +3720,12 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) u32 val, rev; val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; if (rev >= 2) { val &= ~CID_ID_MASK; - val |= BCM4339_CHIP_ID; + val |= BRCM_CC_4339_CHIP_ID; } } return val; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index b732a99e402cb8..dc135915470d2c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -913,16 +914,16 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) static bool brcmf_usb_chip_support(int chipid, int chiprev) { switch(chipid) { - case 43143: + case BRCM_CC_43143_CHIP_ID: return true; - case 43235: - case 43236: - case 43238: + case BRCM_CC_43235_CHIP_ID: + case BRCM_CC_43236_CHIP_ID: + case BRCM_CC_43238_CHIP_ID: return (chiprev == 3); - case 43242: + case BRCM_CC_43242_CHIP_ID: return true; - case 43566: - case 43569: + case BRCM_CC_43566_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: return true; default: break; @@ -1016,16 +1017,16 @@ static int check_file(const u8 *headers) static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) { switch (devinfo->bus_pub.devid) { - case 43143: + case BRCM_CC_43143_CHIP_ID: return BRCMF_USB_43143_FW_NAME; - case 43235: - case 43236: - case 43238: + case BRCM_CC_43235_CHIP_ID: + case BRCM_CC_43236_CHIP_ID: + case BRCM_CC_43238_CHIP_ID: return BRCMF_USB_43236_FW_NAME; - case 43242: + case BRCM_CC_43242_CHIP_ID: return BRCMF_USB_43242_FW_NAME; - case 43566: - case 43569: + case BRCM_CC_43566_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: return BRCMF_USB_43569_FW_NAME; default: return NULL; @@ -1366,21 +1367,17 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) brcmf_usb_probe_phase2); } -#define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c -#define BRCMF_USB_DEVICE_ID_43143 0xbd1e -#define BRCMF_USB_DEVICE_ID_43236 0xbd17 -#define BRCMF_USB_DEVICE_ID_43242 0xbd1f -#define BRCMF_USB_DEVICE_ID_43569 0xbd27 -#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc +#define BRCMF_USB_DEVICE(dev_id) \ + { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) } static struct usb_device_id brcmf_usb_devid_table[] = { - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) }, - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) }, - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) }, - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) }, + BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID), /* special entry for device with firmware loaded and running */ - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) }, - { } + BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), + { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index d816270db3be56..64d1a7ba040ccc 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -17,32 +17,56 @@ #ifndef _BRCM_HW_IDS_H_ #define _BRCM_HW_IDS_H_ -#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ +#include +#include + +#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c +#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM +#define BRCM_SDIO_VENDOR_ID_BROADCOM SDIO_VENDOR_ID_BROADCOM + +/* Chipcommon Core Chip IDs */ +#define BRCM_CC_43143_CHIP_ID 43143 +#define BRCM_CC_43235_CHIP_ID 43235 +#define BRCM_CC_43236_CHIP_ID 43236 +#define BRCM_CC_43238_CHIP_ID 43238 +#define BRCM_CC_43241_CHIP_ID 0x4324 +#define BRCM_CC_43242_CHIP_ID 43242 +#define BRCM_CC_4329_CHIP_ID 0x4329 +#define BRCM_CC_4330_CHIP_ID 0x4330 +#define BRCM_CC_4334_CHIP_ID 0x4334 +#define BRCM_CC_43362_CHIP_ID 43362 +#define BRCM_CC_4335_CHIP_ID 0x4335 +#define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_4354_CHIP_ID 0x4354 +#define BRCM_CC_43566_CHIP_ID 43566 +#define BRCM_CC_43569_CHIP_ID 43569 + +/* SDIO Device IDs */ +#define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID +#define BRCM_SDIO_43241_DEVICE_ID BRCM_CC_43241_CHIP_ID +#define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID +#define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID +#define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID +#define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID +#define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID +#define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID +/* USB Device IDs */ +#define BRCM_USB_43143_DEVICE_ID 0xbd1e +#define BRCM_USB_43236_DEVICE_ID 0xbd17 +#define BRCM_USB_43242_DEVICE_ID 0xbd1f +#define BRCM_USB_43569_DEVICE_ID 0xbd27 +#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc + +/* brcmsmac IDs */ +#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ #define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ #define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */ - #define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */ - #define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */ #define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */ -/* Chipcommon Core Chip IDs */ #define BCM4313_CHIP_ID 0x4313 -#define BCM43143_CHIP_ID 43143 #define BCM43224_CHIP_ID 43224 -#define BCM43225_CHIP_ID 43225 -#define BCM43235_CHIP_ID 43235 -#define BCM43236_CHIP_ID 43236 -#define BCM43238_CHIP_ID 43238 -#define BCM43241_CHIP_ID 0x4324 -#define BCM4329_CHIP_ID 0x4329 -#define BCM4330_CHIP_ID 0x4330 -#define BCM4331_CHIP_ID 0x4331 -#define BCM4334_CHIP_ID 0x4334 -#define BCM4335_CHIP_ID 0x4335 -#define BCM43362_CHIP_ID 43362 -#define BCM4339_CHIP_ID 0x4339 -#define BCM4354_CHIP_ID 0x4354 #endif /* _BRCM_HW_IDS_H_ */ From 5494309ce033823e26d46e7c022c3d7f73ffc9c9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:35 +0200 Subject: [PATCH 0160/1983] brcmfmac: make use of seq_file API for debugfs entries Upstream-commit: 1b1e4e9e3abca01b7be6400db879ca8c475a2c96 The use of seq_file simplifies the debugfs code. Simpler is better. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 249 +++++++++--------- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 122 ++++----- 2 files changed, 170 insertions(+), 201 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 03fe8aca4d3261..e1ac932a315453 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -41,28 +41,27 @@ void brcmf_debugfs_exit(void) root_folder = NULL; } -static -ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data) { - struct brcmf_pub *drvr = f->private_data; + struct brcmf_pub *drvr = seq->private; struct brcmf_bus *bus = drvr->bus_if; - char buf[40]; - int res; - /* only allow read from start */ - if (*ppos > 0) - return 0; + seq_printf(seq, "chip: %x(%u) rev %u\n", + bus->chip, bus->chip, bus->chiprev); + return 0; +} - res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n", - bus->chip, bus->chip, bus->chiprev); - return simple_read_from_buffer(data, count, ppos, buf, res); +static int brcmf_debugfs_chipinfo_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_debugfs_chipinfo_read, inode->i_private); } static const struct file_operations brcmf_debugfs_chipinfo_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_debugfs_chipinfo_read + .open = brcmf_debugfs_chipinfo_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr) @@ -98,55 +97,54 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) return drvr->dbgfs_dir; } -static -ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) { - struct brcmf_sdio_count *sdcnt = f->private_data; - char buf[750]; - int res; - - /* only allow read from start */ - if (*ppos > 0) - return 0; - - res = scnprintf(buf, sizeof(buf), - "intrcount: %u\nlastintrs: %u\n" - "pollcnt: %u\nregfails: %u\n" - "tx_sderrs: %u\nfcqueued: %u\n" - "rxrtx: %u\nrx_toolong: %u\n" - "rxc_errors: %u\nrx_hdrfail: %u\n" - "rx_badhdr: %u\nrx_badseq: %u\n" - "fc_rcvd: %u\nfc_xoff: %u\n" - "fc_xon: %u\nrxglomfail: %u\n" - "rxglomframes: %u\nrxglompkts: %u\n" - "f2rxhdrs: %u\nf2rxdata: %u\n" - "f2txdata: %u\nf1regdata: %u\n" - "tickcnt: %u\ntx_ctlerrs: %lu\n" - "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" - "rx_ctlpkts: %lu\nrx_readahead: %lu\n", - sdcnt->intrcount, sdcnt->lastintrs, - sdcnt->pollcnt, sdcnt->regfails, - sdcnt->tx_sderrs, sdcnt->fcqueued, - sdcnt->rxrtx, sdcnt->rx_toolong, - sdcnt->rxc_errors, sdcnt->rx_hdrfail, - sdcnt->rx_badhdr, sdcnt->rx_badseq, - sdcnt->fc_rcvd, sdcnt->fc_xoff, - sdcnt->fc_xon, sdcnt->rxglomfail, - sdcnt->rxglomframes, sdcnt->rxglompkts, - sdcnt->f2rxhdrs, sdcnt->f2rxdata, - sdcnt->f2txdata, sdcnt->f1regdata, - sdcnt->tickcnt, sdcnt->tx_ctlerrs, - sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, - sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); - - return simple_read_from_buffer(data, count, ppos, buf, res); + struct brcmf_sdio_count *sdcnt = seq->private; + + seq_printf(seq, + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + + return 0; +} + +static int brcmf_debugfs_sdio_count_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_debugfs_sdio_count_read, inode->i_private); } static const struct file_operations brcmf_debugfs_sdio_counter_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_debugfs_sdio_counter_read + .open = brcmf_debugfs_sdio_count_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, @@ -159,79 +157,78 @@ void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, sdcnt, &brcmf_debugfs_sdio_counter_ops); } -static -ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + struct brcmf_fws_stats *fwstats = seq->private; + + seq_printf(seq, + "header_pulls: %u\n" + "header_only_pkt: %u\n" + "tlv_parse_failed: %u\n" + "tlv_invalid_type: %u\n" + "mac_update_fails: %u\n" + "ps_update_fails: %u\n" + "if_update_fails: %u\n" + "pkt2bus: %u\n" + "generic_error: %u\n" + "rollback_success: %u\n" + "rollback_failed: %u\n" + "delayq_full: %u\n" + "supprq_full: %u\n" + "txs_indicate: %u\n" + "txs_discard: %u\n" + "txs_suppr_core: %u\n" + "txs_suppr_ps: %u\n" + "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" + "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + fwstats->header_pulls, + fwstats->header_only_pkt, + fwstats->tlv_parse_failed, + fwstats->tlv_invalid_type, + fwstats->mac_update_failed, + fwstats->mac_ps_update_failed, + fwstats->if_update_failed, + fwstats->pkt2bus, + fwstats->generic_error, + fwstats->rollback_success, + fwstats->rollback_failed, + fwstats->delayq_full_error, + fwstats->supprq_full_error, + fwstats->txs_indicate, + fwstats->txs_discard, + fwstats->txs_supp_core, + fwstats->txs_supp_ps, + fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, + fwstats->send_pkts[0], fwstats->send_pkts[1], + fwstats->send_pkts[2], fwstats->send_pkts[3], + fwstats->send_pkts[4], + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); + + return 0; +} + +static int brcmf_debugfs_fws_stats_open(struct inode *inode, struct file *f) { - struct brcmf_fws_stats *fwstats = f->private_data; - char buf[650]; - int res; - - /* only allow read from start */ - if (*ppos > 0) - return 0; - - res = scnprintf(buf, sizeof(buf), - "header_pulls: %u\n" - "header_only_pkt: %u\n" - "tlv_parse_failed: %u\n" - "tlv_invalid_type: %u\n" - "mac_update_fails: %u\n" - "ps_update_fails: %u\n" - "if_update_fails: %u\n" - "pkt2bus: %u\n" - "generic_error: %u\n" - "rollback_success: %u\n" - "rollback_failed: %u\n" - "delayq_full: %u\n" - "supprq_full: %u\n" - "txs_indicate: %u\n" - "txs_discard: %u\n" - "txs_suppr_core: %u\n" - "txs_suppr_ps: %u\n" - "txs_tossed: %u\n" - "txs_host_tossed: %u\n" - "bus_flow_block: %u\n" - "fws_flow_block: %u\n" - "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", - fwstats->header_pulls, - fwstats->header_only_pkt, - fwstats->tlv_parse_failed, - fwstats->tlv_invalid_type, - fwstats->mac_update_failed, - fwstats->mac_ps_update_failed, - fwstats->if_update_failed, - fwstats->pkt2bus, - fwstats->generic_error, - fwstats->rollback_success, - fwstats->rollback_failed, - fwstats->delayq_full_error, - fwstats->supprq_full_error, - fwstats->txs_indicate, - fwstats->txs_discard, - fwstats->txs_supp_core, - fwstats->txs_supp_ps, - fwstats->txs_tossed, - fwstats->txs_host_tossed, - fwstats->bus_flow_block, - fwstats->fws_flow_block, - fwstats->send_pkts[0], fwstats->send_pkts[1], - fwstats->send_pkts[2], fwstats->send_pkts[3], - fwstats->send_pkts[4], - fwstats->requested_sent[0], - fwstats->requested_sent[1], - fwstats->requested_sent[2], - fwstats->requested_sent[3], - fwstats->requested_sent[4]); - - return simple_read_from_buffer(data, count, ppos, buf, res); + return single_open(f, brcmf_debugfs_fws_stats_read, inode->i_private); } static const struct file_operations brcmf_debugfs_fws_stats_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_debugfs_fws_stats_read + .open = brcmf_debugfs_fws_stats_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index bf9bc2d8923019..0c98a7942a0ff2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2898,16 +2898,13 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #ifdef DEBUG -static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, - struct sdpcm_shared *sh, char __user *data, - size_t count) +static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) { u32 addr, console_ptr, console_size, console_index; char *conbuf = NULL; __le32 sh_val; int rv; - loff_t pos = 0; - int nbytes = 0; /* obtain console information from device memory */ addr = sh->console_addr + offsetof(struct rte_console, log_le); @@ -2945,33 +2942,24 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, if (rv < 0) goto done; - rv = simple_read_from_buffer(data, count, &pos, - conbuf + console_index, - console_size - console_index); + rv = seq_write(seq, conbuf + console_index, + console_size - console_index); if (rv < 0) goto done; - nbytes = rv; - if (console_index > 0) { - pos = 0; - rv = simple_read_from_buffer(data+nbytes, count, &pos, - conbuf, console_index - 1); - if (rv < 0) - goto done; - rv += nbytes; - } + if (console_index > 0) + rv = seq_write(seq, conbuf, console_index - 1); + done: vfree(conbuf); return rv; } -static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, - char __user *data, size_t count) +static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) { - int error, res; - char buf[350]; + int error; struct brcmf_trap_info tr; - loff_t pos = 0; if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { brcmf_dbg(INFO, "no trap in firmware\n"); @@ -2983,34 +2971,30 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, if (error < 0) return error; - res = scnprintf(buf, sizeof(buf), - "dongle trap info: type 0x%x @ epc 0x%08x\n" - " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" - " lr 0x%08x pc 0x%08x offset 0x%x\n" - " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" - " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", - le32_to_cpu(tr.type), le32_to_cpu(tr.epc), - le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), - le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), - le32_to_cpu(tr.pc), sh->trap_addr, - le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), - le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), - le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), - le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); - - return simple_read_from_buffer(data, count, &pos, buf, res); + seq_printf(seq, + "dongle trap info: type 0x%x @ epc 0x%08x\n" + " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" + " lr 0x%08x pc 0x%08x offset 0x%x\n" + " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", + le32_to_cpu(tr.type), le32_to_cpu(tr.epc), + le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), + le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), + le32_to_cpu(tr.pc), sh->trap_addr, + le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), + le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), + le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), + le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); + + return 0; } -static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, - struct sdpcm_shared *sh, char __user *data, - size_t count) +static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) { int error = 0; - char buf[200]; char file[80] = "?"; char expr[80] = ""; - int res; - loff_t pos = 0; if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { brcmf_dbg(INFO, "firmware not built with -assert\n"); @@ -3035,10 +3019,9 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, } sdio_release_host(bus->sdiodev->func[1]); - res = scnprintf(buf, sizeof(buf), - "dongle assert: %s:%d: assert(%s)\n", - file, sh->assert_line, expr); - return simple_read_from_buffer(data, count, &pos, buf, res); + seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n", + file, sh->assert_line, expr); + return 0; } static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) @@ -3062,58 +3045,47 @@ static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) return 0; } -static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) { int error = 0; struct sdpcm_shared sh; - int nbytes = 0; - loff_t pos = *ppos; - - if (pos != 0) - return 0; error = brcmf_sdio_readshared(bus, &sh); if (error < 0) goto done; - error = brcmf_sdio_assert_info(bus, &sh, data, count); + error = brcmf_sdio_assert_info(seq, bus, &sh); if (error < 0) goto done; - nbytes = error; - error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count); + error = brcmf_sdio_trap_info(seq, bus, &sh); if (error < 0) goto done; - nbytes += error; - error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count); - if (error < 0) - goto done; - nbytes += error; + error = brcmf_sdio_dump_console(seq, bus, &sh); - error = nbytes; - *ppos += nbytes; done: return error; } -static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) { - struct brcmf_sdio *bus = f->private_data; - int res; + struct brcmf_sdio *bus = seq->private; - res = brcmf_sdio_died_dump(bus, data, count, ppos); - if (res > 0) - *ppos += res; - return (ssize_t)res; + return brcmf_sdio_died_dump(seq, bus); +} + +static int brcmf_sdio_forensic_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_sdio_forensic_read, inode->i_private); } static const struct file_operations brcmf_sdio_forensic_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_sdio_forensic_read + .open = brcmf_sdio_forensic_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) From b82c9c6f575887b7aee84e5083429c1f8049579d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:36 +0200 Subject: [PATCH 0161/1983] brcmfmac: rework debugfs functions in the driver Upstream-commit: 82d957e09d4f0ff8091ca67e39b51ec6bdc672b1 Reworked the debugfs functions in the driver making it easier for other driver parts to add a debugfs entry and keeping the information they want to expose in debugfs private, ie. not in a header. This is accomplished by providing the function brcmf_debugfs_add_entry() in which the caller provides a read function in which they provide the content. The debugfs function will take care of creating the debugfs entry and cleaning up upon removal. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 179 +++--------------- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 74 +------- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 89 +++++++-- .../wireless/brcm80211/brcmfmac/fwsignal.c | 100 +++++++++- 4 files changed, 207 insertions(+), 235 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index e1ac932a315453..be9f4f829192cc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -43,37 +43,13 @@ void brcmf_debugfs_exit(void) static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data) { - struct brcmf_pub *drvr = seq->private; - struct brcmf_bus *bus = drvr->bus_if; + struct brcmf_bus *bus = dev_get_drvdata(seq->private); seq_printf(seq, "chip: %x(%u) rev %u\n", bus->chip, bus->chip, bus->chiprev); return 0; } -static int brcmf_debugfs_chipinfo_open(struct inode *inode, struct file *f) -{ - return single_open(f, brcmf_debugfs_chipinfo_read, inode->i_private); -} - -static const struct file_operations brcmf_debugfs_chipinfo_ops = { - .owner = THIS_MODULE, - .open = brcmf_debugfs_chipinfo_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; - -static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr) -{ - struct dentry *dentry = drvr->dbgfs_dir; - - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr, - &brcmf_debugfs_chipinfo_ops); - return 0; -} - int brcmf_debugfs_attach(struct brcmf_pub *drvr) { struct device *dev = drvr->bus_if->dev; @@ -82,7 +58,8 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr) return -ENODEV; drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); - brcmf_debugfs_create_chipinfo(drvr); + brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read); + return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); } @@ -97,146 +74,44 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) return drvr->dbgfs_dir; } -static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) -{ - struct brcmf_sdio_count *sdcnt = seq->private; - - seq_printf(seq, - "intrcount: %u\nlastintrs: %u\n" - "pollcnt: %u\nregfails: %u\n" - "tx_sderrs: %u\nfcqueued: %u\n" - "rxrtx: %u\nrx_toolong: %u\n" - "rxc_errors: %u\nrx_hdrfail: %u\n" - "rx_badhdr: %u\nrx_badseq: %u\n" - "fc_rcvd: %u\nfc_xoff: %u\n" - "fc_xon: %u\nrxglomfail: %u\n" - "rxglomframes: %u\nrxglompkts: %u\n" - "f2rxhdrs: %u\nf2rxdata: %u\n" - "f2txdata: %u\nf1regdata: %u\n" - "tickcnt: %u\ntx_ctlerrs: %lu\n" - "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" - "rx_ctlpkts: %lu\nrx_readahead: %lu\n", - sdcnt->intrcount, sdcnt->lastintrs, - sdcnt->pollcnt, sdcnt->regfails, - sdcnt->tx_sderrs, sdcnt->fcqueued, - sdcnt->rxrtx, sdcnt->rx_toolong, - sdcnt->rxc_errors, sdcnt->rx_hdrfail, - sdcnt->rx_badhdr, sdcnt->rx_badseq, - sdcnt->fc_rcvd, sdcnt->fc_xoff, - sdcnt->fc_xon, sdcnt->rxglomfail, - sdcnt->rxglomframes, sdcnt->rxglompkts, - sdcnt->f2rxhdrs, sdcnt->f2rxdata, - sdcnt->f2txdata, sdcnt->f1regdata, - sdcnt->tickcnt, sdcnt->tx_ctlerrs, - sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, - sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); - - return 0; -} +struct brcmf_debugfs_entry { + int (*read)(struct seq_file *seq, void *data); + struct brcmf_pub *drvr; +}; -static int brcmf_debugfs_sdio_count_open(struct inode *inode, struct file *f) +static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f) { - return single_open(f, brcmf_debugfs_sdio_count_read, inode->i_private); + struct brcmf_debugfs_entry *entry = inode->i_private; + + return single_open(f, entry->read, entry->drvr->bus_if->dev); } -static const struct file_operations brcmf_debugfs_sdio_counter_ops = { +static const struct file_operations brcmf_debugfs_def_ops = { .owner = THIS_MODULE, - .open = brcmf_debugfs_sdio_count_open, + .open = brcmf_debugfs_entry_open, .release = single_release, .read = seq_read, .llseek = seq_lseek }; -void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, - struct brcmf_sdio_count *sdcnt) +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) { - struct dentry *dentry = drvr->dbgfs_dir; + struct dentry *dentry = drvr->dbgfs_dir; + struct brcmf_debugfs_entry *entry; - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("counters", S_IRUGO, dentry, - sdcnt, &brcmf_debugfs_sdio_counter_ops); -} + if (IS_ERR_OR_NULL(dentry)) + return -ENOENT; -static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) -{ - struct brcmf_fws_stats *fwstats = seq->private; - - seq_printf(seq, - "header_pulls: %u\n" - "header_only_pkt: %u\n" - "tlv_parse_failed: %u\n" - "tlv_invalid_type: %u\n" - "mac_update_fails: %u\n" - "ps_update_fails: %u\n" - "if_update_fails: %u\n" - "pkt2bus: %u\n" - "generic_error: %u\n" - "rollback_success: %u\n" - "rollback_failed: %u\n" - "delayq_full: %u\n" - "supprq_full: %u\n" - "txs_indicate: %u\n" - "txs_discard: %u\n" - "txs_suppr_core: %u\n" - "txs_suppr_ps: %u\n" - "txs_tossed: %u\n" - "txs_host_tossed: %u\n" - "bus_flow_block: %u\n" - "fws_flow_block: %u\n" - "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", - fwstats->header_pulls, - fwstats->header_only_pkt, - fwstats->tlv_parse_failed, - fwstats->tlv_invalid_type, - fwstats->mac_update_failed, - fwstats->mac_ps_update_failed, - fwstats->if_update_failed, - fwstats->pkt2bus, - fwstats->generic_error, - fwstats->rollback_success, - fwstats->rollback_failed, - fwstats->delayq_full_error, - fwstats->supprq_full_error, - fwstats->txs_indicate, - fwstats->txs_discard, - fwstats->txs_supp_core, - fwstats->txs_supp_ps, - fwstats->txs_tossed, - fwstats->txs_host_tossed, - fwstats->bus_flow_block, - fwstats->fws_flow_block, - fwstats->send_pkts[0], fwstats->send_pkts[1], - fwstats->send_pkts[2], fwstats->send_pkts[3], - fwstats->send_pkts[4], - fwstats->requested_sent[0], - fwstats->requested_sent[1], - fwstats->requested_sent[2], - fwstats->requested_sent[3], - fwstats->requested_sent[4]); + entry = devm_kzalloc(drvr->bus_if->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; - return 0; -} + entry->read = read_fn; + entry->drvr = drvr; -static int brcmf_debugfs_fws_stats_open(struct inode *inode, struct file *f) -{ - return single_open(f, brcmf_debugfs_fws_stats_read, inode->i_private); -} - -static const struct file_operations brcmf_debugfs_fws_stats_ops = { - .owner = THIS_MODULE, - .open = brcmf_debugfs_fws_stats_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; - -void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, - struct brcmf_fws_stats *stats) -{ - struct dentry *dentry = drvr->dbgfs_dir; + dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry, + &brcmf_debugfs_def_ops); - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("fws_stats", S_IRUGO, dentry, - stats, &brcmf_debugfs_fws_stats_ops); + return PTR_ERR_OR_ZERO(dentry); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index ef52ed7abc6934..6eade7c60c6355 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -100,68 +100,6 @@ do { \ extern int brcmf_msg_level; -/* - * hold counter variables used in brcmfmac sdio driver. - */ -struct brcmf_sdio_count { - uint intrcount; /* Count of device interrupt callbacks */ - uint lastintrs; /* Count as of last watchdog timer */ - uint pollcnt; /* Count of active polls */ - uint regfails; /* Count of R_REG failures */ - uint tx_sderrs; /* Count of tx attempts with sd errors */ - uint fcqueued; /* Tx packets that got queued */ - uint rxrtx; /* Count of rtx requests (NAK to dongle) */ - uint rx_toolong; /* Receive frames too long to receive */ - uint rxc_errors; /* SDIO errors when reading control frames */ - uint rx_hdrfail; /* SDIO errors on header reads */ - uint rx_badhdr; /* Bad received headers (roosync?) */ - uint rx_badseq; /* Mismatched rx sequence number */ - uint fc_rcvd; /* Number of flow-control events received */ - uint fc_xoff; /* Number which turned on flow-control */ - uint fc_xon; /* Number which turned off flow-control */ - uint rxglomfail; /* Failed deglom attempts */ - uint rxglomframes; /* Number of glom frames (superframes) */ - uint rxglompkts; /* Number of packets from glom frames */ - uint f2rxhdrs; /* Number of header reads */ - uint f2rxdata; /* Number of frame data reads */ - uint f2txdata; /* Number of f2 frame writes */ - uint f1regdata; /* Number of f1 register accesses */ - uint tickcnt; /* Number of watchdog been schedule */ - ulong tx_ctlerrs; /* Err of sending ctrl frames */ - ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ - ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ - ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ - ulong rx_readahead_cnt; /* packets where header read-ahead was used */ -}; - -struct brcmf_fws_stats { - u32 tlv_parse_failed; - u32 tlv_invalid_type; - u32 header_only_pkt; - u32 header_pulls; - u32 pkt2bus; - u32 send_pkts[5]; - u32 requested_sent[5]; - u32 generic_error; - u32 mac_update_failed; - u32 mac_ps_update_failed; - u32 if_update_failed; - u32 packet_request_failed; - u32 credit_request_failed; - u32 rollback_success; - u32 rollback_failed; - u32 delayq_full_error; - u32 supprq_full_error; - u32 txs_indicate; - u32 txs_discard; - u32 txs_supp_core; - u32 txs_supp_ps; - u32 txs_tossed; - u32 txs_host_tossed; - u32 bus_flow_block; - u32 fws_flow_block; -}; - struct brcmf_pub; #ifdef DEBUG void brcmf_debugfs_init(void); @@ -169,10 +107,8 @@ void brcmf_debugfs_exit(void); int brcmf_debugfs_attach(struct brcmf_pub *drvr); void brcmf_debugfs_detach(struct brcmf_pub *drvr); struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); -void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, - struct brcmf_sdio_count *sdcnt); -void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, - struct brcmf_fws_stats *stats); +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)); #else static inline void brcmf_debugfs_init(void) { @@ -187,9 +123,11 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) { } -static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, - struct brcmf_fws_stats *stats) +static inline +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) { + return 0; } #endif diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 0c98a7942a0ff2..c0486329331b00 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -391,6 +391,40 @@ struct brcmf_sdio_hdrinfo { u16 tail_pad; }; +/* + * hold counter variables + */ +struct brcmf_sdio_count { + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint pollcnt; /* Count of active polls */ + uint regfails; /* Count of R_REG failures */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + uint tickcnt; /* Number of watchdog been schedule */ + ulong tx_ctlerrs; /* Err of sending ctrl frames */ + ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ + ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ + ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ + ulong rx_readahead_cnt; /* packets where header read-ahead was used */ +}; + /* misc chip info needed by some of the routines */ /* Private data for SDIO bus interaction */ struct brcmf_sdio { @@ -3070,23 +3104,50 @@ static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) { - struct brcmf_sdio *bus = seq->private; + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus; return brcmf_sdio_died_dump(seq, bus); } -static int brcmf_sdio_forensic_open(struct inode *inode, struct file *f) +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) { - return single_open(f, brcmf_sdio_forensic_read, inode->i_private); -} + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt; -static const struct file_operations brcmf_sdio_forensic_ops = { - .owner = THIS_MODULE, - .open = brcmf_sdio_forensic_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; + seq_printf(seq, + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + + return 0; +} static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) { @@ -3096,9 +3157,9 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) if (IS_ERR_OR_NULL(dentry)) return; - debugfs_create_file("forensics", S_IRUGO, dentry, bus, - &brcmf_sdio_forensic_ops); - brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); + brcmf_debugfs_add_entry(drvr, "counters", + brcmf_debugfs_sdio_count_read); debugfs_create_u32("console_interval", 0644, dentry, &bus->console_interval); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 699908de314a94..d42f7d04b65f11 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -454,6 +454,34 @@ struct brcmf_fws_macdesc_table { struct brcmf_fws_mac_descriptor other; }; +struct brcmf_fws_stats { + u32 tlv_parse_failed; + u32 tlv_invalid_type; + u32 header_only_pkt; + u32 header_pulls; + u32 pkt2bus; + u32 send_pkts[5]; + u32 requested_sent[5]; + u32 generic_error; + u32 mac_update_failed; + u32 mac_ps_update_failed; + u32 if_update_failed; + u32 packet_request_failed; + u32 credit_request_failed; + u32 rollback_success; + u32 rollback_failed; + u32 delayq_full_error; + u32 supprq_full_error; + u32 txs_indicate; + u32 txs_discard; + u32 txs_supp_core; + u32 txs_supp_ps; + u32 txs_tossed; + u32 txs_host_tossed; + u32 bus_flow_block; + u32 fws_flow_block; +}; + struct brcmf_fws_info { struct brcmf_pub *drvr; spinlock_t spinlock; @@ -2017,6 +2045,75 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) brcmf_fws_unlock(fws); } +#ifdef DEBUG +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats; + + seq_printf(seq, + "header_pulls: %u\n" + "header_only_pkt: %u\n" + "tlv_parse_failed: %u\n" + "tlv_invalid_type: %u\n" + "mac_update_fails: %u\n" + "ps_update_fails: %u\n" + "if_update_fails: %u\n" + "pkt2bus: %u\n" + "generic_error: %u\n" + "rollback_success: %u\n" + "rollback_failed: %u\n" + "delayq_full: %u\n" + "supprq_full: %u\n" + "txs_indicate: %u\n" + "txs_discard: %u\n" + "txs_suppr_core: %u\n" + "txs_suppr_ps: %u\n" + "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" + "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + fwstats->header_pulls, + fwstats->header_only_pkt, + fwstats->tlv_parse_failed, + fwstats->tlv_invalid_type, + fwstats->mac_update_failed, + fwstats->mac_ps_update_failed, + fwstats->if_update_failed, + fwstats->pkt2bus, + fwstats->generic_error, + fwstats->rollback_success, + fwstats->rollback_failed, + fwstats->delayq_full_error, + fwstats->supprq_full_error, + fwstats->txs_indicate, + fwstats->txs_discard, + fwstats->txs_supp_core, + fwstats->txs_supp_ps, + fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, + fwstats->send_pkts[0], fwstats->send_pkts[1], + fwstats->send_pkts[2], fwstats->send_pkts[3], + fwstats->send_pkts[4], + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); + + return 0; +} +#else +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif + int brcmf_fws_init(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; @@ -2107,7 +2204,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr) BRCMF_FWS_PSQ_LEN); /* create debugfs file for statistics */ - brcmf_debugfs_create_fws_stats(drvr, &fws->stats); + brcmf_debugfs_add_entry(drvr, "fws_stats", + brcmf_debugfs_fws_stats_read); brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", fws->fw_signals ? "enabled" : "disabled", tlv); From 6c900c96e2350483180d5f2ee942b51689710270 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Sat, 12 Jul 2014 08:49:37 +0200 Subject: [PATCH 0162/1983] brcmfmac: Make firmware path a module parameter Upstream-commit: c1b20532ef395f23c2d24bc9c7f772a45e0420c7 This patch makes firmware path a module parameter so that firmware and nvram files can be loaded from the specified path. Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 47 +++++++++++-------- .../wireless/brcm80211/brcmfmac/firmware.c | 5 ++ .../wireless/brcm80211/brcmfmac/firmware.h | 5 ++ .../wireless/brcm80211/brcmfmac/sdio_host.h | 4 ++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index c0486329331b00..67d91d5cc13d85 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -666,28 +666,34 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; -static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, - enum brcmf_firmware_type type) +static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, + struct brcmf_sdio_dev *sdiodev) { int i; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { if (brcmf_fwname_data[i].chipid == ci->chip && - brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { - switch (type) { - case BRCMF_FIRMWARE_BIN: - return brcmf_fwname_data[i].bin; - case BRCMF_FIRMWARE_NVRAM: - return brcmf_fwname_data[i].nv; - default: - brcmf_err("invalid firmware type (%d)\n", type); - return NULL; - } - } + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) + break; } - brcmf_err("Unknown chipid %d [%d]\n", - ci->chip, ci->chiprev); - return NULL; + + if (i == ARRAY_SIZE(brcmf_fwname_data)) { + brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); + return -ENODEV; + } + + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/') + strcat(brcmf_firmware_path, "/"); + + strcpy(sdiodev->fw_name, brcmf_firmware_path); + strcpy(sdiodev->nvram_name, brcmf_firmware_path); + } + strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin); + strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv); + + return 0; } static void pkt_align(struct sk_buff *p, int len, int align) @@ -4160,11 +4166,12 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); + ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); + if (ret) + goto fail; + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, - brcmf_sdio_get_fwname(bus->ci, - BRCMF_FIRMWARE_BIN), - brcmf_sdio_get_fwname(bus->ci, - BRCMF_FIRMWARE_NVRAM), + sdiodev->fw_name, sdiodev->nvram_name, brcmf_sdio_firmware_callback); if (ret != 0) { brcmf_err("async firmware request failed: %d\n", ret); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 7b7d237c1ddb27..8ea9f283d2b806 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -18,10 +18,15 @@ #include #include #include +#include #include "dhd_dbg.h" #include "firmware.h" +char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; +module_param_string(firmware_path, brcmf_firmware_path, + BRCMF_FW_PATH_LEN, 0440); + enum nvram_parser_state { IDLE, KEY, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 6431bfd7afffc6..4d3482356b77b0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -21,6 +21,11 @@ #define BRCMF_FW_REQ_FLAGS 0x00F0 #define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 +#define BRCMF_FW_PATH_LEN 256 +#define BRCMF_FW_NAME_LEN 32 + +extern char brcmf_firmware_path[]; + void brcmf_fw_nvram_free(void *nvram); /* * Request firmware(s) asynchronously. When the asynchronous request diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 3deab7959a0d9a..6c5e585ccda99f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -18,6 +18,8 @@ #define _BRCM_SDH_H_ #include +#include +#include "firmware.h" #define SDIO_FUNC_0 0 #define SDIO_FUNC_1 1 @@ -182,6 +184,8 @@ struct brcmf_sdio_dev { uint max_segment_size; uint txglomsz; struct sg_table sgtable; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; }; /* sdio core registers */ From 5989d7f6732030e98a5fdb346fec27227a750f8a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:38 +0200 Subject: [PATCH 0163/1983] brcmfmac: move attach and detach functions in wl_cfg80211.c Upstream-commit: ccfd1e810bb7f053e74bc796b84c8e1de37c21fa Preparing for another patch move the functions in separate commit. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 215 +++++++++--------- 1 file changed, 107 insertions(+), 108 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 31206fe78bf2ed..2f7d4ee6f879de 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4967,114 +4967,6 @@ static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) return err; } -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev) -{ - struct net_device *ndev = drvr->iflist[0]->ndev; - struct brcmf_cfg80211_info *cfg; - struct wiphy *wiphy; - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - s32 err = 0; - s32 io_type; - - if (!ndev) { - brcmf_err("ndev is invalid\n"); - return NULL; - } - - ifp = netdev_priv(ndev); - wiphy = brcmf_setup_wiphy(busdev); - if (IS_ERR(wiphy)) - return NULL; - - cfg = wiphy_priv(wiphy); - cfg->wiphy = wiphy; - cfg->pub = drvr; - init_vif_event(&cfg->vif_event); - INIT_LIST_HEAD(&cfg->vif_list); - - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); - if (IS_ERR(vif)) { - wiphy_free(wiphy); - return NULL; - } - - vif->ifp = ifp; - vif->wdev.netdev = ndev; - ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); - - err = wl_init_priv(cfg); - if (err) { - brcmf_err("Failed to init iwm_priv (%d)\n", err); - goto cfg80211_attach_out; - } - ifp->vif = vif; - - err = brcmf_p2p_attach(cfg); - if (err) { - brcmf_err("P2P initilisation failed (%d)\n", err); - goto cfg80211_p2p_attach_out; - } - err = brcmf_btcoex_attach(cfg); - if (err) { - brcmf_err("BT-coex initialisation failed (%d)\n", err); - brcmf_p2p_detach(&cfg->p2p); - goto cfg80211_p2p_attach_out; - } - - /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), - * setup 40MHz in 2GHz band and enable OBSS scanning. - */ - if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - err = brcmf_enable_bw40_2g(ifp); - if (!err) - err = brcmf_fil_iovar_int_set(ifp, "obss_coex", - BRCMF_OBSS_COEX_AUTO); - } - /* clear for now and rely on update later */ - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; - - err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); - if (err) { - brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); - wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; - } - - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, - &io_type); - if (err) { - brcmf_err("Failed to get D11 version (%d)\n", err); - goto cfg80211_p2p_attach_out; - } - cfg->d11inf.io_type = (u8)io_type; - brcmu_d11_attach(&cfg->d11inf); - - return cfg; - -cfg80211_p2p_attach_out: - wl_deinit_priv(cfg); - -cfg80211_attach_out: - brcmf_free_vif(vif); - return NULL; -} - -void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) -{ - if (!cfg) - return; - - WARN_ON(!list_empty(&cfg->vif_list)); - wiphy_unregister(cfg->wiphy); - brcmf_btcoex_detach(cfg); - wl_deinit_priv(cfg); - wiphy_free(cfg->wiphy); -} - static s32 brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { @@ -5658,3 +5550,110 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, + struct device *busdev) +{ + struct net_device *ndev = drvr->iflist[0]->ndev; + struct brcmf_cfg80211_info *cfg; + struct wiphy *wiphy; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + s32 err = 0; + s32 io_type; + + if (!ndev) { + brcmf_err("ndev is invalid\n"); + return NULL; + } + + ifp = netdev_priv(ndev); + wiphy = brcmf_setup_wiphy(busdev); + if (IS_ERR(wiphy)) + return NULL; + + cfg = wiphy_priv(wiphy); + cfg->wiphy = wiphy; + cfg->pub = drvr; + init_vif_event(&cfg->vif_event); + INIT_LIST_HEAD(&cfg->vif_list); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); + if (IS_ERR(vif)) { + wiphy_free(wiphy); + return NULL; + } + + vif->ifp = ifp; + vif->wdev.netdev = ndev; + ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); + + err = wl_init_priv(cfg); + if (err) { + brcmf_err("Failed to init iwm_priv (%d)\n", err); + goto cfg80211_attach_out; + } + ifp->vif = vif; + + err = brcmf_p2p_attach(cfg); + if (err) { + brcmf_err("P2P initilisation failed (%d)\n", err); + goto cfg80211_p2p_attach_out; + } + err = brcmf_btcoex_attach(cfg); + if (err) { + brcmf_err("BT-coex initialisation failed (%d)\n", err); + brcmf_p2p_detach(&cfg->p2p); + goto cfg80211_p2p_attach_out; + } + + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + err = brcmf_enable_bw40_2g(ifp); + if (!err) + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + } + /* clear for now and rely on update later */ + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; + + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + if (err) { + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } + + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, + &io_type); + if (err) { + brcmf_err("Failed to get D11 version (%d)\n", err); + goto cfg80211_p2p_attach_out; + } + cfg->d11inf.io_type = (u8)io_type; + brcmu_d11_attach(&cfg->d11inf); + + return cfg; + +cfg80211_p2p_attach_out: + wl_deinit_priv(cfg); + +cfg80211_attach_out: + brcmf_free_vif(vif); + return NULL; +} + +void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) +{ + if (!cfg) + return; + + WARN_ON(!list_empty(&cfg->vif_list)); + wiphy_unregister(cfg->wiphy); + brcmf_btcoex_detach(cfg); + wl_deinit_priv(cfg); + wiphy_free(cfg->wiphy); +} From a96dbb4bcc8d3162c5b81bcedc7fa9a693387c38 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:39 +0200 Subject: [PATCH 0164/1983] brcmfmac: introduce feature and quirk handling Upstream-commit: c08437b4b5c89677a81b543644b2d0a99484d947 Introducing a new source module that will be responsible for identifying features and quirks related to the device being handled. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 1 + drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 5 +- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 13 +- .../net/wireless/brcm80211/brcmfmac/feature.c | 136 ++++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/feature.h | 86 +++++++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 30 ++-- 6 files changed, 244 insertions(+), 27 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/feature.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/feature.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 4cffb2ee367381..de0cff3df38950 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -34,6 +34,7 @@ brcmfmac-objs += \ dhd_common.o \ dhd_linux.o \ firmware.o \ + feature.o \ btcoex.o \ vendor.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index a8998eb60d2216..7da6441bcfa8e8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -103,6 +103,10 @@ struct brcmf_pub { struct brcmf_ampdu_rx_reorder *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; + + u32 feat_flags; + u32 chip_quirks; + #ifdef DEBUG struct dentry *dbgfs_dir; #endif @@ -175,7 +179,6 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -u32 brcmf_get_chip_info(struct brcmf_if *ifp); void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 09dd8c13d84483..bf448081d6fb98 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -30,6 +30,7 @@ #include "wl_cfg80211.h" #include "fwil.h" #include "fwsignal.h" +#include "feature.h" #include "proto.h" MODULE_AUTHOR("Broadcom Corporation"); @@ -936,6 +937,8 @@ int brcmf_bus_start(struct device *dev) if (ret < 0) goto fail; + brcmf_feat_attach(drvr); + ret = brcmf_fws_init(drvr); if (ret < 0) goto fail; @@ -1073,16 +1076,6 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev) return !err; } -/* - * return chip id and rev of the device encoded in u32. - */ -u32 brcmf_get_chip_info(struct brcmf_if *ifp) -{ - struct brcmf_bus *bus = ifp->drvr->bus_if; - - return bus->chip << 4 | bus->chiprev; -} - static void brcmf_driver_register(struct work_struct *work) { #ifdef CONFIG_BRCMFMAC_SDIO diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c new file mode 100644 index 00000000000000..50877e3c5d2fdb --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include "dhd.h" +#include "dhd_bus.h" +#include "dhd_dbg.h" +#include "fwil.h" +#include "feature.h" + +/* + * firmware error code received if iovar is unsupported. + */ +#define EBRCMF_FEAT_UNSUPPORTED 23 + +/* + * expand feature list to array of feature strings. + */ +#define BRCMF_FEAT_DEF(_f) \ + #_f, +static const char *brcmf_feat_names[] = { + BRCMF_FEAT_LIST +}; +#undef BRCMF_FEAT_DEF + +#ifdef DEBUG +/* + * expand quirk list to array of quirk strings. + */ +#define BRCMF_QUIRK_DEF(_q) \ + #_q, +static const char * const brcmf_quirk_names[] = { + BRCMF_QUIRK_LIST +}; +#undef BRCMF_QUIRK_DEF + +/** + * brcmf_feat_debugfs_read() - expose feature info to debugfs. + * + * @seq: sequence for debugfs entry. + * @data: raw data pointer. + */ +static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + u32 feats = bus_if->drvr->feat_flags; + u32 quirks = bus_if->drvr->chip_quirks; + int id; + + seq_printf(seq, "Features: %08x\n", feats); + for (id = 0; id < BRCMF_FEAT_LAST; id++) + if (feats & BIT(id)) + seq_printf(seq, "\t%s\n", brcmf_feat_names[id]); + seq_printf(seq, "\nQuirks: %08x\n", quirks); + for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++) + if (quirks & BIT(id)) + seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]); + return 0; +} +#else +static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif /* DEBUG */ + +/** + * brcmf_feat_iovar_int_get() - determine feature through iovar query. + * + * @ifp: interface to query. + * @id: feature id. + * @name: iovar name. + */ +static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, + enum brcmf_feat_id id, char *name) +{ + u32 data; + int err; + + err = brcmf_fil_iovar_int_get(ifp, name, &data); + if (err == 0) { + brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } else { + brcmf_dbg(TRACE, "%s feature check failed: %d\n", + brcmf_feat_names[id], err); + } +} + +void brcmf_feat_attach(struct brcmf_pub *drvr) +{ + struct brcmf_if *ifp = drvr->iflist[0]; + + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + + /* set chip related quirks */ + switch (drvr->bus_if->chip) { + case BRCM_CC_43236_CHIP_ID: + drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH); + break; + case BRCM_CC_4329_CHIP_ID: + drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC); + break; + default: + /* no quirks */ + break; + } + + brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read); +} + +bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id) +{ + return (ifp->drvr->feat_flags & BIT(id)); +} + +bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, + enum brcmf_feat_quirk quirk) +{ + return (ifp->drvr->chip_quirks & BIT(quirk)); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h new file mode 100644 index 00000000000000..961d175f8afb0d --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _BRCMF_FEATURE_H +#define _BRCMF_FEATURE_H + +/* + * Features: + * + * MCHAN: multi-channel for concurrent P2P. + */ +#define BRCMF_FEAT_LIST \ + BRCMF_FEAT_DEF(MCHAN) +/* + * Quirks: + * + * AUTO_AUTH: workaround needed for automatic authentication type. + * NEED_MPC: driver needs to disable MPC during scanning operation. + */ +#define BRCMF_QUIRK_LIST \ + BRCMF_QUIRK_DEF(AUTO_AUTH) \ + BRCMF_QUIRK_DEF(NEED_MPC) + +#define BRCMF_FEAT_DEF(_f) \ + BRCMF_FEAT_ ## _f, +/* + * expand feature list to enumeration. + */ +enum brcmf_feat_id { + BRCMF_FEAT_LIST + BRCMF_FEAT_LAST +}; +#undef BRCMF_FEAT_DEF + +#define BRCMF_QUIRK_DEF(_q) \ + BRCMF_FEAT_QUIRK_ ## _q, +/* + * expand quirk list to enumeration. + */ +enum brcmf_feat_quirk { + BRCMF_QUIRK_LIST + BRCMF_FEAT_QUIRK_LAST +}; +#undef BRCMF_QUIRK_DEF + +/** + * brcmf_feat_attach() - determine features and quirks. + * + * @drvr: driver instance. + */ +void brcmf_feat_attach(struct brcmf_pub *drvr); + +/** + * brcmf_feat_is_enabled() - query feature. + * + * @ifp: interface instance. + * @id: feature id to check. + * + * Return: true is feature is enabled; otherwise false. + */ +bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id); + +/** + * brcmf_feat_is_quirk_enabled() - query chip quirk. + * + * @ifp: interface instance. + * @quirk: quirk id to check. + * + * Return: true is quirk is enabled; otherwise false. + */ +bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, + enum brcmf_feat_quirk quirk); + +#endif /* _BRCMF_FEATURE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 2f7d4ee6f879de..68757da697385d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -33,6 +33,7 @@ #include "p2p.h" #include "btcoex.h" #include "wl_cfg80211.h" +#include "feature.h" #include "fwil.h" #include "vendor.h" @@ -592,7 +593,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) { - if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329) + if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) brcmf_set_mpc(ifp, mpc); } @@ -1619,17 +1620,10 @@ static enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, enum nl80211_auth_type type) { - u32 ci; - if (type == NL80211_AUTHTYPE_AUTOMATIC) { - /* shift to ignore chip revision */ - ci = brcmf_get_chip_info(ifp) >> 4; - switch (ci) { - case 43236: - brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n"); - return NL80211_AUTHTYPE_OPEN_SYSTEM; - default: - break; - } + if (type == NL80211_AUTHTYPE_AUTOMATIC && + brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { + brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); + type = NL80211_AUTHTYPE_OPEN_SYSTEM; } return type; } @@ -4310,10 +4304,10 @@ static const struct ieee80211_iface_limit brcmf_iface_limits[] = { .types = BIT(NL80211_IFTYPE_P2P_DEVICE) } }; -static const struct ieee80211_iface_combination brcmf_iface_combos[] = { +static struct ieee80211_iface_combination brcmf_iface_combos[] = { { .max_interfaces = BRCMF_IFACE_MAX_CNT, - .num_different_channels = 2, + .num_different_channels = 1, .n_limits = ARRAY_SIZE(brcmf_iface_limits), .limits = brcmf_iface_limits } @@ -4348,7 +4342,8 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { } }; -static struct wiphy *brcmf_setup_wiphy(struct device *phydev) +static +struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, struct device *phydev) { struct wiphy *wiphy; s32 err = 0; @@ -4368,6 +4363,9 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); + /* need VSDB firmware feature for concurrent channels */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) + brcmf_iface_combos[0].num_different_channels = 2; wiphy->iface_combinations = brcmf_iface_combos; wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; @@ -5567,7 +5565,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, } ifp = netdev_priv(ndev); - wiphy = brcmf_setup_wiphy(busdev); + wiphy = brcmf_setup_wiphy(ifp, busdev); if (IS_ERR(wiphy)) return NULL; From 21d7bc8d162986f55454e07eb3cdefdd534c3f5d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:40 +0200 Subject: [PATCH 0165/1983] brcmfmac: moving some functions around Upstream-commit: aa70b4fa43dfe5dc3df2161d814ccaa7897d73c9 Just reordering the functions in preparation of subsequent changes. Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 288 +++++++++--------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 68757da697385d..ff196cfe150cdd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4278,126 +4278,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { .tdls_oper = brcmf_cfg80211_tdls_oper, }; -static void brcmf_wiphy_pno_params(struct wiphy *wiphy) -{ - /* scheduled scan settings */ - wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; -} - -static const struct ieee80211_iface_limit brcmf_iface_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE) - } -}; -static struct ieee80211_iface_combination brcmf_iface_combos[] = { - { - .max_interfaces = BRCMF_IFACE_MAX_CNT, - .num_different_channels = 1, - .n_limits = ARRAY_SIZE(brcmf_iface_limits), - .limits = brcmf_iface_limits - } -}; - -static const struct ieee80211_txrx_stypes -brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { - [NL80211_IFTYPE_STATION] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_CLIENT] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_GO] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | - BIT(IEEE80211_STYPE_DISASSOC >> 4) | - BIT(IEEE80211_STYPE_AUTH >> 4) | - BIT(IEEE80211_STYPE_DEAUTH >> 4) | - BIT(IEEE80211_STYPE_ACTION >> 4) - }, - [NL80211_IFTYPE_P2P_DEVICE] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - } -}; - -static -struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, struct device *phydev) -{ - struct wiphy *wiphy; - s32 err = 0; - - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); - if (!wiphy) { - brcmf_err("Could not allocate wiphy device\n"); - return ERR_PTR(-ENOMEM); - } - set_wiphy_dev(wiphy, phydev); - wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; - wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE); - /* need VSDB firmware feature for concurrent channels */ - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) - brcmf_iface_combos[0].num_different_channels = 2; - wiphy->iface_combinations = brcmf_iface_combos; - wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); - wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->cipher_suites = __wl_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); - wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | - WIPHY_FLAG_OFFCHAN_TX | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_TDLS; - if (!brcmf_roamoff) - wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; - wiphy->mgmt_stypes = brcmf_txrx_stypes; - wiphy->max_remain_on_channel_duration = 5000; - brcmf_wiphy_pno_params(wiphy); - brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); - - /* vendor commands/events support */ - wiphy->vendor_commands = brcmf_vendor_cmds; - wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; - - err = wiphy_register(wiphy); - if (err < 0) { - brcmf_err("Could not register wiphy device (%d)\n", err); - wiphy_free(wiphy); - return ERR_PTR(err); - } - return wiphy; -} - struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype type, bool pm_block) @@ -4941,30 +4821,6 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } -static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) -{ - struct brcmf_fil_bwcap_le band_bwcap; - u32 val; - int err; - - /* verify support for bw_cap command */ - val = WLC_BAND_5G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); - - if (!err) { - /* only set 2G bandwidth using bw_cap command */ - band_bwcap.band = cpu_to_le32(WLC_BAND_2G); - band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); - err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, - sizeof(band_bwcap)); - } else { - brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); - val = WLC_N_BW_40ALL; - err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); - } - return err; -} - static s32 brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { @@ -5194,6 +5050,30 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, return err; } +static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) +{ + struct brcmf_fil_bwcap_le band_bwcap; + u32 val; + int err; + + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } + return err; +} + static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) { u32 band, mimo_bwcap; @@ -5377,6 +5257,126 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) return err; } +static const struct ieee80211_iface_limit brcmf_iface_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + } +}; +static struct ieee80211_iface_combination brcmf_iface_combos[] = { + { + .max_interfaces = BRCMF_IFACE_MAX_CNT, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(brcmf_iface_limits), + .limits = brcmf_iface_limits + } +}; + +static const struct ieee80211_txrx_stypes +brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + } +}; + +static void brcmf_wiphy_pno_params(struct wiphy *wiphy) +{ + /* scheduled scan settings */ + wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +} + +static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, + struct device *phydev) +{ + struct wiphy *wiphy; + s32 err = 0; + + wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); + if (!wiphy) { + brcmf_err("Could not allocate wiphy device\n"); + return ERR_PTR(-ENOMEM); + } + set_wiphy_dev(wiphy, phydev); + wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; + wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + /* need VSDB firmware feature for concurrent channels */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) + brcmf_iface_combos[0].num_different_channels = 2; + wiphy->iface_combinations = brcmf_iface_combos; + wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); + wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->cipher_suites = __wl_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | + WIPHY_FLAG_OFFCHAN_TX | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + wiphy->mgmt_stypes = brcmf_txrx_stypes; + wiphy->max_remain_on_channel_duration = 5000; + brcmf_wiphy_pno_params(wiphy); + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* vendor commands/events support */ + wiphy->vendor_commands = brcmf_vendor_cmds; + wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + + err = wiphy_register(wiphy); + if (err < 0) { + brcmf_err("Could not register wiphy device (%d)\n", err); + wiphy_free(wiphy); + return ERR_PTR(err); + } + return wiphy; +} + static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg) { From 4f0283c050d1cbd02597436060b79bee3d435757 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:41 +0200 Subject: [PATCH 0166/1983] brcmfmac: rework wiphy structure setup Upstream-commit: b48d891676f756d48b4d0ee131e4a7a5d43ca417 Instead of waiting for IFF_UP of the primary net device to determine the band and channel information of the wiphy structure, this is now done during driver initialization in brcmf_cfg80211_attach(). The channel information is obtained from the device and the 2G band is updated when 40MHz bandwidth is enabled for that band. Before this change the band and channel objects were common between multiple brcmfmac devices in the system, which make that information rather unreliable. That is also fixed with this reworked implementation. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 566 +++++++++--------- 1 file changed, 298 insertions(+), 268 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index ff196cfe150cdd..34922ba4618a85 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -103,24 +103,6 @@ static bool check_vif_up(struct brcmf_cfg80211_vif *vif) return true; } -#define CHAN2G(_channel, _freq, _flags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -#define CHAN5G(_channel, _flags) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 5000 + (5 * (_channel)), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - #define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) #define RATETAB_ENT(_rateid, _flags) \ { \ @@ -149,58 +131,17 @@ static struct ieee80211_rate __wl_rates[] = { #define wl_g_rates (__wl_rates + 0) #define wl_g_rates_size 12 -static struct ieee80211_channel __wl_2ghz_channels[] = { - CHAN2G(1, 2412, 0), - CHAN2G(2, 2417, 0), - CHAN2G(3, 2422, 0), - CHAN2G(4, 2427, 0), - CHAN2G(5, 2432, 0), - CHAN2G(6, 2437, 0), - CHAN2G(7, 2442, 0), - CHAN2G(8, 2447, 0), - CHAN2G(9, 2452, 0), - CHAN2G(10, 2457, 0), - CHAN2G(11, 2462, 0), - CHAN2G(12, 2467, 0), - CHAN2G(13, 2472, 0), - CHAN2G(14, 2484, 0), -}; - -static struct ieee80211_channel __wl_5ghz_a_channels[] = { - CHAN5G(34, 0), CHAN5G(36, 0), - CHAN5G(38, 0), CHAN5G(40, 0), - CHAN5G(42, 0), CHAN5G(44, 0), - CHAN5G(46, 0), CHAN5G(48, 0), - CHAN5G(52, 0), CHAN5G(56, 0), - CHAN5G(60, 0), CHAN5G(64, 0), - CHAN5G(100, 0), CHAN5G(104, 0), - CHAN5G(108, 0), CHAN5G(112, 0), - CHAN5G(116, 0), CHAN5G(120, 0), - CHAN5G(124, 0), CHAN5G(128, 0), - CHAN5G(132, 0), CHAN5G(136, 0), - CHAN5G(140, 0), CHAN5G(149, 0), - CHAN5G(153, 0), CHAN5G(157, 0), - CHAN5G(161, 0), CHAN5G(165, 0), - CHAN5G(184, 0), CHAN5G(188, 0), - CHAN5G(192, 0), CHAN5G(196, 0), - CHAN5G(200, 0), CHAN5G(204, 0), - CHAN5G(208, 0), CHAN5G(212, 0), - CHAN5G(216, 0), -}; - -static struct ieee80211_supported_band __wl_band_2ghz = { +/* Band templates duplicated per wiphy. The channel info + * is filled in after querying the device. + */ +static const struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, - .channels = __wl_2ghz_channels, - .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size, - .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true}, }; -static struct ieee80211_supported_band __wl_band_5ghz_a = { +static const struct ieee80211_supported_band __wl_band_5ghz_a = { .band = IEEE80211_BAND_5GHZ, - .channels = __wl_5ghz_a_channels, - .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, }; @@ -4913,25 +4854,77 @@ brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, return err; } +/* Filter the list of channels received from firmware counting only + * the 20MHz channels. The wiphy band data only needs those which get + * flagged to indicate if they can take part in higher bandwidth. + */ +static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, + struct brcmf_chanspec_list *chlist, + u32 chcnt[]) +{ + u32 total = le32_to_cpu(chlist->count); + struct brcmu_chan ch; + int i; + + for (i = 0; i <= total; i++) { + ch.chspec = (u16)le32_to_cpu(chlist->element[i]); + cfg->d11inf.decchspec(&ch); + + /* Firmware gives a ordered list. We skip non-20MHz + * channels is 2G. For 5G we can abort upon reaching + * a non-20MHz channel in the list. + */ + if (ch.bw != BRCMU_CHAN_BW_20) { + if (ch.band == BRCMU_CHAN_BAND_5G) + break; + else + continue; + } + + if (ch.band == BRCMU_CHAN_BAND_2G) + chcnt[0] += 1; + else if (ch.band == BRCMU_CHAN_BAND_5G) + chcnt[1] += 1; + } +} + +static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, + struct brcmu_chan *ch) +{ + u32 ht40_flag; -static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, - u32 bw_cap[]) + ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; + if (ch->sb == BRCMU_CHAN_SB_U) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + * IEEE80211_CHAN_NO_HT40 or + * IEEE80211_CHAN_NO_HT40PLUS + */ + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + } +} + +static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, + u32 bw_cap[]) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct ieee80211_channel *band_chan_arr; + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; + struct wiphy *wiphy; struct brcmf_chanspec_list *list; struct brcmu_chan ch; - s32 err; + int err; u8 *pbuf; u32 i, j; u32 total; - enum ieee80211_band band; - u32 channel; - u32 *n_cnt; + u32 chaninfo; + u32 chcnt[2] = { 0, 0 }; u32 index; - u32 ht40_flag; - bool update; - u32 array_size; pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); @@ -4944,11 +4937,45 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, BRCMF_DCMD_MEDLEN); if (err) { brcmf_err("get chanspecs error (%d)\n", err); - goto exit; + goto fail_pbuf; } - __wl_band_2ghz.n_channels = 0; - __wl_band_5ghz_a.n_channels = 0; + brcmf_count_20mhz_channels(cfg, list, chcnt); + wiphy = cfg_to_wiphy(cfg); + if (chcnt[0]) { + band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), + GFP_KERNEL); + if (band == NULL) { + err = -ENOMEM; + goto fail_pbuf; + } + band->channels = kcalloc(chcnt[0], sizeof(*channel), + GFP_KERNEL); + if (band->channels == NULL) { + kfree(band); + err = -ENOMEM; + goto fail_pbuf; + } + band->n_channels = 0; + wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + if (chcnt[1]) { + band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a), + GFP_KERNEL); + if (band == NULL) { + err = -ENOMEM; + goto fail_band2g; + } + band->channels = kcalloc(chcnt[1], sizeof(*channel), + GFP_KERNEL); + if (band->channels == NULL) { + kfree(band); + err = -ENOMEM; + goto fail_band2g; + } + band->n_channels = 0; + wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } total = le32_to_cpu(list->count); for (i = 0; i < total; i++) { @@ -4956,105 +4983,88 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, cfg->d11inf.decchspec(&ch); if (ch.band == BRCMU_CHAN_BAND_2G) { - band_chan_arr = __wl_2ghz_channels; - array_size = ARRAY_SIZE(__wl_2ghz_channels); - n_cnt = &__wl_band_2ghz.n_channels; - band = IEEE80211_BAND_2GHZ; + band = wiphy->bands[IEEE80211_BAND_2GHZ]; } else if (ch.band == BRCMU_CHAN_BAND_5G) { - band_chan_arr = __wl_5ghz_a_channels; - array_size = ARRAY_SIZE(__wl_5ghz_a_channels); - n_cnt = &__wl_band_5ghz_a.n_channels; - band = IEEE80211_BAND_5GHZ; + band = wiphy->bands[IEEE80211_BAND_5GHZ]; } else { brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); continue; } - if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) && + if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; - if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) && + if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_80) continue; - update = false; - for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { - if (band_chan_arr[j].hw_value == ch.chnum) { - update = true; + + channel = band->channels; + index = band->n_channels; + for (j = 0; j < band->n_channels; j++) { + if (channel[j].hw_value == ch.chnum) { + index = j; break; } } - if (update) - index = j; - else - index = *n_cnt; - if (index < array_size) { - band_chan_arr[index].center_freq = - ieee80211_channel_to_frequency(ch.chnum, band); - band_chan_arr[index].hw_value = ch.chnum; - - /* assuming the chanspecs order is HT20, - * HT40 upper, HT40 lower, and VHT80. + channel[index].center_freq = + ieee80211_channel_to_frequency(ch.chnum, band->band); + channel[index].hw_value = ch.chnum; + + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; + } else if (ch.bw == BRCMU_CHAN_BW_40) { + brcmf_update_bw40_channel_flag(&channel[index], &ch); + } else { + /* disable other bandwidths for now as mentioned + * order assure they are enabled for subsequent + * chanspecs. */ - if (ch.bw == BRCMU_CHAN_BW_80) { - band_chan_arr[index].flags &= - ~IEEE80211_CHAN_NO_80MHZ; - } else if (ch.bw == BRCMU_CHAN_BW_40) { - ht40_flag = band_chan_arr[index].flags & - IEEE80211_CHAN_NO_HT40; - if (ch.sb == BRCMU_CHAN_SB_U) { - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - band_chan_arr[index].flags &= - ~IEEE80211_CHAN_NO_HT40; - band_chan_arr[index].flags |= - IEEE80211_CHAN_NO_HT40PLUS; - } else { - /* It should be one of - * IEEE80211_CHAN_NO_HT40 or - * IEEE80211_CHAN_NO_HT40PLUS - */ - band_chan_arr[index].flags &= - ~IEEE80211_CHAN_NO_HT40; - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - band_chan_arr[index].flags |= - IEEE80211_CHAN_NO_HT40MINUS; - } - } else { - /* disable other bandwidths for now as mentioned - * order assure they are enabled for subsequent - * chanspecs. - */ - band_chan_arr[index].flags = - IEEE80211_CHAN_NO_HT40 | - IEEE80211_CHAN_NO_80MHZ; - ch.bw = BRCMU_CHAN_BW_20; - cfg->d11inf.encchspec(&ch); - channel = ch.chspec; - err = brcmf_fil_bsscfg_int_get(ifp, - "per_chan_info", - &channel); - if (!err) { - if (channel & WL_CHAN_RADAR) - band_chan_arr[index].flags |= - (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR); - if (channel & WL_CHAN_PASSIVE) - band_chan_arr[index].flags |= - IEEE80211_CHAN_NO_IR; - } + channel[index].flags = IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; + ch.bw = BRCMU_CHAN_BW_20; + cfg->d11inf.encchspec(&ch); + chaninfo = ch.chspec; + err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", + &chaninfo); + if (!err) { + if (chaninfo & WL_CHAN_RADAR) + channel[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR); + if (chaninfo & WL_CHAN_PASSIVE) + channel[index].flags |= + IEEE80211_CHAN_NO_IR; } - if (!update) - (*n_cnt)++; } + if (index == band->n_channels) + band->n_channels++; } -exit: + kfree(pbuf); + return 0; + +fail_band2g: + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); + wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; +fail_pbuf: kfree(pbuf); return err; } -static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) +static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) { + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_supported_band *band; struct brcmf_fil_bwcap_le band_bwcap; + struct brcmf_chanspec_list *list; + u8 *pbuf; u32 val; int err; + struct brcmu_chan ch; + u32 num_chan; + int i, j; /* verify support for bw_cap command */ val = WLC_BAND_5G; @@ -5071,6 +5081,50 @@ static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) val = WLC_N_BW_40ALL; err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); } + + if (!err) { + /* update channel info in 2G band */ + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_40; + ch.chnum = 0; + cfg->d11inf.encchspec(&ch); + + /* pass encoded chanspec in query */ + *(__le16 *)pbuf = cpu_to_le16(ch.chspec); + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + kfree(pbuf); + return err; + } + + band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ]; + list = (struct brcmf_chanspec_list *)pbuf; + num_chan = le32_to_cpu(list->count); + for (i = 0; i < num_chan; i++) { + ch.chspec = (u16)le32_to_cpu(list->element[i]); + cfg->d11inf.decchspec(&ch); + if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) + continue; + if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) + continue; + for (j = 0; j < band->n_channels; j++) { + if (band->channels[j].hw_value == ch.chnum) + break; + } + if (WARN_ON(j == band->n_channels)) + continue; + + brcmf_update_bw40_channel_flag(&band->channels[j], &ch); + } + } return err; } @@ -5164,44 +5218,19 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; } -static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) +static int brcmf_setup_wiphybands(struct wiphy *wiphy) { + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct wiphy *wiphy; - s32 phy_list; - u32 band_list[3]; u32 nmode = 0; u32 vhtmode = 0; - u32 bw_cap[2] = { 0, 0 }; + u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; u32 rxchain; u32 nchain; - s8 phy; - s32 err; - u32 nband; + int err; s32 i; - struct ieee80211_supported_band *bands[2] = { NULL, NULL }; struct ieee80211_supported_band *band; - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST, - &phy_list, sizeof(phy_list)); - if (err) { - brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err); - return err; - } - - phy = ((char *)&phy_list)[0]; - brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy); - - - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, - &band_list, sizeof(band_list)); - if (err) { - brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err); - return err; - } - brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", - band_list[0], band_list[1], band_list[2]); - (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); if (err) { @@ -5223,38 +5252,25 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) } brcmf_dbg(INFO, "nchain=%d\n", nchain); - err = brcmf_construct_reginfo(cfg, bw_cap); + err = brcmf_construct_chaninfo(cfg, bw_cap); if (err) { - brcmf_err("brcmf_construct_reginfo failed (%d)\n", err); + brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err); return err; } - nband = band_list[0]; - - for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) { - band = NULL; - if ((band_list[i] == WLC_BAND_5G) && - (__wl_band_5ghz_a.n_channels > 0)) - band = &__wl_band_5ghz_a; - else if ((band_list[i] == WLC_BAND_2G) && - (__wl_band_2ghz.n_channels > 0)) - band = &__wl_band_2ghz; - else + wiphy = cfg_to_wiphy(cfg); + for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { + band = wiphy->bands[i]; + if (band == NULL) continue; if (nmode) brcmf_update_ht_cap(band, bw_cap, nchain); if (vhtmode) brcmf_update_vht_cap(band, bw_cap, nchain); - bands[band->band] = band; } - wiphy = cfg_to_wiphy(cfg); - wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; - wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); - - return err; + return 0; } static const struct ieee80211_iface_limit brcmf_iface_limits[] = { @@ -5321,18 +5337,9 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } -static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, - struct device *phydev) +static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { - struct wiphy *wiphy; - s32 err = 0; - - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); - if (!wiphy) { - brcmf_err("Could not allocate wiphy device\n"); - return ERR_PTR(-ENOMEM); - } - set_wiphy_dev(wiphy, phydev); + struct ieee80211_iface_combination ifc_combo; wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; @@ -5343,11 +5350,13 @@ static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); /* need VSDB firmware feature for concurrent channels */ + ifc_combo = brcmf_iface_combos[0]; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) - brcmf_iface_combos[0].num_different_channels = 2; - wiphy->iface_combinations = brcmf_iface_combos; + ifc_combo.num_different_channels = 2; + wiphy->iface_combinations = kmemdup(&ifc_combo, + sizeof(ifc_combo), + GFP_KERNEL); wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); - wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = __wl_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); @@ -5360,27 +5369,12 @@ static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); - brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; - err = wiphy_register(wiphy); - if (err < 0) { - brcmf_err("Could not register wiphy device (%d)\n", err); - wiphy_free(wiphy); - return ERR_PTR(err); - } - return wiphy; -} - - -static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg) -{ - return brcmf_update_wiphybands(cfg); + return brcmf_setup_wiphybands(wiphy); } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) @@ -5418,9 +5412,6 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) NULL, NULL); if (err) goto default_conf_out; - err = brcmf_dongle_probecap(cfg); - if (err) - goto default_conf_out; brcmf_configure_arp_offload(ifp, true); @@ -5548,6 +5539,20 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static void brcmf_free_wiphy(struct wiphy *wiphy) +{ + kfree(wiphy->iface_combinations); + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); + } + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]); + } + wiphy_free(wiphy); +} + struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { @@ -5558,6 +5563,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct brcmf_if *ifp; s32 err = 0; s32 io_type; + u16 *cap = NULL; if (!ndev) { brcmf_err("ndev is invalid\n"); @@ -5565,9 +5571,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, } ifp = netdev_priv(ndev); - wiphy = brcmf_setup_wiphy(ifp, busdev); - if (IS_ERR(wiphy)) + wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); + if (!wiphy) { + brcmf_err("Could not allocate wiphy device\n"); return NULL; + } + set_wiphy_dev(wiphy, busdev); cfg = wiphy_priv(wiphy); cfg->wiphy = wiphy; @@ -5576,10 +5585,8 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, INIT_LIST_HEAD(&cfg->vif_list); vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); - if (IS_ERR(vif)) { - wiphy_free(wiphy); - return NULL; - } + if (IS_ERR(vif)) + goto wiphy_out; vif->ifp = ifp; vif->wdev.netdev = ndev; @@ -5589,58 +5596,81 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, err = wl_init_priv(cfg); if (err) { brcmf_err("Failed to init iwm_priv (%d)\n", err); - goto cfg80211_attach_out; + brcmf_free_vif(vif); + goto wiphy_out; } ifp->vif = vif; - err = brcmf_p2p_attach(cfg); + /* determine d11 io type before wiphy setup */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); if (err) { - brcmf_err("P2P initilisation failed (%d)\n", err); - goto cfg80211_p2p_attach_out; + brcmf_err("Failed to get D11 version (%d)\n", err); + goto priv_out; } - err = brcmf_btcoex_attach(cfg); - if (err) { - brcmf_err("BT-coex initialisation failed (%d)\n", err); - brcmf_p2p_detach(&cfg->p2p); - goto cfg80211_p2p_attach_out; + cfg->d11inf.io_type = (u8)io_type; + brcmu_d11_attach(&cfg->d11inf); + + err = brcmf_setup_wiphy(wiphy, ifp); + if (err < 0) + goto priv_out; + + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* firmware defaults to 40MHz disabled in 2G band. We signal + * cfg80211 here that we do and have it decide we can enable + * it. But first check if device does support 2G operation. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap; + *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + err = wiphy_register(wiphy); + if (err < 0) { + brcmf_err("Could not register wiphy device (%d)\n", err); + goto priv_out; } /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), * setup 40MHz in 2GHz band and enable OBSS scanning. */ - if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - err = brcmf_enable_bw40_2g(ifp); + if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { + err = brcmf_enable_bw40_2g(cfg); if (!err) err = brcmf_fil_iovar_int_set(ifp, "obss_coex", BRCMF_OBSS_COEX_AUTO); + else + *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; } - /* clear for now and rely on update later */ - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; - err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + err = brcmf_p2p_attach(cfg); if (err) { - brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); - wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + brcmf_err("P2P initilisation failed (%d)\n", err); + goto wiphy_unreg_out; + } + err = brcmf_btcoex_attach(cfg); + if (err) { + brcmf_err("BT-coex initialisation failed (%d)\n", err); + brcmf_p2p_detach(&cfg->p2p); + goto wiphy_unreg_out; } - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, - &io_type); + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { - brcmf_err("Failed to get D11 version (%d)\n", err); - goto cfg80211_p2p_attach_out; + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; } - cfg->d11inf.io_type = (u8)io_type; - brcmu_d11_attach(&cfg->d11inf); return cfg; -cfg80211_p2p_attach_out: +wiphy_unreg_out: + wiphy_unregister(cfg->wiphy); +priv_out: wl_deinit_priv(cfg); - -cfg80211_attach_out: brcmf_free_vif(vif); +wiphy_out: + brcmf_free_wiphy(wiphy); return NULL; } @@ -5653,5 +5683,5 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) wiphy_unregister(cfg->wiphy); brcmf_btcoex_detach(cfg); wl_deinit_priv(cfg); - wiphy_free(cfg->wiphy); + brcmf_free_wiphy(cfg->wiphy); } From ab4d7f0643efd20ba883b9b9da69c03e5c95188d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:42 +0200 Subject: [PATCH 0167/1983] brcmfmac: add brcmf_p2p_detach() call in brcmf_cfg80211_detach() Upstream-commit: c3da74bbbb8b230ad8bb84ca324c4e07607c9a7f The function brcmf_p2p_detach() was only called in error flow of the brcmf_cfg80211_attach() routine, but it also needs to be called upon brcmf_cfg80211_detach(). Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 34922ba4618a85..c78f0fed93f0b6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5682,6 +5682,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) WARN_ON(!list_empty(&cfg->vif_list)); wiphy_unregister(cfg->wiphy); brcmf_btcoex_detach(cfg); + brcmf_p2p_detach(&cfg->p2p); wl_deinit_priv(cfg); brcmf_free_wiphy(cfg->wiphy); } From 05ba2cfba29ae5be9f12f5a9ccbdb1186e1b4b07 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sun, 20 Jul 2014 16:16:11 +0200 Subject: [PATCH 0168/1983] brcm80211: remove unnecessary break after return Upstream-commit: 18a12348a5bce991d94f7d8c252f19e46cecb2f6 Signed-off-by: Fabian Frederick Signed-off-by: David S. Miller --- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 1 - drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 588fdbdb325539..057b982ea8b37f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -2364,7 +2364,6 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) return 0; default: return -ENOTSUPP; - break; } clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c index 3e9f5b25be637d..93869e89aa3dcf 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c @@ -22916,7 +22916,6 @@ static void wlc_phy_rssi_cal_nphy_rev2(struct brcms_phy *pi, u8 rssi_type) break; default: return; - break; } classif_state = wlc_phy_classifier_nphy(pi, 0, 0); From 6b7febc0ff0526a5f777c704299637c54cc6133d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Sun, 29 Jun 2014 16:16:59 +0200 Subject: [PATCH 0169/1983] brcmfmac: add device tree support for SDIO devices Upstream-commit: 61f663dfc1a091c7c04fa73666af52a42aaa083f brcmfmac devices can use an out-of-band interrupt on a GPIO line. Currently this is specified using platform data. Add support for specifying out-of-band interrupt via device tree. Signed-off-by: Chen-Yu Tsai [arend@broadcom.com: conditionalize more of-code, use driver debug routines] Signed-off-by: Arend van Spriel [hdegoede@redhat.com: drop clk / reg_on gpio handling, as there is no consensus on how to handle this yet] Signed-off-by: Hans de Goede Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 2 + .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 4 ++ drivers/net/wireless/brcm80211/brcmfmac/of.c | 56 +++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/of.h | 22 ++++++++ 4 files changed, 84 insertions(+) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/of.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/of.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index de0cff3df38950..14e8a8d33520a9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -46,3 +46,5 @@ brcmfmac-$(CONFIG_BRCMDBG) += \ dhd_dbg.o brcmfmac-$(CONFIG_BRCM_TRACING) += \ tracepoint.o +brcmfmac-$(CONFIG_OF) += \ + of.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index f467cafe3e8f08..dc2db47a1c73a9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -42,6 +42,7 @@ #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" +#include "of.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -1044,6 +1045,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, sdiodev->dev = &sdiodev->func[1]->dev; sdiodev->pdata = brcmfmac_sdio_pdata; + if (!sdiodev->pdata) + brcmf_of_probe(sdiodev); + atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->request_word_wait); init_waitqueue_head(&sdiodev->request_buffer_wait); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c new file mode 100644 index 00000000000000..f05f5270fec109 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include +#include "dhd_dbg.h" +#include "sdio_host.h" + +void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +{ + struct device *dev = sdiodev->dev; + struct device_node *np = dev->of_node; + int irq; + u32 irqf; + u32 val; + + if (!np || !of_device_is_compatible(np, "brcm,bcm4329-fmac")) + return; + + sdiodev->pdata = devm_kzalloc(dev, sizeof(*sdiodev->pdata), GFP_KERNEL); + if (!sdiodev->pdata) + return; + + irq = irq_of_parse_and_map(np, 0); + if (irq < 0) { + brcmf_err("interrupt could not be mapped: err=%d\n", irq); + devm_kfree(dev, sdiodev->pdata); + return; + } + irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); + + sdiodev->pdata->oob_irq_supported = true; + sdiodev->pdata->oob_irq_nr = irq; + sdiodev->pdata->oob_irq_flags = irqf; + + if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) + sdiodev->pdata->drive_strength = val; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.h b/drivers/net/wireless/brcm80211/brcmfmac/of.h new file mode 100644 index 00000000000000..5f7c3550deda4c --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef CONFIG_OF +void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev); +#else +static void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_OF */ From 1d6f9b1550853c4c4d455f809634f1eb6c841adc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 29 Jun 2014 16:17:00 +0200 Subject: [PATCH 0170/1983] brcmfmac: Fix some wrong register defines Upstream-commit: 3cdf0a81ab22f64d6160072d52afd29f5f8f4da6 Signed-off-by: Hans de Goede Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 6c5e585ccda99f..f2d06cae366af2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -74,12 +74,12 @@ #define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access addr byte 0 */ #define SBSDIO_SPROM_ADDR_LOW 0x10004 -/* sprom indirect access addr byte 0 */ -#define SBSDIO_SPROM_ADDR_HIGH 0x10005 -/* xtal_pu (gpio) output */ -#define SBSDIO_CHIP_CTRL_DATA 0x10006 -/* xtal_pu (gpio) enable */ -#define SBSDIO_CHIP_CTRL_EN 0x10007 +/* gpio select */ +#define SBSDIO_GPIO_SELECT 0x10005 +/* gpio output */ +#define SBSDIO_GPIO_OUT 0x10006 +/* gpio enable */ +#define SBSDIO_GPIO_EN 0x10007 /* rev < 7, watermark for sdio device */ #define SBSDIO_WATERMARK 0x10008 /* control busy signal generation */ From 316585b93f01ba97c54ef1758e185633258121fc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 29 Jul 2014 14:23:23 +0200 Subject: [PATCH 0171/1983] brcmfmac: Fix OOB interrupt not working for BCM43362 Upstream-commit: 568ba389be505f505b7fbeedb9ab4ece27603fc9 It has taken me a long long time to get the OOB interrupt working on the AP6210 sdio wifi/bt module found on various Allwinner A20 boards. In the end I found these magic register pokes in the cubietruck kernel tree: https://github.com/cubieboard2/linux-sunxi/commit/7f08ba395617d17e7a711507503d89a50406fe7a This is also done for the bcm43362 in broadcom's internal/proprietary driver. Signed-off-by: Hans de Goede Reviewed-by: Arend van Spriel [arend@broadcom.com: rebased changing BCM43362 chip id to fix compilation] Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index dc2db47a1c73a9..8dbd5dbb78fd14 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -38,7 +38,9 @@ #include #include #include +#include #include +#include "chip.h" #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" @@ -118,6 +120,7 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; u8 data; + u32 addr, gpiocontrol; unsigned long flags; if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { @@ -147,6 +150,19 @@ int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) sdio_claim_host(sdiodev->func[1]); + if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) { + /* assign GPIO to SDIO core */ + addr = CORE_CC_REG(SI_ENUM_BASE, gpiocontrol); + gpiocontrol = brcmf_sdiod_regrl(sdiodev, addr, &ret); + gpiocontrol |= 0x2; + brcmf_sdiod_regwl(sdiodev, addr, gpiocontrol, &ret); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_SELECT, 0xf, + &ret); + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret); + brcmf_sdiod_regwb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret); + } + /* must configure SDIO_CCCR_IENx to enable irq */ data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret); data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; From 13bf9293bf0b27a70907c81e68120f2b2724b1cd Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Wed, 30 Jul 2014 13:20:00 +0200 Subject: [PATCH 0172/1983] brcmfmac: Do not use strcpy and strcat Upstream-commit: 46de06839b4936cc5fd4e6638b8fbf8437bce29e Commit "c1b2053 brcmfmac: Make firmware path a module parameter" introduced use of strcpy and strcat. The strcpy and strcat require using null terminated strings and can cause out-of-bounds memory access and subsequent corruption. This patch replaces these by strncpy and strncat respectively to assure array boundaries are not crossed. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend Van Spriel Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 67d91d5cc13d85..f55f625fd06b4a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -670,6 +670,8 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, struct brcmf_sdio_dev *sdiodev) { int i; + uint fw_len, nv_len; + char end; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { if (brcmf_fwname_data[i].chipid == ci->chip && @@ -682,16 +684,25 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, return -ENODEV; } + fw_len = sizeof(sdiodev->fw_name) - 1; + nv_len = sizeof(sdiodev->nvram_name) - 1; /* check if firmware path is provided by module parameter */ if (brcmf_firmware_path[0] != '\0') { - if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/') - strcat(brcmf_firmware_path, "/"); - - strcpy(sdiodev->fw_name, brcmf_firmware_path); - strcpy(sdiodev->nvram_name, brcmf_firmware_path); + strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len); + strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len); + fw_len -= strlen(sdiodev->fw_name); + nv_len -= strlen(sdiodev->nvram_name); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strncat(sdiodev->fw_name, "/", fw_len); + strncat(sdiodev->nvram_name, "/", nv_len); + fw_len--; + nv_len--; + } } - strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin); - strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv); + strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len); + strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len); return 0; } From d539c7963d47ad9aa5a1e9da93de2c32a9338171 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:01 +0200 Subject: [PATCH 0173/1983] brcmfmac: Export brcmf_netif_rx for new protocol msgbuf. Upstream-commit: 9374a2b514194acd4865f0183acd7c48c3b36b05 Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 1 + drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 7da6441bcfa8e8..1825f736fd4502 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -181,6 +181,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success); +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index bf448081d6fb98..7e14e5fa47449b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -288,7 +288,7 @@ void brcmf_txflowblock(struct device *dev, bool state) brcmf_fws_bus_blocked(drvr, state); } -static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) { skb->dev = ifp->ndev; skb->protocol = eth_type_trans(skb, skb->dev); From 2a91df4e67924228359b8082a622b1215b871067 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:02 +0200 Subject: [PATCH 0174/1983] brcmfmac: Add protocol addressing mode and peer deletion API. Upstream-commit: 8851cce085dce026f14e9fc525acc71e4c9d305b The soon to be added protocol msgbuf requires information about interface addressing mode and peer deletion. This patch adds the necessary APIs to proto, implements dummy functions in BCDC and adds calls to proto wherever necessary by wl_cfg80211. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcdc.c | 13 ++++++++ .../net/wireless/brcm80211/brcmfmac/proto.c | 8 ++++- .../net/wireless/brcm80211/brcmfmac/proto.h | 24 +++++++++++++- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 33 ++++++++++++++++++- 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index c229210d50bade..10b48c2c4b36ab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -337,6 +337,17 @@ brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, return brcmf_bus_txdata(drvr->bus_if, pktbuf); } +static void +brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ +} + +static void +brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]) +{ +} int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { @@ -356,6 +367,8 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; drvr->proto->txdata = brcmf_proto_bcdc_txdata; + drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; + drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index b6b4641849463a..d333ff8fcfffcc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -30,6 +30,8 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) { struct brcmf_proto *proto; + brcmf_dbg(TRACE, "Enter\n"); + proto = kzalloc(sizeof(*proto), GFP_ATOMIC); if (!proto) goto fail; @@ -40,7 +42,9 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) goto fail; if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || - (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL)) { + (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || + (proto->configure_addr_mode == NULL) || + (proto->delete_peer == NULL)) { brcmf_err("Not all proto handlers have been installed\n"); goto fail; } @@ -54,6 +58,8 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) void brcmf_proto_detach(struct brcmf_pub *drvr) { + brcmf_dbg(TRACE, "Enter\n"); + if (drvr->proto) { brcmf_proto_bcdc_detach(drvr); kfree(drvr->proto); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 482fb0ba4a30f3..55942e3561a3ff 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -16,6 +16,13 @@ #ifndef BRCMFMAC_PROTO_H #define BRCMFMAC_PROTO_H + +enum proto_addr_mode { + ADDR_INDIRECT = 0, + ADDR_DIRECT +}; + + struct brcmf_proto { int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, struct sk_buff *skb); @@ -25,6 +32,10 @@ struct brcmf_proto { uint len); int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, struct sk_buff *skb); + void (*configure_addr_mode)(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode); + void (*delete_peer)(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); void *pd; }; @@ -48,10 +59,21 @@ static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); } static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, - u8 offset, struct sk_buff *skb) + u8 offset, struct sk_buff *skb) { return drvr->proto->txdata(drvr, ifidx, offset, skb); } +static inline void +brcmf_proto_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + drvr->proto->configure_addr_mode(drvr, ifidx, addr_mode); +} +static inline void +brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->delete_peer(drvr, ifidx, peer); +} #endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index c78f0fed93f0b6..2316250fff8505 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -35,6 +35,7 @@ #include "wl_cfg80211.h" #include "feature.h" #include "fwil.h" +#include "proto.h" #include "vendor.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 @@ -493,6 +494,22 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) return err; } +static void +brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) +{ + struct net_device *ndev = wdev->netdev; + struct brcmf_if *ifp = netdev_priv(ndev); + + if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || + (wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_DIRECT); + else + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_INDIRECT); +} + static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) { enum nl80211_iftype iftype; @@ -512,6 +529,8 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, u32 *flags, struct vif_params *params) { + struct wireless_dev *wdev; + brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); switch (type) { case NL80211_IFTYPE_ADHOC: @@ -525,7 +544,10 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: - return brcmf_p2p_add_vif(wiphy, name, type, flags, params); + wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; case NL80211_IFTYPE_UNSPECIFIED: default: return ERR_PTR(-EINVAL); @@ -720,6 +742,8 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, } ndev->ieee80211_ptr->iftype = type; + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + done: brcmf_dbg(TRACE, "Exit\n"); @@ -4525,6 +4549,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct ieee80211_channel *chan; s32 err = 0; + if ((e->event_code == BRCMF_E_DEAUTH) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DISASSOC_IND) || + ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + } + if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(e)) { From b6370c05a337cfefe68477368d5f1a9e0291bb01 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:03 +0200 Subject: [PATCH 0175/1983] brcmfmac: Adding msgbuf protocol. Upstream-commit: 9a1bb60250d2b6b546a62e5b73f55c4f1d22016b This patch will add the msgbuf protocol. This protocol is used by the soon to be added new bus interface PCIe. Msgbuf is a protocol where most data is and remains located on the host (driver) side and transferred by DMA from and to device. Msgbuf is the protocol which takes care of the signalling of the buffers between host and device which identifies this DMA-able data. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 3 + .../wireless/brcm80211/brcmfmac/commonring.c | 273 ++++ .../wireless/brcm80211/brcmfmac/commonring.h | 69 + .../net/wireless/brcm80211/brcmfmac/dhd_bus.h | 32 + .../net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 35 +- .../wireless/brcm80211/brcmfmac/flowring.c | 362 +++++ .../wireless/brcm80211/brcmfmac/flowring.h | 74 + .../net/wireless/brcm80211/brcmfmac/msgbuf.c | 1387 +++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/msgbuf.h | 40 + .../net/wireless/brcm80211/brcmfmac/proto.c | 21 +- 10 files changed, 2275 insertions(+), 21 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/commonring.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/commonring.h create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/flowring.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/flowring.h create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 14e8a8d33520a9..0447a47fe237a5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -31,6 +31,9 @@ brcmfmac-objs += \ p2p.o \ proto.o \ bcdc.o \ + commonring.o \ + flowring.o \ + msgbuf.o \ dhd_common.o \ dhd_linux.o \ firmware.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c new file mode 100644 index 00000000000000..c6d65b8e1e1565 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include + +#include "dhd.h" +#include "commonring.h" + + +/* dma flushing needs implementation for mips and arm platforms. Should + * be put in util. Note, this is not real flushing. It is virtual non + * cached memory. Only write buffers should have to be drained. Though + * this may be different depending on platform...... + * SEE ALSO msgbuf.c + */ +#define brcmf_dma_flush(addr, len) +#define brcmf_dma_invalidate_cache(addr, len) + + +void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, + int (*cr_ring_bell)(void *ctx), + int (*cr_update_rptr)(void *ctx), + int (*cr_update_wptr)(void *ctx), + int (*cr_write_rptr)(void *ctx), + int (*cr_write_wptr)(void *ctx), void *ctx) +{ + commonring->cr_ring_bell = cr_ring_bell; + commonring->cr_update_rptr = cr_update_rptr; + commonring->cr_update_wptr = cr_update_wptr; + commonring->cr_write_rptr = cr_write_rptr; + commonring->cr_write_wptr = cr_write_wptr; + commonring->cr_ctx = ctx; +} + + +void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, + u16 item_len, void *buf_addr) +{ + commonring->depth = depth; + commonring->item_len = item_len; + commonring->buf_addr = buf_addr; + if (!commonring->inited) { + spin_lock_init(&commonring->lock); + commonring->inited = true; + } + commonring->r_ptr = 0; + if (commonring->cr_write_rptr) + commonring->cr_write_rptr(commonring->cr_ctx); + commonring->w_ptr = 0; + if (commonring->cr_write_wptr) + commonring->cr_write_wptr(commonring->cr_ctx); + commonring->f_ptr = 0; +} + + +void brcmf_commonring_lock(struct brcmf_commonring *commonring) + __acquires(&commonring->lock) +{ + unsigned long flags; + + spin_lock_irqsave(&commonring->lock, flags); + commonring->flags = flags; +} + + +void brcmf_commonring_unlock(struct brcmf_commonring *commonring) + __releases(&commonring->lock) +{ + spin_unlock_irqrestore(&commonring->lock, commonring->flags); +} + + +bool brcmf_commonring_write_available(struct brcmf_commonring *commonring) +{ + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + if (!commonring->was_full) + return true; + if (available > commonring->depth / 8) { + commonring->was_full = false; + return true; + } + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + return false; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return false; +} + + +void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring) +{ + void *ret_ptr; + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + ret_ptr = commonring->buf_addr + + (commonring->w_ptr * commonring->item_len); + commonring->w_ptr++; + if (commonring->w_ptr == commonring->depth) + commonring->w_ptr = 0; + return ret_ptr; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return NULL; +} + + +void * +brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, + u16 n_items, u16 *alloced) +{ + void *ret_ptr; + u16 available; + bool retry = true; + +again: + if (commonring->r_ptr <= commonring->w_ptr) + available = commonring->depth - commonring->w_ptr + + commonring->r_ptr; + else + available = commonring->r_ptr - commonring->w_ptr; + + if (available > 1) { + ret_ptr = commonring->buf_addr + + (commonring->w_ptr * commonring->item_len); + *alloced = min_t(u16, n_items, available - 1); + if (*alloced + commonring->w_ptr > commonring->depth) + *alloced = commonring->depth - commonring->w_ptr; + commonring->w_ptr += *alloced; + if (commonring->w_ptr == commonring->depth) + commonring->w_ptr = 0; + return ret_ptr; + } + + if (retry) { + if (commonring->cr_update_rptr) + commonring->cr_update_rptr(commonring->cr_ctx); + retry = false; + goto again; + } + + commonring->was_full = true; + return NULL; +} + + +int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) +{ + void *address; + + address = commonring->buf_addr; + address += (commonring->f_ptr * commonring->item_len); + if (commonring->f_ptr > commonring->w_ptr) { + brcmf_dma_flush(address, + (commonring->depth - commonring->f_ptr) * + commonring->item_len); + address = commonring->buf_addr; + commonring->f_ptr = 0; + } + brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * + commonring->item_len); + + commonring->f_ptr = commonring->w_ptr; + + if (commonring->cr_write_wptr) + commonring->cr_write_wptr(commonring->cr_ctx); + if (commonring->cr_ring_bell) + return commonring->cr_ring_bell(commonring->cr_ctx); + + return -EIO; +} + + +void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, + u16 n_items) +{ + if (commonring->w_ptr == 0) + commonring->w_ptr = commonring->depth - n_items; + else + commonring->w_ptr -= n_items; +} + + +void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, + u16 *n_items) +{ + void *ret_addr; + + if (commonring->cr_update_wptr) + commonring->cr_update_wptr(commonring->cr_ctx); + + *n_items = (commonring->w_ptr >= commonring->r_ptr) ? + (commonring->w_ptr - commonring->r_ptr) : + (commonring->depth - commonring->r_ptr); + + if (*n_items == 0) + return NULL; + + ret_addr = commonring->buf_addr + + (commonring->r_ptr * commonring->item_len); + + commonring->r_ptr += *n_items; + if (commonring->r_ptr == commonring->depth) + commonring->r_ptr = 0; + + brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); + + return ret_addr; +} + + +int brcmf_commonring_read_complete(struct brcmf_commonring *commonring) +{ + if (commonring->cr_write_rptr) + return commonring->cr_write_rptr(commonring->cr_ctx); + + return -EIO; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h new file mode 100644 index 00000000000000..002336e3576404 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_COMMONRING_H +#define BRCMFMAC_COMMONRING_H + + +struct brcmf_commonring { + u16 r_ptr; + u16 w_ptr; + u16 f_ptr; + u16 depth; + u16 item_len; + + void *buf_addr; + + int (*cr_ring_bell)(void *ctx); + int (*cr_update_rptr)(void *ctx); + int (*cr_update_wptr)(void *ctx); + int (*cr_write_rptr)(void *ctx); + int (*cr_write_wptr)(void *ctx); + + void *cr_ctx; + + spinlock_t lock; + unsigned long flags; + bool inited; + bool was_full; +}; + + +void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, + int (*cr_ring_bell)(void *ctx), + int (*cr_update_rptr)(void *ctx), + int (*cr_update_wptr)(void *ctx), + int (*cr_write_rptr)(void *ctx), + int (*cr_write_wptr)(void *ctx), void *ctx); +void brcmf_commonring_config(struct brcmf_commonring *commonring, u16 depth, + u16 item_len, void *buf_addr); +void brcmf_commonring_lock(struct brcmf_commonring *commonring); +void brcmf_commonring_unlock(struct brcmf_commonring *commonring); +bool brcmf_commonring_write_available(struct brcmf_commonring *commonring); +void *brcmf_commonring_reserve_for_write(struct brcmf_commonring *commonring); +void * +brcmf_commonring_reserve_for_write_multiple(struct brcmf_commonring *commonring, + u16 n_items, u16 *alloced); +int brcmf_commonring_write_complete(struct brcmf_commonring *commonring); +void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring, + u16 n_items); +void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, + u16 *n_items); +int brcmf_commonring_read_complete(struct brcmf_commonring *commonring); + +#define brcmf_commonring_n_items(commonring) (commonring->depth) +#define brcmf_commonring_len_item(commonring) (commonring->item_len) + + +#endif /* BRCMFMAC_COMMONRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 7735328fff21e6..4053368eb743c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -19,6 +19,18 @@ #include "dhd_dbg.h" +/* IDs of the 6 default common rings of msgbuf protocol */ +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 +#define BRCMF_D2H_MSGRING_TX_COMPLETE 3 +#define BRCMF_D2H_MSGRING_RX_COMPLETE 4 + +#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 +#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 +#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ + BRCMF_NROF_D2H_COMMON_MSGRINGS) + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_UNKNOWN, /* Not determined yet */ @@ -70,6 +82,25 @@ struct brcmf_bus_ops { struct pktq * (*gettxq)(struct device *dev); }; + +/** + * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf. + * + * @commonrings: commonrings which are always there. + * @flowrings: commonrings which are dynamically created and destroyed for data. + * @rx_dataoffset: if set then all rx data has this this offset. + * @max_rxbufpost: maximum number of buffers to post for rx. + * @nrof_flowrings: number of flowrings. + */ +struct brcmf_bus_msgbuf { + struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_commonring **flowrings; + u32 rx_dataoffset; + u32 max_rxbufpost; + u32 nrof_flowrings; +}; + + /** * struct brcmf_bus - interface structure between common and bus layer * @@ -101,6 +132,7 @@ struct brcmf_bus { bool always_use_fws_queue; struct brcmf_bus_ops *ops; + struct brcmf_bus_msgbuf *msgbuf; }; /* diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 6eade7c60c6355..6804eeca76884e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -18,23 +18,24 @@ #define _BRCMF_DBG_H_ /* message levels */ -#define BRCMF_TRACE_VAL 0x00000002 -#define BRCMF_INFO_VAL 0x00000004 -#define BRCMF_DATA_VAL 0x00000008 -#define BRCMF_CTL_VAL 0x00000010 -#define BRCMF_TIMER_VAL 0x00000020 -#define BRCMF_HDRS_VAL 0x00000040 -#define BRCMF_BYTES_VAL 0x00000080 -#define BRCMF_INTR_VAL 0x00000100 -#define BRCMF_GLOM_VAL 0x00000200 -#define BRCMF_EVENT_VAL 0x00000400 -#define BRCMF_BTA_VAL 0x00000800 -#define BRCMF_FIL_VAL 0x00001000 -#define BRCMF_USB_VAL 0x00002000 -#define BRCMF_SCAN_VAL 0x00004000 -#define BRCMF_CONN_VAL 0x00008000 -#define BRCMF_BCDC_VAL 0x00010000 -#define BRCMF_SDIO_VAL 0x00020000 +#define BRCMF_TRACE_VAL 0x00000002 +#define BRCMF_INFO_VAL 0x00000004 +#define BRCMF_DATA_VAL 0x00000008 +#define BRCMF_CTL_VAL 0x00000010 +#define BRCMF_TIMER_VAL 0x00000020 +#define BRCMF_HDRS_VAL 0x00000040 +#define BRCMF_BYTES_VAL 0x00000080 +#define BRCMF_INTR_VAL 0x00000100 +#define BRCMF_GLOM_VAL 0x00000200 +#define BRCMF_EVENT_VAL 0x00000400 +#define BRCMF_BTA_VAL 0x00000800 +#define BRCMF_FIL_VAL 0x00001000 +#define BRCMF_USB_VAL 0x00002000 +#define BRCMF_SCAN_VAL 0x00004000 +#define BRCMF_CONN_VAL 0x00008000 +#define BRCMF_BCDC_VAL 0x00010000 +#define BRCMF_SDIO_VAL 0x00020000 +#define BRCMF_MSGBUF_VAL 0x00040000 /* set default print format */ #undef pr_fmt diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c new file mode 100644 index 00000000000000..26cbb7c4ec4c9e --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -0,0 +1,362 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#include +#include +#include +#include + +#include "dhd.h" +#include "dhd_dbg.h" +#include "dhd_bus.h" +#include "proto.h" +#include "flowring.h" +#include "msgbuf.h" + + +#define BRCMF_FLOWRING_HIGH 1024 +#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256) +#define BRCMF_FLOWRING_INVALID_IFIDX 0xff + +#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) +#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) + +static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static const u8 brcmf_flowring_prio2fifo[] = { + 1, + 0, + 0, + 1, + 2, + 2, + 3, + 3 +}; + + +u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx) +{ + struct brcmf_flowring_hash *hash; + u8 hash_idx; + u32 i; + bool found; + bool sta; + u8 fifo; + u8 *mac; + + fifo = brcmf_flowring_prio2fifo[prio]; + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + mac = da; + if ((!sta) && (is_multicast_ether_addr(da))) { + mac = (u8 *)ALLFFMAC; + fifo = 0; + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) && + (hash[hash_idx].fifo == fifo) && + (hash[hash_idx].ifidx == ifidx)) { + found = true; + break; + } + hash_idx++; + } + if (found) + return hash[hash_idx].flowid; + + return BRCMF_FLOWRING_INVALID_ID; +} + + +u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx) +{ + struct brcmf_flowring_ring *ring; + struct brcmf_flowring_hash *hash; + u8 hash_idx; + u32 i; + bool found; + u8 fifo; + bool sta; + u8 *mac; + + fifo = brcmf_flowring_prio2fifo[prio]; + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + mac = da; + if ((!sta) && (is_multicast_ether_addr(da))) { + mac = (u8 *)ALLFFMAC; + fifo = 0; + } + hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : + BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); + found = false; + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if (((sta) && + (hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX)) || + ((!sta) && + (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0))) { + found = true; + break; + } + hash_idx++; + } + if (found) { + for (i = 0; i < flow->nrofrings; i++) { + if (flow->rings[i] == NULL) + break; + } + if (i == flow->nrofrings) + return -ENOMEM; + + ring = kzalloc(sizeof(*ring), GFP_ATOMIC); + if (!ring) + return -ENOMEM; + + memcpy(hash[hash_idx].mac, mac, ETH_ALEN); + hash[hash_idx].fifo = fifo; + hash[hash_idx].ifidx = ifidx; + hash[hash_idx].flowid = i; + + ring->hash_id = hash_idx; + ring->status = RING_CLOSED; + skb_queue_head_init(&ring->skblist); + flow->rings[i] = ring; + + return i; + } + return BRCMF_FLOWRING_INVALID_ID; +} + + +u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + return flow->hash[ring->hash_id].fifo; +} + + +void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + u8 hash_idx; + struct sk_buff *skb; + + ring = flow->rings[flowid]; + if (!ring) + return; + hash_idx = ring->hash_id; + flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; + memset(flow->hash[hash_idx].mac, 0, ETH_ALEN); + flow->rings[flowid] = NULL; + + skb = skb_dequeue(&ring->skblist); + while (skb) { + brcmu_pkt_buf_free_skb(skb); + skb = skb_dequeue(&ring->skblist); + } + + kfree(ring); +} + + +void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + skb_queue_tail(&ring->skblist, skb); + + if (!ring->blocked && + (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { + brcmf_txflowblock(flow->dev, true); + brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); + ring->blocked = 1; + } +} + + +struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + struct sk_buff *skb; + + ring = flow->rings[flowid]; + if (ring->status != RING_OPEN) + return NULL; + + skb = skb_dequeue(&ring->skblist); + + if (ring->blocked && + (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { + brcmf_txflowblock(flow->dev, false); + brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); + ring->blocked = 0; + } + + return skb; +} + + +void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + + skb_queue_head(&ring->skblist, skb); +} + + +u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + if (!ring) + return 0; + + if (ring->status != RING_OPEN) + return 0; + + return skb_queue_len(&ring->skblist); +} + + +void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + + ring = flow->rings[flowid]; + if (!ring) { + brcmf_err("Ring NULL, for flowid %d\n", flowid); + return; + } + + ring->status = RING_OPEN; +} + + +u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid) +{ + struct brcmf_flowring_ring *ring; + u8 hash_idx; + + ring = flow->rings[flowid]; + hash_idx = ring->hash_id; + + return flow->hash[hash_idx].ifidx; +} + + +struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) +{ + struct brcmf_flowring *flow; + u32 i; + + flow = kzalloc(sizeof(*flow), GFP_ATOMIC); + if (flow) { + flow->dev = dev; + flow->nrofrings = nrofrings; + for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) + flow->addr_mode[i] = ADDR_INDIRECT; + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) + flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; + flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), + GFP_ATOMIC); + if (!flow->rings) { + kfree(flow); + flow = NULL; + } + } + + return flow; +} + + +void brcmf_flowring_detach(struct brcmf_flowring *flow) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + u8 flowid; + + for (flowid = 0; flowid < flow->nrofrings; flowid++) { + if (flow->rings[flowid]) + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + kfree(flow->rings); + kfree(flow); +} + + +void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, + enum proto_addr_mode addr_mode) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + u32 i; + u8 flowid; + + if (flow->addr_mode[ifidx] != addr_mode) { + for (i = 0; i < ARRAY_SIZE(flow->hash); i++) { + if (flow->hash[i].ifidx == ifidx) { + flowid = flow->hash[i].flowid; + if (flow->rings[flowid]->status != RING_OPEN) + continue; + flow->rings[flowid]->status = RING_CLOSING; + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + } + flow->addr_mode[ifidx] = addr_mode; + } +} + + +void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_hash *hash; + u32 i; + u8 flowid; + bool sta; + + sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + hash = flow->hash; + for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { + if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && + (hash[i].ifidx == ifidx)) { + flowid = flow->hash[i].flowid; + if (flow->rings[flowid]->status == RING_OPEN) { + flow->rings[flowid]->status = RING_CLOSING; + brcmf_msgbuf_delete_flowring(drvr, flowid); + } + } + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h new file mode 100644 index 00000000000000..677f4b8065f6c5 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_FLOWRING_H +#define BRCMFMAC_FLOWRING_H + + +#define BRCMF_FLOWRING_HASHSIZE 256 +#define BRCMF_FLOWRING_INVALID_ID 0xFFFFFFFF + + +struct brcmf_flowring_hash { + u8 mac[ETH_ALEN]; + u8 fifo; + u8 ifidx; + u8 flowid; +}; + +enum ring_status { + RING_CLOSED, + RING_CLOSING, + RING_OPEN +}; + +struct brcmf_flowring_ring { + u8 hash_id; + u8 blocked; + enum ring_status status; + struct sk_buff_head skblist; +}; + +struct brcmf_flowring { + struct device *dev; + struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; + struct brcmf_flowring_ring **rings; + enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; + u16 nrofrings; +}; + + +u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], + u8 prio, u8 ifidx); +void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); +u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); +struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); +void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); +u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid); +u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid); +struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings); +void brcmf_flowring_detach(struct brcmf_flowring *flow); +void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, + enum proto_addr_mode addr_mode); +void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]); + + +#endif /* BRCMFMAC_FLOWRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c new file mode 100644 index 00000000000000..c7a1c59ba6c3e3 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -0,0 +1,1387 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/******************************************************************************* + * Communicates with the dongle by using dcmd codes. + * For certain dcmd codes, the dongle interprets string data from the host. + ******************************************************************************/ + +#include +#include + +#include +#include + +#include "dhd.h" +#include "dhd_dbg.h" +#include "proto.h" +#include "msgbuf.h" +#include "commonring.h" +#include "flowring.h" +#include "dhd_bus.h" +#include "tracepoint.h" + + +#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 + +#define MSGBUF_TYPE_GEN_STATUS 0x1 +#define MSGBUF_TYPE_RING_STATUS 0x2 +#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3 +#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4 +#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5 +#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6 +#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7 +#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8 +#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9 +#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA +#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB +#define MSGBUF_TYPE_IOCTL_CMPLT 0xC +#define MSGBUF_TYPE_EVENT_BUF_POST 0xD +#define MSGBUF_TYPE_WL_EVENT 0xE +#define MSGBUF_TYPE_TX_POST 0xF +#define MSGBUF_TYPE_TX_STATUS 0x10 +#define MSGBUF_TYPE_RXBUF_POST 0x11 +#define MSGBUF_TYPE_RX_CMPLT 0x12 +#define MSGBUF_TYPE_LPBK_DMAXFER 0x13 +#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 + +#define NR_TX_PKTIDS 2048 +#define NR_RX_PKTIDS 1024 + +#define BRCMF_IOCTL_REQ_PKTID 0xFFFE + +#define BRCMF_MSGBUF_MAX_PKT_SIZE 2048 +#define BRCMF_MSGBUF_RXBUFPOST_THRESHOLD 32 +#define BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST 8 +#define BRCMF_MSGBUF_MAX_EVENTBUF_POST 8 + +#define BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3 0x01 +#define BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5 + +#define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 +#define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 + + +struct msgbuf_common_hdr { + u8 msgtype; + u8 ifidx; + u8 flags; + u8 rsvd0; + __le32 request_id; +}; + +struct msgbuf_buf_addr { + __le32 low_addr; + __le32 high_addr; +}; + +struct msgbuf_ioctl_req_hdr { + struct msgbuf_common_hdr msg; + __le32 cmd; + __le16 trans_id; + __le16 input_buf_len; + __le16 output_buf_len; + __le16 rsvd0[3]; + struct msgbuf_buf_addr req_buf_addr; + __le32 rsvd1[2]; +}; + +struct msgbuf_tx_msghdr { + struct msgbuf_common_hdr msg; + u8 txhdr[ETH_HLEN]; + u8 flags; + u8 seg_cnt; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; + __le16 metadata_buf_len; + __le16 data_len; + __le32 rsvd0; +}; + +struct msgbuf_rx_bufpost { + struct msgbuf_common_hdr msg; + __le16 metadata_buf_len; + __le16 data_buf_len; + __le32 rsvd0; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; +}; + +struct msgbuf_rx_ioctl_resp_or_event { + struct msgbuf_common_hdr msg; + __le16 host_buf_len; + __le16 rsvd0[3]; + struct msgbuf_buf_addr host_buf_addr; + __le32 rsvd1[4]; +}; + +struct msgbuf_completion_hdr { + __le16 status; + __le16 flow_ring_id; +}; + +struct msgbuf_rx_event { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 event_data_len; + __le16 seqnum; + __le16 rsvd0[4]; +}; + +struct msgbuf_ioctl_resp_hdr { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 resp_len; + __le16 trans_id; + __le32 cmd; + __le32 rsvd0; +}; + +struct msgbuf_tx_status { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 metadata_len; + __le16 tx_status; +}; + +struct msgbuf_rx_complete { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le16 metadata_len; + __le16 data_len; + __le16 data_offset; + __le16 flags; + __le32 rx_status_0; + __le32 rx_status_1; + __le32 rsvd0; +}; + +struct msgbuf_tx_flowring_create_req { + struct msgbuf_common_hdr msg; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 tid; + u8 if_flags; + __le16 flow_ring_id; + u8 tc; + u8 priority; + __le16 int_vector; + __le16 max_items; + __le16 len_item; + struct msgbuf_buf_addr flow_ring_addr; +}; + +struct msgbuf_tx_flowring_delete_req { + struct msgbuf_common_hdr msg; + __le16 flow_ring_id; + __le16 reason; + __le32 rsvd0[7]; +}; + +struct msgbuf_flowring_create_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct msgbuf_flowring_delete_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct msgbuf_flowring_flush_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 rsvd0[3]; +}; + +struct brcmf_msgbuf { + struct brcmf_pub *drvr; + + struct brcmf_commonring **commonrings; + struct brcmf_commonring **flowrings; + dma_addr_t *flowring_dma_handle; + u16 nrof_flowrings; + + u16 rx_dataoffset; + u32 max_rxbufpost; + u16 rx_metadata_offset; + u32 rxbufpost; + + u32 max_ioctlrespbuf; + u32 cur_ioctlrespbuf; + u32 max_eventbuf; + u32 cur_eventbuf; + + void *ioctbuf; + dma_addr_t ioctbuf_handle; + u32 ioctbuf_phys_hi; + u32 ioctbuf_phys_lo; + u32 ioctl_resp_status; + u32 ioctl_resp_ret_len; + u32 ioctl_resp_pktid; + + u16 data_seq_no; + u16 ioctl_seq_no; + u32 reqid; + wait_queue_head_t ioctl_resp_wait; + bool ctl_completed; + + struct brcmf_msgbuf_pktids *tx_pktids; + struct brcmf_msgbuf_pktids *rx_pktids; + struct brcmf_flowring *flow; + + struct workqueue_struct *txflow_wq; + struct work_struct txflow_work; + unsigned long *flow_map; + unsigned long *txstatus_done_map; +}; + +struct brcmf_msgbuf_pktid { + atomic_t allocated; + u16 data_offset; + struct sk_buff *skb; + dma_addr_t physaddr; +}; + +struct brcmf_msgbuf_pktids { + u32 array_size; + u32 last_allocated_idx; + enum dma_data_direction direction; + struct brcmf_msgbuf_pktid *array; +}; + + +/* dma flushing needs implementation for mips and arm platforms. Should + * be put in util. Note, this is not real flushing. It is virtual non + * cached memory. Only write buffers should have to be drained. Though + * this may be different depending on platform...... + */ +#define brcmf_dma_flush(addr, len) +#define brcmf_dma_invalidate_cache(addr, len) + + +static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); + + +static struct brcmf_msgbuf_pktids * +brcmf_msgbuf_init_pktids(u32 nr_array_entries, + enum dma_data_direction direction) +{ + struct brcmf_msgbuf_pktid *array; + struct brcmf_msgbuf_pktids *pktids; + + array = kcalloc(nr_array_entries, sizeof(*array), GFP_ATOMIC); + if (!array) + return NULL; + + pktids = kzalloc(sizeof(*pktids), GFP_ATOMIC); + if (!pktids) { + kfree(array); + return NULL; + } + pktids->array = array; + pktids->array_size = nr_array_entries; + + return pktids; +} + + +static int +brcmf_msgbuf_alloc_pktid(struct device *dev, + struct brcmf_msgbuf_pktids *pktids, + struct sk_buff *skb, u16 data_offset, + dma_addr_t *physaddr, u32 *idx) +{ + struct brcmf_msgbuf_pktid *array; + u32 count; + + array = pktids->array; + + *physaddr = dma_map_single(dev, skb->data + data_offset, + skb->len - data_offset, pktids->direction); + + if (dma_mapping_error(dev, *physaddr)) { + brcmf_err("dma_map_single failed !!\n"); + return -ENOMEM; + } + + *idx = pktids->last_allocated_idx; + + count = 0; + do { + (*idx)++; + if (*idx == pktids->array_size) + *idx = 0; + if (array[*idx].allocated.counter == 0) + if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0) + break; + count++; + } while (count < pktids->array_size); + + if (count == pktids->array_size) + return -ENOMEM; + + array[*idx].data_offset = data_offset; + array[*idx].physaddr = *physaddr; + array[*idx].skb = skb; + + pktids->last_allocated_idx = *idx; + + return 0; +} + + +static struct sk_buff * +brcmf_msgbuf_get_pktid(struct device *dev, struct brcmf_msgbuf_pktids *pktids, + u32 idx) +{ + struct brcmf_msgbuf_pktid *pktid; + struct sk_buff *skb; + + if (idx >= pktids->array_size) { + brcmf_err("Invalid packet id %d (max %d)\n", idx, + pktids->array_size); + return NULL; + } + if (pktids->array[idx].allocated.counter) { + pktid = &pktids->array[idx]; + dma_unmap_single(dev, pktid->physaddr, + pktid->skb->len - pktid->data_offset, + pktids->direction); + skb = pktid->skb; + pktid->allocated.counter = 0; + return skb; + } else { + brcmf_err("Invalid packet id %d (not in use)\n", idx); + } + + return NULL; +} + + +static void +brcmf_msgbuf_release_array(struct device *dev, + struct brcmf_msgbuf_pktids *pktids) +{ + struct brcmf_msgbuf_pktid *array; + struct brcmf_msgbuf_pktid *pktid; + u32 count; + + array = pktids->array; + count = 0; + do { + if (array[count].allocated.counter) { + pktid = &array[count]; + dma_unmap_single(dev, pktid->physaddr, + pktid->skb->len - pktid->data_offset, + pktids->direction); + brcmu_pkt_buf_free_skb(pktid->skb); + } + count++; + } while (count < pktids->array_size); + + kfree(array); + kfree(pktids); +} + + +static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf) +{ + if (msgbuf->rx_pktids) + brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids); + if (msgbuf->tx_pktids) + brcmf_msgbuf_release_array(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids); +} + + +static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + struct msgbuf_ioctl_req_hdr *request; + u16 buf_len; + void *ret_ptr; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return -ENOMEM; + } + + msgbuf->reqid++; + + request = (struct msgbuf_ioctl_req_hdr *)ret_ptr; + request->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; + request->msg.ifidx = (u8)ifidx; + request->msg.flags = 0; + request->msg.request_id = cpu_to_le32(BRCMF_IOCTL_REQ_PKTID); + request->cmd = cpu_to_le32(cmd); + request->output_buf_len = cpu_to_le16(len); + request->trans_id = cpu_to_le16(msgbuf->reqid); + + buf_len = min_t(u16, len, BRCMF_TX_IOCTL_MAX_MSG_SIZE); + request->input_buf_len = cpu_to_le16(buf_len); + request->req_buf_addr.high_addr = cpu_to_le32(msgbuf->ioctbuf_phys_hi); + request->req_buf_addr.low_addr = cpu_to_le32(msgbuf->ioctbuf_phys_lo); + if (buf) + memcpy(msgbuf->ioctbuf, buf, buf_len); + else + memset(msgbuf->ioctbuf, 0, buf_len); + brcmf_dma_flush(ioctl_buf, buf_len); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + + return err; +} + + +static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) +{ + return wait_event_timeout(msgbuf->ioctl_resp_wait, + msgbuf->ctl_completed, + msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT)); +} + + +static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) +{ + if (waitqueue_active(&msgbuf->ioctl_resp_wait)) { + msgbuf->ctl_completed = true; + wake_up(&msgbuf->ioctl_resp_wait); + } +} + + +static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct sk_buff *skb = NULL; + int timeout; + int err; + + brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len); + msgbuf->ctl_completed = false; + err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len); + if (err) + return err; + + timeout = brcmf_msgbuf_ioctl_resp_wait(msgbuf); + if (!timeout) { + brcmf_err("Timeout on response for query command\n"); + return -EIO; + } + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, + msgbuf->ioctl_resp_pktid); + if (msgbuf->ioctl_resp_ret_len != 0) { + if (!skb) { + brcmf_err("Invalid packet id idx recv'd %d\n", + msgbuf->ioctl_resp_pktid); + return -EBADF; + } + memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? + len : msgbuf->ioctl_resp_ret_len); + } + if (skb) + brcmu_pkt_buf_free_skb(skb); + + return msgbuf->ioctl_resp_status; +} + + +static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len); +} + + +static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, + u8 *ifidx, struct sk_buff *skb) +{ + return -ENODEV; +} + + +static void +brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) +{ + u32 dma_sz; + void *dma_buf; + + brcmf_dbg(MSGBUF, "Removing flowring %d\n", flowid); + + dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; + dma_buf = msgbuf->flowrings[flowid]->buf_addr; + dma_free_coherent(msgbuf->drvr->bus_if->dev, dma_sz, dma_buf, + msgbuf->flowring_dma_handle[flowid]); + + brcmf_flowring_delete(msgbuf->flow, flowid); +} + + +static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, + struct sk_buff *skb) +{ + struct msgbuf_tx_flowring_create_req *create; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + struct brcmf_commonring *commonring; + void *ret_ptr; + u32 flowid; + void *dma_buf; + u32 dma_sz; + long long address; + int err; + + flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, + skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) + return flowid; + + dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; + + dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz, + &msgbuf->flowring_dma_handle[flowid], + GFP_ATOMIC); + if (!dma_buf) { + brcmf_err("dma_alloc_coherent failed\n"); + brcmf_flowring_delete(msgbuf->flow, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + brcmf_commonring_config(msgbuf->flowrings[flowid], + BRCMF_H2D_TXFLOWRING_MAX_ITEM, + BRCMF_H2D_TXFLOWRING_ITEMSIZE, dma_buf); + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + create = (struct msgbuf_tx_flowring_create_req *)ret_ptr; + create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; + create->msg.ifidx = ifidx; + create->msg.request_id = 0; + create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); + create->flow_ring_id = cpu_to_le16(flowid + + BRCMF_NROF_H2D_COMMON_MSGRINGS); + memcpy(create->sa, eh->h_source, ETH_ALEN); + memcpy(create->da, eh->h_dest, ETH_ALEN); + address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; + create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); + create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); + create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); + create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE); + + brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n", + flowid, eh->h_dest, create->tid, ifidx); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + if (err) { + brcmf_err("Failed to write commonring\n"); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return BRCMF_FLOWRING_INVALID_ID; + } + + return flowid; +} + + +static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) +{ + struct brcmf_flowring *flow = msgbuf->flow; + struct brcmf_commonring *commonring; + void *ret_ptr; + u32 count; + struct sk_buff *skb; + dma_addr_t physaddr; + u32 pktid; + struct msgbuf_tx_msghdr *tx_msghdr; + long long address; + + commonring = msgbuf->flowrings[flowid]; + if (!brcmf_commonring_write_available(commonring)) + return; + + brcmf_commonring_lock(commonring); + + count = BRCMF_MSGBUF_TX_FLUSH_CNT2 - BRCMF_MSGBUF_TX_FLUSH_CNT1; + while (brcmf_flowring_qlen(flow, flowid)) { + skb = brcmf_flowring_dequeue(flow, flowid); + if (skb == NULL) { + brcmf_err("No SKB, but qlen %d\n", + brcmf_flowring_qlen(flow, flowid)); + break; + } + skb_orphan(skb); + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, skb, ETH_HLEN, + &physaddr, &pktid)) { + brcmf_flowring_reinsert(flow, flowid, skb); + brcmf_err("No PKTID available !!\n"); + break; + } + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, pktid); + brcmf_flowring_reinsert(flow, flowid, skb); + break; + } + count++; + + tx_msghdr = (struct msgbuf_tx_msghdr *)ret_ptr; + + tx_msghdr->msg.msgtype = MSGBUF_TYPE_TX_POST; + tx_msghdr->msg.request_id = cpu_to_le32(pktid); + tx_msghdr->msg.ifidx = brcmf_flowring_ifidx_get(flow, flowid); + tx_msghdr->flags = BRCMF_MSGBUF_PKT_FLAGS_FRAME_802_3; + tx_msghdr->flags |= (skb->priority & 0x07) << + BRCMF_MSGBUF_PKT_FLAGS_PRIO_SHIFT; + tx_msghdr->seg_cnt = 1; + memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); + tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); + address = (long long)(long)physaddr; + tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); + tx_msghdr->data_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + tx_msghdr->metadata_buf_len = 0; + tx_msghdr->metadata_buf_addr.high_addr = 0; + tx_msghdr->metadata_buf_addr.low_addr = 0; + if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) { + brcmf_commonring_write_complete(commonring); + count = 0; + } + } + if (count) + brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); +} + + +static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) +{ + struct brcmf_msgbuf *msgbuf; + u32 flowid; + + msgbuf = container_of(worker, struct brcmf_msgbuf, txflow_work); + for_each_set_bit(flowid, msgbuf->flow_map, msgbuf->nrof_flowrings) { + clear_bit(flowid, msgbuf->flow_map); + brcmf_msgbuf_txflow(msgbuf, flowid); + } +} + + +static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid) +{ + set_bit(flowid, msgbuf->flow_map); + queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); + + return 0; +} + + +static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_flowring *flow = msgbuf->flow; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + u32 flowid; + + flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) { + flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); + if (flowid == BRCMF_FLOWRING_INVALID_ID) + return -ENOMEM; + } + brcmf_flowring_enqueue(flow, flowid, skb); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + + return 0; +} + + +static void +brcmf_msgbuf_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, + enum proto_addr_mode addr_mode) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_configure_addr_mode(msgbuf->flow, ifidx, addr_mode); +} + + +static void +brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_delete_peer(msgbuf->flow, ifidx, peer); +} + + +static void +brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_ioctl_resp_hdr *ioctl_resp; + + ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf; + + msgbuf->ioctl_resp_status = le16_to_cpu(ioctl_resp->compl_hdr.status); + msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len); + msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id); + + brcmf_msgbuf_ioctl_resp_wake(msgbuf); + + if (msgbuf->cur_ioctlrespbuf) + msgbuf->cur_ioctlrespbuf--; + brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); +} + + +static void +brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_tx_status *tx_status; + u32 idx; + struct sk_buff *skb; + u16 flowid; + + tx_status = (struct msgbuf_tx_status *)buf; + idx = le32_to_cpu(tx_status->msg.request_id); + flowid = le16_to_cpu(tx_status->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->tx_pktids, idx); + if (!skb) { + brcmf_err("Invalid packet id idx recv'd %d\n", idx); + return; + } + + set_bit(flowid, msgbuf->txstatus_done_map); + + brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); +} + + +static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) +{ + struct brcmf_commonring *commonring; + void *ret_ptr; + struct sk_buff *skb; + u16 alloced; + u32 pktlen; + dma_addr_t physaddr; + struct msgbuf_rx_bufpost *rx_bufpost; + long long address; + u32 pktid; + u32 i; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; + ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, + count, + &alloced); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + return 0; + } + + for (i = 0; i < alloced; i++) { + rx_bufpost = (struct msgbuf_rx_bufpost *)ret_ptr; + memset(rx_bufpost, 0, sizeof(*rx_bufpost)); + + skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); + + if (skb == NULL) { + brcmf_err("Failed to alloc SKB\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + pktlen = skb->len; + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, skb, 0, + &physaddr, &pktid)) { + dev_kfree_skb_any(skb); + brcmf_err("No PKTID available !!\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + if (msgbuf->rx_metadata_offset) { + address = (long long)(long)physaddr; + rx_bufpost->metadata_buf_len = + cpu_to_le16(msgbuf->rx_metadata_offset); + rx_bufpost->metadata_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->metadata_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + skb_pull(skb, msgbuf->rx_metadata_offset); + pktlen = skb->len; + physaddr += msgbuf->rx_metadata_offset; + } + rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; + rx_bufpost->msg.request_id = cpu_to_le32(pktid); + + address = (long long)(long)physaddr; + rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); + rx_bufpost->data_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->data_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + ret_ptr += brcmf_commonring_len_item(commonring); + } + + if (i) + brcmf_commonring_write_complete(commonring); + + return i; +} + + +static void +brcmf_msgbuf_rxbuf_data_fill(struct brcmf_msgbuf *msgbuf) +{ + u32 fillbufs; + u32 retcount; + + fillbufs = msgbuf->max_rxbufpost - msgbuf->rxbufpost; + + while (fillbufs) { + retcount = brcmf_msgbuf_rxbuf_data_post(msgbuf, fillbufs); + if (!retcount) + break; + msgbuf->rxbufpost += retcount; + fillbufs -= retcount; + } +} + + +static void +brcmf_msgbuf_update_rxbufpost_count(struct brcmf_msgbuf *msgbuf, u16 rxcnt) +{ + msgbuf->rxbufpost -= rxcnt; + if (msgbuf->rxbufpost <= (msgbuf->max_rxbufpost - + BRCMF_MSGBUF_RXBUFPOST_THRESHOLD)) + brcmf_msgbuf_rxbuf_data_fill(msgbuf); +} + + +static u32 +brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, + u32 count) +{ + struct brcmf_commonring *commonring; + void *ret_ptr; + struct sk_buff *skb; + u16 alloced; + u32 pktlen; + dma_addr_t physaddr; + struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; + long long address; + u32 pktid; + u32 i; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write_multiple(commonring, + count, + &alloced); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return 0; + } + + for (i = 0; i < alloced; i++) { + rx_bufpost = (struct msgbuf_rx_ioctl_resp_or_event *)ret_ptr; + memset(rx_bufpost, 0, sizeof(*rx_bufpost)); + + skb = brcmu_pkt_buf_get_skb(BRCMF_MSGBUF_MAX_PKT_SIZE); + + if (skb == NULL) { + brcmf_err("Failed to alloc SKB\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + + pktlen = skb->len; + if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, skb, 0, + &physaddr, &pktid)) { + dev_kfree_skb_any(skb); + brcmf_err("No PKTID available !!\n"); + brcmf_commonring_write_cancel(commonring, alloced - i); + break; + } + if (event_buf) + rx_bufpost->msg.msgtype = MSGBUF_TYPE_EVENT_BUF_POST; + else + rx_bufpost->msg.msgtype = + MSGBUF_TYPE_IOCTLRESP_BUF_POST; + rx_bufpost->msg.request_id = cpu_to_le32(pktid); + + address = (long long)(long)physaddr; + rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); + rx_bufpost->host_buf_addr.high_addr = + cpu_to_le32(address >> 32); + rx_bufpost->host_buf_addr.low_addr = + cpu_to_le32(address & 0xffffffff); + + ret_ptr += brcmf_commonring_len_item(commonring); + } + + if (i) + brcmf_commonring_write_complete(commonring); + + brcmf_commonring_unlock(commonring); + + return i; +} + + +static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf) +{ + u32 count; + + count = msgbuf->max_ioctlrespbuf - msgbuf->cur_ioctlrespbuf; + count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, false, count); + msgbuf->cur_ioctlrespbuf += count; +} + + +static void brcmf_msgbuf_rxbuf_event_post(struct brcmf_msgbuf *msgbuf) +{ + u32 count; + + count = msgbuf->max_eventbuf - msgbuf->cur_eventbuf; + count = brcmf_msgbuf_rxbuf_ctrl_post(msgbuf, true, count); + msgbuf->cur_eventbuf += count; +} + + +static void +brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, + u8 ifidx) +{ + struct brcmf_if *ifp; + + ifp = msgbuf->drvr->iflist[ifidx]; + if (!ifp || !ifp->ndev) { + brcmu_pkt_buf_free_skb(skb); + return; + } + brcmf_netif_rx(ifp, skb); +} + + +static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_rx_event *event; + u32 idx; + u16 buflen; + struct sk_buff *skb; + + event = (struct msgbuf_rx_event *)buf; + idx = le32_to_cpu(event->msg.request_id); + buflen = le16_to_cpu(event->event_data_len); + + if (msgbuf->cur_eventbuf) + msgbuf->cur_eventbuf--; + brcmf_msgbuf_rxbuf_event_post(msgbuf); + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, idx); + if (!skb) + return; + + if (msgbuf->rx_dataoffset) + skb_pull(skb, msgbuf->rx_dataoffset); + + skb_trim(skb, buflen); + + brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx); +} + + +static void +brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_rx_complete *rx_complete; + struct sk_buff *skb; + u16 data_offset; + u16 buflen; + u32 idx; + + brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1); + + rx_complete = (struct msgbuf_rx_complete *)buf; + data_offset = le16_to_cpu(rx_complete->data_offset); + buflen = le16_to_cpu(rx_complete->data_len); + idx = le32_to_cpu(rx_complete->msg.request_id); + + skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, + msgbuf->rx_pktids, idx); + + if (data_offset) + skb_pull(skb, data_offset); + else if (msgbuf->rx_dataoffset) + skb_pull(skb, msgbuf->rx_dataoffset); + + skb_trim(skb, buflen); + + brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx); +} + + +static void +brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_flowring_create_resp *flowring_create_resp; + u16 status; + u16 flowid; + + flowring_create_resp = (struct msgbuf_flowring_create_resp *)buf; + + flowid = le16_to_cpu(flowring_create_resp->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + status = le16_to_cpu(flowring_create_resp->compl_hdr.status); + + if (status) { + brcmf_err("Flowring creation failed, code %d\n", status); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return; + } + brcmf_dbg(MSGBUF, "Flowring %d Create response status %d\n", flowid, + status); + + brcmf_flowring_open(msgbuf->flow, flowid); + + brcmf_msgbuf_schedule_txdata(msgbuf, flowid); +} + + +static void +brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_flowring_delete_resp *flowring_delete_resp; + u16 status; + u16 flowid; + + flowring_delete_resp = (struct msgbuf_flowring_delete_resp *)buf; + + flowid = le16_to_cpu(flowring_delete_resp->compl_hdr.flow_ring_id); + flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; + status = le16_to_cpu(flowring_delete_resp->compl_hdr.status); + + if (status) { + brcmf_err("Flowring deletion failed, code %d\n", status); + brcmf_flowring_delete(msgbuf->flow, flowid); + return; + } + brcmf_dbg(MSGBUF, "Flowring %d Delete response status %d\n", flowid, + status); + + brcmf_msgbuf_remove_flowring(msgbuf, flowid); +} + + +static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) +{ + struct msgbuf_common_hdr *msg; + + msg = (struct msgbuf_common_hdr *)buf; + switch (msg->msgtype) { + case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT\n"); + brcmf_msgbuf_process_flow_ring_create_response(msgbuf, buf); + break; + case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT\n"); + brcmf_msgbuf_process_flow_ring_delete_response(msgbuf, buf); + break; + case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTLPTR_REQ_ACK\n"); + break; + case MSGBUF_TYPE_IOCTL_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_IOCTL_CMPLT\n"); + brcmf_msgbuf_process_ioctl_complete(msgbuf, buf); + break; + case MSGBUF_TYPE_WL_EVENT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_WL_EVENT\n"); + brcmf_msgbuf_process_event(msgbuf, buf); + break; + case MSGBUF_TYPE_TX_STATUS: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_TX_STATUS\n"); + brcmf_msgbuf_process_txstatus(msgbuf, buf); + break; + case MSGBUF_TYPE_RX_CMPLT: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); + brcmf_msgbuf_process_rx_complete(msgbuf, buf); + break; + default: + brcmf_err("Unsupported msgtype %d\n", msg->msgtype); + break; + } +} + + +static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf, + struct brcmf_commonring *commonring) +{ + void *buf; + u16 count; + +again: + buf = brcmf_commonring_get_read_ptr(commonring, &count); + if (buf == NULL) + return; + + while (count) { + brcmf_msgbuf_process_msgtype(msgbuf, + buf + msgbuf->rx_dataoffset); + buf += brcmf_commonring_len_item(commonring); + count--; + } + brcmf_commonring_read_complete(commonring); + + if (commonring->r_ptr == 0) + goto again; +} + + +int brcmf_proto_msgbuf_rx_trigger(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + void *buf; + u32 flowid; + + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; + brcmf_msgbuf_process_rx(msgbuf, buf); + + for_each_set_bit(flowid, msgbuf->txstatus_done_map, + msgbuf->nrof_flowrings) { + clear_bit(flowid, msgbuf->txstatus_done_map); + if (brcmf_flowring_qlen(msgbuf->flow, flowid)) + brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + } + + return 0; +} + + +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct msgbuf_tx_flowring_delete_req *delete; + struct brcmf_commonring *commonring; + void *ret_ptr; + u8 ifidx; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("FW unaware, flowring will be removed !!\n"); + brcmf_commonring_unlock(commonring); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + return; + } + + delete = (struct msgbuf_tx_flowring_delete_req *)ret_ptr; + + ifidx = brcmf_flowring_ifidx_get(msgbuf->flow, flowid); + + delete->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE; + delete->msg.ifidx = ifidx; + delete->msg.request_id = 0; + + delete->flow_ring_id = cpu_to_le16(flowid + + BRCMF_NROF_H2D_COMMON_MSGRINGS); + delete->reason = 0; + + brcmf_dbg(MSGBUF, "Send Flow Delete Req flow ID %d, ifindex %d\n", + flowid, ifidx); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + if (err) { + brcmf_err("Failed to submit RING_DELETE, flowring will be removed\n"); + brcmf_msgbuf_remove_flowring(msgbuf, flowid); + } +} + + +int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) +{ + struct brcmf_bus_msgbuf *if_msgbuf; + struct brcmf_msgbuf *msgbuf; + long long address; + u32 count; + + if_msgbuf = drvr->bus_if->msgbuf; + msgbuf = kzalloc(sizeof(*msgbuf), GFP_ATOMIC); + if (!msgbuf) + goto fail; + + msgbuf->txflow_wq = create_singlethread_workqueue("msgbuf_txflow"); + if (msgbuf->txflow_wq == NULL) { + brcmf_err("workqueue creation failed\n"); + goto fail; + } + INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); + count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); + msgbuf->flow_map = kzalloc(count, GFP_ATOMIC); + if (!msgbuf->flow_map) + goto fail; + + msgbuf->txstatus_done_map = kzalloc(count, GFP_ATOMIC); + if (!msgbuf->txstatus_done_map) + goto fail; + + msgbuf->drvr = drvr; + msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + &msgbuf->ioctbuf_handle, + GFP_ATOMIC); + if (!msgbuf->ioctbuf) + goto fail; + address = (long long)(long)msgbuf->ioctbuf_handle; + msgbuf->ioctbuf_phys_hi = address >> 32; + msgbuf->ioctbuf_phys_lo = address & 0xffffffff; + + drvr->proto->hdrpull = brcmf_msgbuf_hdrpull; + drvr->proto->query_dcmd = brcmf_msgbuf_query_dcmd; + drvr->proto->set_dcmd = brcmf_msgbuf_set_dcmd; + drvr->proto->txdata = brcmf_msgbuf_txdata; + drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; + drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; + drvr->proto->pd = msgbuf; + + init_waitqueue_head(&msgbuf->ioctl_resp_wait); + + msgbuf->commonrings = + (struct brcmf_commonring **)if_msgbuf->commonrings; + msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; + msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; + msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * + sizeof(*msgbuf->flowring_dma_handle), GFP_ATOMIC); + + msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset; + msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost; + + msgbuf->max_ioctlrespbuf = BRCMF_MSGBUF_MAX_IOCTLRESPBUF_POST; + msgbuf->max_eventbuf = BRCMF_MSGBUF_MAX_EVENTBUF_POST; + + msgbuf->tx_pktids = brcmf_msgbuf_init_pktids(NR_TX_PKTIDS, + DMA_TO_DEVICE); + if (!msgbuf->tx_pktids) + goto fail; + msgbuf->rx_pktids = brcmf_msgbuf_init_pktids(NR_RX_PKTIDS, + DMA_FROM_DEVICE); + if (!msgbuf->rx_pktids) + goto fail; + + msgbuf->flow = brcmf_flowring_attach(drvr->bus_if->dev, + if_msgbuf->nrof_flowrings); + if (!msgbuf->flow) + goto fail; + + + brcmf_dbg(MSGBUF, "Feeding buffers, rx data %d, rx event %d, rx ioctl resp %d\n", + msgbuf->max_rxbufpost, msgbuf->max_eventbuf, + msgbuf->max_ioctlrespbuf); + count = 0; + do { + brcmf_msgbuf_rxbuf_data_fill(msgbuf); + if (msgbuf->max_rxbufpost != msgbuf->rxbufpost) + msleep(10); + else + break; + count++; + } while (count < 10); + brcmf_msgbuf_rxbuf_event_post(msgbuf); + brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); + + return 0; + +fail: + if (msgbuf) { + kfree(msgbuf->flow_map); + kfree(msgbuf->txstatus_done_map); + brcmf_msgbuf_release_pktids(msgbuf); + if (msgbuf->ioctbuf) + dma_free_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + msgbuf->ioctbuf, + msgbuf->ioctbuf_handle); + kfree(msgbuf); + } + return -ENOMEM; +} + + +void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) +{ + struct brcmf_msgbuf *msgbuf; + + brcmf_dbg(TRACE, "Enter\n"); + if (drvr->proto->pd) { + msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + kfree(msgbuf->flow_map); + kfree(msgbuf->txstatus_done_map); + if (msgbuf->txflow_wq) + destroy_workqueue(msgbuf->txflow_wq); + + brcmf_flowring_detach(msgbuf->flow); + dma_free_coherent(drvr->bus_if->dev, + BRCMF_TX_IOCTL_MAX_MSG_SIZE, + msgbuf->ioctbuf, msgbuf->ioctbuf_handle); + brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf); + drvr->proto->pd = NULL; + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h new file mode 100644 index 00000000000000..f901ae52bf2b41 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_MSGBUF_H +#define BRCMFMAC_MSGBUF_H + + +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 20 +#define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 256 +#define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512 + +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE 32 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE 24 +#define BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE 16 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE 32 +#define BRCMF_H2D_TXFLOWRING_ITEMSIZE 48 + + +int brcmf_proto_msgbuf_rx_trigger(struct device *dev); +int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); +void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); + + +#endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index d333ff8fcfffcc..44b1cb466d4e6f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -21,9 +21,11 @@ #include #include "dhd.h" +#include "dhd_bus.h" #include "dhd_dbg.h" #include "proto.h" #include "bcdc.h" +#include "msgbuf.h" int brcmf_proto_attach(struct brcmf_pub *drvr) @@ -37,10 +39,18 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) goto fail; drvr->proto = proto; - /* BCDC protocol is only protocol supported for the moment */ - if (brcmf_proto_bcdc_attach(drvr)) - goto fail; + if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) { + if (brcmf_proto_bcdc_attach(drvr)) + goto fail; + } else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) { + if (brcmf_proto_msgbuf_attach(drvr)) + goto fail; + } else { + brcmf_err("Unsupported proto type %d\n", + drvr->bus_if->proto_type); + goto fail; + } if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || (proto->configure_addr_mode == NULL) || @@ -61,7 +71,10 @@ void brcmf_proto_detach(struct brcmf_pub *drvr) brcmf_dbg(TRACE, "Enter\n"); if (drvr->proto) { - brcmf_proto_bcdc_detach(drvr); + if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) + brcmf_proto_bcdc_detach(drvr); + else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) + brcmf_proto_msgbuf_detach(drvr); kfree(drvr->proto); drvr->proto = NULL; } From ac0fe6de2602fcc0b908e606e5ef4c60e26221bd Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:04 +0200 Subject: [PATCH 0176/1983] brcmfmac: Adding PCIe bus layer support. Upstream-commit: 9e37f045d5e7f33450515f237c2f6f6bfee137dd This patch will add PCIe support. With this patch the PCIe chipsets 43602, 4354, 4356, 43567, and 43570 will be supported. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/Kconfig | 10 + .../net/wireless/brcm80211/brcmfmac/Makefile | 2 + .../net/wireless/brcm80211/brcmfmac/chip.c | 8 + .../net/wireless/brcm80211/brcmfmac/dhd_bus.h | 1 + .../net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 1 + .../wireless/brcm80211/brcmfmac/dhd_linux.c | 7 + .../net/wireless/brcm80211/brcmfmac/pcie.c | 1811 +++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/pcie.h | 29 + .../wireless/brcm80211/include/brcm_hw_ids.h | 11 + 9 files changed, 1880 insertions(+) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/pcie.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/pcie.h diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index fcfed6b99a62d6..b8e2561ea64582 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig @@ -48,6 +48,16 @@ config BRCMFMAC_USB IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to use the driver for an USB wireless card. +config BRCMFMAC_PCIE + bool "PCIE bus interface support for FullMAC driver" + depends on BRCMFMAC + depends on PCI + select FW_LOADER + ---help--- + This option enables the PCIE bus interface support for Broadcom + IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to + use the driver for an PCIE wireless card. + config BRCM_TRACING bool "Broadcom device tracing" depends on BRCMSMAC || BRCMFMAC diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 0447a47fe237a5..c35adf4bc70b72 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -45,6 +45,8 @@ brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o +brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \ + pcie.o brcmfmac-$(CONFIG_BRCMDBG) += \ dhd_dbg.o brcmfmac-$(CONFIG_BRCM_TRACING) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 96800db0536b57..95efde868db82e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -506,9 +506,17 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) break; case BRCM_CC_4339_CHIP_ID: case BRCM_CC_4354_CHIP_ID: + case BRCM_CC_4356_CHIP_ID: + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; + case BRCM_CC_43602_CHIP_ID: + ci->pub.ramsize = 0xf0000; + ci->pub.rambase = 0x180000; + break; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 4053368eb743c1..3122b86050a1a6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -120,6 +120,7 @@ struct brcmf_bus { union { struct brcmf_sdio_dev *sdio; struct brcmf_usbdev *usb; + struct brcmf_pciedev *pcie; } bus_priv; enum brcmf_bus_protocol_type proto_type; struct device *dev; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 6804eeca76884e..dec40d316c8291 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -36,6 +36,7 @@ #define BRCMF_BCDC_VAL 0x00010000 #define BRCMF_SDIO_VAL 0x00020000 #define BRCMF_MSGBUF_VAL 0x00040000 +#define BRCMF_PCIE_VAL 0x00080000 /* set default print format */ #undef pr_fmt diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 7e14e5fa47449b..b456bcb7f9164c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -32,6 +32,7 @@ #include "fwsignal.h" #include "feature.h" #include "proto.h" +#include "pcie.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -1084,6 +1085,9 @@ static void brcmf_driver_register(struct work_struct *work) #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_register(); #endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_register(); +#endif } static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); @@ -1108,6 +1112,9 @@ static void __exit brcmfmac_module_exit(void) #endif #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_exit(); #endif brcmf_debugfs_exit(); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c new file mode 100644 index 00000000000000..89be96d3b6e964 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -0,0 +1,1811 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dhd_dbg.h" +#include "dhd_bus.h" +#include "commonring.h" +#include "msgbuf.h" +#include "pcie.h" +#include "firmware.h" +#include "chip.h" + + +enum brcmf_pcie_state { + BRCMFMAC_PCIE_STATE_DOWN, + BRCMFMAC_PCIE_STATE_UP +}; + + +#define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" +#define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" +#define BRCMF_PCIE_4354_FW_NAME "brcm/brcmfmac4354-pcie.bin" +#define BRCMF_PCIE_4354_NVRAM_NAME "brcm/brcmfmac4354-pcie.txt" +#define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" +#define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" +#define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" +#define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" + +#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ + +#define BRCMF_PCIE_TCM_MAP_SIZE (4096 * 1024) +#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) + +/* backplane addres space accessed by BAR0 */ +#define BRCMF_PCIE_BAR0_WINDOW 0x80 +#define BRCMF_PCIE_BAR0_REG_SIZE 0x1000 +#define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70 + +#define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000 +#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000 + +#define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40 +#define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C + +#define BRCMF_PCIE_REG_INTSTATUS 0x90 +#define BRCMF_PCIE_REG_INTMASK 0x94 +#define BRCMF_PCIE_REG_SBMBX 0x98 + +#define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 +#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 +#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C +#define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120 +#define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 +#define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX 0x140 + +#define BRCMF_PCIE_GENREV1 1 +#define BRCMF_PCIE_GENREV2 2 + +#define BRCMF_PCIE2_INTA 0x01 +#define BRCMF_PCIE2_INTB 0x02 + +#define BRCMF_PCIE_INT_0 0x01 +#define BRCMF_PCIE_INT_1 0x02 +#define BRCMF_PCIE_INT_DEF (BRCMF_PCIE_INT_0 | \ + BRCMF_PCIE_INT_1) + +#define BRCMF_PCIE_MB_INT_FN0_0 0x0100 +#define BRCMF_PCIE_MB_INT_FN0_1 0x0200 +#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 +#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 +#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 +#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 +#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 +#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 +#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 +#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 + +#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1) + +#define BRCMF_PCIE_MIN_SHARED_VERSION 4 +#define BRCMF_PCIE_MAX_SHARED_VERSION 5 +#define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF +#define BRCMF_PCIE_SHARED_TXPUSH_SUPPORT 0x4000 + +#define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 +#define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 + +#define BRCMF_SHARED_MAX_RXBUFPOST_OFFSET 34 +#define BRCMF_SHARED_RING_BASE_OFFSET 52 +#define BRCMF_SHARED_RX_DATAOFFSET_OFFSET 36 +#define BRCMF_SHARED_CONSOLE_ADDR_OFFSET 20 +#define BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET 40 +#define BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET 44 +#define BRCMF_SHARED_RING_INFO_ADDR_OFFSET 48 +#define BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET 52 +#define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 +#define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 +#define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 + +#define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 +#define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 +#define BRCMF_RING_H2D_RING_MEM_OFFSET 4 +#define BRCMF_RING_H2D_RING_STATE_OFFSET 8 + +#define BRCMF_RING_MEM_BASE_ADDR_OFFSET 8 +#define BRCMF_RING_MAX_ITEM_OFFSET 4 +#define BRCMF_RING_LEN_ITEMS_OFFSET 6 +#define BRCMF_RING_MEM_SZ 16 +#define BRCMF_RING_STATE_SZ 8 + +#define BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET 4 +#define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 +#define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 +#define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 +#define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 +#define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 + +#define BRCMF_DEF_MAX_RXBUFPOST 255 + +#define BRCMF_CONSOLE_BUFADDR_OFFSET 8 +#define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 +#define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 + +#define BRCMF_DMA_D2H_SCRATCH_BUF_LEN 8 +#define BRCMF_DMA_D2H_RINGUPD_BUF_LEN 1024 + +#define BRCMF_D2H_DEV_D3_ACK 0x00000001 +#define BRCMF_D2H_DEV_DS_ENTER_REQ 0x00000002 +#define BRCMF_D2H_DEV_DS_EXIT_NOTE 0x00000004 + +#define BRCMF_H2D_HOST_D3_INFORM 0x00000001 +#define BRCMF_H2D_HOST_DS_ACK 0x00000002 + +#define BRCMF_PCIE_MBDATA_TIMEOUT 2000 + + +MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); + + +struct brcmf_pcie_console { + u32 base_addr; + u32 buf_addr; + u32 bufsize; + u32 read_idx; + u8 log_str[256]; + u8 log_idx; +}; + +struct brcmf_pcie_shared_info { + u32 tcm_base_address; + u32 flags; + struct brcmf_pcie_ringbuf *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_pcie_ringbuf *flowrings; + u16 max_rxbufpost; + u32 nrof_flowrings; + u32 rx_dataoffset; + u32 htod_mb_data_addr; + u32 dtoh_mb_data_addr; + u32 ring_info_addr; + struct brcmf_pcie_console console; + void *scratch; + dma_addr_t scratch_dmahandle; + void *ringupd; + dma_addr_t ringupd_dmahandle; +}; + +struct brcmf_pcie_core_info { + u32 base; + u32 wrapbase; +}; + +struct brcmf_pciedev_info { + enum brcmf_pcie_state state; + bool in_irq; + bool irq_requested; + struct pci_dev *pdev; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + void __iomem *regs; + void __iomem *tcm; + u32 tcm_size; + u32 ram_base; + u32 ram_size; + struct brcmf_chip *ci; + u32 coreid; + u32 generic_corerev; + struct brcmf_pcie_shared_info shared; + void (*ringbell)(struct brcmf_pciedev_info *devinfo); + wait_queue_head_t mbdata_resp_wait; + bool mbdata_completed; + bool irq_allocated; +}; + +struct brcmf_pcie_ringbuf { + struct brcmf_commonring commonring; + dma_addr_t dma_handle; + u32 w_idx_addr; + u32 r_idx_addr; + struct brcmf_pciedev_info *devinfo; + u8 id; +}; + + +static const u32 brcmf_ring_max_item[BRCMF_NROF_COMMON_MSGRINGS] = { + BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM, + BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM, + BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM, + BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM, + BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM +}; + +static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { + BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE, + BRCMF_H2D_MSGRING_RXPOST_SUBMIT_ITEMSIZE, + BRCMF_D2H_MSGRING_CONTROL_COMPLETE_ITEMSIZE, + BRCMF_D2H_MSGRING_TX_COMPLETE_ITEMSIZE, + BRCMF_D2H_MSGRING_RX_COMPLETE_ITEMSIZE +}; + + +/* dma flushing needs implementation for mips and arm platforms. Should + * be put in util. Note, this is not real flushing. It is virtual non + * cached memory. Only write buffers should have to be drained. Though + * this may be different depending on platform...... + */ +#define brcmf_dma_flush(addr, len) +#define brcmf_dma_invalidate_cache(addr, len) + + +static u32 +brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) +{ + void __iomem *address = devinfo->regs + reg_offset; + + return (ioread32(address)); +} + + +static void +brcmf_pcie_write_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset, + u32 value) +{ + void __iomem *address = devinfo->regs + reg_offset; + + iowrite32(value, address); +} + + +static u8 +brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread8(address)); +} + + +static u16 +brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread16(address)); +} + + +static void +brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + iowrite16(value, address); +} + + +static u32 +brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + return (ioread32(address)); +} + + +static void +brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u32 value) +{ + void __iomem *address = devinfo->tcm + mem_offset; + + iowrite32(value, address); +} + + +static u32 +brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + + return (ioread32(addr)); +} + + +static void +brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u32 value) +{ + void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + + iowrite32(value, addr); +} + + +static void +brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *srcaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *src32; + __le16 *src16; + u8 *src8; + + if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { + src8 = (u8 *)srcaddr; + while (len) { + iowrite8(*src8, address); + address++; + src8++; + len--; + } + } else { + len = len / 2; + src16 = (__le16 *)srcaddr; + while (len) { + iowrite16(le16_to_cpu(*src16), address); + address += 2; + src16++; + len--; + } + } + } else { + len = len / 4; + src32 = (__le32 *)srcaddr; + while (len) { + iowrite32(le32_to_cpu(*src32), address); + address += 4; + src32++; + len--; + } + } +} + + +#define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \ + CHIPCREGOFFS(reg), value) + + +static void +brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) +{ + const struct pci_dev *pdev = devinfo->pdev; + struct brcmf_core *core; + u32 bar0_win; + + core = brcmf_chip_get_core(devinfo->ci, coreid); + if (core) { + bar0_win = core->base; + pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, bar0_win); + if (pci_read_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, + &bar0_win) == 0) { + if (bar0_win != core->base) { + bar0_win = core->base; + pci_write_config_dword(pdev, + BRCMF_PCIE_BAR0_WINDOW, + bar0_win); + } + } + } else { + brcmf_err("Unsupported core selected %x\n", coreid); + } +} + + +static void brcmf_pcie_detach(struct brcmf_pciedev_info *devinfo) +{ + u16 cfg_offset[] = { 0x4, 0x4C, 0x58, 0x5C, 0x60, 0x64, 0xDC, 0x228, + 0x248, 0x4e0, 0x4f4 }; + u32 i; + u32 val; + + if (!devinfo->ci) + return; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); + WRITECC32(devinfo, watchdog, 0x4e0); + + msleep(100); + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, + cfg_offset[i]); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", + cfg_offset[i], val); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, + val); + } +} + + +static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) +{ + u32 config; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) + brcmf_pcie_detach(devinfo); + /* BAR1 window may not be sized properly */ + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); + config = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, config); + + device_wakeup_enable(&devinfo->pdev->dev); +} + + +static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) +{ + brcmf_chip_enter_download(devinfo->ci); + + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { + brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, + 5); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, + 0); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, + 7); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, + 0); + } + return 0; +} + + +static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, + u32 resetintr) +{ + struct brcmf_core *core; + + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); + } + + return !brcmf_chip_exit_download(devinfo->ci, resetintr); +} + + +static void +brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 cur_htod_mb_data; + u32 i; + + shared = &devinfo->shared; + addr = shared->htod_mb_data_addr; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + + if (cur_htod_mb_data != 0) + brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", + cur_htod_mb_data); + + i = 0; + while (cur_htod_mb_data != 0) { + msleep(10); + i++; + if (i > 100) + break; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + } + + brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); +} + + +static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 dtoh_mb_data; + + shared = &devinfo->shared; + addr = shared->dtoh_mb_data_addr; + dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + + if (!dtoh_mb_data) + return; + + brcmf_pcie_write_tcm32(devinfo, addr, 0); + + brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); + if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { + brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); + brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); + } + if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) + brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); + if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) + brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); + if (waitqueue_active(&devinfo->mbdata_resp_wait)) { + devinfo->mbdata_completed = true; + wake_up(&devinfo->mbdata_resp_wait); + } +} + + +static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_shared_info *shared; + struct brcmf_pcie_console *console; + u32 addr; + + shared = &devinfo->shared; + console = &shared->console; + addr = shared->tcm_base_address + BRCMF_SHARED_CONSOLE_ADDR_OFFSET; + console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET; + console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; + console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); + + brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n", + console->base_addr, console->buf_addr, console->bufsize); +} + + +static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_console *console; + u32 addr; + u8 ch; + u32 newidx; + + console = &devinfo->shared.console; + addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; + newidx = brcmf_pcie_read_tcm32(devinfo, addr); + while (newidx != console->read_idx) { + addr = console->buf_addr + console->read_idx; + ch = brcmf_pcie_read_tcm8(devinfo, addr); + console->read_idx++; + if (console->read_idx == console->bufsize) + console->read_idx = 0; + if (ch == '\r') + continue; + console->log_str[console->log_idx] = ch; + console->log_idx++; + if ((ch != '\n') && + (console->log_idx == (sizeof(console->log_str) - 2))) { + ch = '\n'; + console->log_str[console->log_idx] = ch; + console->log_idx++; + } + + if (ch == '\n') { + console->log_str[console->log_idx] = 0; + brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str); + console->log_idx = 0; + } + } +} + + +static __used void brcmf_pcie_ringbell_v1(struct brcmf_pciedev_info *devinfo) +{ + u32 reg_value; + + brcmf_dbg(PCIE, "RING !\n"); + reg_value = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT); + reg_value |= BRCMF_PCIE2_INTB; + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + reg_value); +} + + +static void brcmf_pcie_ringbell_v2(struct brcmf_pciedev_info *devinfo) +{ + brcmf_dbg(PCIE, "RING !\n"); + /* Any arbitrary value will do, lets use 1 */ + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_H2D_MAILBOX, 1); +} + + +static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, + 0); + else + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + 0); +} + + +static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTMASK, + BRCMF_PCIE_INT_DEF); + else + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, + BRCMF_PCIE_MB_INT_D2H_DB | + BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1); +} + + +static irqreturn_t brcmf_pcie_quick_check_isr_v1(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + u32 status; + + status = 0; + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + if (status) { + brcmf_pcie_intr_disable(devinfo); + brcmf_dbg(PCIE, "Enter\n"); + return IRQ_WAKE_THREAD; + } + return IRQ_NONE; +} + + +static irqreturn_t brcmf_pcie_quick_check_isr_v2(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + + if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) { + brcmf_pcie_intr_disable(devinfo); + brcmf_dbg(PCIE, "Enter\n"); + return IRQ_WAKE_THREAD; + } + return IRQ_NONE; +} + + +static irqreturn_t brcmf_pcie_isr_thread_v1(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + const struct pci_dev *pdev = devinfo->pdev; + u32 status; + + devinfo->in_irq = true; + status = 0; + pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + brcmf_dbg(PCIE, "Enter %x\n", status); + if (status) { + pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_proto_msgbuf_rx_trigger(&devinfo->pdev->dev); + } + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_pcie_intr_enable(devinfo); + devinfo->in_irq = false; + return IRQ_HANDLED; +} + + +static irqreturn_t brcmf_pcie_isr_thread_v2(int irq, void *arg) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; + u32 status; + + devinfo->in_irq = true; + status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_dbg(PCIE, "Enter %x\n", status); + if (status) { + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + status); + if (status & (BRCMF_PCIE_MB_INT_FN0_0 | + BRCMF_PCIE_MB_INT_FN0_1)) + brcmf_pcie_handle_mb_data(devinfo); + if (status & BRCMF_PCIE_MB_INT_D2H_DB) { + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_proto_msgbuf_rx_trigger( + &devinfo->pdev->dev); + } + } + brcmf_pcie_bus_console_read(devinfo); + if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) + brcmf_pcie_intr_enable(devinfo); + devinfo->in_irq = false; + return IRQ_HANDLED; +} + + +static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + + pdev = devinfo->pdev; + + brcmf_pcie_intr_disable(devinfo); + + brcmf_dbg(PCIE, "Enter\n"); + /* is it a v1 or v2 implementation */ + devinfo->irq_requested = false; + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { + if (request_threaded_irq(pdev->irq, + brcmf_pcie_quick_check_isr_v1, + brcmf_pcie_isr_thread_v1, + IRQF_SHARED, "brcmf_pcie_intr", + devinfo)) { + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; + } + } else { + if (request_threaded_irq(pdev->irq, + brcmf_pcie_quick_check_isr_v2, + brcmf_pcie_isr_thread_v2, + IRQF_SHARED, "brcmf_pcie_intr", + devinfo)) { + brcmf_err("Failed to request IRQ %d\n", pdev->irq); + return -EIO; + } + } + devinfo->irq_requested = true; + devinfo->irq_allocated = true; + return 0; +} + + +static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + u32 status; + u32 count; + + if (!devinfo->irq_allocated) + return; + + pdev = devinfo->pdev; + + brcmf_pcie_intr_disable(devinfo); + if (!devinfo->irq_requested) + return; + devinfo->irq_requested = false; + free_irq(pdev->irq, devinfo); + + msleep(50); + count = 0; + while ((devinfo->in_irq) && (count < 20)) { + msleep(50); + count++; + } + if (devinfo->in_irq) + brcmf_err("Still in IRQ (processing) !!!\n"); + + if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { + status = 0; + pci_read_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, &status); + pci_write_config_dword(pdev, BRCMF_PCIE_REG_INTSTATUS, status); + } else { + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + status); + } + devinfo->irq_allocated = false; +} + + +static int brcmf_pcie_ring_mb_write_rptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, + commonring->w_ptr, ring->id); + + brcmf_pcie_write_tcm16(devinfo, ring->r_idx_addr, commonring->r_ptr); + + return 0; +} + + +static int brcmf_pcie_ring_mb_write_wptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, + commonring->r_ptr, ring->id); + + brcmf_pcie_write_tcm16(devinfo, ring->w_idx_addr, commonring->w_ptr); + + return 0; +} + + +static int brcmf_pcie_ring_mb_ring_bell(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + devinfo->ringbell(devinfo); + + return 0; +} + + +static int brcmf_pcie_ring_mb_update_rptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + commonring->r_ptr = brcmf_pcie_read_tcm16(devinfo, ring->r_idx_addr); + + brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, + commonring->w_ptr, ring->id); + + return 0; +} + + +static int brcmf_pcie_ring_mb_update_wptr(void *ctx) +{ + struct brcmf_pcie_ringbuf *ring = (struct brcmf_pcie_ringbuf *)ctx; + struct brcmf_pciedev_info *devinfo = ring->devinfo; + struct brcmf_commonring *commonring = &ring->commonring; + + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) + return -EIO; + + commonring->w_ptr = brcmf_pcie_read_tcm16(devinfo, ring->w_idx_addr); + + brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, + commonring->r_ptr, ring->id); + + return 0; +} + + +static void * +brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, + u32 size, u32 tcm_dma_phys_addr, + dma_addr_t *dma_handle) +{ + void *ring; + long long address; + + ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, + GFP_KERNEL); + if (!ring) + return NULL; + + address = (long long)(long)*dma_handle; + brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, + address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); + + memset(ring, 0, size); + + return (ring); +} + + +static struct brcmf_pcie_ringbuf * +brcmf_pcie_alloc_dma_and_ring(struct brcmf_pciedev_info *devinfo, u32 ring_id, + u32 tcm_ring_phys_addr) +{ + void *dma_buf; + dma_addr_t dma_handle; + struct brcmf_pcie_ringbuf *ring; + u32 size; + u32 addr; + + size = brcmf_ring_max_item[ring_id] * brcmf_ring_itemsize[ring_id]; + dma_buf = brcmf_pcie_init_dmabuffer_for_device(devinfo, size, + tcm_ring_phys_addr + BRCMF_RING_MEM_BASE_ADDR_OFFSET, + &dma_handle); + if (!dma_buf) + return NULL; + + addr = tcm_ring_phys_addr + BRCMF_RING_MAX_ITEM_OFFSET; + brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_max_item[ring_id]); + addr = tcm_ring_phys_addr + BRCMF_RING_LEN_ITEMS_OFFSET; + brcmf_pcie_write_tcm16(devinfo, addr, brcmf_ring_itemsize[ring_id]); + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) { + dma_free_coherent(&devinfo->pdev->dev, size, dma_buf, + dma_handle); + return NULL; + } + brcmf_commonring_config(&ring->commonring, brcmf_ring_max_item[ring_id], + brcmf_ring_itemsize[ring_id], dma_buf); + ring->dma_handle = dma_handle; + ring->devinfo = devinfo; + brcmf_commonring_register_cb(&ring->commonring, + brcmf_pcie_ring_mb_ring_bell, + brcmf_pcie_ring_mb_update_rptr, + brcmf_pcie_ring_mb_update_wptr, + brcmf_pcie_ring_mb_write_rptr, + brcmf_pcie_ring_mb_write_wptr, ring); + + return (ring); +} + + +static void brcmf_pcie_release_ringbuffer(struct device *dev, + struct brcmf_pcie_ringbuf *ring) +{ + void *dma_buf; + u32 size; + + if (!ring) + return; + + dma_buf = ring->commonring.buf_addr; + if (dma_buf) { + size = ring->commonring.depth * ring->commonring.item_len; + dma_free_coherent(dev, size, dma_buf, ring->dma_handle); + } + kfree(ring); +} + + +static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) +{ + u32 i; + + for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) { + brcmf_pcie_release_ringbuffer(&devinfo->pdev->dev, + devinfo->shared.commonrings[i]); + devinfo->shared.commonrings[i] = NULL; + } + kfree(devinfo->shared.flowrings); + devinfo->shared.flowrings = NULL; +} + + +static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) +{ + struct brcmf_pcie_ringbuf *ring; + struct brcmf_pcie_ringbuf *rings; + u32 ring_addr; + u32 d2h_w_idx_ptr; + u32 d2h_r_idx_ptr; + u32 h2d_w_idx_ptr; + u32 h2d_r_idx_ptr; + u32 addr; + u32 ring_mem_ptr; + u32 i; + u16 max_sub_queues; + + ring_addr = devinfo->shared.ring_info_addr; + brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); + + addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; + d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; + d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; + h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; + h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; + ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + + for (i = 0; i < BRCMF_NROF_H2D_COMMON_MSGRINGS; i++) { + ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); + if (!ring) + goto fail; + ring->w_idx_addr = h2d_w_idx_ptr; + ring->r_idx_addr = h2d_r_idx_ptr; + ring->id = i; + devinfo->shared.commonrings[i] = ring; + + h2d_w_idx_ptr += sizeof(u32); + h2d_r_idx_ptr += sizeof(u32); + ring_mem_ptr += BRCMF_RING_MEM_SZ; + } + + for (i = BRCMF_NROF_H2D_COMMON_MSGRINGS; + i < BRCMF_NROF_COMMON_MSGRINGS; i++) { + ring = brcmf_pcie_alloc_dma_and_ring(devinfo, i, ring_mem_ptr); + if (!ring) + goto fail; + ring->w_idx_addr = d2h_w_idx_ptr; + ring->r_idx_addr = d2h_r_idx_ptr; + ring->id = i; + devinfo->shared.commonrings[i] = ring; + + d2h_w_idx_ptr += sizeof(u32); + d2h_r_idx_ptr += sizeof(u32); + ring_mem_ptr += BRCMF_RING_MEM_SZ; + } + + addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; + max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + devinfo->shared.nrof_flowrings = + max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; + rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), + GFP_KERNEL); + if (!rings) + goto fail; + + brcmf_dbg(PCIE, "Nr of flowrings is %d\n", + devinfo->shared.nrof_flowrings); + + for (i = 0; i < devinfo->shared.nrof_flowrings; i++) { + ring = &rings[i]; + ring->devinfo = devinfo; + ring->id = i + BRCMF_NROF_COMMON_MSGRINGS; + brcmf_commonring_register_cb(&ring->commonring, + brcmf_pcie_ring_mb_ring_bell, + brcmf_pcie_ring_mb_update_rptr, + brcmf_pcie_ring_mb_update_wptr, + brcmf_pcie_ring_mb_write_rptr, + brcmf_pcie_ring_mb_write_wptr, + ring); + ring->w_idx_addr = h2d_w_idx_ptr; + ring->r_idx_addr = h2d_r_idx_ptr; + h2d_w_idx_ptr += sizeof(u32); + h2d_r_idx_ptr += sizeof(u32); + } + devinfo->shared.flowrings = rings; + + return 0; + +fail: + brcmf_err("Allocating commonring buffers failed\n"); + brcmf_pcie_release_ringbuffers(devinfo); + return -ENOMEM; +} + + +static void +brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->shared.scratch) + dma_free_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_SCRATCH_BUF_LEN, + devinfo->shared.scratch, + devinfo->shared.scratch_dmahandle); + if (devinfo->shared.ringupd) + dma_free_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_RINGUPD_BUF_LEN, + devinfo->shared.ringupd, + devinfo->shared.ringupd_dmahandle); +} + +static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) +{ + long long address; + u32 addr; + + devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_SCRATCH_BUF_LEN, + &devinfo->shared.scratch_dmahandle, GFP_KERNEL); + if (!devinfo->shared.scratch) + goto fail; + + memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + brcmf_dma_flush(devinfo->shared.scratch, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; + address = (long long)(long)devinfo->shared.scratch_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_SCRATCH_LEN_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); + + devinfo->shared.ringupd = dma_alloc_coherent(&devinfo->pdev->dev, + BRCMF_DMA_D2H_RINGUPD_BUF_LEN, + &devinfo->shared.ringupd_dmahandle, GFP_KERNEL); + if (!devinfo->shared.ringupd) + goto fail; + + memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + brcmf_dma_flush(devinfo->shared.ringupd, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; + address = (long long)(long)devinfo->shared.ringupd_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + addr = devinfo->shared.tcm_base_address + + BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET; + brcmf_pcie_write_tcm32(devinfo, addr, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); + return 0; + +fail: + brcmf_err("Allocating scratch buffers failed\n"); + brcmf_pcie_release_scratchbuffers(devinfo); + return -ENOMEM; +} + + +static void brcmf_pcie_down(struct device *dev) +{ +} + + +static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) +{ + return 0; +} + + +static int brcmf_pcie_tx_ctlpkt(struct device *dev, unsigned char *msg, + uint len) +{ + return 0; +} + + +static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg, + uint len) +{ + return 0; +} + + +static struct brcmf_bus_ops brcmf_pcie_bus_ops = { + .txdata = brcmf_pcie_tx, + .stop = brcmf_pcie_down, + .txctl = brcmf_pcie_tx_ctlpkt, + .rxctl = brcmf_pcie_rx_ctlpkt, +}; + + +static int +brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, + u32 sharedram_addr) +{ + struct brcmf_pcie_shared_info *shared; + u32 addr; + u32 version; + + shared = &devinfo->shared; + shared->tcm_base_address = sharedram_addr; + + shared->flags = brcmf_pcie_read_tcm32(devinfo, sharedram_addr); + version = shared->flags & BRCMF_PCIE_SHARED_VERSION_MASK; + brcmf_dbg(PCIE, "PCIe protocol version %d\n", version); + if ((version > BRCMF_PCIE_MAX_SHARED_VERSION) || + (version < BRCMF_PCIE_MIN_SHARED_VERSION)) { + brcmf_err("Unsupported PCIE version %d\n", version); + return -EINVAL; + } + if (shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT) { + brcmf_err("Unsupported legacy TX mode 0x%x\n", + shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT); + return -EINVAL; + } + + addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; + shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); + if (shared->max_rxbufpost == 0) + shared->max_rxbufpost = BRCMF_DEF_MAX_RXBUFPOST; + + addr = sharedram_addr + BRCMF_SHARED_RX_DATAOFFSET_OFFSET; + shared->rx_dataoffset = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_HTOD_MB_DATA_ADDR_OFFSET; + shared->htod_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_DTOH_MB_DATA_ADDR_OFFSET; + shared->dtoh_mb_data_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; + shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); + + brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", + shared->max_rxbufpost, shared->rx_dataoffset); + + brcmf_pcie_bus_console_init(devinfo); + + return 0; +} + + +static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) +{ + char *fw_name; + char *nvram_name; + uint fw_len, nv_len; + char end; + + brcmf_dbg(PCIE, "Enter, chip 0x%04x chiprev %d\n", devinfo->ci->chip, + devinfo->ci->chiprev); + + switch (devinfo->ci->chip) { + case BRCM_CC_43602_CHIP_ID: + fw_name = BRCMF_PCIE_43602_FW_NAME; + nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; + break; + case BRCM_CC_4354_CHIP_ID: + fw_name = BRCMF_PCIE_4354_FW_NAME; + nvram_name = BRCMF_PCIE_4354_NVRAM_NAME; + break; + case BRCM_CC_4356_CHIP_ID: + fw_name = BRCMF_PCIE_4356_FW_NAME; + nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; + break; + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: + fw_name = BRCMF_PCIE_43570_FW_NAME; + nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; + break; + default: + brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); + return -ENODEV; + } + + fw_len = sizeof(devinfo->fw_name) - 1; + nv_len = sizeof(devinfo->nvram_name) - 1; + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + strncpy(devinfo->fw_name, brcmf_firmware_path, fw_len); + strncpy(devinfo->nvram_name, brcmf_firmware_path, nv_len); + fw_len -= strlen(devinfo->fw_name); + nv_len -= strlen(devinfo->nvram_name); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strncat(devinfo->fw_name, "/", fw_len); + strncat(devinfo->nvram_name, "/", nv_len); + fw_len--; + nv_len--; + } + } + strncat(devinfo->fw_name, fw_name, fw_len); + strncat(devinfo->nvram_name, nvram_name, nv_len); + + return 0; +} + + +static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, + const struct firmware *fw, void *nvram, + u32 nvram_len) +{ + u32 sharedram_addr; + u32 sharedram_addr_written; + u32 loop_counter; + int err; + u32 address; + u32 resetintr; + + devinfo->ringbell = brcmf_pcie_ringbell_v2; + devinfo->generic_corerev = BRCMF_PCIE_GENREV2; + + brcmf_dbg(PCIE, "Halt ARM.\n"); + err = brcmf_pcie_enter_download_state(devinfo); + if (err) + return err; + + brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); + brcmf_pcie_copy_mem_todev(devinfo, devinfo->ci->rambase, + (void *)fw->data, fw->size); + + resetintr = get_unaligned_le32(fw->data); + release_firmware(fw); + + /* reset last 4 bytes of RAM address. to be used for shared + * area. This identifies when FW is running + */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + + if (nvram) { + brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); + address = devinfo->ci->rambase + devinfo->ci->ramsize - + nvram_len; + brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); + brcmf_fw_nvram_free(nvram); + } else { + brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", + devinfo->nvram_name); + } + + sharedram_addr_written = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - + 4); + brcmf_dbg(PCIE, "Bring ARM in running state\n"); + err = brcmf_pcie_exit_download_state(devinfo, resetintr); + if (err) + return err; + + brcmf_dbg(PCIE, "Wait for FW init\n"); + sharedram_addr = sharedram_addr_written; + loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50; + while ((sharedram_addr == sharedram_addr_written) && (loop_counter)) { + msleep(50); + sharedram_addr = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - + 4); + loop_counter--; + } + if (sharedram_addr == sharedram_addr_written) { + brcmf_err("FW failed to initialize\n"); + return -ENODEV; + } + brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", sharedram_addr); + + return (brcmf_pcie_init_share_ram_info(devinfo, sharedram_addr)); +} + + +static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) +{ + struct pci_dev *pdev; + int err; + phys_addr_t bar0_addr, bar1_addr; + ulong bar1_size; + + pdev = devinfo->pdev; + + err = pci_enable_device(pdev); + if (err) { + brcmf_err("pci_enable_device failed err=%d\n", err); + return err; + } + + pci_set_master(pdev); + + /* Bar-0 mapped address */ + bar0_addr = pci_resource_start(pdev, 0); + /* Bar-1 mapped address */ + bar1_addr = pci_resource_start(pdev, 2); + /* read Bar-1 mapped memory range */ + bar1_size = pci_resource_len(pdev, 2); + if ((bar1_size == 0) || (bar1_addr == 0)) { + brcmf_err("BAR1 Not enabled, device size=%ld, addr=%#016llx\n", + bar1_size, (unsigned long long)bar1_addr); + return -EINVAL; + } + + devinfo->regs = ioremap_nocache(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); + devinfo->tcm = ioremap_nocache(bar1_addr, BRCMF_PCIE_TCM_MAP_SIZE); + devinfo->tcm_size = BRCMF_PCIE_TCM_MAP_SIZE; + + if (!devinfo->regs || !devinfo->tcm) { + brcmf_err("ioremap() failed (%p,%p)\n", devinfo->regs, + devinfo->tcm); + return -EINVAL; + } + brcmf_dbg(PCIE, "Phys addr : reg space = %p base addr %#016llx\n", + devinfo->regs, (unsigned long long)bar0_addr); + brcmf_dbg(PCIE, "Phys addr : mem space = %p base addr %#016llx\n", + devinfo->tcm, (unsigned long long)bar1_addr); + + return 0; +} + + +static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) +{ + if (devinfo->tcm) + iounmap(devinfo->tcm); + if (devinfo->regs) + iounmap(devinfo->regs); + + pci_disable_device(devinfo->pdev); +} + + +static int brcmf_pcie_attach_bus(struct device *dev) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + } else { + ret = brcmf_bus_start(dev); + if (ret) + brcmf_err("dongle is not responding\n"); + } + + return ret; +} + + +static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr) +{ + u32 ret_addr; + + ret_addr = addr & (BRCMF_PCIE_BAR0_REG_SIZE - 1); + addr &= ~(BRCMF_PCIE_BAR0_REG_SIZE - 1); + pci_write_config_dword(pdev, BRCMF_PCIE_BAR0_WINDOW, addr); + + return ret_addr; +} + + +static u32 brcmf_pcie_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); + return brcmf_pcie_read_reg32(devinfo, addr); +} + + +static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + addr = brcmf_pcie_buscore_prep_addr(devinfo->pdev, addr); + brcmf_pcie_write_reg32(devinfo, addr, value); +} + + +static int brcmf_pcie_buscoreprep(void *ctx) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + int err; + + err = brcmf_pcie_get_resource(devinfo); + if (err == 0) { + /* Set CC watchdog to reset all the cores on the chip to bring + * back dongle to a sane state. + */ + brcmf_pcie_buscore_write32(ctx, CORE_CC_REG(SI_ENUM_BASE, + watchdog), 4); + msleep(100); + } + + return err; +} + + +static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_write_tcm32(devinfo, 0, rstvec); +} + + +static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { + .prepare = brcmf_pcie_buscoreprep, + .exit_dl = brcmf_pcie_buscore_exitdl, + .read32 = brcmf_pcie_buscore_read32, + .write32 = brcmf_pcie_buscore_write32, +}; + +static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, + void *nvram, u32 nvram_len) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_pciedev *pcie_bus_dev = bus->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct brcmf_commonring **flowrings; + int ret; + u32 i; + + brcmf_pcie_attach(devinfo); + + ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); + if (ret) + goto fail; + + devinfo->state = BRCMFMAC_PCIE_STATE_UP; + + ret = brcmf_pcie_init_ringbuffers(devinfo); + if (ret) + goto fail; + + ret = brcmf_pcie_init_scratchbuffers(devinfo); + if (ret) + goto fail; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + ret = brcmf_pcie_request_irq(devinfo); + if (ret) + goto fail; + + /* hook the commonrings in the bus structure. */ + for (i = 0; i < BRCMF_NROF_COMMON_MSGRINGS; i++) + bus->msgbuf->commonrings[i] = + &devinfo->shared.commonrings[i]->commonring; + + flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(flowrings), + GFP_KERNEL); + if (!flowrings) + goto fail; + + for (i = 0; i < devinfo->shared.nrof_flowrings; i++) + flowrings[i] = &devinfo->shared.flowrings[i].commonring; + bus->msgbuf->flowrings = flowrings; + + bus->msgbuf->rx_dataoffset = devinfo->shared.rx_dataoffset; + bus->msgbuf->max_rxbufpost = devinfo->shared.max_rxbufpost; + bus->msgbuf->nrof_flowrings = devinfo->shared.nrof_flowrings; + + init_waitqueue_head(&devinfo->mbdata_resp_wait); + + brcmf_pcie_intr_enable(devinfo); + if (brcmf_pcie_attach_bus(bus->dev) == 0) + return; + + brcmf_pcie_bus_console_read(devinfo); + +fail: + device_release_driver(dev); +} + +static int +brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int ret; + struct brcmf_pciedev_info *devinfo; + struct brcmf_pciedev *pcie_bus_dev; + struct brcmf_bus *bus; + + brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device); + + ret = -ENOMEM; + devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); + if (devinfo == NULL) + return ret; + + devinfo->pdev = pdev; + pcie_bus_dev = NULL; + devinfo->ci = brcmf_chip_attach(devinfo, &brcmf_pcie_buscore_ops); + if (IS_ERR(devinfo->ci)) { + ret = PTR_ERR(devinfo->ci); + devinfo->ci = NULL; + goto fail; + } + + pcie_bus_dev = kzalloc(sizeof(*pcie_bus_dev), GFP_KERNEL); + if (pcie_bus_dev == NULL) { + ret = -ENOMEM; + goto fail; + } + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + ret = -ENOMEM; + goto fail; + } + bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL); + if (!bus->msgbuf) { + ret = -ENOMEM; + kfree(bus); + goto fail; + } + + /* hook it all together. */ + pcie_bus_dev->devinfo = devinfo; + pcie_bus_dev->bus = bus; + bus->dev = &pdev->dev; + bus->bus_priv.pcie = pcie_bus_dev; + bus->ops = &brcmf_pcie_bus_ops; + bus->proto_type = BRCMF_PROTO_MSGBUF; + bus->chip = devinfo->coreid; + dev_set_drvdata(&pdev->dev, bus); + + ret = brcmf_pcie_get_fwnames(devinfo); + if (ret) + goto fail_bus; + + ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup); + if (ret == 0) + return 0; +fail_bus: + kfree(bus->msgbuf); + kfree(bus); +fail: + brcmf_err("failed %x:%x\n", pdev->vendor, pdev->device); + brcmf_pcie_release_resource(devinfo); + if (devinfo->ci) + brcmf_chip_detach(devinfo->ci); + kfree(pcie_bus_dev); + kfree(devinfo); + return ret; +} + + +static void +brcmf_pcie_remove(struct pci_dev *pdev) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + + brcmf_dbg(PCIE, "Enter\n"); + + bus = dev_get_drvdata(&pdev->dev); + if (bus == NULL) + return; + + devinfo = bus->bus_priv.pcie->devinfo; + + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; + if (devinfo->ci) + brcmf_pcie_intr_disable(devinfo); + + brcmf_detach(&pdev->dev); + + kfree(bus->bus_priv.pcie); + kfree(bus->msgbuf->flowrings); + kfree(bus->msgbuf); + kfree(bus); + + brcmf_pcie_release_irq(devinfo); + brcmf_pcie_release_scratchbuffers(devinfo); + brcmf_pcie_release_ringbuffers(devinfo); + brcmf_pcie_detach(devinfo); + brcmf_pcie_release_resource(devinfo); + + if (devinfo->ci) + brcmf_chip_detach(devinfo->ci); + + kfree(devinfo); + dev_set_drvdata(&pdev->dev, NULL); +} + + +#ifdef CONFIG_PM + + +static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; + int err; + + brcmf_dbg(PCIE, "Enter, state=%d, pdev=%p\n", state.event, pdev); + + bus = dev_get_drvdata(&pdev->dev); + devinfo = bus->bus_priv.pcie->devinfo; + + brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); + + devinfo->mbdata_completed = false; + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM); + + wait_event_timeout(devinfo->mbdata_resp_wait, + devinfo->mbdata_completed, + msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT)); + if (!devinfo->mbdata_completed) { + brcmf_err("Timeout on response for entering D3 substate\n"); + return -EIO; + } + brcmf_pcie_release_irq(devinfo); + + err = pci_save_state(pdev); + if (err) { + brcmf_err("pci_save_state failed, err=%d\n", err); + return err; + } + + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + + brcmf_pcie_remove(pdev); + + return pci_prepare_to_sleep(pdev); +} + + +static int brcmf_pcie_resume(struct pci_dev *pdev) +{ + int err; + + brcmf_dbg(PCIE, "Enter, pdev=%p\n", pdev); + + err = pci_set_power_state(pdev, PCI_D0); + if (err) { + brcmf_err("pci_set_power_state failed, err=%d\n", err); + return err; + } + pci_restore_state(pdev); + + err = brcmf_pcie_probe(pdev, NULL); + if (err) + brcmf_err("probe after resume failed, err=%d\n", err); + + return err; +} + + +#endif /* CONFIG_PM */ + + +#define BRCMF_PCIE_DEVICE(dev_id) { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } + +static struct pci_device_id brcmf_pcie_devid_table[] = { + BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), + { /* end: all zeroes */ } +}; + + +MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); + + +static struct pci_driver brcmf_pciedrvr = { + .node = {}, + .name = KBUILD_MODNAME, + .id_table = brcmf_pcie_devid_table, + .probe = brcmf_pcie_probe, + .remove = brcmf_pcie_remove, +#ifdef CONFIG_PM + .suspend = brcmf_pcie_suspend, + .resume = brcmf_pcie_resume +#endif /* CONFIG_PM */ +}; + + +void brcmf_pcie_register(void) +{ + int err; + + brcmf_dbg(PCIE, "Enter\n"); + err = pci_register_driver(&brcmf_pciedrvr); + if (err) + brcmf_err("PCIE driver registration failed, err=%d\n", err); +} + + +void brcmf_pcie_exit(void) +{ + brcmf_dbg(PCIE, "Enter\n"); + pci_unregister_driver(&brcmf_pciedrvr); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/brcm80211/brcmfmac/pcie.h new file mode 100644 index 00000000000000..6edaaf8ef5ce8b --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_PCIE_H +#define BRCMFMAC_PCIE_H + + +struct brcmf_pciedev { + struct brcmf_bus *bus; + struct brcmf_pciedev_info *devinfo; +}; + + +void brcmf_pcie_exit(void); +void brcmf_pcie_register(void); + + +#endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 64d1a7ba040ccc..af26e0de1e5c6b 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -38,8 +38,12 @@ #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 #define BRCM_CC_4354_CHIP_ID 0x4354 +#define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 +#define BRCM_CC_43567_CHIP_ID 43567 #define BRCM_CC_43569_CHIP_ID 43569 +#define BRCM_CC_43570_CHIP_ID 43570 +#define BRCM_CC_43602_CHIP_ID 43602 /* SDIO Device IDs */ #define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID @@ -58,6 +62,13 @@ #define BRCM_USB_43569_DEVICE_ID 0xbd27 #define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc +/* PCIE Device IDs */ +#define BRCM_PCIE_4354_DEVICE_ID 0x43df +#define BRCM_PCIE_4356_DEVICE_ID 0x43ec +#define BRCM_PCIE_43567_DEVICE_ID 0x43d3 +#define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_43602_DEVICE_ID 0x43ba + /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ #define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ From bd8c10be73a69d8aeeecd6df3a4a34a0e883ec44 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:05 +0200 Subject: [PATCH 0177/1983] brcmfmac: Update pcie reset device routine. Upstream-commit: bd4f82e3b2899829c7f87dde0d20daeb780ad014 When a pcie device gets reset then the low power modes l1 and l2 should be temporarily disabled. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/pcie.c | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 89be96d3b6e964..bc972c0ba5f89a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -168,6 +168,20 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_MBDATA_TIMEOUT 2000 +#define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4 +#define BRCMF_PCIE_CFGREG_PM_CSR 0x4C +#define BRCMF_PCIE_CFGREG_MSI_CAP 0x58 +#define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C +#define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60 +#define BRCMF_PCIE_CFGREG_MSI_DATA 0x64 +#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC +#define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC +#define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228 +#define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248 +#define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0 +#define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 +#define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 + MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); @@ -423,21 +437,42 @@ brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) } -static void brcmf_pcie_detach(struct brcmf_pciedev_info *devinfo) +static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) { - u16 cfg_offset[] = { 0x4, 0x4C, 0x58, 0x5C, 0x60, 0x64, 0xDC, 0x228, - 0x248, 0x4e0, 0x4f4 }; + u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, + BRCMF_PCIE_CFGREG_PM_CSR, + BRCMF_PCIE_CFGREG_MSI_CAP, + BRCMF_PCIE_CFGREG_MSI_ADDR_L, + BRCMF_PCIE_CFGREG_MSI_ADDR_H, + BRCMF_PCIE_CFGREG_MSI_DATA, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, + BRCMF_PCIE_CFGREG_RBAR_CTRL, + BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, + BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG, + BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG }; u32 i; u32 val; + u32 lsc; if (!devinfo->ci) return; - brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); - WRITECC32(devinfo, watchdog, 0x4e0); + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); + lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val); + brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); + WRITECC32(devinfo, watchdog, 4); msleep(100); + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, + BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc); + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, @@ -458,7 +493,7 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) - brcmf_pcie_detach(devinfo); + brcmf_pcie_reset_device(devinfo); /* BAR1 window may not be sized properly */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); @@ -1686,7 +1721,7 @@ brcmf_pcie_remove(struct pci_dev *pdev) brcmf_pcie_release_irq(devinfo); brcmf_pcie_release_scratchbuffers(devinfo); brcmf_pcie_release_ringbuffers(devinfo); - brcmf_pcie_detach(devinfo); + brcmf_pcie_reset_device(devinfo); brcmf_pcie_release_resource(devinfo); if (devinfo->ci) From 8736e73f3acfdfed4d9dd9cbec9927ecd7faba64 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:06 +0200 Subject: [PATCH 0178/1983] brcmfmac: Fix msgbuf flow control. Upstream-commit: 17ca5c718414d605f0060336e071fb77359be790 Msgbuf flow control was using a function to flow off and on which was not supported without proptx enabled. Also flow control needs to be handled per ifidx. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 6 +- .../wireless/brcm80211/brcmfmac/flowring.c | 61 +++++++++++++++++-- .../wireless/brcm80211/brcmfmac/flowring.h | 3 +- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 1825f736fd4502..5e4317dbc2b0a1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -121,12 +121,12 @@ struct brcmf_fws_mac_descriptor; * * @BRCMF_NETIF_STOP_REASON_FWS_FC: * netif stopped due to firmware signalling flow control. - * @BRCMF_NETIF_STOP_REASON_BLOCK_BUS: - * netif stopped due to bus blocking. + * @BRCMF_NETIF_STOP_REASON_FLOW: + * netif stopped due to flowring full. */ enum brcmf_netif_stop_reason { BRCMF_NETIF_STOP_REASON_FWS_FC = 1, - BRCMF_NETIF_STOP_REASON_BLOCK_BUS = 2 + BRCMF_NETIF_STOP_REASON_FLOW = 2 }; /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 26cbb7c4ec4c9e..07009046fda50c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -158,6 +158,51 @@ u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid) } +static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, + bool blocked) +{ + struct brcmf_flowring_ring *ring; + struct brcmf_bus *bus_if; + struct brcmf_pub *drvr; + struct brcmf_if *ifp; + bool currently_blocked; + int i; + u8 ifidx; + unsigned long flags; + + spin_lock_irqsave(&flow->block_lock, flags); + + ring = flow->rings[flowid]; + ifidx = brcmf_flowring_ifidx_get(flow, flowid); + + currently_blocked = false; + for (i = 0; i < flow->nrofrings; i++) { + if (flow->rings[i]) { + ring = flow->rings[i]; + if ((ring->status == RING_OPEN) && + (brcmf_flowring_ifidx_get(flow, i) == ifidx)) { + if (ring->blocked) { + currently_blocked = true; + break; + } + } + } + } + ring->blocked = blocked; + if (currently_blocked == blocked) { + spin_unlock_irqrestore(&flow->block_lock, flags); + return; + } + + bus_if = dev_get_drvdata(flow->dev); + drvr = bus_if->drvr; + ifp = drvr->iflist[ifidx]; + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); + + spin_unlock_irqrestore(&flow->block_lock, flags); +} + + void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) { struct brcmf_flowring_ring *ring; @@ -167,6 +212,7 @@ void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) ring = flow->rings[flowid]; if (!ring) return; + brcmf_flowring_block(flow, flowid, false); hash_idx = ring->hash_id; flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; memset(flow->hash[hash_idx].mac, 0, ETH_ALEN); @@ -193,9 +239,16 @@ void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, if (!ring->blocked && (skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) { - brcmf_txflowblock(flow->dev, true); + brcmf_flowring_block(flow, flowid, true); brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid); - ring->blocked = 1; + /* To prevent (work around) possible race condition, check + * queue len again. It is also possible to use locking to + * protect, but that is undesirable for every enqueue and + * dequeue. This simple check will solve a possible race + * condition if it occurs. + */ + if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) + brcmf_flowring_block(flow, flowid, false); } } @@ -213,9 +266,8 @@ struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid) if (ring->blocked && (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) { - brcmf_txflowblock(flow->dev, false); + brcmf_flowring_block(flow, flowid, false); brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid); - ring->blocked = 0; } return skb; @@ -283,6 +335,7 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) if (flow) { flow->dev = dev; flow->nrofrings = nrofrings; + spin_lock_init(&flow->block_lock); for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++) flow->addr_mode[i] = ADDR_INDIRECT; for (i = 0; i < ARRAY_SIZE(flow->hash); i++) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index 677f4b8065f6c5..cb9644ca6ecec5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -35,7 +35,7 @@ enum ring_status { struct brcmf_flowring_ring { u8 hash_id; - u8 blocked; + bool blocked; enum ring_status status; struct sk_buff_head skblist; }; @@ -44,6 +44,7 @@ struct brcmf_flowring { struct device *dev; struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; struct brcmf_flowring_ring **rings; + spinlock_t block_lock; enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; u16 nrofrings; }; From d5bb861c17801ee3cf45e761fe0e7a405b9c2707 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 30 Jul 2014 13:20:07 +0200 Subject: [PATCH 0179/1983] brcmfmac: Add TDLS support to msgbuf. Upstream-commit: 70b7d94bcc1266fb91686bcd539ef81dff40eb3a TDLS connections require dedicated flowrings. This patches adds TDLS event handling and flowring creation/deletion based on these events. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcdc.c | 7 ++ .../wireless/brcm80211/brcmfmac/flowring.c | 94 ++++++++++++++++++- .../wireless/brcm80211/brcmfmac/flowring.h | 9 ++ .../net/wireless/brcm80211/brcmfmac/fweh.c | 6 +- .../net/wireless/brcm80211/brcmfmac/fweh.h | 5 + .../net/wireless/brcm80211/brcmfmac/msgbuf.c | 10 ++ .../net/wireless/brcm80211/brcmfmac/proto.c | 2 +- .../net/wireless/brcm80211/brcmfmac/proto.h | 7 ++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 24 +++++ 9 files changed, 158 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 10b48c2c4b36ab..a159ff3427de9f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -349,6 +349,12 @@ brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx, { } +static void +brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]) +{ +} + int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { struct brcmf_bcdc *bcdc; @@ -369,6 +375,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->txdata = brcmf_proto_bcdc_txdata; drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode; drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; + drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 07009046fda50c..a1016b811284cd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -49,6 +49,23 @@ static const u8 brcmf_flowring_prio2fifo[] = { }; +static bool +brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN]) +{ + struct brcmf_flowring_tdls_entry *search; + + search = flow->tdls_entry; + + while (search) { + if (memcmp(search->mac, mac, ETH_ALEN) == 0) + return true; + search = search->next; + } + + return false; +} + + u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], u8 prio, u8 ifidx) { @@ -67,6 +84,10 @@ u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN], mac = (u8 *)ALLFFMAC; fifo = 0; } + if ((sta) && (flow->tdls_active) && + (brcmf_flowring_is_tdls_mac(flow, da))) { + sta = false; + } hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); found = false; @@ -106,15 +127,17 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], mac = (u8 *)ALLFFMAC; fifo = 0; } + if ((sta) && (flow->tdls_active) && + (brcmf_flowring_is_tdls_mac(flow, da))) { + sta = false; + } hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) : BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx); found = false; hash = flow->hash; for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { - if (((sta) && - (hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX)) || - ((!sta) && - (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0))) { + if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && + (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) { found = true; break; } @@ -356,12 +379,21 @@ void brcmf_flowring_detach(struct brcmf_flowring *flow) { struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_flowring_tdls_entry *search; + struct brcmf_flowring_tdls_entry *remove; u8 flowid; for (flowid = 0; flowid < flow->nrofrings; flowid++) { if (flow->rings[flowid]) brcmf_msgbuf_delete_flowring(drvr, flowid); } + + search = flow->tdls_entry; + while (search) { + remove = search; + search = search->next; + kfree(remove); + } kfree(flow->rings); kfree(flow); } @@ -396,11 +428,25 @@ void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_flowring_hash *hash; + struct brcmf_flowring_tdls_entry *prev; + struct brcmf_flowring_tdls_entry *search; u32 i; u8 flowid; bool sta; sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT); + + search = flow->tdls_entry; + prev = NULL; + while (search) { + if (memcmp(search->mac, peer, ETH_ALEN) == 0) { + sta = false; + break; + } + prev = search; + search = search->next; + } + hash = flow->hash; for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && @@ -412,4 +458,44 @@ void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, } } } + + if (search) { + if (prev) + prev->next = search->next; + else + flow->tdls_entry = search->next; + kfree(search); + if (flow->tdls_entry == NULL) + flow->tdls_active = false; + } +} + + +void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]) +{ + struct brcmf_flowring_tdls_entry *tdls_entry; + struct brcmf_flowring_tdls_entry *search; + + tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC); + if (tdls_entry == NULL) + return; + + memcpy(tdls_entry->mac, peer, ETH_ALEN); + tdls_entry->next = NULL; + if (flow->tdls_entry == NULL) { + flow->tdls_entry = tdls_entry; + } else { + search = flow->tdls_entry; + if (memcmp(search->mac, peer, ETH_ALEN) == 0) + return; + while (search->next) { + search = search->next; + if (memcmp(search->mac, peer, ETH_ALEN) == 0) + return; + } + search->next = tdls_entry; + } + + flow->tdls_active = true; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index cb9644ca6ecec5..a34cd394c616c0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -40,6 +40,11 @@ struct brcmf_flowring_ring { struct sk_buff_head skblist; }; +struct brcmf_flowring_tdls_entry { + u8 mac[ETH_ALEN]; + struct brcmf_flowring_tdls_entry *next; +}; + struct brcmf_flowring { struct device *dev; struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE]; @@ -47,6 +52,8 @@ struct brcmf_flowring { spinlock_t block_lock; enum proto_addr_mode addr_mode[BRCMF_MAX_IFS]; u16 nrofrings; + bool tdls_active; + struct brcmf_flowring_tdls_entry *tdls_entry; }; @@ -70,6 +77,8 @@ void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, enum proto_addr_mode addr_mode); void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, u8 peer[ETH_ALEN]); +void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx, + u8 peer[ETH_ALEN]); #endif /* BRCMFMAC_FLOWRING_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 3f9cb894d001fc..44fc85f68f7ace 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -299,7 +299,11 @@ static void brcmf_fweh_event_worker(struct work_struct *work) goto event_free; } - ifp = drvr->iflist[emsg.bsscfgidx]; + if ((event->code == BRCMF_E_TDLS_PEER_EVENT) && + (emsg.bsscfgidx == 1)) + ifp = drvr->iflist[0]; + else + ifp = drvr->iflist[emsg.bsscfgidx]; err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg, event->data); if (err) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index d26b47698f68bc..cbf033f59109db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -102,6 +102,7 @@ struct brcmf_event; BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ + BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128) @@ -155,6 +156,10 @@ enum brcmf_fweh_event_code { #define BRCMF_E_REASON_TSPEC_REJECTED 7 #define BRCMF_E_REASON_BETTER_AP 8 +#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED 0 +#define BRCMF_E_REASON_TDLS_PEER_CONNECTED 1 +#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED 2 + /* action field values for brcmf_ifevent */ #define BRCMF_E_IF_ADD 1 #define BRCMF_E_IF_DEL 2 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index c7a1c59ba6c3e3..535c7eb01b3a42 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -751,6 +751,15 @@ brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) } +static void +brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + + brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer); +} + + static void brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) { @@ -1298,6 +1307,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) drvr->proto->txdata = brcmf_msgbuf_txdata; drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode; drvr->proto->delete_peer = brcmf_msgbuf_delete_peer; + drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer; drvr->proto->pd = msgbuf; init_waitqueue_head(&msgbuf->ioctl_resp_wait); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index 44b1cb466d4e6f..62b940723339f8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -54,7 +54,7 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) || (proto->configure_addr_mode == NULL) || - (proto->delete_peer == NULL)) { + (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) { brcmf_err("Not all proto handlers have been installed\n"); goto fail; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 55942e3561a3ff..971172ff686c80 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -36,6 +36,8 @@ struct brcmf_proto { enum proto_addr_mode addr_mode); void (*delete_peer)(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]); + void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx, + u8 peer[ETH_ALEN]); void *pd; }; @@ -74,6 +76,11 @@ brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) { drvr->proto->delete_peer(drvr, ifidx, peer); } +static inline void +brcmf_proto_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]) +{ + drvr->proto->add_tdls_peer(drvr, ifidx, peer); +} #endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 2316250fff8505..558ae872813c1f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4155,6 +4155,27 @@ static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); } +static s32 +brcmf_notify_tdls_peer_event(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + switch (e->reason) { + case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: + brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); + break; + case BRCMF_E_REASON_TDLS_PEER_CONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Connected\n"); + brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + } + + return 0; +} + static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) { int ret; @@ -5691,6 +5712,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, if (err) { brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } else { + brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, + brcmf_notify_tdls_peer_event); } return cfg; From b18a6d382ff2c9cc7a7a6a1f3e483aecdcbe148b Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 7 Aug 2014 14:45:08 +0200 Subject: [PATCH 0180/1983] brcmfmac: fix curly brace mistake in brcmf_pcie_handle_mb_data() Upstream-commit: ebcc2f517d2a667d4f034171ed8f373f85b035df Running coccicheck on brcm80211 drivers resulted in following report: $ make coccicheck MODE=report M=drivers/net/wireless/brcm80211 drivers/net/wireless/brcm80211/brcmfmac/pcie.c:595:2-43: code aligned with following code on line 596 It revealed that due to a merge failure a block statement lost its curly braces where it should not. Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index bc972c0ba5f89a..e5101b287e4eeb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -591,12 +591,13 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) } if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); - if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) + if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); if (waitqueue_active(&devinfo->mbdata_resp_wait)) { devinfo->mbdata_completed = true; wake_up(&devinfo->mbdata_resp_wait); } + } } From 385f6f62b80f3b0045a9873397ca2b1cf6a408c8 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 7 Aug 2014 14:45:09 +0200 Subject: [PATCH 0181/1983] brcmfmac: fix memory leakage in msgbuf Upstream-commit: 2d116b8849474bfce8f8dac5ef671d0d7053c6fc The kbuild robot came up with the following warning: tree: .../kernel/git/linville/wireless-next.git master head: dc6be9f54a4ecb0a09765d1f515ed947d86b7528 commit: 9a1bb60250d2b6b546a62e5b73f55c4f1d22016b [5/13] brcmfmac: Adding msgbuf protocol. coccinelle warnings: drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c:1309:1-28: alloc with no test, possible model on line 1318 Looking into the issue, it turned out that the referred allocation buffer was not being released in failure path nor upon module unload. Reported-by: Fengguang Wu Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 535c7eb01b3a42..8f8b9373de95b6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -1318,6 +1318,8 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * sizeof(*msgbuf->flowring_dma_handle), GFP_ATOMIC); + if (!msgbuf->flowring_dma_handle) + goto fail; msgbuf->rx_dataoffset = if_msgbuf->rx_dataoffset; msgbuf->max_rxbufpost = if_msgbuf->max_rxbufpost; @@ -1362,6 +1364,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) kfree(msgbuf->flow_map); kfree(msgbuf->txstatus_done_map); brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf->flowring_dma_handle); if (msgbuf->ioctbuf) dma_free_coherent(drvr->bus_if->dev, BRCMF_TX_IOCTL_MAX_MSG_SIZE, @@ -1391,6 +1394,7 @@ void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) BRCMF_TX_IOCTL_MAX_MSG_SIZE, msgbuf->ioctbuf, msgbuf->ioctbuf_handle); brcmf_msgbuf_release_pktids(msgbuf); + kfree(msgbuf->flowring_dma_handle); kfree(msgbuf); drvr->proto->pd = NULL; } From 20d9b09e1779d3324eeb0f6d1768bbd875f4151a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 11 Sep 2014 22:51:30 +0200 Subject: [PATCH 0182/1983] brcmfmac: Fix memory leak and missing assignment. Upstream-commit: fac7d2a3b8798f8b58004c74c4b37b9643b5897a The function brcmf_enable_bw40_2g contains a memory leak. The function is also missing initialisation of one of the members of ch struct, which can lead to warning but this has no impact on result. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 558ae872813c1f..dbe8ee589494d7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5143,6 +5143,7 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) ch.band = BRCMU_CHAN_BAND_2G; ch.bw = BRCMU_CHAN_BW_40; + ch.sb = BRCMU_CHAN_SB_NONE; ch.chnum = 0; cfg->d11inf.encchspec(&ch); @@ -5176,6 +5177,7 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) brcmf_update_bw40_channel_flag(&band->channels[j], &ch); } + kfree(pbuf); } return err; } From d4649524d1c2e9db3ae2e4126edad3bf05174562 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 11 Sep 2014 22:51:31 +0200 Subject: [PATCH 0183/1983] brcmfmac: conditionally compile firmware protocol source code Upstream-commit: f1d56039b58f6f786450a858b2c8d2459a3382cc The host-interface can select which protocol implementation it needs. Selecting PCIe will include the msgbuf protocol and selecting USB and/or SDIO will include the bcdc protocol. The PCIe kconfig option assures the dependencies for msgbuf are met, ie. HAS_DMA. Reported-by: Geert Uytterhoeven Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/Kconfig | 10 ++++++++++ drivers/net/wireless/brcm80211/brcmfmac/Makefile | 10 ++++++---- drivers/net/wireless/brcm80211/brcmfmac/bcdc.h | 7 +++++-- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h | 11 +++++++++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index b8e2561ea64582..fe3dc126b149f8 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig @@ -27,10 +27,17 @@ config BRCMFMAC one of the bus interface support. If you choose to build a module, it'll be called brcmfmac.ko. +config BRCMFMAC_PROTO_BCDC + bool + +config BRCMFMAC_PROTO_MSGBUF + bool + config BRCMFMAC_SDIO bool "SDIO bus interface support for FullMAC driver" depends on (MMC = y || MMC = BRCMFMAC) depends on BRCMFMAC + select BRCMFMAC_PROTO_BCDC select FW_LOADER default y ---help--- @@ -42,6 +49,7 @@ config BRCMFMAC_USB bool "USB bus interface support for FullMAC driver" depends on (USB = y || USB = BRCMFMAC) depends on BRCMFMAC + select BRCMFMAC_PROTO_BCDC select FW_LOADER ---help--- This option enables the USB bus interface support for Broadcom @@ -52,6 +60,8 @@ config BRCMFMAC_PCIE bool "PCIE bus interface support for FullMAC driver" depends on BRCMFMAC depends on PCI + depends on HAS_DMA + select BRCMFMAC_PROTO_MSGBUF select FW_LOADER ---help--- This option enables the PCIE bus interface support for Broadcom diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index c35adf4bc70b72..90a977fe9a648d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -30,16 +30,18 @@ brcmfmac-objs += \ fwsignal.o \ p2p.o \ proto.o \ - bcdc.o \ - commonring.o \ - flowring.o \ - msgbuf.o \ dhd_common.o \ dhd_linux.o \ firmware.o \ feature.o \ btcoex.o \ vendor.o +brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \ + bcdc.o +brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ + commonring.o \ + flowring.o \ + msgbuf.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ bcmsdh.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h index 17e8c039ff321d..6003179c0ceb28 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h @@ -16,9 +16,12 @@ #ifndef BRCMFMAC_BCDC_H #define BRCMFMAC_BCDC_H - +#ifdef CONFIG_BRCMFMAC_PROTO_BCDC int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); - +#else +static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } +static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} +#endif #endif /* BRCMFMAC_BCDC_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h index f901ae52bf2b41..77a51b8c1e1208 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h @@ -15,6 +15,7 @@ #ifndef BRCMFMAC_MSGBUF_H #define BRCMFMAC_MSGBUF_H +#ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20 #define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256 @@ -32,9 +33,15 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev); +void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr); void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr); -void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid); - +#else +static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) +{ + return 0; +} +static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} +#endif #endif /* BRCMFMAC_MSGBUF_H */ From 4625310f8c3e7576acb49369ff7d453eba38f730 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 11 Sep 2014 22:51:32 +0200 Subject: [PATCH 0184/1983] brcmfmac: obtain ifp through wdev structure Upstream-commit: 8f2b45971b32824b830a544ed1d179215770b1eb For P2P_DEVICE interface the function brcmf_cfg80211_update_proto_addr_mode() resulted in a crash, because it assumed wdev->netdev would be valid. The ifp should be obtained through the driver vif structure which contains the wireless_dev. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index dbe8ee589494d7..aae3ba87395643 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -497,8 +497,11 @@ brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) static void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) { - struct net_device *ndev = wdev->netdev; - struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || (wdev->iftype == NL80211_IFTYPE_AP) || From 6d2b4fa647cfdb6089f7d5213b429b6d1fd2f28f Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Tue, 23 Sep 2014 00:49:55 +0200 Subject: [PATCH 0185/1983] brcmfmac: Fix off by one bug in brcmf_count_20mhz_channels() Upstream-commit: f8adaf0ae978252c9f7e29e96aefcd8fcaf806ba In the brcmf_count_20mhz_channels function we are looping through a list of channels received from firmware. Since the index of the first channel is 0 the condition leads to an off by one bug. This is causing us to hit the WARN_ON_ONCE(1) calls in the brcmu_d11n_decchspec function, which is how I discovered the bug. Introduced by: commit b48d891676f756d48b4d0ee131e4a7a5d43ca417 ("brcmfmac: rework wiphy structure setup") Acked-by: Arend van Spriel Signed-off-by: Emil Goode Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index aae3ba87395643..8072ce60ff1be2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4921,7 +4921,7 @@ static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, struct brcmu_chan ch; int i; - for (i = 0; i <= total; i++) { + for (i = 0; i < total; i++) { ch.chspec = (u16)le32_to_cpu(chlist->element[i]); cfg->d11inf.decchspec(&ch); From 36aa1525a45fd1190829e2a0d5df0d4cccb78d9e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 30 Sep 2014 10:23:13 +0200 Subject: [PATCH 0186/1983] brcmfmac: On scan timeout do send received results. Upstream-commit: ef8596e1f1ce06b4398db4752d04bb1a67a14a02 Increase driver scan timeout from 8 to 10 seconds and report results to cfg80211. Without this patch the already received results were dropped on driver timeout. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 8 +++----- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 8072ce60ff1be2..eb7421073decf0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2425,7 +2425,7 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) s32 err = 0; int i; - bss_list = cfg->bss_list; + bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; if (bss_list->count != 0 && bss_list->version != BRCMF_BSS_INFO_VERSION) { brcmf_err("Version %d != WL_BSS_INFO_VERSION\n", @@ -2599,6 +2599,7 @@ static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) container_of(work, struct brcmf_cfg80211_info, escan_timeout_work); + brcmf_inform_bss(cfg); brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); } @@ -2737,12 +2738,9 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) goto exit; if (cfg->scan_request) { - cfg->bss_list = (struct brcmf_scan_results *) - cfg->escan_info.escan_buf; brcmf_inform_bss(cfg); aborted = status != BRCMF_E_STATUS_SUCCESS; - brcmf_notify_escan_complete(cfg, ifp, aborted, - false); + brcmf_notify_escan_complete(cfg, ifp, aborted, false); } else brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", status); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index f9fb10998e7960..82f8778a93fbb7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -35,7 +35,7 @@ #define WL_SCAN_PASSIVE_TIME 120 #define WL_ESCAN_BUF_SIZE (1024 * 64) -#define WL_ESCAN_TIMER_INTERVAL_MS 8000 /* E-Scan timeout */ +#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ #define WL_ESCAN_ACTION_START 1 #define WL_ESCAN_ACTION_CONTINUE 2 @@ -371,7 +371,6 @@ struct brcmf_cfg80211_info { struct brcmf_btcoex_info *btcoex; struct cfg80211_scan_request *scan_request; struct mutex usr_sync; - struct brcmf_scan_results *bss_list; struct brcmf_cfg80211_scan_req scan_req_int; struct wl_cfg80211_bss_info *bss_info; struct brcmf_cfg80211_ie ie; From e6bac9071371f5a0d17bb26246cdfaaae6c229e5 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 30 Sep 2014 10:23:14 +0200 Subject: [PATCH 0187/1983] brcmfmac: Fix sign issue with IOCTL return code in msgbuf. Upstream-commit: ff0a6230603c089fdc12a06b824c21498eb24691 Need a cast to assure correct value is propagated. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 8f8b9373de95b6..106e6bf0a5da14 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -230,7 +230,7 @@ struct brcmf_msgbuf { dma_addr_t ioctbuf_handle; u32 ioctbuf_phys_hi; u32 ioctbuf_phys_lo; - u32 ioctl_resp_status; + int ioctl_resp_status; u32 ioctl_resp_ret_len; u32 ioctl_resp_pktid; @@ -767,7 +767,8 @@ brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) ioctl_resp = (struct msgbuf_ioctl_resp_hdr *)buf; - msgbuf->ioctl_resp_status = le16_to_cpu(ioctl_resp->compl_hdr.status); + msgbuf->ioctl_resp_status = + (s16)le16_to_cpu(ioctl_resp->compl_hdr.status); msgbuf->ioctl_resp_ret_len = le16_to_cpu(ioctl_resp->resp_len); msgbuf->ioctl_resp_pktid = le32_to_cpu(ioctl_resp->msg.request_id); From dc92b177357c1e0ef9b6636c6ff26983237e26d0 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 30 Sep 2014 10:23:15 +0200 Subject: [PATCH 0188/1983] brcmfmac: Avoid usage of GFP_ATOMIC. Upstream-commit: 3ba066109974dff307f143ffdeddc7af83b2bb8e Msgbuf is using GFP_ATOMIC where GFP_KERNEL is also sufficient. On some platforms the coherent DMA memory is very limited when using GFP_ATOMIC. This patch changes usage of GFP_ATOMIC to GFP_KERNEL and uses worker to make this possible for creation of flowring. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/flowring.c | 4 +- .../net/wireless/brcm80211/brcmfmac/msgbuf.c | 128 +++++++++++++++--- 2 files changed, 108 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index a1016b811284cd..1faa929f5fff2c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -354,7 +354,7 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) struct brcmf_flowring *flow; u32 i; - flow = kzalloc(sizeof(*flow), GFP_ATOMIC); + flow = kzalloc(sizeof(*flow), GFP_KERNEL); if (flow) { flow->dev = dev; flow->nrofrings = nrofrings; @@ -364,7 +364,7 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings) for (i = 0; i < ARRAY_SIZE(flow->hash); i++) flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX; flow->rings = kcalloc(nrofrings, sizeof(*flow->rings), - GFP_ATOMIC); + GFP_KERNEL); if (!flow->rings) { kfree(flow); flow = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 106e6bf0a5da14..11cc051f97cd42 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -208,6 +208,14 @@ struct msgbuf_flowring_flush_resp { __le32 rsvd0[3]; }; +struct brcmf_msgbuf_work_item { + struct list_head queue; + u32 flowid; + int ifidx; + u8 sa[ETH_ALEN]; + u8 da[ETH_ALEN]; +}; + struct brcmf_msgbuf { struct brcmf_pub *drvr; @@ -248,6 +256,10 @@ struct brcmf_msgbuf { struct work_struct txflow_work; unsigned long *flow_map; unsigned long *txstatus_done_map; + + struct work_struct flowring_work; + spinlock_t flowring_work_lock; + struct list_head work_queue; }; struct brcmf_msgbuf_pktid { @@ -284,11 +296,11 @@ brcmf_msgbuf_init_pktids(u32 nr_array_entries, struct brcmf_msgbuf_pktid *array; struct brcmf_msgbuf_pktids *pktids; - array = kcalloc(nr_array_entries, sizeof(*array), GFP_ATOMIC); + array = kcalloc(nr_array_entries, sizeof(*array), GFP_KERNEL); if (!array) return NULL; - pktids = kzalloc(sizeof(*pktids), GFP_ATOMIC); + pktids = kzalloc(sizeof(*pktids), GFP_KERNEL); if (!pktids) { kfree(array); return NULL; @@ -544,11 +556,29 @@ brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid) } -static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, - struct sk_buff *skb) +static struct brcmf_msgbuf_work_item * +brcmf_msgbuf_dequeue_work(struct brcmf_msgbuf *msgbuf) +{ + struct brcmf_msgbuf_work_item *work = NULL; + ulong flags; + + spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); + if (!list_empty(&msgbuf->work_queue)) { + work = list_first_entry(&msgbuf->work_queue, + struct brcmf_msgbuf_work_item, queue); + list_del(&work->queue); + } + spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); + + return work; +} + + +static u32 +brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, + struct brcmf_msgbuf_work_item *work) { struct msgbuf_tx_flowring_create_req *create; - struct ethhdr *eh = (struct ethhdr *)(skb->data); struct brcmf_commonring *commonring; void *ret_ptr; u32 flowid; @@ -557,16 +587,11 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, long long address; int err; - flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, - skb->priority, ifidx); - if (flowid == BRCMF_FLOWRING_INVALID_ID) - return flowid; - + flowid = work->flowid; dma_sz = BRCMF_H2D_TXFLOWRING_MAX_ITEM * BRCMF_H2D_TXFLOWRING_ITEMSIZE; - dma_buf = dma_alloc_coherent(msgbuf->drvr->bus_if->dev, dma_sz, &msgbuf->flowring_dma_handle[flowid], - GFP_ATOMIC); + GFP_KERNEL); if (!dma_buf) { brcmf_err("dma_alloc_coherent failed\n"); brcmf_flowring_delete(msgbuf->flow, flowid); @@ -589,13 +614,13 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, create = (struct msgbuf_tx_flowring_create_req *)ret_ptr; create->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; - create->msg.ifidx = ifidx; + create->msg.ifidx = work->ifidx; create->msg.request_id = 0; create->tid = brcmf_flowring_tid(msgbuf->flow, flowid); create->flow_ring_id = cpu_to_le16(flowid + BRCMF_NROF_H2D_COMMON_MSGRINGS); - memcpy(create->sa, eh->h_source, ETH_ALEN); - memcpy(create->da, eh->h_dest, ETH_ALEN); + memcpy(create->sa, work->sa, ETH_ALEN); + memcpy(create->da, work->da, ETH_ALEN); address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); @@ -603,7 +628,7 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, create->len_item = cpu_to_le16(BRCMF_H2D_TXFLOWRING_ITEMSIZE); brcmf_dbg(MSGBUF, "Send Flow Create Req flow ID %d for peer %pM prio %d ifindex %d\n", - flowid, eh->h_dest, create->tid, ifidx); + flowid, work->da, create->tid, work->ifidx); err = brcmf_commonring_write_complete(commonring); brcmf_commonring_unlock(commonring); @@ -617,6 +642,53 @@ static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, } +static void brcmf_msgbuf_flowring_worker(struct work_struct *work) +{ + struct brcmf_msgbuf *msgbuf; + struct brcmf_msgbuf_work_item *create; + + msgbuf = container_of(work, struct brcmf_msgbuf, flowring_work); + + while ((create = brcmf_msgbuf_dequeue_work(msgbuf))) { + brcmf_msgbuf_flowring_create_worker(msgbuf, create); + kfree(create); + } +} + + +static u32 brcmf_msgbuf_flowring_create(struct brcmf_msgbuf *msgbuf, int ifidx, + struct sk_buff *skb) +{ + struct brcmf_msgbuf_work_item *create; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + u32 flowid; + ulong flags; + + create = kzalloc(sizeof(*create), GFP_ATOMIC); + if (create == NULL) + return BRCMF_FLOWRING_INVALID_ID; + + flowid = brcmf_flowring_create(msgbuf->flow, eh->h_dest, + skb->priority, ifidx); + if (flowid == BRCMF_FLOWRING_INVALID_ID) { + kfree(create); + return flowid; + } + + create->flowid = flowid; + create->ifidx = ifidx; + memcpy(create->sa, eh->h_source, ETH_ALEN); + memcpy(create->da, eh->h_dest, ETH_ALEN); + + spin_lock_irqsave(&msgbuf->flowring_work_lock, flags); + list_add_tail(&create->queue, &msgbuf->work_queue); + spin_unlock_irqrestore(&msgbuf->flowring_work_lock, flags); + schedule_work(&msgbuf->flowring_work); + + return flowid; +} + + static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) { struct brcmf_flowring *flow = msgbuf->flow; @@ -1272,7 +1344,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) u32 count; if_msgbuf = drvr->bus_if->msgbuf; - msgbuf = kzalloc(sizeof(*msgbuf), GFP_ATOMIC); + msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL); if (!msgbuf) goto fail; @@ -1283,11 +1355,11 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) } INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); - msgbuf->flow_map = kzalloc(count, GFP_ATOMIC); + msgbuf->flow_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->flow_map) goto fail; - msgbuf->txstatus_done_map = kzalloc(count, GFP_ATOMIC); + msgbuf->txstatus_done_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->txstatus_done_map) goto fail; @@ -1295,7 +1367,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) msgbuf->ioctbuf = dma_alloc_coherent(drvr->bus_if->dev, BRCMF_TX_IOCTL_MAX_MSG_SIZE, &msgbuf->ioctbuf_handle, - GFP_ATOMIC); + GFP_KERNEL); if (!msgbuf->ioctbuf) goto fail; address = (long long)(long)msgbuf->ioctbuf_handle; @@ -1318,7 +1390,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) msgbuf->flowrings = (struct brcmf_commonring **)if_msgbuf->flowrings; msgbuf->nrof_flowrings = if_msgbuf->nrof_flowrings; msgbuf->flowring_dma_handle = kzalloc(msgbuf->nrof_flowrings * - sizeof(*msgbuf->flowring_dma_handle), GFP_ATOMIC); + sizeof(*msgbuf->flowring_dma_handle), GFP_KERNEL); if (!msgbuf->flowring_dma_handle) goto fail; @@ -1358,6 +1430,10 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) brcmf_msgbuf_rxbuf_event_post(msgbuf); brcmf_msgbuf_rxbuf_ioctlresp_post(msgbuf); + INIT_WORK(&msgbuf->flowring_work, brcmf_msgbuf_flowring_worker); + spin_lock_init(&msgbuf->flowring_work_lock); + INIT_LIST_HEAD(&msgbuf->work_queue); + return 0; fail: @@ -1380,11 +1456,19 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) { struct brcmf_msgbuf *msgbuf; + struct brcmf_msgbuf_work_item *work; brcmf_dbg(TRACE, "Enter\n"); if (drvr->proto->pd) { msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; - + cancel_work_sync(&msgbuf->flowring_work); + while (!list_empty(&msgbuf->work_queue)) { + work = list_first_entry(&msgbuf->work_queue, + struct brcmf_msgbuf_work_item, + queue); + list_del(&work->queue); + kfree(work); + } kfree(msgbuf->flow_map); kfree(msgbuf->txstatus_done_map); if (msgbuf->txflow_wq) From 43fe4efadd86a371fd8a2d572bac1c2feef3c1e6 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 30 Sep 2014 10:23:16 +0200 Subject: [PATCH 0189/1983] brcmfmac: assure P2P discovery is disabled when setting P2P_DEVICE mac address Upstream-commit: f48556e1b6a60f1b394a6c4b0a7b8cacba3eb107 In order to provision the P2P_DEVICE mac address using p2p_da_override iovar the discovery interface must be disabled. On some targets setting the mac address failed so disable the discovery interface to be certain. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 057b982ea8b37f..9585ef440e5c16 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -440,8 +440,11 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) /* In case of COB type, firmware has default mac address * After Initializing firmware, we have to set current mac address to - * firmware for P2P device address + * firmware for P2P device address. This must be done with discovery + * disabled. */ + brcmf_fil_iovar_int_set(ifp, "p2p_disc", 0); + ret = brcmf_fil_iovar_data_set(ifp, "p2p_da_override", p2p_mac, ETH_ALEN); if (ret) From 07998f6605e1e641e09854c8173909f412dd3b33 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 30 Sep 2014 10:23:17 +0200 Subject: [PATCH 0190/1983] brcmfmac: Fix crash on cleanup. Upstream-commit: 58b2251e96d8230e81f65a0196afc914a17e1806 When driver gets unloaded due to error situation there is a chance a packet gets received while fws has already been cleaned up. This will result in kernel crash. This patch adds a check to avoid this crash. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index d42f7d04b65f11..183f08d7fc8c8e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1636,7 +1636,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, if (!signal_len) return 0; /* if flow control disabled, skip to packet data and leave */ - if (!fws->fw_signals) { + if ((!fws) || (!fws->fw_signals)) { skb_pull(skb, signal_len); return 0; } From 6fd886da824770af6e8eab6a3ebece123d976497 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 30 Sep 2014 10:23:18 +0200 Subject: [PATCH 0191/1983] brcmfmac: Add wowl support for PCIE devices. Upstream-commit: 4eb3af7c1df32dcd4362c2f20928f679ed78f2e5 Add basic wowl (magic packet and disconnect) support. This patch adds this support only for PCIE bus devices. This feature requires FW which has support for wowl built in. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_bus.h | 11 ++ .../net/wireless/brcm80211/brcmfmac/feature.c | 2 + .../net/wireless/brcm80211/brcmfmac/feature.h | 3 +- .../wireless/brcm80211/brcmfmac/fwil_types.h | 56 +++++++++ .../net/wireless/brcm80211/brcmfmac/pcie.c | 74 +++++++++--- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 106 ++++++++++++++---- .../wireless/brcm80211/brcmfmac/wl_cfg80211.h | 4 + drivers/net/wireless/brcm80211/include/defs.h | 5 +- 8 files changed, 220 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 3122b86050a1a6..80e73a1262be81 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -67,6 +67,7 @@ struct brcmf_bus_dcmd { * @txctl: transmit a control request message to dongle. * @rxctl: receive a control response message from dongle. * @gettxq: obtain a reference of bus transmit queue (optional). + * @wowl_config: specify if dongle is configured for wowl when going to suspend * * This structure provides an abstract interface towards the * bus specific driver. For control messages to common driver @@ -80,6 +81,7 @@ struct brcmf_bus_ops { int (*txctl)(struct device *dev, unsigned char *msg, uint len); int (*rxctl)(struct device *dev, unsigned char *msg, uint len); struct pktq * (*gettxq)(struct device *dev); + void (*wowl_config)(struct device *dev, bool enabled); }; @@ -114,6 +116,7 @@ struct brcmf_bus_msgbuf { * @dstats: dongle-based statistical data. * @dcmd_list: bus/device specific dongle initialization commands. * @chip: device identifier of the dongle chip. + * @wowl_supported: is wowl supported by bus driver. * @chiprev: revision of the dongle chip. */ struct brcmf_bus { @@ -131,6 +134,7 @@ struct brcmf_bus { u32 chip; u32 chiprev; bool always_use_fws_queue; + bool wowl_supported; struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; @@ -177,6 +181,13 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) return bus->ops->gettxq(bus->dev); } +static inline +void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) +{ + if (bus->ops->wowl_config) + bus->ops->wowl_config(bus->dev, enabled); +} + static inline bool brcmf_bus_ready(struct brcmf_bus *bus) { return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 50877e3c5d2fdb..aed53acef456a4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -107,6 +107,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) struct brcmf_if *ifp = drvr->iflist[0]; brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + if (drvr->bus_if->wowl_supported) + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index 961d175f8afb0d..b9a796d0a44d9d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -22,7 +22,8 @@ * MCHAN: multi-channel for concurrent P2P. */ #define BRCMF_FEAT_LIST \ - BRCMF_FEAT_DEF(MCHAN) + BRCMF_FEAT_DEF(MCHAN) \ + BRCMF_FEAT_DEF(WOWL) /* * Quirks: * diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 2bc68a2137fccb..5ff5cd0bb0325f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -53,6 +53,62 @@ #define BRCMF_OBSS_COEX_OFF 0 #define BRCMF_OBSS_COEX_ON 1 +/* WOWL bits */ +/* Wakeup on Magic packet: */ +#define WL_WOWL_MAGIC (1 << 0) +/* Wakeup on Netpattern */ +#define WL_WOWL_NET (1 << 1) +/* Wakeup on loss-of-link due to Disassoc/Deauth: */ +#define WL_WOWL_DIS (1 << 2) +/* Wakeup on retrograde TSF: */ +#define WL_WOWL_RETR (1 << 3) +/* Wakeup on loss of beacon: */ +#define WL_WOWL_BCN (1 << 4) +/* Wakeup after test: */ +#define WL_WOWL_TST (1 << 5) +/* Wakeup after PTK refresh: */ +#define WL_WOWL_M1 (1 << 6) +/* Wakeup after receipt of EAP-Identity Req: */ +#define WL_WOWL_EAPID (1 << 7) +/* Wakeind via PME(0) or GPIO(1): */ +#define WL_WOWL_PME_GPIO (1 << 8) +/* need tkip phase 1 key to be updated by the driver: */ +#define WL_WOWL_NEEDTKIP1 (1 << 9) +/* enable wakeup if GTK fails: */ +#define WL_WOWL_GTK_FAILURE (1 << 10) +/* support extended magic packets: */ +#define WL_WOWL_EXTMAGPAT (1 << 11) +/* support ARP/NS/keepalive offloading: */ +#define WL_WOWL_ARPOFFLOAD (1 << 12) +/* read protocol version for EAPOL frames: */ +#define WL_WOWL_WPA2 (1 << 13) +/* If the bit is set, use key rotaton: */ +#define WL_WOWL_KEYROT (1 << 14) +/* If the bit is set, frm received was bcast frame: */ +#define WL_WOWL_BCAST (1 << 15) +/* If the bit is set, scan offload is enabled: */ +#define WL_WOWL_SCANOL (1 << 16) +/* Wakeup on tcpkeep alive timeout: */ +#define WL_WOWL_TCPKEEP_TIME (1 << 17) +/* Wakeup on mDNS Conflict Resolution: */ +#define WL_WOWL_MDNS_CONFLICT (1 << 18) +/* Wakeup on mDNS Service Connect: */ +#define WL_WOWL_MDNS_SERVICE (1 << 19) +/* tcp keepalive got data: */ +#define WL_WOWL_TCPKEEP_DATA (1 << 20) +/* Firmware died in wowl mode: */ +#define WL_WOWL_FW_HALT (1 << 21) +/* Enable detection of radio button changes: */ +#define WL_WOWL_ENAB_HWRADIO (1 << 22) +/* Offloads detected MIC failure(s): */ +#define WL_WOWL_MIC_FAIL (1 << 23) +/* Wakeup in Unassociated state (Net/Magic Pattern): */ +#define WL_WOWL_UNASSOC (1 << 24) +/* Wakeup if received matched secured pattern: */ +#define WL_WOWL_SECURE (1 << 25) +/* Link Down indication in WoWL mode: */ +#define WL_WOWL_LINKDOWN (1 << 31) + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index e5101b287e4eeb..8c0632ec9f7a60 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -165,6 +165,8 @@ enum brcmf_pcie_state { #define BRCMF_H2D_HOST_D3_INFORM 0x00000001 #define BRCMF_H2D_HOST_DS_ACK 0x00000002 +#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008 +#define BRCMF_H2D_HOST_D0_INFORM 0x00000010 #define BRCMF_PCIE_MBDATA_TIMEOUT 2000 @@ -243,6 +245,7 @@ struct brcmf_pciedev_info { wait_queue_head_t mbdata_resp_wait; bool mbdata_completed; bool irq_allocated; + bool wowl_enabled; }; struct brcmf_pcie_ringbuf { @@ -537,7 +540,7 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, } -static void +static int brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) { struct brcmf_pcie_shared_info *shared; @@ -558,13 +561,15 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) msleep(10); i++; if (i > 100) - break; + return -EIO; cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); } brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + + return 0; } @@ -1229,11 +1234,27 @@ static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg, } +static void brcmf_pcie_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled); + devinfo->wowl_enabled = enabled; + if (enabled) + device_set_wakeup_enable(&devinfo->pdev->dev, true); + else + device_set_wakeup_enable(&devinfo->pdev->dev, false); +} + + static struct brcmf_bus_ops brcmf_pcie_bus_ops = { .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, .rxctl = brcmf_pcie_rx_ctlpkt, + .wowl_config = brcmf_pcie_wowl_config, }; @@ -1668,6 +1689,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) bus->ops = &brcmf_pcie_bus_ops; bus->proto_type = BRCMF_PROTO_MSGBUF; bus->chip = devinfo->coreid; + bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot); dev_set_drvdata(&pdev->dev, bus); ret = brcmf_pcie_get_fwnames(devinfo); @@ -1759,36 +1781,62 @@ static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state) brcmf_err("Timeout on response for entering D3 substate\n"); return -EIO; } - brcmf_pcie_release_irq(devinfo); + brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE); err = pci_save_state(pdev); - if (err) { + if (err) brcmf_err("pci_save_state failed, err=%d\n", err); - return err; + if ((err) || (!devinfo->wowl_enabled)) { + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + brcmf_pcie_remove(pdev); + return 0; } - brcmf_chip_detach(devinfo->ci); - devinfo->ci = NULL; - - brcmf_pcie_remove(pdev); - return pci_prepare_to_sleep(pdev); } - static int brcmf_pcie_resume(struct pci_dev *pdev) { + struct brcmf_pciedev_info *devinfo; + struct brcmf_bus *bus; int err; - brcmf_dbg(PCIE, "Enter, pdev=%p\n", pdev); + bus = dev_get_drvdata(&pdev->dev); + brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus); err = pci_set_power_state(pdev, PCI_D0); if (err) { brcmf_err("pci_set_power_state failed, err=%d\n", err); - return err; + goto cleanup; } pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D3hot, false); + pci_enable_wake(pdev, PCI_D3cold, false); + + /* Check if device is still up and running, if so we are ready */ + if (bus) { + devinfo = bus->bus_priv.pcie->devinfo; + if (brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { + if (brcmf_pcie_send_mb_data(devinfo, + BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + brcmf_dbg(PCIE, "Hot resume, continue....\n"); + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_bus_change_state(bus, BRCMF_BUS_DATA); + brcmf_pcie_intr_enable(devinfo); + return 0; + } + } +cleanup: + if (bus) { + devinfo = bus->bus_priv.pcie->devinfo; + brcmf_chip_detach(devinfo->ci); + devinfo->ci = NULL; + brcmf_pcie_remove(pdev); + } err = brcmf_pcie_probe(pdev, NULL); if (err) brcmf_err("probe after resume failed, err=%d\n", err); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index eb7421073decf0..4d363f2a4e2226 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -37,6 +37,7 @@ #include "fwil.h" #include "proto.h" #include "vendor.h" +#include "dhd_bus.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -2774,50 +2775,91 @@ static __always_inline void brcmf_delay(u32 ms) static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + brcmf_dbg(TRACE, "Enter\n"); + if (cfg->wowl_enabled) { + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, + cfg->pre_wowl_pmmode); + brcmf_fil_iovar_data_set(ifp, "wowl_pattern", "clr", 4); + brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + cfg->wowl_enabled = false; + } return 0; } +static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, + struct cfg80211_wowlan *wowl) +{ + u32 wowl_config; + + brcmf_dbg(TRACE, "Suspend, wowl config.\n"); + + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); + + wowl_config = 0; + if (wowl->disconnect) + wowl_config |= WL_WOWL_DIS | WL_WOWL_BCN | WL_WOWL_RETR; + /* Note: if "wowl" target and not "wowlpf" then wowl_bcn_loss + * should be configured. This paramater is not supported by + * wowlpf. + */ + if (wowl->magic_pkt) + wowl_config |= WL_WOWL_MAGIC; + brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); + brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); + brcmf_bus_wowl_config(cfg->pub->bus_if, true); + cfg->wowl_enabled = true; +} + static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wow) + struct cfg80211_wowlan *wowl) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_vif *vif; brcmf_dbg(TRACE, "Enter\n"); - /* - * if the primary net_device is not READY there is nothing + /* if the primary net_device is not READY there is nothing * we can do but pray resume goes smoothly. */ - vif = ((struct brcmf_if *)netdev_priv(ndev))->vif; - if (!check_vif_up(vif)) + if (!check_vif_up(ifp->vif)) goto exit; - list_for_each_entry(vif, &cfg->vif_list, list) { - if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) - continue; - /* - * While going to suspend if associated with AP disassociate - * from AP to save power while system is in suspended state - */ - brcmf_link_down(vif); - - /* Make sure WPA_Supplicant receives all the event - * generated due to DISASSOC call to the fw to keep - * the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - /* end any scanning */ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); - /* Turn off watchdog timer */ - brcmf_set_mpc(netdev_priv(ndev), 1); + if (wowl == NULL) { + brcmf_bus_wowl_config(cfg->pub->bus_if, false); + list_for_each_entry(vif, &cfg->vif_list, list) { + if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) + continue; + /* While going to suspend if associated with AP + * disassociate from AP to save power while system is + * in suspended state + */ + brcmf_link_down(vif); + /* Make sure WPA_Supplicant receives all the event + * generated due to DISASSOC call to the fw to keep + * the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); + } + /* Configure MPC */ + brcmf_set_mpc(ifp, 1); + + } else { + /* Configure WOWL paramaters */ + brcmf_configure_wowl(cfg, ifp, wowl); + } exit: brcmf_dbg(TRACE, "Exit\n"); @@ -5392,6 +5434,21 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support brcmf_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) +{ +#ifdef CONFIG_PM + /* wowl settings */ + wiphy->wowlan = &brcmf_wowlan_support; +#endif +} + static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { struct ieee80211_iface_combination ifc_combo; @@ -5429,6 +5486,9 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + brcmf_wiphy_wowl_params(wiphy); + return brcmf_setup_wiphybands(wiphy); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 82f8778a93fbb7..6abf94e41d3d06 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -363,6 +363,8 @@ struct brcmf_cfg80211_vif_event { * @vif_list: linked list of vif instances. * @vif_cnt: number of vif instances. * @vif_event: vif event signalling. + * @wowl_enabled; set during suspend, is wowl used. + * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. */ struct brcmf_cfg80211_info { struct wiphy *wiphy; @@ -396,6 +398,8 @@ struct brcmf_cfg80211_info { struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; struct brcmu_d11inf d11inf; + bool wowl_enabled; + u32 pre_wowl_pmmode; }; /** diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/brcm80211/include/defs.h index fb7cbcf81179bb..8d1e85e0ed5102 100644 --- a/drivers/net/wireless/brcm80211/include/defs.h +++ b/drivers/net/wireless/brcm80211/include/defs.h @@ -74,10 +74,6 @@ #define BRCM_BAND_2G 2 /* 2.4 Ghz */ #define BRCM_BAND_ALL 3 /* all bands */ -/* Values for PM */ -#define PM_OFF 0 -#define PM_MAX 1 - /* Debug levels */ #define BRCM_DL_INFO 0x00000001 #define BRCM_DL_MAC80211 0x00000002 @@ -87,6 +83,7 @@ #define BRCM_DL_DMA 0x00000020 #define BRCM_DL_HT 0x00000040 +/* Values for PM */ #define PM_OFF 0 #define PM_MAX 1 #define PM_FAST 2 From 3523d767635a6eb21692dff044914bc9c2924a56 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 20 Oct 2014 16:21:17 +0200 Subject: [PATCH 0192/1983] net: wireless: brcm80211: brcmfmac: drop owner assignment from platform_drivers Upstream-commit: e8460665ed5081b9f93349fe05ad3529f887c3d5 A platform_driver does not need to set an owner, it will be populated by the driver core. Signed-off-by: Wolfram Sang --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 8dbd5dbb78fd14..602034306076bb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1204,7 +1204,6 @@ static struct platform_driver brcmf_sdio_pd = { .remove = brcmf_sdio_pd_remove, .driver = { .name = BRCMFMAC_SDIO_PDATA_NAME, - .owner = THIS_MODULE, } }; From 8f12bc24c7bc61db648492f13ba1c20f189c5efa Mon Sep 17 00:00:00 2001 From: Rickard Strandqvist Date: Sun, 12 Oct 2014 13:42:14 +0200 Subject: [PATCH 0193/1983] brcmfmac: dhd_sdio.c: Cleaning up missing null-terminate in conjunction with strncpy Upstream-commit: 59dfdd92288e55bed374309a9944c3a95b4e13c9 Replacing strncpy with strlcpy to avoid strings that lacks null terminate. And changed from using strncat to strlcat to simplify code. Signed-off-by: Rickard Strandqvist Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index f55f625fd06b4a..d20d4e6f391ae8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -670,7 +670,6 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, struct brcmf_sdio_dev *sdiodev) { int i; - uint fw_len, nv_len; char end; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { @@ -684,25 +683,25 @@ static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, return -ENODEV; } - fw_len = sizeof(sdiodev->fw_name) - 1; - nv_len = sizeof(sdiodev->nvram_name) - 1; /* check if firmware path is provided by module parameter */ if (brcmf_firmware_path[0] != '\0') { - strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len); - strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len); - fw_len -= strlen(sdiodev->fw_name); - nv_len -= strlen(sdiodev->nvram_name); + strlcpy(sdiodev->fw_name, brcmf_firmware_path, + sizeof(sdiodev->fw_name)); + strlcpy(sdiodev->nvram_name, brcmf_firmware_path, + sizeof(sdiodev->nvram_name)); end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; if (end != '/') { - strncat(sdiodev->fw_name, "/", fw_len); - strncat(sdiodev->nvram_name, "/", nv_len); - fw_len--; - nv_len--; + strlcat(sdiodev->fw_name, "/", + sizeof(sdiodev->fw_name)); + strlcat(sdiodev->nvram_name, "/", + sizeof(sdiodev->nvram_name)); } } - strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len); - strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len); + strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin, + sizeof(sdiodev->fw_name)); + strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, + sizeof(sdiodev->nvram_name)); return 0; } From a81fa43c4109fdc8b9d266c8378bfeb2f4251774 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:04 +0100 Subject: [PATCH 0194/1983] brcmfmac: Add wowl support for USB devices. Upstream-commit: 244b124c6a43eebaa746780d8390b93ec1519c8f This patch adds wowl support for USB bus devices. This feature requires FW which has support for wowl built in. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index dc135915470d2c..e533000787f327 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -93,6 +93,8 @@ struct brcmf_usbdev_info { u8 ifnum; struct urb *bulk_urb; /* used for FW download */ + + bool wowl_enabled; }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, @@ -600,6 +602,16 @@ static int brcmf_usb_up(struct device *dev) return 0; } +static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) +{ + if (devinfo->ctl_urb) + usb_kill_urb(devinfo->ctl_urb); + if (devinfo->bulk_urb) + usb_kill_urb(devinfo->bulk_urb); + brcmf_usb_free_q(&devinfo->tx_postq, true); + brcmf_usb_free_q(&devinfo->rx_postq, true); +} + static void brcmf_usb_down(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); @@ -613,14 +625,7 @@ static void brcmf_usb_down(struct device *dev) brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); - if (devinfo->ctl_urb) - usb_kill_urb(devinfo->ctl_urb); - - if (devinfo->bulk_urb) - usb_kill_urb(devinfo->bulk_urb); - brcmf_usb_free_q(&devinfo->tx_postq, true); - - brcmf_usb_free_q(&devinfo->rx_postq, true); + brcmf_cancel_all_urbs(devinfo); } static void @@ -1094,11 +1099,24 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, return NULL; } +static void brcmf_usb_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled); + devinfo->wowl_enabled = enabled; + if (enabled) + device_set_wakeup_enable(devinfo->dev, true); + else + device_set_wakeup_enable(devinfo->dev, false); +} + static struct brcmf_bus_ops brcmf_usb_bus_ops = { .txdata = brcmf_usb_tx, .stop = brcmf_usb_down, .txctl = brcmf_usb_tx_ctlpkt, .rxctl = brcmf_usb_rx_ctlpkt, + .wowl_config = brcmf_usb_wowl_config, }; static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) @@ -1186,6 +1204,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->ops = &brcmf_usb_bus_ops; bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; +#ifdef CONFIG_PM + bus->wowl_supported = true; +#endif if (!brcmf_usb_dlneeded(devinfo)) { ret = brcmf_usb_bus_setup(devinfo); @@ -1339,7 +1360,10 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) brcmf_dbg(USB, "Enter\n"); devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; - brcmf_detach(&usb->dev); + if (devinfo->wowl_enabled) + brcmf_cancel_all_urbs(devinfo); + else + brcmf_detach(&usb->dev); return 0; } @@ -1352,7 +1376,12 @@ static int brcmf_usb_resume(struct usb_interface *intf) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(USB, "Enter\n"); - return brcmf_usb_bus_setup(devinfo); + if (!devinfo->wowl_enabled) + return brcmf_usb_bus_setup(devinfo); + + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP; + brcmf_usb_rx_fill_all(devinfo); + return 0; } static int brcmf_usb_reset_resume(struct usb_interface *intf) From 66c4658b4ab6c59328cad13e1e0864b315dc1fe1 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:05 +0100 Subject: [PATCH 0195/1983] brcmfmac: Add wowl support for SDIO devices. Upstream-commit: 330b4e4be937bf0ef126e01323f2756645b92c06 This patch adds wowl support for SDIO bus devices. This feature requires FW which has support for wowl built in. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 45 +++++++++++++------ .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 + .../wireless/brcm80211/brcmfmac/sdio_host.h | 2 + 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 602034306076bb..b9eaf5b513f2df 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1064,6 +1064,16 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, if (!sdiodev->pdata) brcmf_of_probe(sdiodev); +#ifdef CONFIG_PM_SLEEP + /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ + * is true or when platform data OOB irq is true). + */ + if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && + ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || + (sdiodev->pdata->oob_irq_supported))) + bus_if->wowl_supported = true; +#endif + atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->request_word_wait); init_waitqueue_head(&sdiodev->request_buffer_wait); @@ -1116,34 +1126,39 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) brcmf_dbg(SDIO, "Exit\n"); } +void brcmf_sdio_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled); + sdiodev->wowl_enabled = enabled; +} + #ifdef CONFIG_PM_SLEEP static int brcmf_ops_sdio_suspend(struct device *dev) { - mmc_pm_flag_t sdio_flags; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - int ret = 0; + mmc_pm_flag_t sdio_flags; brcmf_dbg(SDIO, "Enter\n"); - sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); - if (!(sdio_flags & MMC_PM_KEEP_POWER)) { - brcmf_err("Host can't keep power while suspended\n"); - return -EINVAL; - } - atomic_set(&sdiodev->suspend, true); - ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); - if (ret) { - brcmf_err("Failed to set pm_flags\n"); - atomic_set(&sdiodev->suspend, false); - return ret; + if (sdiodev->wowl_enabled) { + sdio_flags = MMC_PM_KEEP_POWER; + if (sdiodev->pdata->oob_irq_supported) + enable_irq_wake(sdiodev->pdata->oob_irq_nr); + else + sdio_flags = MMC_PM_WAKE_SDIO_IRQ; + if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) + brcmf_err("Failed to set pm_flags %x\n", sdio_flags); } brcmf_sdio_wd_timer(sdiodev->bus, 0); - return ret; + return 0; } static int brcmf_ops_sdio_resume(struct device *dev) @@ -1152,6 +1167,8 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(SDIO, "Enter\n"); + if (sdiodev->pdata->oob_irq_supported) + disable_irq_wake(sdiodev->pdata->oob_irq_nr); brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); atomic_set(&sdiodev->suspend, false); return 0; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index d20d4e6f391ae8..d06542691ee982 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3948,6 +3948,7 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .txctl = brcmf_sdio_bus_txctl, .rxctl = brcmf_sdio_bus_rxctl, .gettxq = brcmf_sdio_bus_gettxq, + .wowl_config = brcmf_sdio_wowl_config }; static void brcmf_sdio_firmware_callback(struct device *dev, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index f2d06cae366af2..262aedfeaa30a9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -186,6 +186,7 @@ struct brcmf_sdio_dev { struct sg_table sgtable; char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + bool wowl_enabled; }; /* sdio core registers */ @@ -334,5 +335,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wowl_config(struct device *dev, bool enabled); #endif /* _BRCM_SDH_H_ */ From 2bbb1bb6362b258ddbb513324c7256f362d3c6d7 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:06 +0100 Subject: [PATCH 0196/1983] brcmfmac: Add wowl patterns support. Upstream-commit: b9a82f892e2a143788d6a4e9fa121837ae1c9a8d Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/fwil_types.h | 89 +++++++++++++------ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 66 ++++++++++++-- 2 files changed, 120 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 5ff5cd0bb0325f..ba64b292f7a516 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -55,59 +55,63 @@ /* WOWL bits */ /* Wakeup on Magic packet: */ -#define WL_WOWL_MAGIC (1 << 0) +#define BRCMF_WOWL_MAGIC (1 << 0) /* Wakeup on Netpattern */ -#define WL_WOWL_NET (1 << 1) +#define BRCMF_WOWL_NET (1 << 1) /* Wakeup on loss-of-link due to Disassoc/Deauth: */ -#define WL_WOWL_DIS (1 << 2) +#define BRCMF_WOWL_DIS (1 << 2) /* Wakeup on retrograde TSF: */ -#define WL_WOWL_RETR (1 << 3) +#define BRCMF_WOWL_RETR (1 << 3) /* Wakeup on loss of beacon: */ -#define WL_WOWL_BCN (1 << 4) +#define BRCMF_WOWL_BCN (1 << 4) /* Wakeup after test: */ -#define WL_WOWL_TST (1 << 5) +#define BRCMF_WOWL_TST (1 << 5) /* Wakeup after PTK refresh: */ -#define WL_WOWL_M1 (1 << 6) +#define BRCMF_WOWL_M1 (1 << 6) /* Wakeup after receipt of EAP-Identity Req: */ -#define WL_WOWL_EAPID (1 << 7) +#define BRCMF_WOWL_EAPID (1 << 7) /* Wakeind via PME(0) or GPIO(1): */ -#define WL_WOWL_PME_GPIO (1 << 8) +#define BRCMF_WOWL_PME_GPIO (1 << 8) /* need tkip phase 1 key to be updated by the driver: */ -#define WL_WOWL_NEEDTKIP1 (1 << 9) +#define BRCMF_WOWL_NEEDTKIP1 (1 << 9) /* enable wakeup if GTK fails: */ -#define WL_WOWL_GTK_FAILURE (1 << 10) +#define BRCMF_WOWL_GTK_FAILURE (1 << 10) /* support extended magic packets: */ -#define WL_WOWL_EXTMAGPAT (1 << 11) +#define BRCMF_WOWL_EXTMAGPAT (1 << 11) /* support ARP/NS/keepalive offloading: */ -#define WL_WOWL_ARPOFFLOAD (1 << 12) +#define BRCMF_WOWL_ARPOFFLOAD (1 << 12) /* read protocol version for EAPOL frames: */ -#define WL_WOWL_WPA2 (1 << 13) +#define BRCMF_WOWL_WPA2 (1 << 13) /* If the bit is set, use key rotaton: */ -#define WL_WOWL_KEYROT (1 << 14) +#define BRCMF_WOWL_KEYROT (1 << 14) /* If the bit is set, frm received was bcast frame: */ -#define WL_WOWL_BCAST (1 << 15) +#define BRCMF_WOWL_BCAST (1 << 15) /* If the bit is set, scan offload is enabled: */ -#define WL_WOWL_SCANOL (1 << 16) +#define BRCMF_WOWL_SCANOL (1 << 16) /* Wakeup on tcpkeep alive timeout: */ -#define WL_WOWL_TCPKEEP_TIME (1 << 17) +#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17) /* Wakeup on mDNS Conflict Resolution: */ -#define WL_WOWL_MDNS_CONFLICT (1 << 18) +#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18) /* Wakeup on mDNS Service Connect: */ -#define WL_WOWL_MDNS_SERVICE (1 << 19) +#define BRCMF_WOWL_MDNS_SERVICE (1 << 19) /* tcp keepalive got data: */ -#define WL_WOWL_TCPKEEP_DATA (1 << 20) +#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20) /* Firmware died in wowl mode: */ -#define WL_WOWL_FW_HALT (1 << 21) +#define BRCMF_WOWL_FW_HALT (1 << 21) /* Enable detection of radio button changes: */ -#define WL_WOWL_ENAB_HWRADIO (1 << 22) +#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22) /* Offloads detected MIC failure(s): */ -#define WL_WOWL_MIC_FAIL (1 << 23) +#define BRCMF_WOWL_MIC_FAIL (1 << 23) /* Wakeup in Unassociated state (Net/Magic Pattern): */ -#define WL_WOWL_UNASSOC (1 << 24) +#define BRCMF_WOWL_UNASSOC (1 << 24) /* Wakeup if received matched secured pattern: */ -#define WL_WOWL_SECURE (1 << 25) +#define BRCMF_WOWL_SECURE (1 << 25) /* Link Down indication in WoWL mode: */ -#define WL_WOWL_LINKDOWN (1 << 31) +#define BRCMF_WOWL_LINKDOWN (1 << 31) + +#define BRCMF_WOWL_MAXPATTERNS 8 +#define BRCMF_WOWL_MAXPATTERNSIZE 128 + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { @@ -124,6 +128,12 @@ enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_DEV, }; +enum brcmf_wowl_pattern_type { + BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0, + BRCMF_WOWL_PATTERN_TYPE_ARP, + BRCMF_WOWL_PATTERN_TYPE_NA +}; + struct brcmf_fil_p2p_if_le { u8 addr[ETH_ALEN]; __le16 type; @@ -484,4 +494,29 @@ struct brcmf_rx_mgmt_data { __be32 rate; }; +/** + * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct. + * + * @cmd: "add", "del" or "clr". + * @masksize: Size of the mask in #of bytes + * @offset: Pattern byte offset in packet + * @patternoffset: Offset of start of pattern. Starting from field masksize. + * @patternsize: Size of the pattern itself in #of bytes + * @id: id + * @reasonsize: Size of the wakeup reason code + * @type: Type of pattern (enum brcmf_wowl_pattern_type) + */ +struct brcmf_fil_wowl_pattern_le { + u8 cmd[4]; + __le32 masksize; + __le32 offset; + __le32 patternoffset; + __le32 patternsize; + __le32 id; + __le32 reasonsize; + __le32 type; + /* u8 mask[] - Mask follows the structure above */ + /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 4d363f2a4e2226..0409e9e2f0261e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2773,6 +2773,44 @@ static __always_inline void brcmf_delay(u32 ms) } } +static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], + u8 *pattern, u32 patternsize, u8 *mask, + u32 packet_offset) +{ + struct brcmf_fil_wowl_pattern_le *filter; + u32 masksize; + u32 patternoffset; + u8 *buf; + u32 bufsize; + s32 ret; + + masksize = (patternsize + 7) / 8; + patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; + + bufsize = sizeof(*filter) + patternsize + masksize; + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + filter = (struct brcmf_fil_wowl_pattern_le *)buf; + + memcpy(filter->cmd, cmd, 4); + filter->masksize = cpu_to_le32(masksize); + filter->offset = cpu_to_le32(packet_offset); + filter->patternoffset = cpu_to_le32(patternoffset); + filter->patternsize = cpu_to_le32(patternsize); + filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); + + if ((mask) && (masksize)) + memcpy(buf + sizeof(*filter), mask, masksize); + if ((pattern) && (patternsize)) + memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); + + ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); + + kfree(buf); + return ret; +} + static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -2782,10 +2820,11 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_dbg(TRACE, "Enter\n"); if (cfg->wowl_enabled) { + brcmf_configure_arp_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, cfg->pre_wowl_pmmode); - brcmf_fil_iovar_data_set(ifp, "wowl_pattern", "clr", 4); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); cfg->wowl_enabled = false; } return 0; @@ -2796,21 +2835,29 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, struct cfg80211_wowlan *wowl) { u32 wowl_config; + u32 i; brcmf_dbg(TRACE, "Suspend, wowl config.\n"); + brcmf_configure_arp_offload(ifp, false); brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; if (wowl->disconnect) - wowl_config |= WL_WOWL_DIS | WL_WOWL_BCN | WL_WOWL_RETR; - /* Note: if "wowl" target and not "wowlpf" then wowl_bcn_loss - * should be configured. This paramater is not supported by - * wowlpf. - */ + wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; if (wowl->magic_pkt) - wowl_config |= WL_WOWL_MAGIC; + wowl_config |= BRCMF_WOWL_MAGIC; + if ((wowl->patterns) && (wowl->n_patterns)) { + wowl_config |= BRCMF_WOWL_NET; + for (i = 0; i < wowl->n_patterns; i++) { + brcmf_config_wowl_pattern(ifp, "add", + (u8 *)wowl->patterns[i].pattern, + wowl->patterns[i].pattern_len, + (u8 *)wowl->patterns[i].mask, + wowl->patterns[i].pkt_offset); + } + } brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); @@ -5434,10 +5481,13 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } - #ifdef CONFIG_PM static const struct wiphy_wowlan_support brcmf_wowlan_support = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = BRCMF_WOWL_MAXPATTERNS, + .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, + .pattern_min_len = 1, + .max_pkt_offset = 1500, }; #endif From 864ae31ebf98019826306c9883f493982a1e7c19 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 28 Oct 2014 14:56:07 +0100 Subject: [PATCH 0197/1983] brcmfmac: show firmware error as string in debug message Upstream-commit: a9a2808f05322fe152672b7e7d09759446947da8 Showing the firmware error allows to quickly give a clue what went wrong and directly look in the firmware code that gave us back the error. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/fwil.c | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index ded328f80cd123..42da73e2614218 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -32,6 +32,76 @@ #define MAX_HEX_DUMP_LEN 64 +#ifdef DEBUG +static const char * const brcmf_fil_errstr[] = { + "BCME_OK", + "BCME_ERROR", + "BCME_BADARG", + "BCME_BADOPTION", + "BCME_NOTUP", + "BCME_NOTDOWN", + "BCME_NOTAP", + "BCME_NOTSTA", + "BCME_BADKEYIDX", + "BCME_RADIOOFF", + "BCME_NOTBANDLOCKED", + "BCME_NOCLK", + "BCME_BADRATESET", + "BCME_BADBAND", + "BCME_BUFTOOSHORT", + "BCME_BUFTOOLONG", + "BCME_BUSY", + "BCME_NOTASSOCIATED", + "BCME_BADSSIDLEN", + "BCME_OUTOFRANGECHAN", + "BCME_BADCHAN", + "BCME_BADADDR", + "BCME_NORESOURCE", + "BCME_UNSUPPORTED", + "BCME_BADLEN", + "BCME_NOTREADY", + "BCME_EPERM", + "BCME_NOMEM", + "BCME_ASSOCIATED", + "BCME_RANGE", + "BCME_NOTFOUND", + "BCME_WME_NOT_ENABLED", + "BCME_TSPEC_NOTFOUND", + "BCME_ACM_NOTSUPPORTED", + "BCME_NOT_WME_ASSOCIATION", + "BCME_SDIO_ERROR", + "BCME_DONGLE_DOWN", + "BCME_VERSION", + "BCME_TXFAIL", + "BCME_RXFAIL", + "BCME_NODEVICE", + "BCME_NMODE_DISABLED", + "BCME_NONRESIDENT", + "BCME_SCANREJECT", + "BCME_USAGE_ERROR", + "BCME_IOCTL_ERROR", + "BCME_SERIAL_PORT_ERR", + "BCME_DISABLED", + "BCME_DECERR", + "BCME_ENCERR", + "BCME_MICERR", + "BCME_REPLAY", + "BCME_IE_NOTFOUND", +}; + +static const char *brcmf_fil_get_errstr(u32 err) +{ + if (err >= ARRAY_SIZE(brcmf_fil_errstr)) + return "(unknown)"; + + return brcmf_fil_errstr[err]; +} +#else +static const char *brcmf_fil_get_errstr(u32 err) +{ + return ""; +} +#endif /* DEBUG */ static s32 brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) @@ -54,7 +124,8 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) if (err >= 0) err = 0; else - brcmf_dbg(FIL, "Failed err=%d\n", err); + brcmf_dbg(FIL, "Failed: %s (%d)\n", + brcmf_fil_get_errstr((u32)(-err)), err); return err; } From cf663f35ddd3236dad460e1f79e176aed14c7b27 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 28 Oct 2014 14:56:08 +0100 Subject: [PATCH 0198/1983] brcmfmac: remove unused defintion Upstream-commit: a3e53bbfd91c155d06bb168ad7a830cdf21b500c The define EBRCMF_UNSUPPORTED is not used in the source file so this patch removes it. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index aed53acef456a4..4eb6a41e72c754 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -23,11 +23,6 @@ #include "fwil.h" #include "feature.h" -/* - * firmware error code received if iovar is unsupported. - */ -#define EBRCMF_FEAT_UNSUPPORTED 23 - /* * expand feature list to array of feature strings. */ From 76a4699717a3f67a1e4a4bc5f634910d1353cd2d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 28 Oct 2014 14:56:09 +0100 Subject: [PATCH 0199/1983] brcmfmac: do not use firmware error code in driver Upstream-commit: 9c6476668025e76a8365be783f2649fe3259b91c Passing the firmware error codes up the driver may be mapped to linux error numbers which may impact proper fault analysis. So better pass up a generic failure code, ie. -EBADE and only show firmware error code in FIL debug message. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 42da73e2614218..dccbdc1aae6df8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -122,12 +122,11 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len); if (err >= 0) - err = 0; - else - brcmf_dbg(FIL, "Failed: %s (%d)\n", - brcmf_fil_get_errstr((u32)(-err)), err); + return 0; - return err; + brcmf_dbg(FIL, "Failed: %s (%d)\n", + brcmf_fil_get_errstr((u32)(-err)), err); + return -EBADE; } s32 From 74489486d2f6d588b5a5f12d37cd36d3e005407b Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:10 +0100 Subject: [PATCH 0200/1983] brcmfmac: (clean) Remove usb_rdl.h as it is not needed. Upstream-commit: ac83d0b0aae152385ae998c65fc03ea790d8c025 Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 88 +++++++++++++++---- .../net/wireless/brcm80211/brcmfmac/usb_rdl.h | 75 ---------------- 2 files changed, 69 insertions(+), 94 deletions(-) delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index e533000787f327..3f12b606839e66 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -27,9 +27,9 @@ #include #include "firmware.h" -#include "usb_rdl.h" #include "usb.h" + #define IOCTL_RESP_TIMEOUT 2000 #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ @@ -49,6 +49,71 @@ #define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" #define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_MAX_OFFSET 3 /* Max number of file offsets */ +#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */ +#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */ +#define TRX_OFFSETS_DLFWLEN_IDX 0 + +/* Control messages: bRequest values */ +#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ +#define DL_CHECK_CRC 1 /* currently unused */ +#define DL_GO 2 /* execute downloaded image */ +#define DL_START 3 /* initialize dl state */ +#define DL_REBOOT 4 /* reboot the device in 2 seconds */ +#define DL_GETVER 5 /* returns the bootrom_id_t struct */ +#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset + * event to occur in 2 seconds. It is the + * responsibility of the downloaded code to + * clear this event + */ +#define DL_EXEC 7 /* jump to a supplied address */ +#define DL_RESETCFG 8 /* To support single enum on dongle + * - Not used by bootloader + */ +#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup + * if resp unavailable + */ + +/* states */ +#define DL_WAITING 0 /* waiting to rx first pkt */ +#define DL_READY 1 /* hdr was good, waiting for more of the + * compressed image + */ +#define DL_BAD_HDR 2 /* hdr was corrupted */ +#define DL_BAD_CRC 3 /* compressed image was corrupted */ +#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ +#define DL_START_FAIL 5 /* failed to initialize correctly */ +#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM + * value + */ +#define DL_IMAGE_TOOBIG 7 /* firmware image too big */ + + +struct trx_header_le { + __le32 magic; /* "HDR0" */ + __le32 len; /* Length of file including header */ + __le32 crc32; /* CRC from flag_version to end of file */ + __le32 flag_version; /* 0:15 flags, 16:31 version */ + __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of + * header + */ +}; + +struct rdl_state_le { + __le32 state; + __le32 bytes; +}; + +struct bootrom_id_le { + __le32 chip; /* Chip id */ + __le32 chiprev; /* Chip rev */ + __le32 ramsize; /* Size of RAM */ + __le32 remapbase; /* Current remap base address */ + __le32 boardtype; /* Type of board */ + __le32 boardrev; /* Board revision */ +}; + struct brcmf_usb_image { struct list_head list; s8 *fwname; @@ -788,7 +853,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen); - bulkchunk = kmalloc(RDL_CHUNK, GFP_ATOMIC); + bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC); if (bulkchunk == NULL) { err = -ENOMEM; goto fail; @@ -815,10 +880,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) /* Wait until the usb device reports it received all * the bytes we sent */ if ((rdlbytes == sent) && (rdlbytes != dllen)) { - if ((dllen-sent) < RDL_CHUNK) + if ((dllen-sent) < TRX_RDL_CHUNK) sendlen = dllen-sent; else - sendlen = RDL_CHUNK; + sendlen = TRX_RDL_CHUNK; /* simply avoid having to send a ZLP by ensuring we * never have an even @@ -983,21 +1048,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) kfree(devinfo->rx_reqs); } -#define TRX_MAGIC 0x30524448 /* "HDR0" */ -#define TRX_VERSION 1 /* Version 1 */ -#define TRX_MAX_LEN 0x3B0000 /* Max length */ -#define TRX_NO_HEADER 1 /* Do not write TRX header */ -#define TRX_MAX_OFFSET 3 /* Max number of individual files */ -#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed image */ - -struct trx_header_le { - __le32 magic; /* "HDR0" */ - __le32 len; /* Length of file including header */ - __le32 crc32; /* CRC from flag_version to end of file */ - __le32 flag_version; /* 0:15 flags, 16:31 version */ - __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of - * header */ -}; static int check_file(const u8 *headers) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h b/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h deleted file mode 100644 index 0a35c51c3da202..00000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2011 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _USB_RDL_H -#define _USB_RDL_H - -/* Control messages: bRequest values */ -#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ -#define DL_CHECK_CRC 1 /* currently unused */ -#define DL_GO 2 /* execute downloaded image */ -#define DL_START 3 /* initialize dl state */ -#define DL_REBOOT 4 /* reboot the device in 2 seconds */ -#define DL_GETVER 5 /* returns the bootrom_id_t struct */ -#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset - * event to occur in 2 seconds. It is the - * responsibility of the downloaded code to - * clear this event - */ -#define DL_EXEC 7 /* jump to a supplied address */ -#define DL_RESETCFG 8 /* To support single enum on dongle - * - Not used by bootloader - */ -#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup - * if resp unavailable - */ - -/* states */ -#define DL_WAITING 0 /* waiting to rx first pkt */ -#define DL_READY 1 /* hdr was good, waiting for more of the - * compressed image */ -#define DL_BAD_HDR 2 /* hdr was corrupted */ -#define DL_BAD_CRC 3 /* compressed image was corrupted */ -#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ -#define DL_START_FAIL 5 /* failed to initialize correctly */ -#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM - * value */ -#define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START - * for rdl) */ - -struct rdl_state_le { - __le32 state; - __le32 bytes; -}; - -struct bootrom_id_le { - __le32 chip; /* Chip id */ - __le32 chiprev; /* Chip rev */ - __le32 ramsize; /* Size of RAM */ - __le32 remapbase; /* Current remap base address */ - __le32 boardtype; /* Type of board */ - __le32 boardrev; /* Board revision */ -}; - -#define RDL_CHUNK 1500 /* size of each dl transfer */ - -#define TRX_OFFSETS_DLFWLEN_IDX 0 -#define TRX_OFFSETS_JUMPTO_IDX 1 -#define TRX_OFFSETS_NVM_LEN_IDX 2 - -#define TRX_OFFSETS_DLBASE_IDX 0 - -#endif /* _USB_RDL_H */ From da843e7f03c80af7aed522c0b4b46f054a96dffd Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:11 +0100 Subject: [PATCH 0201/1983] brcmfmac: (clean) Remove packet filter configuration. Upstream-commit: d3c80372e00c792cbde42fa1abda3b8b08575d59 Packet filters got configured but never used. Reviewed-by: Franky Lin Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_common.c | 166 ------------------ 1 file changed, 166 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index d991f8e3d9ece1..6028fc46657507 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -26,11 +26,9 @@ #include "fwil_types.h" #include "tracepoint.h" -#define PKTFILTER_BUF_SIZE 128 #define BRCMF_DEFAULT_BCN_TIMEOUT 3 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 -#define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" /* boost value for RSSI_DELTA in preferred join selection */ #define BRCMF_JOIN_PREF_RSSI_BOOST 8 @@ -86,165 +84,6 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, return p != NULL; } -/* Convert user's input in hex pattern to byte-size mask */ -static int brcmf_c_pattern_atoh(char *src, char *dst) -{ - int i; - if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { - brcmf_err("Mask invalid format. Needs to start with 0x\n"); - return -EINVAL; - } - src = src + 2; /* Skip past 0x */ - if (strlen(src) % 2 != 0) { - brcmf_err("Mask invalid format. Length must be even.\n"); - return -EINVAL; - } - for (i = 0; *src != '\0'; i++) { - unsigned long res; - char num[3]; - strncpy(num, src, 2); - num[2] = '\0'; - if (kstrtoul(num, 16, &res)) - return -EINVAL; - dst[i] = (u8)res; - src += 2; - } - return i; -} - -static void -brcmf_c_pktfilter_offload_enable(struct brcmf_if *ifp, char *arg, int enable, - int master_mode) -{ - unsigned long res; - char *argv; - char *arg_save = NULL, *arg_org = NULL; - s32 err; - struct brcmf_pkt_filter_enable_le enable_parm; - - arg_save = kstrdup(arg, GFP_ATOMIC); - if (!arg_save) - goto fail; - - arg_org = arg_save; - - argv = strsep(&arg_save, " "); - - if (argv == NULL) { - brcmf_err("No args provided\n"); - goto fail; - } - - /* Parse packet filter id. */ - enable_parm.id = 0; - if (!kstrtoul(argv, 0, &res)) - enable_parm.id = cpu_to_le32((u32)res); - - /* Enable/disable the specified filter. */ - enable_parm.enable = cpu_to_le32(enable); - - err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", &enable_parm, - sizeof(enable_parm)); - if (err) - brcmf_err("Set pkt_filter_enable error (%d)\n", err); - - /* Control the master mode */ - err = brcmf_fil_iovar_int_set(ifp, "pkt_filter_mode", master_mode); - if (err) - brcmf_err("Set pkt_filter_mode error (%d)\n", err); - -fail: - kfree(arg_org); -} - -static void brcmf_c_pktfilter_offload_set(struct brcmf_if *ifp, char *arg) -{ - struct brcmf_pkt_filter_le *pkt_filter; - unsigned long res; - int buf_len; - s32 err; - u32 mask_size; - u32 pattern_size; - char *argv[8], *buf = NULL; - int i = 0; - char *arg_save = NULL, *arg_org = NULL; - - arg_save = kstrdup(arg, GFP_ATOMIC); - if (!arg_save) - goto fail; - - arg_org = arg_save; - - buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC); - if (!buf) - goto fail; - - argv[i] = strsep(&arg_save, " "); - while (argv[i]) { - i++; - if (i >= 8) { - brcmf_err("Too many parameters\n"); - goto fail; - } - argv[i] = strsep(&arg_save, " "); - } - - if (i != 6) { - brcmf_err("Not enough args provided %d\n", i); - goto fail; - } - - pkt_filter = (struct brcmf_pkt_filter_le *)buf; - - /* Parse packet filter id. */ - pkt_filter->id = 0; - if (!kstrtoul(argv[0], 0, &res)) - pkt_filter->id = cpu_to_le32((u32)res); - - /* Parse filter polarity. */ - pkt_filter->negate_match = 0; - if (!kstrtoul(argv[1], 0, &res)) - pkt_filter->negate_match = cpu_to_le32((u32)res); - - /* Parse filter type. */ - pkt_filter->type = 0; - if (!kstrtoul(argv[2], 0, &res)) - pkt_filter->type = cpu_to_le32((u32)res); - - /* Parse pattern filter offset. */ - pkt_filter->u.pattern.offset = 0; - if (!kstrtoul(argv[3], 0, &res)) - pkt_filter->u.pattern.offset = cpu_to_le32((u32)res); - - /* Parse pattern filter mask. */ - mask_size = brcmf_c_pattern_atoh(argv[4], - (char *)pkt_filter->u.pattern.mask_and_pattern); - - /* Parse pattern filter pattern. */ - pattern_size = brcmf_c_pattern_atoh(argv[5], - (char *)&pkt_filter->u.pattern.mask_and_pattern[mask_size]); - - if (mask_size != pattern_size) { - brcmf_err("Mask and pattern not the same size\n"); - goto fail; - } - - pkt_filter->u.pattern.size_bytes = cpu_to_le32(mask_size); - buf_len = offsetof(struct brcmf_pkt_filter_le, - u.pattern.mask_and_pattern); - buf_len += mask_size + pattern_size; - - err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filter, - buf_len); - if (err) - brcmf_err("Set pkt_filter_add error (%d)\n", err); - -fail: - kfree(arg_org); - - kfree(buf); -} - int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; @@ -356,11 +195,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } - /* Setup packet filter */ - brcmf_c_pktfilter_offload_set(ifp, BRCMF_DEFAULT_PACKET_FILTER); - brcmf_c_pktfilter_offload_enable(ifp, BRCMF_DEFAULT_PACKET_FILTER, - 0, true); - /* do bus specific preinit here */ err = brcmf_bus_preinit(ifp->drvr->bus_if); done: From 3c37c0e459b71e7d30cdbf67e72b5d74bfa75d5c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:12 +0100 Subject: [PATCH 0202/1983] brcmfmac: (clean) Move tracepoint related function. Upstream-commit: 4a1c61508b4830a3c36257a0995646d7b22b29a9 __brcmf_err is a tracepoint specific function. Move it to tracepoint.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 15 --------------- .../net/wireless/brcm80211/brcmfmac/tracepoint.c | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 6028fc46657507..98bc2226a119a3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -201,21 +201,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) return err; } -#ifdef CONFIG_BRCM_TRACING -void __brcmf_err(const char *func, const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; - pr_err("%s: %pV", func, &vaf); - trace_brcmf_err(func, &vaf); - va_end(args); -} -#endif #if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c index b505db48c60df1..a10f35c5eb3dae 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c @@ -19,4 +19,19 @@ #ifndef __CHECKER__ #define CREATE_TRACE_POINTS #include "tracepoint.h" + +void __brcmf_err(const char *func, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + pr_err("%s: %pV", func, &vaf); + trace_brcmf_err(func, &vaf); + va_end(args); +} + #endif From 6243a1224b83a4fe6e2a3329f047a5a5707b462c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:13 +0100 Subject: [PATCH 0203/1983] brcmfmac: (clean) Rename files dhd_dbg to debug Upstream-commit: a8e8ed3446a32a2323e70bc1cef05c5119d893e0 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 2 +- .../net/wireless/brcm80211/brcmfmac/{dhd_dbg.c => debug.c} | 2 +- .../net/wireless/brcm80211/brcmfmac/{dhd_dbg.h => debug.h} | 6 +++--- drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/of.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 25 files changed, 27 insertions(+), 27 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd_dbg.c => debug.c} (99%) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd_dbg.h => debug.h} (98%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 90a977fe9a648d..1da9042c8aba0f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -50,7 +50,7 @@ brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \ pcie.o brcmfmac-$(CONFIG_BRCMDBG) += \ - dhd_dbg.o + debug.o brcmfmac-$(CONFIG_BRCM_TRACING) += \ tracepoint.o brcmfmac-$(CONFIG_OF) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index a159ff3427de9f..11d3dfae8bac97 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -28,7 +28,7 @@ #include "dhd.h" #include "dhd_bus.h" #include "fwsignal.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "proto.h" #include "bcdc.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index b9eaf5b513f2df..8a146b814e9927 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -42,7 +42,7 @@ #include #include "chip.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "sdio_host.h" #include "of.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index a29ac4977b3a12..a2f7e2ccfc8462 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include "debug.h" #include "fwil.h" #include "fwil_types.h" #include "btcoex.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 95efde868db82e..ddae0b5e56eced 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -25,7 +25,7 @@ #include #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "chip.h" /* SOC Interconnect types (aka chip types) */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c similarity index 99% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c rename to drivers/net/wireless/brcm80211/brcmfmac/debug.c index be9f4f829192cc..27e51085d33a8a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -21,7 +21,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" static struct dentry *root_folder; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h similarity index 98% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h rename to drivers/net/wireless/brcm80211/brcmfmac/debug.h index dec40d316c8291..eb0b8c47479d1f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h @@ -14,8 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _BRCMF_DBG_H_ -#define _BRCMF_DBG_H_ +#ifndef BRCMFMAC_DEBUG_H +#define BRCMFMAC_DEBUG_H /* message levels */ #define BRCMF_TRACE_VAL 0x00000002 @@ -133,4 +133,4 @@ int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, } #endif -#endif /* _BRCMF_DBG_H_ */ +#endif /* BRCMFMAC_DEBUG_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 80e73a1262be81..0421cfea9a257f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -17,7 +17,7 @@ #ifndef _BRCMF_BUS_H_ #define _BRCMF_BUS_H_ -#include "dhd_dbg.h" +#include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */ #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 98bc2226a119a3..78ada880506f56 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -21,7 +21,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "fwil.h" #include "fwil_types.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index b456bcb7f9164c..ab1c61f1f7683b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -24,7 +24,7 @@ #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "fwil_types.h" #include "p2p.h" #include "wl_cfg80211.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index d06542691ee982..525d7073198c10 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -97,7 +97,7 @@ struct rte_console { #include #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #define TXQLEN 2048 /* bulk tx queue length */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 4eb6a41e72c754..86b854e83f7103 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -19,7 +19,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "fwil.h" #include "feature.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 8ea9f283d2b806..0f157f151282eb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -20,7 +20,7 @@ #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "firmware.h" char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 1faa929f5fff2c..15a2cc56baf058 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -20,7 +20,7 @@ #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "dhd_bus.h" #include "proto.h" #include "flowring.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 44fc85f68f7ace..ce98ce55685b4b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -19,7 +19,7 @@ #include "brcmu_utils.h" #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "fwsignal.h" #include "fweh.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index dccbdc1aae6df8..424146c226aad0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -24,7 +24,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "fwil.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 183f08d7fc8c8e..5a3cf644af2f1b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -27,7 +27,7 @@ #include #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "dhd_bus.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 11cc051f97cd42..87924d32d54065 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -25,7 +25,7 @@ #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "proto.h" #include "msgbuf.h" #include "commonring.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index f05f5270fec109..875060c54a0091 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -21,7 +21,7 @@ #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "sdio_host.h" void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 9585ef440e5c16..4e5aa5236983f5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include "debug.h" #include "fwil.h" #include "fwil_types.h" #include "p2p.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 8c0632ec9f7a60..d8fe7abd96c8db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -30,7 +30,7 @@ #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "dhd_bus.h" #include "commonring.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index 62b940723339f8..f96b9326fe3d12 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -22,7 +22,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "proto.h" #include "bcdc.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 3f12b606839e66..4eb9b2c158b34b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -24,8 +24,8 @@ #include #include #include -#include +#include "debug.h" #include "firmware.h" #include "usb.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 5960d827508c9d..f204c83cd61f60 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -22,7 +22,7 @@ #include "fwil_types.h" #include "dhd.h" #include "p2p.h" -#include "dhd_dbg.h" +#include "debug.h" #include "wl_cfg80211.h" #include "vendor.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 0409e9e2f0261e..267f1e78ee5496 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -27,7 +27,7 @@ #include #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "fwil_types.h" #include "p2p.h" From bf32b1ef80b2ddf6ba13b7d1a6ff517a2edf77ee Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:14 +0100 Subject: [PATCH 0204/1983] brcmfmac: (clean) Rename dhd_bus.h in bus.h Upstream-commit: d14f78b990ec4d66e9509bc42a6fce2d217880ec Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- .../net/wireless/brcm80211/brcmfmac/{dhd_bus.h => bus.h} | 9 ++++----- drivers/net/wireless/brcm80211/brcmfmac/debug.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 3 +-- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 16 files changed, 19 insertions(+), 21 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd_bus.h => bus.h} (98%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 11d3dfae8bac97..d5a2d94ffde43f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -26,7 +26,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "fwsignal.h" #include "debug.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 8a146b814e9927..1037bba219722b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -41,7 +41,7 @@ #include #include #include "chip.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "sdio_host.h" #include "of.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h similarity index 98% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h rename to drivers/net/wireless/brcm80211/brcmfmac/bus.h index 0421cfea9a257f..ef344e47218a3a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -14,8 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _BRCMF_BUS_H_ -#define _BRCMF_BUS_H_ +#ifndef BRCMFMAC_BUS_H +#define BRCMFMAC_BUS_H #include "debug.h" @@ -227,8 +227,7 @@ void brcmf_txflowblock(struct device *dev, bool state); void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); int brcmf_bus_start(struct device *dev); -s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, - u32 len); +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); void brcmf_bus_add_txhdrlen(struct device *dev, uint len); #ifdef CONFIG_BRCMFMAC_SDIO @@ -241,4 +240,4 @@ void brcmf_usb_exit(void); void brcmf_usb_register(void); #endif -#endif /* _BRCMF_BUS_H_ */ +#endif /* BRCMFMAC_BUS_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c index 27e51085d33a8a..340b10447ab912 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -20,7 +20,7 @@ #include #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" static struct dentry *root_folder; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 78ada880506f56..7723994136a1e3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -20,7 +20,7 @@ #include #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index ab1c61f1f7683b..f7c2ca27c2879e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -23,7 +23,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "fwil_types.h" #include "p2p.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 525d7073198c10..641c8d52248427 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -96,7 +96,7 @@ struct rte_console { #endif /* DEBUG */ #include -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 86b854e83f7103..d4be4c41691252 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -18,7 +18,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "fwil.h" #include "feature.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 15a2cc56baf058..e9cb550f5c5a8b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -21,7 +21,7 @@ #include "dhd.h" #include "debug.h" -#include "dhd_bus.h" +#include "bus.h" #include "proto.h" #include "flowring.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 424146c226aad0..70db41f51b7b5c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -23,7 +23,7 @@ #include #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "tracepoint.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5a3cf644af2f1b..dbd52ccbde5603 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -28,7 +28,7 @@ #include #include "dhd.h" #include "debug.h" -#include "dhd_bus.h" +#include "bus.h" #include "fwil.h" #include "fwil_types.h" #include "fweh.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 87924d32d54065..18e1770b8d8cb2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -30,7 +30,7 @@ #include "msgbuf.h" #include "commonring.h" #include "flowring.h" -#include "dhd_bus.h" +#include "bus.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index d8fe7abd96c8db..b0ae7993e2e885 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -31,7 +31,7 @@ #include #include "debug.h" -#include "dhd_bus.h" +#include "bus.h" #include "commonring.h" #include "msgbuf.h" #include "pcie.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index f96b9326fe3d12..5e83b2bee9d8d4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -21,7 +21,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "proto.h" #include "bcdc.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 4eb9b2c158b34b..5265aa70b094f3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -23,8 +23,7 @@ #include #include #include -#include - +#include "bus.h" #include "debug.h" #include "firmware.h" #include "usb.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 267f1e78ee5496..a8799f31fb52f8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -37,7 +37,7 @@ #include "fwil.h" #include "proto.h" #include "vendor.h" -#include "dhd_bus.h" +#include "bus.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 From de7282206cb89f17baf30923cb062b699f96eeeb Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:15 +0100 Subject: [PATCH 0205/1983] brcmfmac: (clean) Rename dhd_common.c in common.c Upstream-commit: 76b5a96d1df10535c7e2e7f876f2230e243aa7b8 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- .../wireless/brcm80211/brcmfmac/{dhd_common.c => common.c} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd_common.c => common.c} (99%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 1da9042c8aba0f..f6e35c95b829bf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -30,7 +30,7 @@ brcmfmac-objs += \ fwsignal.o \ p2p.o \ proto.o \ - dhd_common.o \ + common.o \ dhd_linux.o \ firmware.o \ feature.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c similarity index 99% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c rename to drivers/net/wireless/brcm80211/brcmfmac/common.c index 7723994136a1e3..75642e46c37adf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -52,9 +52,9 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, } /* Determine precedence from which to evict packet, if any */ - if (pktq_pfull(q, prec)) + if (pktq_pfull(q, prec)) { eprec = prec; - else if (pktq_full(q)) { + } else if (pktq_full(q)) { p = brcmu_pktq_peek_tail(q, &eprec); if (eprec > prec) return false; From d2de6e62127242e91d2eb7af4d9d30cc21134a74 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:16 +0100 Subject: [PATCH 0206/1983] brcmfmac: (clean) Rename files wl_cfg80211 to cfg80211 Upstream-commit: bfe8197582f369dea55e1ef431a51d0b227704c8 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 2 +- .../wireless/brcm80211/brcmfmac/{wl_cfg80211.c => cfg80211.c} | 2 +- .../wireless/brcm80211/brcmfmac/{wl_cfg80211.h => cfg80211.h} | 0 drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{wl_cfg80211.c => cfg80211.c} (99%) rename drivers/net/wireless/brcm80211/brcmfmac/{wl_cfg80211.h => cfg80211.h} (100%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index f6e35c95b829bf..35b680550446aa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -23,7 +23,7 @@ ccflags-y += -D__CHECK_ENDIAN__ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ - wl_cfg80211.o \ + cfg80211.o \ chip.o \ fwil.o \ fweh.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index a2f7e2ccfc8462..e324414fcf6dc3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -26,7 +26,7 @@ #include "fwil_types.h" #include "btcoex.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" /* T1 start SCO/eSCO priority suppression */ #define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c similarity index 99% rename from drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c rename to drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index a8799f31fb52f8..237a493dcd5a77 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -32,7 +32,7 @@ #include "fwil_types.h" #include "p2p.h" #include "btcoex.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "feature.h" #include "fwil.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h similarity index 100% rename from drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h rename to drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index f7c2ca27c2879e..e1276831320945 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -27,7 +27,7 @@ #include "debug.h" #include "fwil_types.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "fwil.h" #include "fwsignal.h" #include "feature.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index dbd52ccbde5603..6d33331dd2b090 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -34,7 +34,7 @@ #include "fweh.h" #include "fwsignal.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "proto.h" /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 4e5aa5236983f5..16890388d68dd6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -26,7 +26,7 @@ #include "fwil.h" #include "fwil_types.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" /* parameters used for p2p escan */ #define P2PAPI_SCAN_NPROBES 1 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index f204c83cd61f60..b86a083695170d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -23,7 +23,7 @@ #include "dhd.h" #include "p2p.h" #include "debug.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "vendor.h" #include "fwil.h" From 624a94e413e57b4a9bb5421fd89c8c5123d96a42 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:17 +0100 Subject: [PATCH 0207/1983] brcmfmac: (clean) Rename sdio related files. Upstream-commit: 888bf76e41119bf72256830bce0eb192a6a4033a Rename sdio_host.h to sdio.h and dhd_sdio.c to sdio.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/of.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/{dhd_sdio.c => sdio.c} | 2 +- drivers/net/wireless/brcm80211/brcmfmac/{sdio_host.h => sdio.h} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd_sdio.c => sdio.c} (99%) rename drivers/net/wireless/brcm80211/brcmfmac/{sdio_host.h => sdio.h} (100%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 35b680550446aa..40ba46b4809d1a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -43,7 +43,7 @@ brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ flowring.o \ msgbuf.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ - dhd_sdio.o \ + sdio.o \ bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 1037bba219722b..3c06e9365949a0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -43,7 +43,7 @@ #include "chip.h" #include "bus.h" #include "debug.h" -#include "sdio_host.h" +#include "sdio.h" #include "of.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index 875060c54a0091..eb3fce82a22316 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -22,7 +22,7 @@ #include #include "debug.h" -#include "sdio_host.h" +#include "sdio.h" void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c similarity index 99% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c rename to drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 641c8d52248427..c13c61fcbcf4c2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -40,7 +40,7 @@ #include #include #include -#include "sdio_host.h" +#include "sdio.h" #include "chip.h" #include "firmware.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h similarity index 100% rename from drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h rename to drivers/net/wireless/brcm80211/brcmfmac/sdio.h From 90d8fc35bcb51128a5f49fb3ab969b26f87f5973 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:18 +0100 Subject: [PATCH 0208/1983] brcmfmac: (clean) Rename sdio related files. Upstream-commit: 122d3d04d769455efba396f516f61c5a55791519 Rename sdio_host.h to sdio.h and dhd_sdio.c to sdio.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/common.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/commonring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/{dhd_linux.c => core.c} | 2 +- drivers/net/wireless/brcm80211/brcmfmac/{dhd.h => core.h} | 0 drivers/net/wireless/brcm80211/brcmfmac/debug.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 2 +- 18 files changed, 17 insertions(+), 17 deletions(-) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd_linux.c => core.c} (99%) rename drivers/net/wireless/brcm80211/brcmfmac/{dhd.h => core.h} (100%) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 40ba46b4809d1a..dc4c75083085bb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -31,7 +31,7 @@ brcmfmac-objs += \ p2p.o \ proto.o \ common.o \ - dhd_linux.o \ + core.o \ firmware.o \ feature.o \ btcoex.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index d5a2d94ffde43f..8e0e91c4a0b167 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -25,7 +25,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "fwsignal.h" #include "debug.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index e324414fcf6dc3..0445163991b72c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include "core.h" #include "debug.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 237a493dcd5a77..4843fbe6d5efe2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -26,7 +26,7 @@ #include #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "tracepoint.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 75642e46c37adf..183cec9939eaff 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -19,7 +19,7 @@ #include #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c index c6d65b8e1e1565..77656c711bedbf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -19,7 +19,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "commonring.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c similarity index 99% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c rename to drivers/net/wireless/brcm80211/brcmfmac/core.c index e1276831320945..3133a09269a979 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -22,7 +22,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h similarity index 100% rename from drivers/net/wireless/brcm80211/brcmfmac/dhd.h rename to drivers/net/wireless/brcm80211/brcmfmac/core.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c index 340b10447ab912..9b473d50b0058d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -19,7 +19,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index d4be4c41691252..931f68aefaa476 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -17,7 +17,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index e9cb550f5c5a8b..44f3a84d1999c6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -19,7 +19,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "bus.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index ce98ce55685b4b..7338b335e153ea 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -18,7 +18,7 @@ #include "brcmu_wifi.h" #include "brcmu_utils.h" -#include "dhd.h" +#include "core.h" #include "debug.h" #include "tracepoint.h" #include "fwsignal.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 70db41f51b7b5c..51f88c11e642e7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -22,7 +22,7 @@ #include #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 6d33331dd2b090..f0dda0ecd23b43 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -26,7 +26,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "bus.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 18e1770b8d8cb2..02d39ce8dbcafc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -24,7 +24,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "proto.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 16890388d68dd6..a53998c3792b37 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include "core.h" #include "debug.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index 5e83b2bee9d8d4..26b68c367f57cc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -20,7 +20,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index b86a083695170d..222f26a3964247 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -20,7 +20,7 @@ #include #include "fwil_types.h" -#include "dhd.h" +#include "core.h" #include "p2p.h" #include "debug.h" #include "cfg80211.h" From 22a6cc48add64e3e8c83a6dcb319986f460531f6 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:19 +0100 Subject: [PATCH 0209/1983] brcmfmac: (clean) Move sdio related function. Upstream-commit: 84936626efbfacaf53505020610ae275fae2bf88 prec_enq is a sdio specific function. Move it to sdio.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/common.c | 51 ------------------- .../net/wireless/brcm80211/brcmfmac/core.h | 1 - .../net/wireless/brcm80211/brcmfmac/sdio.c | 46 ++++++++++++++++- 3 files changed, 44 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 183cec9939eaff..1861a13e8d03d6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -33,57 +33,6 @@ /* boost value for RSSI_DELTA in preferred join selection */ #define BRCMF_JOIN_PREF_RSSI_BOOST 8 - -bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, - struct sk_buff *pkt, int prec) -{ - struct sk_buff *p; - int eprec = -1; /* precedence to evict from */ - bool discard_oldest; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - /* Fast case, precedence queue is not full and we are also not - * exceeding total queue length - */ - if (!pktq_pfull(q, prec) && !pktq_full(q)) { - brcmu_pktq_penq(q, prec, pkt); - return true; - } - - /* Determine precedence from which to evict packet, if any */ - if (pktq_pfull(q, prec)) { - eprec = prec; - } else if (pktq_full(q)) { - p = brcmu_pktq_peek_tail(q, &eprec); - if (eprec > prec) - return false; - } - - /* Evict if needed */ - if (eprec >= 0) { - /* Detect queueing to unconfigured precedence */ - discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec); - if (eprec == prec && !discard_oldest) - return false; /* refuse newer (incoming) packet */ - /* Evict packet according to discard policy */ - p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : - brcmu_pktq_pdeq_tail(q, eprec); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed, oldest %d\n", - discard_oldest); - - brcmu_pkt_buf_free_skb(p); - } - - /* Enqueue */ - p = brcmu_pktq_penq(q, prec, pkt); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed\n"); - - return p != NULL; -} - int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 5e4317dbc2b0a1..40cb539f1f50f6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -83,7 +83,6 @@ struct brcmf_pub { /* Internal brcmf items */ uint hdrlen; /* Total BRCMF header length (proto + bus) */ uint rxsz; /* Rx buffer size bus module should use */ - u8 wme_dp; /* wme discard priority */ /* Dongle media info */ char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index c13c61fcbcf4c2..c3c83f9d3945e3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -2762,6 +2762,48 @@ static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) return &bus->txq; } +static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec) +{ + struct sk_buff *p; + int eprec = -1; /* precedence to evict from */ + + /* Fast case, precedence queue is not full and we are also not + * exceeding total queue length + */ + if (!pktq_pfull(q, prec) && !pktq_full(q)) { + brcmu_pktq_penq(q, prec, pkt); + return true; + } + + /* Determine precedence from which to evict packet, if any */ + if (pktq_pfull(q, prec)) { + eprec = prec; + } else if (pktq_full(q)) { + p = brcmu_pktq_peek_tail(q, &eprec); + if (eprec > prec) + return false; + } + + /* Evict if needed */ + if (eprec >= 0) { + /* Detect queueing to unconfigured precedence */ + if (eprec == prec) + return false; /* refuse newer (incoming) packet */ + /* Evict packet according to discard policy */ + p = brcmu_pktq_pdeq_tail(q, eprec); + if (p == NULL) + brcmf_err("brcmu_pktq_pdeq_tail() failed\n"); + brcmu_pkt_buf_free_skb(p); + } + + /* Enqueue */ + p = brcmu_pktq_penq(q, prec, pkt); + if (p == NULL) + brcmf_err("brcmu_pktq_penq() failed\n"); + + return p != NULL; +} + static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; @@ -2787,7 +2829,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ *(u16 *)(pkt->cb) = 0; - if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { + if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) { skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); ret = -ENOSR; @@ -2797,7 +2839,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) if (pktq_len(&bus->txq) >= TXHI) { bus->txoff = true; - brcmf_txflowblock(bus->sdiodev->dev, true); + brcmf_txflowblock(dev, true); } spin_unlock_bh(&bus->txq_lock); From c08fcdd483babd097870f326a214a79ce523a93c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 11 Nov 2014 13:58:44 +0100 Subject: [PATCH 0210/1983] brcmfmac: fix conversion of channel width 20MHZ_NOHT Upstream-commit: 0cd75b19899fd86b51a6480fb8c00dcd85a54591 The function chandef_to_chanspec() failed when converting a chandef with bandwidth set to NL80211_CHAN_WIDTH_20_NOHT. This was reported by user running the device in AP mode. ------------[ cut here ]------------ WARNING: CPU: 0 PID: 304 at drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c:381 chandef_to_chanspec.isra.11+0x158/0x184() Modules linked in: CPU: 0 PID: 304 Comm: hostapd Not tainted 3.16.0-rc7-abb+g64aa90f #8 [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (warn_slowpath_common+0x6c/0x8c) [] (warn_slowpath_common) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null) from [] (chandef_to_chanspec.isra.11+0x158/0x184) [] (chandef_to_chanspec.isra.11) from [] (brcmf_cfg80211_start_ap+0x1e4/0x614) [] (brcmf_cfg80211_start_ap) from [] (nl80211_start_ap+0x288/0x414) [] (nl80211_start_ap) from [] (genl_rcv_msg+0x21c/0x38c) [] (genl_rcv_msg) from [] (netlink_rcv_skb+0xac/0xc0) [] (netlink_rcv_skb) from [] (genl_rcv+0x20/0x34) [] (genl_rcv) from [] (netlink_unicast+0x150/0x20c) [] (netlink_unicast) from [] (netlink_sendmsg+0x2b8/0x398) [] (netlink_sendmsg) from [] (sock_sendmsg+0x84/0xa8) [] (sock_sendmsg) from [] (___sys_sendmsg.part.29+0x268/0x278) [] (___sys_sendmsg.part.29) from [] (__sys_sendmsg+0x4c/0x7c) [] (__sys_sendmsg) from [] (ret_fast_syscall+0x0/0x44) ---[ end trace 965ee2158c9905a2 ]--- Cc: stable@vger.kernel.org # v3.17 Reported-by: Pontus Fuchs Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 4843fbe6d5efe2..6b9c99f9bc16d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -299,6 +299,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, primary_offset = ch->center_freq1 - ch->chan->center_freq; switch (ch->width) { case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: ch_inf.bw = BRCMU_CHAN_BW_20; WARN_ON(primary_offset != 0); break; @@ -323,6 +324,10 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, ch_inf.sb = BRCMU_CHAN_SB_LU; } break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: default: WARN_ON_ONCE(1); } @@ -333,6 +338,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, case IEEE80211_BAND_5GHZ: ch_inf.band = BRCMU_CHAN_BAND_5G; break; + case IEEE80211_BAND_60GHZ: default: WARN_ON_ONCE(1); } From 49b68cf8fe7bd538746c739daec20d0e727d7b74 Mon Sep 17 00:00:00 2001 From: Mathy Vanhoef Date: Wed, 12 Nov 2014 21:33:34 -0500 Subject: [PATCH 0211/1983] brcmfmac: kill URB when request timed out Upstream-commit: 8180bd47b043507568056f74f69b6a5abea26514 Kill the submitted URB in brcmf_usb_dl_cmd if the request timed out. This assures the URB is never submitted twice. It also prevents a possible use-after-free of the URB transfer buffer if a timeout occurs. Signed-off-by: Mathy Vanhoef Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 5265aa70b094f3..4572defc280fa4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -738,10 +738,12 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, goto finalize; } - if (!brcmf_usb_ioctl_resp_wait(devinfo)) + if (!brcmf_usb_ioctl_resp_wait(devinfo)) { + usb_kill_urb(devinfo->ctl_urb); ret = -ETIMEDOUT; - else + } else { memcpy(buffer, tmpbuf, buflen); + } finalize: kfree(tmpbuf); From 3eb0fe7338960e5881dc1fbcf317092a1c286a47 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Nov 2014 14:12:21 -0800 Subject: [PATCH 0212/1983] brcmfmac: fix error handling of irq_of_parse_and_map Upstream-commit: 4c69f05eaa428e37890daf88b86a567ce615570b Return value of irq_of_parse_and_map() is unsigned int, with 0 indicating failure, so testing for negative result never works. Signed-off-by: Dmitry Torokhov Cc: stable@vger.kernel.org # v3.17 Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/of.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index eb3fce82a22316..c824570ddea320 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -40,8 +40,8 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) return; irq = irq_of_parse_and_map(np, 0); - if (irq < 0) { - brcmf_err("interrupt could not be mapped: err=%d\n", irq); + if (!irq) { + brcmf_err("interrupt could not be mapped\n"); devm_kfree(dev, sdiodev->pdata); return; } From 00a2cfca3ef105acd7111538bb8c7fa9c0eac28b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Nov 2014 22:13:10 +0100 Subject: [PATCH 0213/1983] brcmfmac: don't include linux/unaligned/access_ok.h Upstream-commit: a1d69c60c44134f64945bbf6a6dfda22eaf4a214 This is a specific implementation, is the multiplexer that has the arch-specific knowledge of which of the implementations needs to be used, so include that. This issue was revealed by kbuild testing when was added in resulting in redefinition of get_unaligned_be16 (and probably others). Cc: stable@vger.kernel.org # v3.17 Reported-by: Fengguang Wu Signed-off-by: Johannes Berg Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index b0ae7993e2e885..138691a1365af6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -19,10 +19,10 @@ #include #include #include -#include #include #include #include +#include #include #include From f4c3b8407812d53df00e9e8b69a786ba4f22290a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 20 Nov 2014 16:42:51 +0100 Subject: [PATCH 0214/1983] net: brcm80211: Deletion of unnecessary checks before two function calls Upstream-commit: ac96ce83ff21c9338afa0e81baabd902d9350015 The functions brcmu_pkt_buf_free_skb() and release_firmware() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 3 +-- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 3 +-- drivers/net/wireless/brcm80211/brcmsmac/main.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 0f157f151282eb..1ff787d1a36be9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -262,8 +262,7 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) fail: brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); - if (fwctx->code) - release_firmware(fwctx->code); + release_firmware(fwctx->code); device_release_driver(fwctx->dev); kfree(fwctx); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 02d39ce8dbcafc..9f783db34ae5f8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -518,8 +518,7 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? len : msgbuf->ioctl_resp_ret_len); } - if (skb) - brcmu_pkt_buf_free_skb(skb); + brcmu_pkt_buf_free_skb(skb); return msgbuf->ioctl_resp_status; } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 9417cb5a2553f7..47a6d42da234fe 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -1009,8 +1009,7 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) if (txh) trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); - if (p) - brcmu_pkt_buf_free_skb(p); + brcmu_pkt_buf_free_skb(p); } if (dma && queue < NFIFO) { From 2ad1b33b757dcb901b23d5ab669144d27d1ab9ab Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Nov 2014 22:26:59 +0100 Subject: [PATCH 0215/1983] brcmfmac: fix static checker warning in pmklist handling Upstream-commit: c15d789e3cfa94f90f86766bb2302dd643ef4621 The patch fixes a static checker warning: drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c:2965 brcmf_cfg80211_set_pmksa() warn: can 'pmkid_len' be negative? The answer to the question above is likely no so changing its type to unsigned is sufficient. Reported-by: Dan Carpenter Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 6b9c99f9bc16d3..e9cbfe3d07b3f0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -2926,7 +2926,7 @@ brcmf_update_pmklist(struct net_device *ndev, struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) { int i, j; - int pmkid_len; + u32 pmkid_len; pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); @@ -2954,8 +2954,7 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; s32 err = 0; - int i; - int pmkid_len; + u32 pmkid_len, i; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -2994,7 +2993,7 @@ brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct pmkid_list pmkid; s32 err = 0; - int i, pmkid_len; + u32 pmkid_len, i; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) From f62bd7150bec066324dc95d766333a77e3f5059a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Nov 2014 22:27:02 +0100 Subject: [PATCH 0216/1983] brcmfmac: correct .disconnect() callback while connecting Upstream-commit: 4f3fff148679d5850994e53c57eb798cd201c88c When the driver has sent a join iovar to the firmware it waits for the events to report result of the connection. However, the wpa_supplicant will request a .disconnect() after a timeout. So upon calling .disconnect() the interface state may still be CONNECTING. Clear the CONNECTING bit as well. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index e9cbfe3d07b3f0..898ffd869808d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -1815,6 +1815,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, return -EIO; clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); From fdf78ac79b3b432d92adb34c91124bb98deca22c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:27 +0100 Subject: [PATCH 0217/1983] brcmfmac: Fix bitmap malloc bug in msgbuf. Upstream-commit: 333c2aa029b847051a2db76a6ca59f699a520030 Cc: stable@vger.kernel.org # v3.17, v3.18 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 9f783db34ae5f8..5b0b70fa1aea84 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -1354,6 +1354,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) } INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); + count = count * sizeof(unsigned long); msgbuf->flow_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->flow_map) goto fail; From fa30b6a21b1c77ad5048e5dcee13470ba0f5991e Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:28 +0100 Subject: [PATCH 0218/1983] brcmfmac: Fix ifidx for rx data by msgbuf. Upstream-commit: 94a612086f5e78272e831c04b673778f8546ea73 The ifidx provided by FW needs to be offsetted when receiving data packets. Cc: stable@vger.kernel.org # v3.17, v3.18 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 5b0b70fa1aea84..456944a6a2db88 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -1080,8 +1080,17 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, { struct brcmf_if *ifp; + /* The ifidx is the idx to map to matching netdev/ifp. When receiving + * events this is easy because it contains the bssidx which maps + * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. + * bssidx 1 is used for p2p0 and no data can be received or + * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 + */ + if (ifidx) + (ifidx)++; ifp = msgbuf->drvr->iflist[ifidx]; if (!ifp || !ifp->ndev) { + brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); brcmu_pkt_buf_free_skb(skb); return; } From ca58dfc5498c4d64a138ef5f2c7a4b470a094a08 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:29 +0100 Subject: [PATCH 0219/1983] brcmfmac: Add PCIE ids for 43602 devices. Upstream-commit: 48fd818f008e1f1fcc630381efa3c06c6320d156 Some 43602 devices are band specific and identify themselves with different PCIE device ID. This patch adds support for the 43602 2.4G and 5.0G devices used in for example R8000 router. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 ++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 138691a1365af6..4724a743b8e88d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -1857,6 +1857,8 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index af26e0de1e5c6b..6996fcc144cfdf 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -68,6 +68,8 @@ #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 #define BRCM_PCIE_43602_DEVICE_ID 0x43ba +#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb +#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ From 5a469a4e502a63467f7d25f79a21cc007c42f5b2 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 3 Dec 2014 21:05:30 +0100 Subject: [PATCH 0220/1983] brcmfmac: switch to single message MSI Upstream-commit: e9efa340c173ccfe641c50ed102f68237f825c2c Use single message MSI to replace legacy interrupt. Reviewed-by: Arend Van Spriel Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 4724a743b8e88d..905991fdb7b101 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -798,12 +798,14 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) brcmf_dbg(PCIE, "Enter\n"); /* is it a v1 or v2 implementation */ devinfo->irq_requested = false; + pci_enable_msi(pdev); if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr_v1, brcmf_pcie_isr_thread_v1, IRQF_SHARED, "brcmf_pcie_intr", devinfo)) { + pci_disable_msi(pdev); brcmf_err("Failed to request IRQ %d\n", pdev->irq); return -EIO; } @@ -813,6 +815,7 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) brcmf_pcie_isr_thread_v2, IRQF_SHARED, "brcmf_pcie_intr", devinfo)) { + pci_disable_msi(pdev); brcmf_err("Failed to request IRQ %d\n", pdev->irq); return -EIO; } @@ -839,6 +842,7 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) return; devinfo->irq_requested = false; free_irq(pdev->irq, devinfo); + pci_disable_msi(pdev); msleep(50); count = 0; From 3bc05b3f9494a5f9b1ec4f23c260625db68788b1 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:31 +0100 Subject: [PATCH 0221/1983] brcmfmac: Fix vendor cmds used interface. Upstream-commit: d2cf344d7532eb2fa4f246ec1e29304974e6a902 The vendor specific commands was always using main interface, change this to use the by caller supplied interface. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 222f26a3964247..50cdf7090198b3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -31,8 +31,8 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; struct sk_buff *reply; int ret, payload, ret_len; @@ -42,6 +42,9 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, cmdhdr->len); + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + len -= sizeof(struct brcmf_vndr_dcmd_hdr); ret_len = cmdhdr->len; if (ret_len > 0 || len > 0) { @@ -63,11 +66,11 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, } if (cmdhdr->set) - ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd, - dcmd_buf, ret_len); + ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, + ret_len); else - ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd, - dcmd_buf, ret_len); + ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf, + ret_len); if (ret != 0) goto exit; From df08fa51e3e64bb5991866ec93538a467824cbc2 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:32 +0100 Subject: [PATCH 0222/1983] brcmfmac: Add ifidx to logging of fwil cmds. Upstream-commit: c4034f43e65466ba020a7bec5038640143465c7c Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 51f88c11e642e7..03f2c406a17bb0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -136,7 +136,7 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) mutex_lock(&ifp->drvr->proto_block); - brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -154,7 +154,7 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) mutex_lock(&ifp->drvr->proto_block); err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); - brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -171,7 +171,7 @@ brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) __le32 data_le = cpu_to_le32(data); mutex_lock(&ifp->drvr->proto_block); - brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, data); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); mutex_unlock(&ifp->drvr->proto_block); @@ -188,7 +188,7 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); mutex_unlock(&ifp->drvr->proto_block); *data = le32_to_cpu(data_le); - brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, *data); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); return err; } @@ -224,7 +224,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, mutex_lock(&drvr->proto_block); - brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); + brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -264,7 +264,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, brcmf_err("Creating iovar failed\n"); } - brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); + brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -347,7 +347,8 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, mutex_lock(&drvr->proto_block); - brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); + brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -386,7 +387,8 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, err = -EPERM; brcmf_err("Creating bsscfg failed\n"); } - brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); + brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); From 50f9fe9608f3038f512e8d17dd3334f31870b2c3 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:33 +0100 Subject: [PATCH 0223/1983] brcmfmac: add multiple BSS support. Upstream-commit: a44aa4001a86d46f936ca449e5d6c268446bfae2 This patch adds support for multiple BSS interfaces (AP). In total three AP configurations can be created. In order to use multiple BSS firmware needs to support it. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 245 +++++++++++++++--- .../wireless/brcm80211/brcmfmac/cfg80211.h | 2 + .../net/wireless/brcm80211/brcmfmac/core.c | 39 ++- .../net/wireless/brcm80211/brcmfmac/core.h | 3 +- .../net/wireless/brcm80211/brcmfmac/feature.c | 23 ++ .../net/wireless/brcm80211/brcmfmac/feature.h | 1 + .../net/wireless/brcm80211/brcmfmac/fweh.c | 6 +- .../wireless/brcm80211/brcmfmac/fwil_types.h | 6 + 8 files changed, 272 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 898ffd869808d3..b1d332b055ae59 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -520,6 +520,95 @@ brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) ADDR_INDIRECT); } +static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) +{ + struct brcmf_mbss_ssid_le mbss_ssid_le; + int bsscfgidx; + int err; + + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); + + err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + if (err < 0) + brcmf_err("setting ssid failed %d\n", err); + + return err; +} + +/** + * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * + * @wiphy: wiphy device of new interface. + * @name: name of the new interface. + * @flags: not used. + * @params: contains mac address for AP device. + */ +static +struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, + u32 *flags, struct vif_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_cfg80211_vif *vif; + int err; + + if (brcmf_cfg80211_vif_event_armed(cfg)) + return ERR_PTR(-EBUSY); + + brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false); + if (IS_ERR(vif)) + return (struct wireless_dev *)vif; + + brcmf_cfg80211_arm_vif_event(cfg, vif); + + err = brcmf_cfg80211_request_ap_if(ifp); + if (err) { + brcmf_cfg80211_arm_vif_event(cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(cfg, NULL); + if (!err) { + brcmf_err("timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* interface created in firmware */ + ifp = vif->ifp; + if (!ifp) { + brcmf_err("no if pointer provided\n"); + err = -ENOENT; + goto fail; + } + + strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); + err = brcmf_net_attach(ifp, true); + if (err) { + brcmf_err("Registering netdevice failed\n"); + goto fail; + } + + return &ifp->vif->wdev; + +fail: + brcmf_free_vif(vif); + return ERR_PTR(err); +} + static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) { enum nl80211_iftype iftype; @@ -545,12 +634,16 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, switch (type) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); + case NL80211_IFTYPE_AP: + wdev = brcmf_ap_add_vif(wiphy, name, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: @@ -3355,11 +3448,10 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) } static s32 -brcmf_configure_wpaie(struct net_device *ndev, +brcmf_configure_wpaie(struct brcmf_if *ifp, const struct brcmf_vs_tlv *wpa_ie, bool is_rsn_ie) { - struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ u16 count; s32 err = 0; @@ -3834,6 +3926,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; u16 chanspec; + bool mbss; brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", settings->chandef.chan->hw_value, @@ -3844,6 +3937,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, settings->inactivity_timeout); dev_role = ifp->vif->wdev.iftype; + mbss = ifp->vif->mbss; memset(&ssid_le, 0, sizeof(ssid_le)); if (settings->ssid == NULL || settings->ssid_len == 0) { @@ -3863,8 +3957,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); } - brcmf_set_mpc(ifp, 0); - brcmf_configure_arp_offload(ifp, false); + if (!mbss) { + brcmf_set_mpc(ifp, 0); + brcmf_configure_arp_offload(ifp, false); + } /* find the RSN_IE */ rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, @@ -3878,13 +3974,16 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "WPA(2) IE is found\n"); if (wpa_ie != NULL) { /* WPA IE */ - err = brcmf_configure_wpaie(ndev, wpa_ie, false); + err = brcmf_configure_wpaie(ifp, wpa_ie, false); if (err < 0) goto exit; } else { + struct brcmf_vs_tlv *tmp_ie; + + tmp_ie = (struct brcmf_vs_tlv *)rsn_ie; + /* RSN IE */ - err = brcmf_configure_wpaie(ndev, - (struct brcmf_vs_tlv *)rsn_ie, true); + err = brcmf_configure_wpaie(ifp, tmp_ie, true); if (err < 0) goto exit; } @@ -3895,45 +3994,53 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); - goto exit; - } - - if (settings->beacon_interval) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, - settings->beacon_interval); + if (!mbss) { + chanspec = chandef_to_chanspec(&cfg->d11inf, + &settings->chandef); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); if (err < 0) { - brcmf_err("Beacon Interval Set Error, %d\n", err); + brcmf_err("Set Channel failed: chspec=%d, %d\n", + chanspec, err); goto exit; } - } - if (settings->dtim_period) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, - settings->dtim_period); - if (err < 0) { - brcmf_err("DTIM Interval Set Error, %d\n", err); - goto exit; + + if (settings->beacon_interval) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, + settings->beacon_interval); + if (err < 0) { + brcmf_err("Beacon Interval Set Error, %d\n", + err); + goto exit; + } + } + if (settings->dtim_period) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, + settings->dtim_period); + if (err < 0) { + brcmf_err("DTIM Interval Set Error, %d\n", err); + goto exit; + } } - } - if (dev_role == NL80211_IFTYPE_AP) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (dev_role == NL80211_IFTYPE_AP) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) { + brcmf_err("BRCMF_C_DOWN error %d\n", err); + goto exit; + } + brcmf_fil_iovar_int_set(ifp, "apsta", 0); + } + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); if (err < 0) { - brcmf_err("BRCMF_C_DOWN error %d\n", err); + brcmf_err("SET INFRA error %d\n", err); goto exit; } - brcmf_fil_iovar_int_set(ifp, "apsta", 0); - } - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); - if (err < 0) { - brcmf_err("SET INFRA error %d\n", err); - goto exit; } if (dev_role == NL80211_IFTYPE_AP) { + if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) + brcmf_fil_iovar_int_set(ifp, "mbss", 1); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); if (err < 0) { brcmf_err("setting AP mode failed %d\n", err); @@ -3978,7 +4085,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); exit: - if (err) { + if ((err) && (!mbss)) { brcmf_set_mpc(ifp, 1); brcmf_configure_arp_offload(ifp, true); } @@ -3999,20 +4106,31 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) /* first to make sure they get processed by fw. */ msleep(400); + if (ifp->vif->mbss) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + return err; + } + memset(&join_params, 0, sizeof(join_params)); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, &join_params, sizeof(join_params)); if (err < 0) brcmf_err("SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); if (err < 0) - brcmf_err("BRCMF_C_UP error %d\n", err); + brcmf_err("BRCMF_C_DOWN error %d\n", err); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); if (err < 0) brcmf_err("setting AP mode failed %d\n", err); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); if (err < 0) brcmf_err("setting INFRA mode failed %d\n", err); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) + brcmf_fil_iovar_int_set(ifp, "mbss", 0); + /* Bring device back up so it can be used again */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err < 0) + brcmf_err("BRCMF_C_UP error %d\n", err); } else { bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); bss_enable.enable = cpu_to_le32(0); @@ -4364,7 +4482,9 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype type, bool pm_block) { + struct brcmf_cfg80211_vif *vif_walk; struct brcmf_cfg80211_vif *vif; + bool mbss; brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", sizeof(*vif)); @@ -4380,6 +4500,17 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, brcmf_init_prof(&vif->profile); + if (type == NL80211_IFTYPE_AP) { + mbss = false; + list_for_each_entry(vif_walk, &cfg->vif_list, list) { + if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { + mbss = true; + break; + } + } + vif->mbss = mbss; + } + list_add_tail(&vif->list, &cfg->vif_list); return vif; } @@ -4622,6 +4753,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { + struct brcmf_if *ifp = netdev_priv(ndev); static int generation; u32 event = e->event_code; u32 reason = e->reason; @@ -4632,6 +4764,8 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, ndev != cfg_to_ndev(cfg)) { brcmf_dbg(CONN, "AP mode link down\n"); complete(&cfg->vif_disabled); + if (ifp->vif->mbss) + brcmf_remove_interface(ifp->drvr, ifp->bssidx); return 0; } @@ -5423,7 +5557,28 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy) return 0; } -static const struct ieee80211_iface_limit brcmf_iface_limits[] = { +static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) + }, + { + .max = 4, + .types = BIT(NL80211_IFTYPE_AP) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + } +}; + +static const struct ieee80211_iface_limit brcmf_iface_limits_sbss[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | @@ -5444,8 +5599,8 @@ static struct ieee80211_iface_combination brcmf_iface_combos[] = { { .max_interfaces = BRCMF_IFACE_MAX_CNT, .num_different_channels = 1, - .n_limits = ARRAY_SIZE(brcmf_iface_limits), - .limits = brcmf_iface_limits + .n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss), + .limits = brcmf_iface_limits_sbss, } }; @@ -5521,6 +5676,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) ifc_combo = brcmf_iface_combos[0]; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) ifc_combo.num_different_channels = 2; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) { + ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss), + ifc_combo.limits = brcmf_iface_limits_mbss; + } wiphy->iface_combinations = kmemdup(&ifc_combo, sizeof(ifc_combo), GFP_KERNEL); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index 6abf94e41d3d06..b50b751b0a3cd5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -183,6 +183,7 @@ struct vif_saved_ie { * @pm_block: power-management blocked. * @list: linked list. * @mgmt_rx_reg: registered rx mgmt frame types. + * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P). */ struct brcmf_cfg80211_vif { struct brcmf_if *ifp; @@ -194,6 +195,7 @@ struct brcmf_cfg80211_vif { struct vif_saved_ie saved_ie; struct list_head list; u16 mgmt_rx_reg; + bool mbss; }; /* association inform */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 3133a09269a979..3771af6046656a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -835,7 +835,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, return ifp; } -void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) +static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) { struct brcmf_if *ifp; @@ -868,6 +868,38 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) } } +void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx) +{ + if (drvr->iflist[bssidx]) { + brcmf_fws_del_interface(drvr->iflist[bssidx]); + brcmf_del_if(drvr, bssidx); + } +} + +int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) +{ + int ifidx; + int bsscfgidx; + bool available; + int highest; + + available = false; + bsscfgidx = 2; + highest = 2; + for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { + if (drvr->iflist[ifidx]) { + if (drvr->iflist[ifidx]->bssidx == bsscfgidx) + bsscfgidx = highest + 1; + else if (drvr->iflist[ifidx]->bssidx > highest) + highest = drvr->iflist[ifidx]->bssidx; + } else { + available = true; + } + } + + return available ? bsscfgidx : -ENOMEM; +} + int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; @@ -1032,10 +1064,7 @@ void brcmf_detach(struct device *dev) /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) - if (drvr->iflist[i]) { - brcmf_fws_del_interface(drvr->iflist[i]); - brcmf_del_if(drvr, i); - } + brcmf_remove_interface(drvr, i); brcmf_cfg80211_detach(drvr->config); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 40cb539f1f50f6..2e033b3e5b46d6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -175,7 +175,8 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, char *name, u8 *mac_addr); -void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); +void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx); +int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 931f68aefaa476..defb7a44e0bc1f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -97,6 +97,28 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, } } +/** + * brcmf_feat_iovar_int_set() - determine feature through iovar set. + * + * @ifp: interface to query. + * @id: feature id. + * @name: iovar name. + */ +static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, + enum brcmf_feat_id id, char *name, u32 val) +{ + int err; + + err = brcmf_fil_iovar_int_set(ifp, name, val); + if (err == 0) { + brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } else { + brcmf_dbg(TRACE, "%s feature check failed: %d\n", + brcmf_feat_names[id], err); + } +} + void brcmf_feat_attach(struct brcmf_pub *drvr) { struct brcmf_if *ifp = drvr->iflist[0]; @@ -104,6 +126,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); + brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index b9a796d0a44d9d..f5832e077bb799 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -22,6 +22,7 @@ * MCHAN: multi-channel for concurrent P2P. */ #define BRCMF_FEAT_LIST \ + BRCMF_FEAT_DEF(MBSS) \ BRCMF_FEAT_DEF(MCHAN) \ BRCMF_FEAT_DEF(WOWL) /* diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 7338b335e153ea..ec62492ffa69fd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -221,10 +221,8 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); - if (ifp && ifevent->action == BRCMF_E_IF_DEL) { - brcmf_fws_del_interface(ifp); - brcmf_del_if(drvr, ifevent->bssidx); - } + if (ifp && ifevent->action == BRCMF_E_IF_DEL) + brcmf_remove_interface(drvr, ifevent->bssidx); } /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index ba64b292f7a516..50891c02c4c162 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -519,4 +519,10 @@ struct brcmf_fil_wowl_pattern_le { /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ }; +struct brcmf_mbss_ssid_le { + __le32 bsscfgidx; + __le32 SSID_len; + unsigned char SSID[32]; +}; + #endif /* FWIL_TYPES_H_ */ From ba9c3c1ed71c6029bb0b4d5a182ccb1064251b0b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 23 Dec 2014 16:48:32 +0200 Subject: [PATCH 0224/1983] brcmfmac: Do not crash if platform data is not populated Upstream-commit: 8975842bed0840f314281c9fbf021a1d29537cf0 The driver looks for pdata->oob_irq_supported to find out if wowl can be supported. However, not all platforms populate pdata in which case we crash the kernel because of NULL pointer dereference. Fixes: 330b4e4be937 ("brcmfmac: Add wowl support for SDIO devices.") Reported-by: Christophe Prigent Signed-off-by: Mika Westerberg Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 3c06e9365949a0..9880dae2a56994 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1070,7 +1070,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, */ if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || - (sdiodev->pdata->oob_irq_supported))) + (sdiodev->pdata && sdiodev->pdata->oob_irq_supported))) bus_if->wowl_supported = true; #endif @@ -1167,7 +1167,7 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(SDIO, "Enter\n"); - if (sdiodev->pdata->oob_irq_supported) + if (sdiodev->pdata && sdiodev->pdata->oob_irq_supported) disable_irq_wake(sdiodev->pdata->oob_irq_nr); brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); atomic_set(&sdiodev->suspend, false); From c6b53493f80a6f5080a4cd8c5494f8cd016ca411 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:44 +0100 Subject: [PATCH 0225/1983] brcmfmac: Fix incorrect casting of 64 bit physical address. Upstream-commit: 83297aaa8fde85154c91a59e522c3d042c1084ec The physical addresses being used by pcie and msgbuf were using a cast to long, which incorrectly caused it to limit the address to 32bit. Now explicit u64 is used where needed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/msgbuf.c | 22 +++++++++---------- .../net/wireless/brcm80211/brcmfmac/pcie.c | 10 ++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 456944a6a2db88..3ff6acfd82a74a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -583,7 +583,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, u32 flowid; void *dma_buf; u32 dma_sz; - long long address; + u64 address; int err; flowid = work->flowid; @@ -620,7 +620,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, BRCMF_NROF_H2D_COMMON_MSGRINGS); memcpy(create->sa, work->sa, ETH_ALEN); memcpy(create->da, work->da, ETH_ALEN); - address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; + address = (u64)msgbuf->flowring_dma_handle[flowid]; create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); @@ -698,7 +698,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) dma_addr_t physaddr; u32 pktid; struct msgbuf_tx_msghdr *tx_msghdr; - long long address; + u64 address; commonring = msgbuf->flowrings[flowid]; if (!brcmf_commonring_write_available(commonring)) @@ -742,7 +742,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) tx_msghdr->seg_cnt = 1; memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); - address = (long long)(long)physaddr; + address = (u64)physaddr; tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); tx_msghdr->data_buf_addr.low_addr = cpu_to_le32(address & 0xffffffff); @@ -885,7 +885,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) u32 pktlen; dma_addr_t physaddr; struct msgbuf_rx_bufpost *rx_bufpost; - long long address; + u64 address; u32 pktid; u32 i; @@ -921,7 +921,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) } if (msgbuf->rx_metadata_offset) { - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->metadata_buf_len = cpu_to_le16(msgbuf->rx_metadata_offset); rx_bufpost->metadata_buf_addr.high_addr = @@ -936,7 +936,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; rx_bufpost->msg.request_id = cpu_to_le32(pktid); - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); rx_bufpost->data_buf_addr.high_addr = cpu_to_le32(address >> 32); @@ -992,7 +992,7 @@ brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, u32 pktlen; dma_addr_t physaddr; struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; - long long address; + u64 address; u32 pktid; u32 i; @@ -1035,7 +1035,7 @@ brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, MSGBUF_TYPE_IOCTLRESP_BUF_POST; rx_bufpost->msg.request_id = cpu_to_le32(pktid); - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); rx_bufpost->host_buf_addr.high_addr = cpu_to_le32(address >> 32); @@ -1348,7 +1348,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) { struct brcmf_bus_msgbuf *if_msgbuf; struct brcmf_msgbuf *msgbuf; - long long address; + u64 address; u32 count; if_msgbuf = drvr->bus_if->msgbuf; @@ -1379,7 +1379,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) GFP_KERNEL); if (!msgbuf->ioctbuf) goto fail; - address = (long long)(long)msgbuf->ioctbuf_handle; + address = (u64)msgbuf->ioctbuf_handle; msgbuf->ioctbuf_phys_hi = address >> 32; msgbuf->ioctbuf_phys_lo = address & 0xffffffff; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 905991fdb7b101..e91fa9a2c8855e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -959,14 +959,14 @@ brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, dma_addr_t *dma_handle) { void *ring; - long long address; + u64 address; ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, GFP_KERNEL); if (!ring) return NULL; - address = (long long)(long)*dma_handle; + address = (u64)*dma_handle; brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); @@ -1166,7 +1166,7 @@ brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - long long address; + u64 address; u32 addr; devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, @@ -1180,7 +1180,7 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; - address = (long long)(long)devinfo->shared.scratch_dmahandle; + address = (u64)devinfo->shared.scratch_dmahandle; brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); addr = devinfo->shared.tcm_base_address + @@ -1198,7 +1198,7 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; - address = (long long)(long)devinfo->shared.ringupd_dmahandle; + address = (u64)devinfo->shared.ringupd_dmahandle; brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); addr = devinfo->shared.tcm_base_address + From 1f77e4dfc5cfe1f601aa14371d0587eac02f62fa Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 21 Dec 2014 12:43:48 +0100 Subject: [PATCH 0226/1983] brcmfmac: remove unused/duplicate defines in chip.c Upstream-commit: f714e58e19a567cc2659ea4645748ba06e3849a7 The source file chip.c contained some duplicate defines and some unused ones. Removing them. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index ddae0b5e56eced..519b79ebaabd93 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -101,14 +101,7 @@ /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 #define BCM4329_RAMSIZE 0x48000 - /* bcm43143 */ -/* SDIO device core */ -#define BCM43143_CORE_BUS_BASE 0x18002000 -/* internal memory core */ -#define BCM43143_CORE_SOCRAM_BASE 0x18004000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM43143_CORE_ARM_BASE 0x18003000 #define BCM43143_RAMSIZE 0x70000 #define CORE_SB(base, field) \ @@ -164,13 +157,6 @@ struct brcmf_core_priv { struct brcmf_chip_priv *chip; }; -/* ARM CR4 core specific control flag bits */ -#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 - -/* D11 core specific control flag bits */ -#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 -#define D11_BCMA_IOCTL_PHYRESET 0x0008 - struct brcmf_chip_priv { struct brcmf_chip pub; const struct brcmf_buscore_ops *ops; From 4b9562a858382e31eb759a8cab9e579320c9b6ad Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:49 +0100 Subject: [PATCH 0227/1983] brcmfmac: Fix WEP configuration for AP mode. Upstream-commit: 118eb304d0554fbeab8567410bbece5bae533c23 When a device is configured for AP mode and it is configured for WEP then the keys are plumbed first, followed by AP configuration. During configuration a down command is given to the firmware which will clear the configured keys. This patch reprograms the WEP keys after AP has been brought up. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 95 ++++++++++++++----- .../wireless/brcm80211/brcmfmac/cfg80211.h | 4 + .../net/wireless/brcm80211/brcmfmac/core.c | 3 +- .../net/wireless/brcm80211/brcmfmac/core.h | 4 +- 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index b1d332b055ae59..6ef64a8e0c666f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -452,16 +452,16 @@ static void convert_key_from_CPU(struct brcmf_wsec_key *key, } static int -send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key) +send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) { int err; struct brcmf_wsec_key_le key_le; convert_key_from_CPU(key, &key_le); - brcmf_netdev_wait_pend8021x(ndev); + brcmf_netdev_wait_pend8021x(ifp); - err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le, + err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le, sizeof(key_le)); if (err) @@ -1670,7 +1670,7 @@ brcmf_set_sharedkey(struct net_device *ndev, brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", key.len, key.index, key.algo); brcmf_dbg(CONN, "key \"%s\"\n", key.data); - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(netdev_priv(ndev), &key); if (err) return err; @@ -2052,7 +2052,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, /* check for key index change */ if (key.len == 0) { /* key delete */ - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); if (err) brcmf_err("key delete error (%d)\n", err); } else { @@ -2108,7 +2108,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("Invalid cipher (0x%x)\n", params->cipher); return -EINVAL; } - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); if (err) brcmf_err("wsec_key error (%d)\n", err); } @@ -2121,7 +2121,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, struct key_params *params) { struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; + struct brcmf_wsec_key *key; s32 val; s32 wsec; s32 err = 0; @@ -2132,54 +2132,62 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + brcmf_err("invalid key index (%d)\n", key_idx); + return -EINVAL; + } + if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) && (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { brcmf_dbg(TRACE, "Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } - memset(&key, 0, sizeof(key)); - key.len = (u32) params->key_len; - key.index = (u32) key_idx; + key = &ifp->vif->profile.key[key_idx]; + memset(key, 0, sizeof(*key)); - if (key.len > sizeof(key.data)) { - brcmf_err("Too long key length (%u)\n", key.len); + if (params->key_len > sizeof(key->data)) { + brcmf_err("Too long key length (%u)\n", params->key_len); err = -EINVAL; goto done; } - memcpy(key.data, params->key, key.len); + key->len = params->key_len; + key->index = key_idx; - key.flags = BRCMF_PRIMARY_KEY; + memcpy(key->data, params->key, key->len); + + key->flags = BRCMF_PRIMARY_KEY; switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; + key->algo = CRYPTO_ALGO_WEP1; val = WEP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); break; case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; + key->algo = CRYPTO_ALGO_WEP128; val = WEP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: if (!brcmf_is_apmode(ifp->vif)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key.data[24], sizeof(keybuf)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); + memcpy(keybuf, &key->data[24], sizeof(keybuf)); + memcpy(&key->data[24], &key->data[16], sizeof(keybuf)); + memcpy(&key->data[16], keybuf, sizeof(keybuf)); } - key.algo = CRYPTO_ALGO_TKIP; + key->algo = CRYPTO_ALGO_TKIP; val = TKIP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); break; case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; + key->algo = CRYPTO_ALGO_AES_CCM; val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); break; case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; + key->algo = CRYPTO_ALGO_AES_CCM; val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); break; @@ -2189,7 +2197,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, key); if (err) goto done; @@ -2222,7 +2230,7 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (key_idx >= DOT11_MAX_DEFAULT_KEYS) { + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { /* we ignore this key index in this case */ brcmf_err("invalid key index (%d)\n", key_idx); return -EINVAL; @@ -2237,7 +2245,7 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "key index (%d)\n", key_idx); /* Set the new key/index */ - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -2305,6 +2313,39 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, return -EOPNOTSUPP; } +static void +brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp) +{ + s32 err; + u8 key_idx; + struct brcmf_wsec_key *key; + s32 wsec; + + for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) { + key = &ifp->vif->profile.key[key_idx]; + if ((key->algo == CRYPTO_ALGO_WEP1) || + (key->algo == CRYPTO_ALGO_WEP128)) + break; + } + if (key_idx == BRCMF_MAX_DEFAULT_KEYS) + return; + + err = send_key_to_dongle(ifp, key); + if (err) { + brcmf_err("Setting WEP key failed (%d)\n", err); + return; + } + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("get wsec error (%d)\n", err); + return; + } + wsec |= WEP_ENABLED; + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err) + brcmf_err("set wsec error (%d)\n", err); +} + static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, u8 *mac, struct station_info *sinfo) @@ -4051,6 +4092,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("BRCMF_C_UP error (%d)\n", err); goto exit; } + /* On DOWN the firmware removes the WEP keys, reconfigure + * them if they were set. + */ + brcmf_cfg80211_reconfigure_wep(ifp); memset(&join_params, 0, sizeof(join_params)); /* join parameters starts with ssid */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index b50b751b0a3cd5..a21e5a90d395e8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -75,6 +75,8 @@ #define BRCMF_VNDR_IE_P2PAF_SHIFT 12 +#define BRCMF_MAX_DEFAULT_KEYS 4 + /** * enum brcmf_scan_status - scan engine status @@ -125,11 +127,13 @@ struct brcmf_cfg80211_security { * @ssid: ssid of associated/associating ap. * @bssid: bssid of joined/joining ibss. * @sec: security information. + * @key: key information */ struct brcmf_cfg80211_profile { struct brcmf_ssid ssid; u8 bssid[ETH_ALEN]; struct brcmf_cfg80211_security sec; + struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS]; }; /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 3771af6046656a..bc69c03a0ef0f6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -1092,9 +1092,8 @@ static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) return atomic_read(&ifp->pend_8021x_cnt); } -int brcmf_netdev_wait_pend8021x(struct net_device *ndev) +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) { - struct brcmf_if *ifp = netdev_priv(ndev); int err; err = wait_event_timeout(ifp->pend_8021x_wait, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 2e033b3e5b46d6..c607a7ba6cdc67 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -29,8 +29,6 @@ /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 -#define DOT11_MAX_DEFAULT_KEYS 4 - /* Small, medium and maximum buffer size for dcmd */ #define BRCMF_DCMD_SMLEN 256 @@ -167,7 +165,7 @@ struct brcmf_skb_reorder_data { u8 *reorder; }; -int brcmf_netdev_wait_pend8021x(struct net_device *ndev); +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); From 96abf419541a40a9818a1819fb6fe7dbc523d1f0 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:50 +0100 Subject: [PATCH 0228/1983] brcmfmac: Change error log in standard log for rxbufpost. Upstream-commit: d2e2472cd197e32f2c90beb016066b521ce68049 When there is no room in the ring for rxbufpost an error is logged, however this happens quite frequently and can be considered normal and is certainly recoverable. This patch changes the erorr into a normal msgbuf log. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 3ff6acfd82a74a..ee147f5c706a3c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -894,7 +894,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) count, &alloced); if (!ret_ptr) { - brcmf_err("Failed to reserve space in commonring\n"); + brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n"); return 0; } From cf977e42300ad50e95ed53a34a0e75e4fbc6a889 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 21 Dec 2014 12:43:51 +0100 Subject: [PATCH 0229/1983] brcmfmac: follow user-space regulatory domain selection Upstream-commit: 63db1a499cee154826d63a7d0eb096fa666ab9fb When user-space uses a valid ISO-3166-1 country code for its regulatory domain selection, the driver will try to configure the firmware to use the same. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 25 ++++++++++++++++++- .../wireless/brcm80211/brcmfmac/fwil_types.h | 14 +++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 6ef64a8e0c666f..a415d23a6a688b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -3976,7 +3976,6 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); - dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; @@ -5914,6 +5913,29 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_fil_country_le ccreq; + int i; + + brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, + req->alpha2[0], req->alpha2[1]); + + /* ignore non-ISO3166 country codes */ + for (i = 0; i < sizeof(req->alpha2); i++) + if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { + brcmf_err("not a ISO3166 code\n"); + return; + } + memset(&ccreq, 0, sizeof(ccreq)); + ccreq.rev = cpu_to_le32(-1); + memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); + brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); +} + static void brcmf_free_wiphy(struct wiphy *wiphy) { kfree(wiphy->iface_combinations); @@ -5990,6 +6012,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto priv_out; brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->reg_notifier = brcmf_cfg80211_reg_notifier; wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 50891c02c4c162..619669bbdb83b6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -112,6 +112,7 @@ #define BRCMF_WOWL_MAXPATTERNS 8 #define BRCMF_WOWL_MAXPATTERNSIZE 128 +#define BRCMF_COUNTRY_BUF_SZ 4 /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { @@ -525,4 +526,17 @@ struct brcmf_mbss_ssid_le { unsigned char SSID[32]; }; +/** + * struct brcmf_fil_country_le - country configuration structure. + * + * @country_abbrev: null-terminated country code used in the country IE. + * @rev: revision specifier for ccode. on set, -1 indicates unspecified. + * @ccode: null-terminated built-in country code. + */ +struct brcmf_fil_country_le { + char country_abbrev[BRCMF_COUNTRY_BUF_SZ]; + __le32 rev; + char ccode[BRCMF_COUNTRY_BUF_SZ]; +}; + #endif /* FWIL_TYPES_H_ */ From 28309d2a3581bcb2d180b9f9507bd184e4e58d01 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 21 Dec 2014 12:43:52 +0100 Subject: [PATCH 0230/1983] brcmfmac: signal completion of 802.1x. Upstream-commit: 6b89dcb35bfc787ce21401417f7170997d8490ed Use cfg80211 change_station to signal the completion of 802.1x to firmware. This allows FW to take appropriate actions. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 30 +++++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/common.c | 3 ++ .../net/wireless/brcm80211/brcmfmac/common.h | 20 +++++++++++++ .../wireless/brcm80211/brcmfmac/flowring.c | 6 ++-- .../net/wireless/brcm80211/brcmfmac/fwil.h | 2 ++ 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/common.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index a415d23a6a688b..712a767dd38a5b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -38,6 +38,7 @@ #include "proto.h" #include "vendor.h" #include "bus.h" +#include "common.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -4235,6 +4236,34 @@ brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, return err; } +static int +brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, + const u8 *mac, struct station_parameters *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac, + params->sta_flags_mask, params->sta_flags_set); + + /* Ignore all 00 MAC */ + if (is_zero_ether_addr(mac)) + return 0; + + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) + return 0; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE, + (void *)mac, ETH_ALEN); + else + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE, + (void *)mac, ETH_ALEN); + if (err < 0) + brcmf_err("Setting SCB (de-)authorize failed, %d\n", err); + + return err; +} static void brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, @@ -4509,6 +4538,7 @@ static struct cfg80211_ops wl_cfg80211_ops = { .stop_ap = brcmf_cfg80211_stop_ap, .change_beacon = brcmf_cfg80211_change_beacon, .del_station = brcmf_cfg80211_del_station, + .change_station = brcmf_cfg80211_change_station, .sched_scan_start = brcmf_cfg80211_sched_scan_start, .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 1861a13e8d03d6..ddf05af13d4465 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -25,6 +25,9 @@ #include "fwil.h" #include "fwil_types.h" #include "tracepoint.h" +#include "common.h" + +const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define BRCMF_DEFAULT_BCN_TIMEOUT 3 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h new file mode 100644 index 00000000000000..0d39d80cee2879 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef BRCMFMAC_COMMON_H +#define BRCMFMAC_COMMON_H + +extern const u8 ALLFFMAC[ETH_ALEN]; + +#endif /* BRCMFMAC_COMMON_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 44f3a84d1999c6..910fbb561469e8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -25,6 +25,7 @@ #include "proto.h" #include "flowring.h" #include "msgbuf.h" +#include "common.h" #define BRCMF_FLOWRING_HIGH 1024 @@ -34,9 +35,6 @@ #define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) -static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; -static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - static const u8 brcmf_flowring_prio2fifo[] = { 1, 0, @@ -137,7 +135,7 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], hash = flow->hash; for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && - (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) { + (is_zero_ether_addr(hash[hash_idx].mac))) { found = true; break; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index a30be683f4a15e..3ede91d11ad27d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -60,6 +60,8 @@ #define BRCMF_C_GET_CURR_RATESET 114 #define BRCMF_C_GET_AP 117 #define BRCMF_C_SET_AP 118 +#define BRCMF_C_SET_SCB_AUTHORIZE 121 +#define BRCMF_C_SET_SCB_DEAUTHORIZE 122 #define BRCMF_C_GET_RSSI 127 #define BRCMF_C_GET_WSEC 133 #define BRCMF_C_SET_WSEC 134 From 52a608251a87a5cf5d9275d2b7b98acfbc9a8f51 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 21 Dec 2014 12:43:53 +0100 Subject: [PATCH 0231/1983] brcmfmac: enable 802.11d support in firmware Upstream-commit: 98027769828f772c7ce69b6e58d37b78ebe8ab28 When the driver gets beacon info containing a country IE from user-space upon .start_ap() callback, it will enable regulatory 802.11d support. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 26 +++++++++++++++++++ .../wireless/brcm80211/brcmfmac/cfg80211.h | 1 + .../net/wireless/brcm80211/brcmfmac/fwil.h | 2 ++ 3 files changed, 29 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 712a767dd38a5b..aa121135523ee9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -3960,6 +3960,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); const struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *country_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; const struct brcmf_tlv *rsn_ie; @@ -3969,6 +3970,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_fil_bss_enable_le bss_enable; u16 chanspec; bool mbss; + int is_11d; brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", settings->chandef.chan->hw_value, @@ -3980,6 +3982,13 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; + /* store current 11d setting */ + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d); + country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, + settings->beacon.tail_len, + WLAN_EID_COUNTRY); + is_11d = country_ie ? 1 : 0; + memset(&ssid_le, 0, sizeof(ssid_le)); if (settings->ssid == NULL || settings->ssid_len == 0) { ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; @@ -4045,6 +4054,14 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, goto exit; } + if (is_11d != ifp->vif->is_11d) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + is_11d); + if (err < 0) { + brcmf_err("Regulatory Set Error, %d\n", err); + goto exit; + } + } if (settings->beacon_interval) { err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, settings->beacon_interval); @@ -4077,6 +4094,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("SET INFRA error %d\n", err); goto exit; } + } else if (WARN_ON(is_11d != ifp->vif->is_11d)) { + /* Multiple-BSS should use same 11d configuration */ + err = -EINVAL; + goto exit; } if (dev_role == NL80211_IFTYPE_AP) { if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) @@ -4172,6 +4193,11 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) brcmf_err("setting INFRA mode failed %d\n", err); if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + ifp->vif->is_11d); + if (err < 0) + brcmf_err("restoring REGULATORY setting failed %d\n", + err); /* Bring device back up so it can be used again */ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index a21e5a90d395e8..36eca510e08596 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -200,6 +200,7 @@ struct brcmf_cfg80211_vif { struct list_head list; u16 mgmt_rx_reg; bool mbss; + int is_11d; }; /* association inform */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 3ede91d11ad27d..37345e7b873d0a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -43,6 +43,8 @@ #define BRCMF_C_SET_RADIO 38 #define BRCMF_C_GET_PHYTYPE 39 #define BRCMF_C_SET_KEY 45 +#define BRCMF_C_GET_REGULATORY 46 +#define BRCMF_C_SET_REGULATORY 47 #define BRCMF_C_SET_PASSIVE_SCAN 49 #define BRCMF_C_SCAN 50 #define BRCMF_C_SCAN_RESULTS 51 From 00b02cd3965db78d36bbe0bcfef08438b1eff741 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 23 Dec 2014 19:04:23 +0100 Subject: [PATCH 0232/1983] brcmfmac: Add support for bcm43340/1 wireless chipsets Upstream-commit: 8b3a38daff6f50027039d6979b9eb026907508eb This patch adds support for the bcm43340 and bcm43341 wireless chipsets. These two chipsets are identical from wireless parts perspective. As such they use the same firmware image. Cc: Samuel Ortiz Cc: Rob Herring Signed-off-by: John Stultz [arend@broadcom.com: squash to single commit, remove 43341 chipid] Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 ++ drivers/net/wireless/brcm80211/brcmfmac/chip.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 +++++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 3 +++ include/linux/mmc/sdio_ids.h | 6 ++++-- 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9880dae2a56994..dffd9e44f5b6b2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1005,6 +1005,8 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43340_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43341_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 519b79ebaabd93..04d2ca0d87d60b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -481,6 +481,7 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) ci->pub.ramsize = 0x48000; break; case BRCM_CC_4334_CHIP_ID: + case BRCM_CC_43340_CHIP_ID: ci->pub.ramsize = 0x80000; break; case BRCM_CC_4335_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index c3c83f9d3945e3..4887f22e62add2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -608,6 +608,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" #define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" #define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin" +#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt" #define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" #define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" #define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" @@ -629,6 +631,8 @@ MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4330_NVRAM_NAME); MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43340_NVRAM_NAME); MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4335_NVRAM_NAME); MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); @@ -660,6 +664,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) }, { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 6996fcc144cfdf..00215efbc13b29 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -34,6 +34,7 @@ #define BRCM_CC_4329_CHIP_ID 0x4329 #define BRCM_CC_4330_CHIP_ID 0x4330 #define BRCM_CC_4334_CHIP_ID 0x4334 +#define BRCM_CC_43340_CHIP_ID 43340 #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 @@ -51,6 +52,8 @@ #define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID #define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID #define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID +#define BRCM_SDIO_43340_DEVICE_ID BRCM_CC_43340_CHIP_ID +#define BRCM_SDIO_43341_DEVICE_ID 43341 #define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID #define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID #define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 0f01fe065424d0..99680796371677 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -24,13 +24,15 @@ * Vendors and devices. Sort key: vendor first, device next. */ #define SDIO_VENDOR_ID_BROADCOM 0x02d0 -#define SDIO_DEVICE_ID_BROADCOM_43143 43143 +#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887 #define SDIO_DEVICE_ID_BROADCOM_43241 0x4324 #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 #define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c +#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 -#define SDIO_DEVICE_ID_BROADCOM_43362 43362 +#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_VENDOR_ID_INTEL 0x0089 From 7f2bf28d49281f1994a71ed3613ccbf1be652894 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 6 Jan 2015 23:02:54 +0100 Subject: [PATCH 0233/1983] brcmfmac: get rid of duplicate SDIO device identifiers Upstream-commit: 8bd61f8d8790c5502f7fdbf49861bbea1339054f Instead of defining SDIO device identifier in brcm80211 code use the defintions in linux/mmc/sdio_ids.h directly. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 22 +++++++++---------- .../net/wireless/brcm80211/brcmfmac/sdio.c | 2 +- .../wireless/brcm80211/include/brcm_hw_ids.h | 13 ----------- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index dffd9e44f5b6b2..00ba90b894555c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -996,20 +996,20 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) } #define BRCMF_SDIO_DEVICE(dev_id) \ - {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)} + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { - BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43340_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43341_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 4887f22e62add2..6712ba6a91f26b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -3816,7 +3816,7 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) u32 val, rev; val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; if (rev >= 2) { diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 00215efbc13b29..2124a17d0bfdda 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -22,7 +22,6 @@ #define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM -#define BRCM_SDIO_VENDOR_ID_BROADCOM SDIO_VENDOR_ID_BROADCOM /* Chipcommon Core Chip IDs */ #define BRCM_CC_43143_CHIP_ID 43143 @@ -46,18 +45,6 @@ #define BRCM_CC_43570_CHIP_ID 43570 #define BRCM_CC_43602_CHIP_ID 43602 -/* SDIO Device IDs */ -#define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID -#define BRCM_SDIO_43241_DEVICE_ID BRCM_CC_43241_CHIP_ID -#define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID -#define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID -#define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID -#define BRCM_SDIO_43340_DEVICE_ID BRCM_CC_43340_CHIP_ID -#define BRCM_SDIO_43341_DEVICE_ID 43341 -#define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID -#define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID -#define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID - /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e #define BRCM_USB_43236_DEVICE_ID 0xbd17 From ea670361f43defdbc8a62ae3a2de66146bf28fd0 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Fri, 16 Jan 2015 21:36:14 +0530 Subject: [PATCH 0234/1983] brcmfmac: Use put_unaligned_le32 Upstream-commit: 362126cddb22256db1e9ab6862aa720c3f091e52 This patch introduces the use of function put_unaligned_le32. This is done using Coccinelle and semantic patch used is as follows: @a@ typedef u32, __le32, uint32_t; {u32,__le32,uint32_t} e32; identifier tmp; expression ptr; expression y,e; type T; type T; @@ - tmp = cpu_to_le32(y); <+... when != tmp ( - memcpy(ptr, (T)&tmp, \(4\|sizeof(u32)\|sizeof(__le32)\|sizeof(uint32_t)\|sizeof(e32)\)); + put_unaligned_le32(y,ptr); | - memcpy(ptr, (T)&tmp, ...); + put_unaligned_le32(y,ptr); ) ...+> ? tmp = e @@ type T; identifier a.tmp; @@ - T tmp; ...when != tmp Signed-off-by: Vaishali Thakkar Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index aa121135523ee9..2029499656af53 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -3731,17 +3731,12 @@ static u32 brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) { - __le32 iecount_le; - __le32 pktflag_le; - strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1); iebuf[VNDR_IE_CMD_LEN - 1] = '\0'; - iecount_le = cpu_to_le32(1); - memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le)); + put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]); - pktflag_le = cpu_to_le32(pktflag); - memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le)); + put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]); memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); From 7df882f07eb37a561e55956ee21db57ec9ca4330 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 25 Jan 2015 20:31:30 +0100 Subject: [PATCH 0235/1983] brcmfmac: Relax scheduling of msgbuf worker on high throughput. Upstream-commit: 5ef1e604194ee629c5d9fd6b9a4d3c424cfd2a84 On every tx the flow worker is triggered. When running high throughput data this causes an excessive amount of times the worker gets activated. This patch starts scheduling the worker more relaxed once outstanding tx has reached a certain depth. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/commonring.h | 2 ++ .../net/wireless/brcm80211/brcmfmac/msgbuf.c | 30 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h index 002336e3576404..3d404016a92eb9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h @@ -37,6 +37,8 @@ struct brcmf_commonring { unsigned long flags; bool inited; bool was_full; + + atomic_t outstanding_tx; }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index ee147f5c706a3c..6262612dec450b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -73,6 +73,8 @@ #define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 #define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 +#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 64 +#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 struct msgbuf_common_hdr { u8 msgtype; @@ -749,6 +751,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) tx_msghdr->metadata_buf_len = 0; tx_msghdr->metadata_buf_addr.high_addr = 0; tx_msghdr->metadata_buf_addr.low_addr = 0; + atomic_inc(&commonring->outstanding_tx); if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) { brcmf_commonring_write_complete(commonring); count = 0; @@ -773,10 +776,16 @@ static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) } -static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid) +static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid, + bool force) { + struct brcmf_commonring *commonring; + set_bit(flowid, msgbuf->flow_map); - queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); + commonring = msgbuf->flowrings[flowid]; + if ((force) || (atomic_read(&commonring->outstanding_tx) < + BRCMF_MSGBUF_DELAY_TXWORKER_THRS)) + queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); return 0; } @@ -797,7 +806,7 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, return -ENOMEM; } brcmf_flowring_enqueue(flow, flowid, skb); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false); return 0; } @@ -854,6 +863,7 @@ brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) static void brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) { + struct brcmf_commonring *commonring; struct msgbuf_tx_status *tx_status; u32 idx; struct sk_buff *skb; @@ -871,6 +881,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) } set_bit(flowid, msgbuf->txstatus_done_map); + commonring = msgbuf->flowrings[flowid]; + atomic_dec(&commonring->outstanding_tx); brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); } @@ -1181,7 +1193,7 @@ brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, brcmf_flowring_open(msgbuf->flow, flowid); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); } @@ -1280,8 +1292,10 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; void *buf; u32 flowid; + int qlen; buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; brcmf_msgbuf_process_rx(msgbuf, buf); @@ -1293,8 +1307,12 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev) for_each_set_bit(flowid, msgbuf->txstatus_done_map, msgbuf->nrof_flowrings) { clear_bit(flowid, msgbuf->txstatus_done_map); - if (brcmf_flowring_qlen(msgbuf->flow, flowid)) - brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + commonring = msgbuf->flowrings[flowid]; + qlen = brcmf_flowring_qlen(msgbuf->flow, flowid); + if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) || + ((qlen) && (atomic_read(&commonring->outstanding_tx) < + BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS))) + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); } return 0; From 25438542931345353e3962653d3b801acf4381f1 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 25 Jan 2015 20:31:31 +0100 Subject: [PATCH 0236/1983] brcmfmac: prevent possible deadlock on resuming SDIO device. Upstream-commit: 69d03ee0b709a282f81e9f81c1359cffe1edcd2b When the system is resumed a deadlock can occur when DPC gets entered before resume is complete. This patch fixes this by properly checking the suspend state outside the claim_host code block. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 33 ------------------- .../net/wireless/brcm80211/brcmfmac/sdio.c | 18 ++++++++++ .../net/wireless/brcm80211/brcmfmac/sdio.h | 2 -- 3 files changed, 18 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 00ba90b894555c..8ba60f6f491510 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -97,25 +97,6 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) { } -static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev) -{ - bool is_err = false; -#ifdef CONFIG_PM_SLEEP - is_err = atomic_read(&sdiodev->suspend); -#endif - return is_err; -} - -static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, - wait_queue_head_t *wq) -{ -#ifdef CONFIG_PM_SLEEP - int retry = 0; - while (atomic_read(&sdiodev->suspend) && retry++ != 30) - wait_event_timeout(*wq, false, HZ/100); -#endif -} - int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; @@ -244,10 +225,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", write, fn, addr, regsz); - brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); - if (brcmf_sdiod_pm_resume_error(sdiodev)) - return -EIO; - /* only allow byte access on F0 */ if (WARN_ON(regsz > 1 && !fn)) return -EINVAL; @@ -462,10 +439,6 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, unsigned int req_sz; int err; - brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_sdiod_pm_resume_error(sdiodev)) - return -EIO; - /* Single skb use the standard mmc interface */ req_sz = pkt->len + 3; req_sz &= (uint)~3; @@ -516,10 +489,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, if (!pktlist->qlen) return -EINVAL; - brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_sdiod_pm_resume_error(sdiodev)) - return -EIO; - target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); @@ -1077,8 +1046,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, #endif atomic_set(&sdiodev->suspend, false); - init_waitqueue_head(&sdiodev->request_word_wait); - init_waitqueue_head(&sdiodev->request_buffer_wait); brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); err = brcmf_sdiod_probe(sdiodev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 6712ba6a91f26b..c7d9750642e7f7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -2609,6 +2609,21 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) return ret; } +static int brcmf_sdio_pm_resume_wait(struct brcmf_sdio_dev *sdiodev) +{ +#ifdef CONFIG_PM_SLEEP + int retry; + + /* Wait for possible resume to complete */ + retry = 0; + while ((atomic_read(&sdiodev->suspend)) && (retry++ != 50)) + msleep(20); + if (atomic_read(&sdiodev->suspend)) + return -EIO; +#endif + return 0; +} + static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; @@ -2619,6 +2634,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); + if (brcmf_sdio_pm_resume_wait(bus->sdiodev)) + return; + sdio_claim_host(bus->sdiodev->func[1]); /* If waiting for HTAVAIL, check status */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index 262aedfeaa30a9..bdc8a7e1740dd5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -169,8 +169,6 @@ struct brcmf_sdio_dev { u32 sbwad; /* Save backplane window address */ struct brcmf_sdio *bus; atomic_t suspend; /* suspend flag */ - wait_queue_head_t request_word_wait; - wait_queue_head_t request_buffer_wait; struct device *dev; struct brcmf_bus *bus_if; struct brcmfmac_sdio_platform_data *pdata; From caaabe6a28a8109dc592838a128aa44aad61cf6f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 25 Jan 2015 20:31:32 +0100 Subject: [PATCH 0237/1983] brcmfmac: use SDIO DPC for control frames. Upstream-commit: 4dd8b26a40acf8450835d27bf922be145f7bd206 Control frames are normally handled outside DPC, but sometimes within DPC. To simplify code always handle control within DPC. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/sdio.c | 87 +++++++------------ 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index c7d9750642e7f7..e8146cbf53ea2f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -44,7 +44,8 @@ #include "chip.h" #include "firmware.h" -#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ +#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ +#define CTL_DONE_TIMEOUT 2000 /* In milli second */ #ifdef DEBUG @@ -495,9 +496,9 @@ struct brcmf_sdio { u8 *ctrl_frame_buf; u16 ctrl_frame_len; bool ctrl_frame_stat; + int ctrl_frame_err; spinlock_t txq_lock; /* protect bus->txq */ - struct semaphore tx_seq_lock; /* protect bus->tx_seq */ wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; @@ -2376,8 +2377,6 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { pkt_num = 1; - if (down_interruptible(&bus->tx_seq_lock)) - return cnt; if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, bus->sdiodev->txglomsz); @@ -2393,13 +2392,10 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) __skb_queue_tail(&pktq, pkt); } spin_unlock_bh(&bus->txq_lock); - if (i == 0) { - up(&bus->tx_seq_lock); + if (i == 0) break; - } ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); - up(&bus->tx_seq_lock); cnt += i; @@ -2743,17 +2739,14 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - (down_interruptible(&bus->tx_seq_lock) == 0)) { - if (data_ok(bus)) { - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, - bus->ctrl_frame_len); - sdio_release_host(bus->sdiodev->func[1]); - - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); - } - up(&bus->tx_seq_lock); + data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); + bus->ctrl_frame_err = err; + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && @@ -2965,43 +2958,30 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - int ret = -1; + int ret; brcmf_dbg(TRACE, "Enter\n"); - if (down_interruptible(&bus->tx_seq_lock)) - return -EINTR; - - if (!data_ok(bus)) { - brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", - bus->tx_max, bus->tx_seq); - up(&bus->tx_seq_lock); - /* Send from dpc */ - bus->ctrl_frame_buf = msg; - bus->ctrl_frame_len = msglen; - bus->ctrl_frame_stat = true; - - wait_event_interruptible_timeout(bus->ctrl_wait, - !bus->ctrl_frame_stat, - msecs_to_jiffies(2000)); - - if (!bus->ctrl_frame_stat) { - brcmf_dbg(SDIO, "ctrl_frame_stat == false\n"); - ret = 0; - } else { - brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); - bus->ctrl_frame_stat = false; - if (down_interruptible(&bus->tx_seq_lock)) - return -EINTR; - ret = -1; - } + /* Send from dpc */ + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; + if (atomic_read(&bus->dpc_tskcnt) == 0) { + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); } - if (ret == -1) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); - sdio_release_host(bus->sdiodev->func[1]); - up(&bus->tx_seq_lock); + + wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, + msecs_to_jiffies(CTL_DONE_TIMEOUT)); + + if (!bus->ctrl_frame_stat) { + brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", + bus->ctrl_frame_err); + ret = bus->ctrl_frame_err; + } else { + brcmf_dbg(SDIO, "ctrl_frame timeout\n"); + bus->ctrl_frame_stat = false; + ret = -ETIMEDOUT; } if (ret) @@ -3009,7 +2989,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) else bus->sdcnt.tx_ctlpkts++; - return ret ? -EIO : 0; + return ret; } #ifdef DEBUG @@ -4165,7 +4145,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) spin_lock_init(&bus->rxctl_lock); spin_lock_init(&bus->txq_lock); - sema_init(&bus->tx_seq_lock, 1); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); From b8333f1069d15773e68ebd1e779b60092bd3a9b4 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:33 +0100 Subject: [PATCH 0238/1983] brcmfmac: pass DEAUTH/DISASSOC reason code to user-space Upstream-commit: 9b7a0ddc6073909bc61399ca3b8126ef886d5e8a The driver always called cfg80211_disconnected() with reason parameter set to zero, ie. unknown. However, firmware does provide a valid 802.11 reason code in DEAUTH and DISASSOC event message to the driver. This patch passes the reason code to cfg80211_disconnected(). Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 2029499656af53..54cb30429df314 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -1229,7 +1229,25 @@ static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) memset(prof, 0, sizeof(*prof)); } -static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) +static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e) +{ + u16 reason; + + switch (e->event_code) { + case BRCMF_E_DEAUTH: + case BRCMF_E_DEAUTH_IND: + case BRCMF_E_DISASSOC_IND: + reason = e->reason; + break; + case BRCMF_E_LINK: + default: + reason = 0; + break; + } + return reason; +} + +static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); s32 err = 0; @@ -1244,7 +1262,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) brcmf_err("WLC_DISASSOC failed (%d)\n", err); } clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); - cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL); + cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, + GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); @@ -1414,7 +1433,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) if (!check_vif_up(ifp->vif)) return -EIO; - brcmf_link_down(ifp->vif); + brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); brcmf_dbg(TRACE, "Exit\n"); @@ -3035,7 +3054,7 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, * disassociate from AP to save power while system is * in suspended state */ - brcmf_link_down(vif); + brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED); /* Make sure WPA_Supplicant receives all the event * generated due to DISASSOC call to the fw to keep * the state fw and WPA_Supplicant state consistent @@ -4922,7 +4941,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); } - brcmf_link_down(ifp->vif); + brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e)); brcmf_init_prof(ndev_to_prof(ndev)); if (ndev != cfg_to_ndev(cfg)) complete(&cfg->vif_disabled); @@ -5863,7 +5882,7 @@ static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) * from AP to save power */ if (check_vif_up(ifp->vif)) { - brcmf_link_down(ifp->vif); + brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED); /* Make sure WPA_Supplicant receives all the event generated due to DISASSOC call to the fw to keep From 918a7664ee6fde5a0e659e53ba366c5211aebb1a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:34 +0100 Subject: [PATCH 0239/1983] brcmfmac: wait for driver to go idle during suspend Upstream-commit: 8982cd40ace9b7f109ac8c63e6763409e39feb55 Before going in suspend state the watchdog thread needs to put the device in bus sleep state, which assures it can go in deep-sleep state during D3 state. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 20 ++++++++++++++----- .../net/wireless/brcm80211/brcmfmac/sdio.c | 18 ++++++++--------- .../net/wireless/brcm80211/brcmfmac/sdio.h | 2 ++ 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 8ba60f6f491510..9afaeb6d2af5df 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1045,7 +1045,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, bus_if->wowl_supported = true; #endif + sdiodev->sleeping = false; atomic_set(&sdiodev->suspend, false); + init_waitqueue_head(&sdiodev->idle_wait); brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); err = brcmf_sdiod_probe(sdiodev); @@ -1107,12 +1109,23 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled) #ifdef CONFIG_PM_SLEEP static int brcmf_ops_sdio_suspend(struct device *dev) { - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t sdio_flags; brcmf_dbg(SDIO, "Enter\n"); + bus_if = dev_get_drvdata(dev); + sdiodev = bus_if->bus_priv.sdio; + + /* wait for watchdog to go idle */ + if (wait_event_timeout(sdiodev->idle_wait, sdiodev->sleeping, + msecs_to_jiffies(3 * BRCMF_WD_POLL_MS)) == 0) { + brcmf_err("bus still active\n"); + return -EBUSY; + } + /* disable watchdog */ + brcmf_sdio_wd_timer(sdiodev->bus, 0); atomic_set(&sdiodev->suspend, true); if (sdiodev->wowl_enabled) { @@ -1124,9 +1137,6 @@ static int brcmf_ops_sdio_suspend(struct device *dev) if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) brcmf_err("Failed to set pm_flags %x\n", sdio_flags); } - - brcmf_sdio_wd_timer(sdiodev->bus, 0); - return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index e8146cbf53ea2f..b2e1bf5fe9264c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -515,7 +515,6 @@ struct brcmf_sdio { bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ - bool sleeping; /* SDIO bus sleeping */ u8 tx_hdrlen; /* sdio bus header length for tx packet */ bool txglom; /* host tx glomming enable flag */ @@ -1014,12 +1013,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), - (bus->sleeping ? "SLEEP" : "WAKE")); + (bus->sdiodev->sleeping ? "SLEEP" : "WAKE")); /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ - if (sleep == bus->sleeping) + if (sleep == bus->sdiodev->sleeping) goto end; /* Going to sleep */ @@ -1051,12 +1050,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) bus->idlecount = 0; err = brcmf_sdio_kso_control(bus, true); } - if (!err) { - /* Change state */ - bus->sleeping = sleep; - brcmf_dbg(SDIO, "new state %s\n", - (sleep ? "SLEEP" : "WAKE")); - } else { + if (err) { brcmf_err("error while changing bus sleep state %d\n", err); goto done; @@ -1071,6 +1065,11 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } + bus->sdiodev->sleeping = sleep; + if (sleep) + wake_up(&bus->sdiodev->idle_wait); + brcmf_dbg(SDIO, "new state %s\n", + (sleep ? "SLEEP" : "WAKE")); done: brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; @@ -4215,7 +4214,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->idleclock = BRCMF_IDLE_ACTIVE; /* SR state */ - bus->sleeping = false; bus->sr_enabled = false; brcmf_sdio_debugfs_create(bus); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index bdc8a7e1740dd5..1fdf7be6c59e21 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -169,6 +169,8 @@ struct brcmf_sdio_dev { u32 sbwad; /* Save backplane window address */ struct brcmf_sdio *bus; atomic_t suspend; /* suspend flag */ + bool sleeping; + wait_queue_head_t idle_wait; struct device *dev; struct brcmf_bus *bus_if; struct brcmfmac_sdio_platform_data *pdata; From 6b62da50635123b412539aac3905df96ada56d8f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 25 Jan 2015 20:31:35 +0100 Subject: [PATCH 0240/1983] brcmfmac: SDIO: avoid using bus state for private states. Upstream-commit: a1cee865c3e79b71c10cd6a3de03d0dd73f7bdd9 Each bus driver is maintaing an exported bus state indicating if upper layers can or cannot send data. SDIO is using this state also for more private states. This makes handling the states and state changes complex. This patch minimises the exposed states and makes SDIO keep track of an internal state where necessary. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 17 ++++--- drivers/net/wireless/brcm80211/brcmfmac/bus.h | 14 +----- .../net/wireless/brcm80211/brcmfmac/core.c | 6 +-- .../net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- .../net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- .../net/wireless/brcm80211/brcmfmac/sdio.c | 45 +++++++------------ .../net/wireless/brcm80211/brcmfmac/sdio.h | 8 ++++ drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 +- 8 files changed, 43 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9afaeb6d2af5df..7944224e3fc901 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -269,6 +269,12 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, return ret; } +static void brcmf_sdiod_nomedium_state(struct brcmf_sdio_dev *sdiodev) +{ + sdiodev->state = BRCMF_STATE_NOMEDIUM; + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); +} + static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { @@ -276,7 +282,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, s32 retry = 0; int ret; - if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + if (sdiodev->state == BRCMF_STATE_NOMEDIUM) return -ENOMEDIUM; /* @@ -302,7 +308,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret == -ENOMEDIUM) - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + brcmf_sdiod_nomedium_state(sdiodev); else if (ret != 0) { /* * SleepCSR register access can fail when @@ -325,7 +331,7 @@ brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) int err = 0, i; u8 addr[3]; - if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + if (sdiodev->state == BRCMF_STATE_NOMEDIUM) return -ENOMEDIUM; addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; @@ -454,7 +460,7 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, req_sz); if (err == -ENOMEDIUM) - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + brcmf_sdiod_nomedium_state(sdiodev); return err; } @@ -589,8 +595,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; if (ret == -ENOMEDIUM) { - brcmf_bus_change_state(sdiodev->bus_if, - BRCMF_BUS_NOMEDIUM); + brcmf_sdiod_nomedium_state(sdiodev); break; } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h index ef344e47218a3a..55d36ff5439d91 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -33,11 +33,8 @@ /* The level of bus communication with the dongle */ enum brcmf_bus_state { - BRCMF_BUS_UNKNOWN, /* Not determined yet */ - BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */ BRCMF_BUS_DOWN, /* Not ready for frame transfers */ - BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ - BRCMF_BUS_DATA /* Ready for frame transfers */ + BRCMF_BUS_UP /* Ready for frame transfers */ }; /* The level of bus communication with the dongle */ @@ -188,18 +185,9 @@ void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) bus->ops->wowl_config(bus->dev, enabled); } -static inline bool brcmf_bus_ready(struct brcmf_bus *bus) -{ - return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; -} - static inline void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state new_state) { - /* NOMEDIUM is permanent */ - if (bus->state == BRCMF_BUS_NOMEDIUM) - return; - brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); bus->state = new_state; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index bc69c03a0ef0f6..5b65db1fb0fb2e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -197,7 +197,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); /* Can the device send data? */ - if (drvr->bus_if->state != BRCMF_BUS_DATA) { + if (drvr->bus_if->state != BRCMF_BUS_UP) { brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); netif_stop_queue(ndev); dev_kfree_skb(skb); @@ -637,7 +637,7 @@ static int brcmf_netdev_open(struct net_device *ndev) brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); /* If bus is not ready, can't continue */ - if (bus_if->state != BRCMF_BUS_DATA) { + if (bus_if->state != BRCMF_BUS_UP) { brcmf_err("failed bus is not ready\n"); return -EAGAIN; } @@ -963,7 +963,7 @@ int brcmf_bus_start(struct device *dev) p2p_ifp = NULL; /* signal bus ready */ - brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA); + brcmf_bus_change_state(bus_if, BRCMF_BUS_UP); /* Bus is ready, do any initialization */ ret = brcmf_c_preinit_dcmds(ifp); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 03f2c406a17bb0..dcfa0bb149ce8e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -109,7 +109,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) struct brcmf_pub *drvr = ifp->drvr; s32 err; - if (drvr->bus_if->state != BRCMF_BUS_DATA) { + if (drvr->bus_if->state != BRCMF_BUS_UP) { brcmf_err("bus is down. we have nothing to do.\n"); return -EIO; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index e91fa9a2c8855e..61c053a729be01 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -1828,7 +1828,7 @@ static int brcmf_pcie_resume(struct pci_dev *pdev) goto cleanup; brcmf_dbg(PCIE, "Hot resume, continue....\n"); brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_bus_change_state(bus, BRCMF_BUS_DATA); + brcmf_bus_change_state(bus, BRCMF_BUS_UP); brcmf_pcie_intr_enable(devinfo); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index b2e1bf5fe9264c..fd290cecff7668 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -1909,7 +1909,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxpending = true; for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if); + !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_STATE_DATA; rd->seq_num++, rxleft--) { /* Handle glomming separately */ @@ -2415,7 +2415,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) } /* Deflow-control stack if needed */ - if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) && + if ((bus->sdiodev->state == BRCMF_STATE_DATA) && bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { bus->txoff = false; brcmf_txflowblock(bus->sdiodev->dev, false); @@ -2503,7 +2503,7 @@ static void brcmf_sdio_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - if (bus_if->state == BRCMF_BUS_DOWN) { + if (sdiodev->state != BRCMF_STATE_NOMEDIUM) { sdio_claim_host(sdiodev->func[1]); /* Enable clock for device interrupts */ @@ -2756,7 +2756,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_sendfromq(bus, framecnt); } - if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { + if ((bus->sdiodev->state != BRCMF_STATE_DATA) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); atomic_set(&bus->intstatus, 0); } else if (atomic_read(&bus->intstatus) || @@ -3411,8 +3411,8 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, goto err; } - /* Allow HT Clock now that the ARM is running. */ - brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD); + /* Allow full data communication using DPC from now on. */ + bus->sdiodev->state = BRCMF_STATE_DATA; bcmerror = 0; err: @@ -3558,7 +3558,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (!brcmf_bus_ready(bus->sdiodev->bus_if)) { + if (bus->sdiodev->state != BRCMF_STATE_DATA) { brcmf_err("bus is down. we have nothing to do\n"); return; } @@ -3581,10 +3581,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) { -#ifdef DEBUG - struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); -#endif /* DEBUG */ - brcmf_dbg(TIMER, "Enter\n"); /* Poll period: check device if appropriate. */ @@ -3628,7 +3624,7 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus_if && bus_if->state == BRCMF_BUS_DATA && + if (bus->sdiodev->state == BRCMF_STATE_DATA && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { @@ -3869,11 +3865,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; } - /* SDIO register access works so moving - * state from UNKNOWN to DOWN. - */ - brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); - bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); if (IS_ERR(bus->ci)) { brcmf_err("brcmf_chip_attach failed!\n"); @@ -4007,18 +3998,16 @@ static void brcmf_sdio_firmware_callback(struct device *dev, brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); - /* try to download image and nvram to the dongle */ - if (bus_if->state == BRCMF_BUS_DOWN) { - bus->alp_only = true; - err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); - if (err) - goto fail; - bus->alp_only = false; - } - if (!bus_if->drvr) return; + /* try to download image and nvram to the dongle */ + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; + /* Start the watchdog timer */ bus->sdcnt.tickcnt = 0; brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); @@ -4254,7 +4243,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) destroy_workqueue(bus->brcmf_wq); if (bus->ci) { - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + if (bus->sdiodev->state != BRCMF_STATE_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is @@ -4289,7 +4278,7 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) } /* don't start the wd until fw is loaded */ - if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA) + if (bus->sdiodev->state != BRCMF_STATE_DATA) return; if (wdtick) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index 1fdf7be6c59e21..e7b70259adae2c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -155,6 +155,13 @@ /* watchdog polling interval in ms */ #define BRCMF_WD_POLL_MS 10 +/* The state of the bus */ +enum brcmf_sdio_state { + BRCMF_STATE_DOWN, /* Device available, still initialising */ + BRCMF_STATE_DATA, /* Ready for data transfers, DPC enabled */ + BRCMF_STATE_NOMEDIUM /* No medium access to dongle possible */ +}; + struct brcmf_sdreg { int func; int offset; @@ -187,6 +194,7 @@ struct brcmf_sdio_dev { char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; bool wowl_enabled; + enum brcmf_sdio_state state; }; /* sdio core registers */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 4572defc280fa4..7e0c9e2fa7f649 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -576,7 +576,7 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN); } else if (state == BRCMFMAC_USB_STATE_UP) { brcmf_dbg(USB, "DBUS is up\n"); - brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DATA); + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP); } else { brcmf_dbg(USB, "DBUS current state=%d\n", state); } From f76cba14baeb5b3f737e045f6dcd26190526cca3 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sun, 25 Jan 2015 20:31:36 +0100 Subject: [PATCH 0241/1983] brcmfmac: Reopen netdev queue on bus state data. Upstream-commit: 649f38ae9e482098da94d30f7f802621e66c6b74 During suspend the bus state is put in the down state. When data is being transmitted during this state then the netdev queue will be close. This patch will wake the queue on state data if the queue was closed. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bus.h | 10 +++------ .../net/wireless/brcm80211/brcmfmac/core.c | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h index 55d36ff5439d91..89e6a4dc105ecf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -185,13 +185,6 @@ void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) bus->ops->wowl_config(bus->dev, enabled); } -static inline void brcmf_bus_change_state(struct brcmf_bus *bus, - enum brcmf_bus_state new_state) -{ - brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); - bus->state = new_state; -} - /* * interface functions from common layer */ @@ -214,6 +207,9 @@ void brcmf_txflowblock(struct device *dev, bool state); /* Notify the bus has transferred the tx packet to firmware */ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); +/* Configure the "global" bus state used by upper layers */ +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); + int brcmf_bus_start(struct device *dev); s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); void brcmf_bus_add_txhdrlen(struct device *dev, uint len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 5b65db1fb0fb2e..c5e0de827a4e0e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -1105,6 +1105,27 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) return !err; } +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) +{ + struct brcmf_pub *drvr = bus->drvr; + struct net_device *ndev; + int ifidx; + + brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state); + bus->state = state; + + if (state == BRCMF_BUS_UP) { + for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { + if ((drvr->iflist[ifidx]) && + (drvr->iflist[ifidx]->ndev)) { + ndev = drvr->iflist[ifidx]->ndev; + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } + } + } +} + static void brcmf_driver_register(struct work_struct *work) { #ifdef CONFIG_BRCMFMAC_SDIO From 5527c7084ad623049d244e03360269da62deb26e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:37 +0100 Subject: [PATCH 0242/1983] brcmfmac: do not load firmware when device is already running Upstream-commit: 86fec35e17c15f466ebf3f942b60d35d47929444 In brcmf_usb_probe_cb() the device is checked to determine whether it is already running firmware. However, when no firmware download is needed it still continues to request the firmware files. This is fixed by returning after successful setup. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 7e0c9e2fa7f649..1b9572988c773c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1263,6 +1263,8 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) ret = brcmf_usb_bus_setup(devinfo); if (ret) goto fail; + /* we are done */ + return 0; } bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; From f5361dd5807dde1376dfab58677a52ed8d0cd3fe Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:39 +0100 Subject: [PATCH 0243/1983] brcmfmac: determine chip info when not provided by bus layer Upstream-commit: 9b1933a3bd7c4568edc4d906bf1cc0e901ce1ec4 In some scenarios the chip number and revision may not be provided by the bus layer. If the chip number is not filled, the common layer will ask the firmware for this information. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/core.c | 15 +++++++ .../net/wireless/brcm80211/brcmfmac/fwil.h | 1 + .../wireless/brcm80211/brcmfmac/fwil_types.h | 41 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index c5e0de827a4e0e..a606ab4a27f782 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -947,6 +947,7 @@ int brcmf_bus_start(struct device *dev) struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_if *ifp; struct brcmf_if *p2p_ifp; + struct brcmf_rev_info_le revinfo; brcmf_dbg(TRACE, "\n"); @@ -970,6 +971,20 @@ int brcmf_bus_start(struct device *dev) if (ret < 0) goto fail; + /* assure we have chipid before feature attach */ + if (!bus_if->chip) { + ret = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, &revinfo, + sizeof(revinfo)); + if (ret < 0) { + brcmf_err("no chipid determined - device may malfunction\n"); + } else { + bus_if->chip = le32_to_cpu(revinfo.chipnum); + bus_if->chiprev = le32_to_cpu(revinfo.chiprev); + brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n", + bus_if->chip, bus_if->chip, + bus_if->chiprev); + } + } brcmf_feat_attach(drvr); ret = brcmf_fws_init(drvr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 37345e7b873d0a..5434dcf64f7d80 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -59,6 +59,7 @@ #define BRCMF_C_SET_COUNTRY 84 #define BRCMF_C_GET_PM 85 #define BRCMF_C_SET_PM 86 +#define BRCMF_C_GET_REVINFO 98 #define BRCMF_C_GET_CURR_RATESET 114 #define BRCMF_C_GET_AP 117 #define BRCMF_C_SET_AP 118 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 619669bbdb83b6..3749209651088b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -539,4 +539,45 @@ struct brcmf_fil_country_le { char ccode[BRCMF_COUNTRY_BUF_SZ]; }; +/** + * struct brcmf_rev_info_le - device revision info. + * + * @vendorid: PCI vendor id. + * @deviceid: device id of chip. + * @radiorev: radio revision. + * @chiprev: chip revision. + * @corerev: core revision. + * @boardid: board identifier (usu. PCI sub-device id). + * @boardvendor: board vendor (usu. PCI sub-vendor id). + * @boardrev: board revision. + * @driverrev: driver version. + * @ucoderev: microcode version. + * @bus: bus type. + * @chipnum: chip number. + * @phytype: phy type. + * @phyrev: phy revision. + * @anarev: anacore rev. + * @chippkg: chip package info. + * @nvramrev: nvram revision number. + */ +struct brcmf_rev_info_le { + __le32 vendorid; + __le32 deviceid; + __le32 radiorev; + __le32 chiprev; + __le32 corerev; + __le32 boardid; + __le32 boardvendor; + __le32 boardrev; + __le32 driverrev; + __le32 ucoderev; + __le32 bus; + __le32 chipnum; + __le32 phytype; + __le32 phyrev; + __le32 anarev; + __le32 chippkg; + __le32 nvramrev; +}; + #endif /* FWIL_TYPES_H_ */ From 21e5e01cd003bdc8ffb9f8959f20123b53e24942 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:40 +0100 Subject: [PATCH 0244/1983] brcmfmac: always obtain device revision info upon intialization Upstream-commit: 4862290319ade31237ea9e5d61fae5d01b76c28f Obtain device revision information and store it. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/common.c | 30 +++++++++++++++++-- .../net/wireless/brcm80211/brcmfmac/core.c | 16 +++------- .../net/wireless/brcm80211/brcmfmac/core.h | 22 ++++++++++++++ 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index ddf05af13d4465..91213a65001738 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -41,6 +41,8 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) s8 eventmask[BRCMF_EVENTING_MASK_LEN]; u8 buf[BRCMF_DCMD_SMLEN]; struct brcmf_join_pref_params join_pref_params[2]; + struct brcmf_rev_info_le revinfo; + struct brcmf_rev_info *ri; char *ptr; s32 err; @@ -48,12 +50,36 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, sizeof(ifp->mac_addr)); if (err < 0) { - brcmf_err("Retreiving cur_etheraddr failed, %d\n", - err); + brcmf_err("Retreiving cur_etheraddr failed, %d\n", err); goto done; } memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, + &revinfo, sizeof(revinfo)); + if (err < 0) { + brcmf_err("retrieving revision info failed, %d\n", err); + } else { + ri = &ifp->drvr->revinfo; + ri->vendorid = le32_to_cpu(revinfo.vendorid); + ri->deviceid = le32_to_cpu(revinfo.deviceid); + ri->radiorev = le32_to_cpu(revinfo.radiorev); + ri->chiprev = le32_to_cpu(revinfo.chiprev); + ri->corerev = le32_to_cpu(revinfo.corerev); + ri->boardid = le32_to_cpu(revinfo.boardid); + ri->boardvendor = le32_to_cpu(revinfo.boardvendor); + ri->boardrev = le32_to_cpu(revinfo.boardrev); + ri->driverrev = le32_to_cpu(revinfo.driverrev); + ri->ucoderev = le32_to_cpu(revinfo.ucoderev); + ri->bus = le32_to_cpu(revinfo.bus); + ri->chipnum = le32_to_cpu(revinfo.chipnum); + ri->phytype = le32_to_cpu(revinfo.phytype); + ri->phyrev = le32_to_cpu(revinfo.phyrev); + ri->anarev = le32_to_cpu(revinfo.anarev); + ri->chippkg = le32_to_cpu(revinfo.chippkg); + ri->nvramrev = le32_to_cpu(revinfo.nvramrev); + } + /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); strcpy(buf, "ver"); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index a606ab4a27f782..9808789c09cbbd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -947,7 +947,6 @@ int brcmf_bus_start(struct device *dev) struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_if *ifp; struct brcmf_if *p2p_ifp; - struct brcmf_rev_info_le revinfo; brcmf_dbg(TRACE, "\n"); @@ -973,17 +972,10 @@ int brcmf_bus_start(struct device *dev) /* assure we have chipid before feature attach */ if (!bus_if->chip) { - ret = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, &revinfo, - sizeof(revinfo)); - if (ret < 0) { - brcmf_err("no chipid determined - device may malfunction\n"); - } else { - bus_if->chip = le32_to_cpu(revinfo.chipnum); - bus_if->chiprev = le32_to_cpu(revinfo.chiprev); - brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n", - bus_if->chip, bus_if->chip, - bus_if->chiprev); - } + bus_if->chip = drvr->revinfo.chipnum; + bus_if->chiprev = drvr->revinfo.chiprev; + brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n", + bus_if->chip, bus_if->chip, bus_if->chiprev); } brcmf_feat_attach(drvr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index c607a7ba6cdc67..dfb370dbd93f88 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -71,6 +71,27 @@ struct brcmf_proto; /* device communication protocol info */ struct brcmf_cfg80211_dev; /* cfg80211 device info */ struct brcmf_fws_info; /* firmware signalling info */ +/* see struct brcmf_rev_info_le in fwil_types.h */ +struct brcmf_rev_info { + u32 vendorid; + u32 deviceid; + u32 radiorev; + u32 chiprev; + u32 corerev; + u32 boardid; + u32 boardvendor; + u32 boardrev; + u32 driverrev; + u32 ucoderev; + u32 bus; + u32 chipnum; + u32 phytype; + u32 phyrev; + u32 anarev; + u32 chippkg; + u32 nvramrev; +}; + /* Common structure for module and instance linkage */ struct brcmf_pub { /* Linkage ponters */ @@ -104,6 +125,7 @@ struct brcmf_pub { u32 feat_flags; u32 chip_quirks; + struct brcmf_rev_info revinfo; #ifdef DEBUG struct dentry *dbgfs_dir; #endif From 0cd53461eee962f15d507221600505f9dc69531f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:41 +0100 Subject: [PATCH 0245/1983] brcmfmac: show firmware release info in ethtool driver info Upstream-commit: e749c7d49c2093d05d6f546f1a1a228cf3088e54 The ethtool driver info already contained the unique firmware identifier. This patch adds the firmware release version. $ ethtool -i wlan4 driver: brcmfmac version: 6.10.224.22 firmware-version: 01-32bd010f Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/core.c | 4 ++- .../net/wireless/brcm80211/brcmutil/utils.c | 27 +++++++++++++++++++ .../wireless/brcm80211/include/brcmu_utils.h | 4 +++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 9808789c09cbbd..6339e6ef8c37ba 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -601,9 +601,11 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; + char drev[BRCMU_DOTREV_LEN]; strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - snprintf(info->version, sizeof(info->version), "n/a"); + strlcpy(info->version, brcmu_dotrev_str(drvr->revinfo.driverrev, drev), + sizeof(info->version)); strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), sizeof(info->bus_info)); diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c index 0f7e1c7b6f58c9..db250344cbaf9d 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c @@ -261,6 +261,33 @@ struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, } EXPORT_SYMBOL(brcmu_pktq_mdeq); +char *brcmu_dotrev_str(u32 dotrev, char *buf) +{ + u8 dotval[4]; + + if (!dotrev) { + snprintf(buf, BRCMU_DOTREV_LEN, "unknown"); + return buf; + } + dotval[0] = (dotrev >> 24) & 0xFF; + dotval[1] = (dotrev >> 16) & 0xFF; + dotval[2] = (dotrev >> 8) & 0xFF; + dotval[3] = dotrev & 0xFF; + + if (dotval[3]) + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0], + dotval[1], dotval[2], dotval[3]); + else if (dotval[2]) + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0], + dotval[1], dotval[2]); + else + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0], + dotval[1]); + + return buf; +} +EXPORT_SYMBOL(brcmu_dotrev_str); + #if defined(DEBUG) /* pretty hex print a pkt buffer chain */ void brcmu_prpkt(const char *msg, struct sk_buff *p0) diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h index 8ba445b3fd72a9..bcaa6fc89d8cf6 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h @@ -218,4 +218,8 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) } #endif +#define BRCMU_DOTREV_LEN 16 + +char *brcmu_dotrev_str(u32 dotrev, char *buf); + #endif /* _BRCMU_UTILS_H_ */ From 80e774d74c5699524987d5af66aa5b3ee6752cc0 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:42 +0100 Subject: [PATCH 0246/1983] brcmfmac: store revinfo retrieval result Upstream-commit: 7f52c81d02a27943e0c45103d7d10f629b0b58ae When revinfo retrieval fails we can not show the firmware version in ethtool driver info. Store the result to be used when handling ethtool driver info callback. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/common.c | 3 ++- drivers/net/wireless/brcm80211/brcmfmac/core.c | 7 ++++--- drivers/net/wireless/brcm80211/brcmfmac/core.h | 10 +++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 91213a65001738..fe54844c75e089 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -57,10 +57,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, &revinfo, sizeof(revinfo)); + ri = &ifp->drvr->revinfo; if (err < 0) { brcmf_err("retrieving revision info failed, %d\n", err); } else { - ri = &ifp->drvr->revinfo; ri->vendorid = le32_to_cpu(revinfo.vendorid); ri->deviceid = le32_to_cpu(revinfo.deviceid); ri->radiorev = le32_to_cpu(revinfo.radiorev); @@ -79,6 +79,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) ri->chippkg = le32_to_cpu(revinfo.chippkg); ri->nvramrev = le32_to_cpu(revinfo.nvramrev); } + ri->result = err; /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 6339e6ef8c37ba..369d2f8bcd811a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -601,11 +601,12 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - char drev[BRCMU_DOTREV_LEN]; + char drev[BRCMU_DOTREV_LEN] = "n/a"; + if (drvr->revinfo.result == 0) + brcmu_dotrev_str(drvr->revinfo.driverrev, drev); strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, brcmu_dotrev_str(drvr->revinfo.driverrev, drev), - sizeof(info->version)); + strlcpy(info->version, drev, sizeof(info->version)); strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), sizeof(info->bus_info)); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index dfb370dbd93f88..6f8ca4f920d4bd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -71,8 +71,16 @@ struct brcmf_proto; /* device communication protocol info */ struct brcmf_cfg80211_dev; /* cfg80211 device info */ struct brcmf_fws_info; /* firmware signalling info */ -/* see struct brcmf_rev_info_le in fwil_types.h */ +/* + * struct brcmf_rev_info + * + * The result field stores the error code of the + * revision info request from firmware. For the + * other fields see struct brcmf_rev_info_le in + * fwil_types.h + */ struct brcmf_rev_info { + int result; u32 vendorid; u32 deviceid; u32 radiorev; From 1a30b3c1133e45435809d367903a9ceaa45ad58e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sun, 25 Jan 2015 20:31:43 +0100 Subject: [PATCH 0247/1983] brcmfmac: fix nvram processing Upstream-commit: 4165fe9a9287aa9c8e7e4152c21ee179d49d129e The nvram file can hold a key=value combination in which the value may have spaces, ie. 'RAW1=80 02 fe ff'. The parsing functionality did not deal with this so it gives an error message: [621746.311635] brcmfmac: brcmf_nvram_handle_key warning: ln=90:col=11: '=' expected, skip invalid key entry because RAW1=80 is being considerd as key=value pair and it expects '=' sign after '02' for next key=value pair. This entry can be completely ignored as firmware does not need it. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 1ff787d1a36be9..9cb99152ad1753 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -103,7 +103,11 @@ static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) c = nvp->fwnv->data[nvp->pos]; if (c == '=') { - st = VALUE; + /* ignore RAW1 by treating as comment */ + if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0) + st = COMMENT; + else + st = VALUE; } else if (!is_nvram_char(c)) { brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", nvp->line, nvp->column); From 337bbc8b0265c0b8086434c47f0b38f29288cb97 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Wed, 4 Feb 2015 20:28:49 +0100 Subject: [PATCH 0248/1983] brcm80211: Delete unnecessary checks before two function calls Upstream-commit: 297540f69fa9c7292c9866e71cb22a7bc4c5003b The functions brcmu_pkt_buf_free_skb() and usb_free_urb() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. Signed-off-by: Markus Elfring Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 3 +-- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index fd290cecff7668..604abdac2daf6b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -2538,8 +2538,7 @@ static void brcmf_sdio_bus_stop(struct device *dev) brcmu_pktq_flush(&bus->txq, true, NULL, NULL); /* Clear any held glomming stuff */ - if (bus->glomd) - brcmu_pkt_buf_free_skb(bus->glomd); + brcmu_pkt_buf_free_skb(bus->glomd); brcmf_sdio_free_glom(bus); /* Clear rx control and wake any waiters */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 1b9572988c773c..5df6aa72cc2db3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -421,7 +421,7 @@ brcmf_usbdev_qinit(struct list_head *q, int qsize) brcmf_err("fail!\n"); while (!list_empty(q)) { req = list_entry(q->next, struct brcmf_usbreq, list); - if (req && req->urb) + if (req) usb_free_urb(req->urb); list_del(q->next); } From c6ac6fc03f4777906fbbf021f9030e9ef242d8a5 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Fri, 6 Feb 2015 05:26:45 -0500 Subject: [PATCH 0249/1983] brcmfmac: use msecs_to_jiffies for time conversion Upstream-commit: 187d3c3386be51f2c9655ec52ddb4a1046c73332 This is only an API consolidation and should make things more readable it replaces var * HZ / 1000 by msecs_to_jiffies(var). Signed-off-by: Nicholas Mc Guire Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 604abdac2daf6b..51ed53e10c490e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -3971,7 +3971,7 @@ brcmf_sdio_watchdog(unsigned long data) /* Reschedule the watchdog */ if (bus->wd_timer_valid) mod_timer(&bus->timer, - jiffies + BRCMF_WD_POLL_MS * HZ / 1000); + jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); } } @@ -4290,13 +4290,13 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) dynamically changed or in the first instance */ bus->timer.expires = - jiffies + BRCMF_WD_POLL_MS * HZ / 1000; + jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); add_timer(&bus->timer); } else { /* Re arm the timer, at last watchdog period */ mod_timer(&bus->timer, - jiffies + BRCMF_WD_POLL_MS * HZ / 1000); + jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); } bus->wd_timer_valid = true; From f172c62bd96de13568699e0c4b403ebc5b0d6123 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 6 Feb 2015 18:36:42 +0100 Subject: [PATCH 0250/1983] brcmfmac: use helper function for changing SDIO state Upstream-commit: a1ce7a0d6a4f1ed178c075c9dc02a06f0c68ab70 Changing the SDIO state of the driver involves changing the bus interface state. Adding a helper function makes sure that knowledge is in one place. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 41 ++++++++++++++----- .../net/wireless/brcm80211/brcmfmac/sdio.c | 18 ++++---- .../net/wireless/brcm80211/brcmfmac/sdio.h | 21 +++++++--- 3 files changed, 54 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 7944224e3fc901..1f7e2f245e9ef1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -197,6 +197,30 @@ int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) return 0; } +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state) +{ + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM || + state == sdiodev->state) + return; + + brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state); + switch (sdiodev->state) { + case BRCMF_SDIOD_DATA: + /* any other state means bus interface is down */ + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); + break; + case BRCMF_SDIOD_DOWN: + /* transition from DOWN to DATA means bus interface is up */ + if (state == BRCMF_SDIOD_DATA) + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP); + break; + default: + break; + } + sdiodev->state = state; +} + static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func, uint regaddr, u8 byte) { @@ -269,12 +293,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, return ret; } -static void brcmf_sdiod_nomedium_state(struct brcmf_sdio_dev *sdiodev) -{ - sdiodev->state = BRCMF_STATE_NOMEDIUM; - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); -} - static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { @@ -282,7 +300,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, s32 retry = 0; int ret; - if (sdiodev->state == BRCMF_STATE_NOMEDIUM) + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) return -ENOMEDIUM; /* @@ -308,7 +326,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret == -ENOMEDIUM) - brcmf_sdiod_nomedium_state(sdiodev); + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); else if (ret != 0) { /* * SleepCSR register access can fail when @@ -331,7 +349,7 @@ brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) int err = 0, i; u8 addr[3]; - if (sdiodev->state == BRCMF_STATE_NOMEDIUM) + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) return -ENOMEDIUM; addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; @@ -460,7 +478,7 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, req_sz); if (err == -ENOMEDIUM) - brcmf_sdiod_nomedium_state(sdiodev); + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); return err; } @@ -595,7 +613,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; if (ret == -ENOMEDIUM) { - brcmf_sdiod_nomedium_state(sdiodev); + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); break; } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", @@ -1050,6 +1068,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, bus_if->wowl_supported = true; #endif + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); sdiodev->sleeping = false; atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->idle_wait); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 51ed53e10c490e..823c4cbdb2ce0b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -1909,7 +1909,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxpending = true; for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_STATE_DATA; + !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_SDIOD_DATA; rd->seq_num++, rxleft--) { /* Handle glomming separately */ @@ -2415,7 +2415,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) } /* Deflow-control stack if needed */ - if ((bus->sdiodev->state == BRCMF_STATE_DATA) && + if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) && bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { bus->txoff = false; brcmf_txflowblock(bus->sdiodev->dev, false); @@ -2503,7 +2503,7 @@ static void brcmf_sdio_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - if (sdiodev->state != BRCMF_STATE_NOMEDIUM) { + if (sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { sdio_claim_host(sdiodev->func[1]); /* Enable clock for device interrupts */ @@ -2755,7 +2755,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_sendfromq(bus, framecnt); } - if ((bus->sdiodev->state != BRCMF_STATE_DATA) || (err != 0)) { + if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); atomic_set(&bus->intstatus, 0); } else if (atomic_read(&bus->intstatus) || @@ -3411,7 +3411,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, } /* Allow full data communication using DPC from now on. */ - bus->sdiodev->state = BRCMF_STATE_DATA; + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); bcmerror = 0; err: @@ -3557,7 +3557,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (bus->sdiodev->state != BRCMF_STATE_DATA) { + if (bus->sdiodev->state != BRCMF_SDIOD_DATA) { brcmf_err("bus is down. we have nothing to do\n"); return; } @@ -3623,7 +3623,7 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus->sdiodev->state == BRCMF_STATE_DATA && + if (bus->sdiodev->state == BRCMF_SDIOD_DATA && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { @@ -4242,7 +4242,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) destroy_workqueue(bus->brcmf_wq); if (bus->ci) { - if (bus->sdiodev->state != BRCMF_STATE_NOMEDIUM) { + if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is @@ -4277,7 +4277,7 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) } /* don't start the wd until fw is loaded */ - if (bus->sdiodev->state != BRCMF_STATE_DATA) + if (bus->sdiodev->state != BRCMF_SDIOD_DATA) return; if (wdtick) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index e7b70259adae2c..c0c2b80b300e37 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -155,11 +155,17 @@ /* watchdog polling interval in ms */ #define BRCMF_WD_POLL_MS 10 -/* The state of the bus */ -enum brcmf_sdio_state { - BRCMF_STATE_DOWN, /* Device available, still initialising */ - BRCMF_STATE_DATA, /* Ready for data transfers, DPC enabled */ - BRCMF_STATE_NOMEDIUM /* No medium access to dongle possible */ +/** + * enum brcmf_sdiod_state - the state of the bus. + * + * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC. + * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled. + * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible. + */ +enum brcmf_sdiod_state { + BRCMF_SDIOD_DOWN, + BRCMF_SDIOD_DATA, + BRCMF_SDIOD_NOMEDIUM }; struct brcmf_sdreg { @@ -194,7 +200,7 @@ struct brcmf_sdio_dev { char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; bool wowl_enabled; - enum brcmf_sdio_state state; + enum brcmf_sdiod_state state; }; /* sdio core registers */ @@ -345,4 +351,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state); + #endif /* _BRCM_SDH_H_ */ From fc81b50f910f699b3e7607f9bf0c3178186fbeb3 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Feb 2015 18:36:43 +0100 Subject: [PATCH 0251/1983] brcmfmac: Remove error print for invalid key index. Upstream-commit: ad5a52518aa5ae85dd73f8062ad58ef8b3f1ecf1 When a key is set or cleared with an unsupported key index then brcmfmac will print an error. With most wpa_supplicants this is happening a lot. The error print is confusing and not needed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 54cb30429df314..b25cf2201287c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -2252,7 +2252,6 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { /* we ignore this key index in this case */ - brcmf_err("invalid key index (%d)\n", key_idx); return -EINVAL; } From cea3c30c2fcf3b74e43ef9ab6ab2eeb07885a942 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 6 Feb 2015 18:36:45 +0100 Subject: [PATCH 0252/1983] brcmfmac: make sdio suspend wait for threads to freeze Upstream-commit: 9982464379e81ece51ced03ebecbbcd34ea367a6 Borrowed the idea of the PM freezer to make sdio suspend wait for watchdog and DPC thread to freeze at a safe point in their thread routine. The suspend takes 20-25 msec. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 125 +++++++++++++++--- .../net/wireless/brcm80211/brcmfmac/sdio.c | 90 +++++++------ .../net/wireless/brcm80211/brcmfmac/sdio.h | 32 ++++- 3 files changed, 184 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 1f7e2f245e9ef1..c438ccdb6ed821 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -58,6 +58,14 @@ #define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ #define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ +struct brcmf_sdiod_freezer { + atomic_t freezing; + atomic_t thread_count; + u32 frozen_count; + wait_queue_head_t thread_freeze; + struct completion resumed; +}; + static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); @@ -895,6 +903,87 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) sdiodev->txglomsz = brcmf_sdiod_txglomsz; } +#ifdef CONFIG_PM_SLEEP +static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev) +{ + sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL); + if (!sdiodev->freezer) + return -ENOMEM; + atomic_set(&sdiodev->freezer->thread_count, 0); + atomic_set(&sdiodev->freezer->freezing, 0); + init_waitqueue_head(&sdiodev->freezer->thread_freeze); + init_completion(&sdiodev->freezer->resumed); + return 0; +} + +static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev) +{ + if (sdiodev->freezer) { + WARN_ON(atomic_read(&sdiodev->freezer->freezing)); + kfree(sdiodev->freezer); + } +} + +static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev) +{ + atomic_t *expect = &sdiodev->freezer->thread_count; + int res = 0; + + sdiodev->freezer->frozen_count = 0; + reinit_completion(&sdiodev->freezer->resumed); + atomic_set(&sdiodev->freezer->freezing, 1); + brcmf_sdio_trigger_dpc(sdiodev->bus); + wait_event(sdiodev->freezer->thread_freeze, + atomic_read(expect) == sdiodev->freezer->frozen_count); + sdio_claim_host(sdiodev->func[1]); + res = brcmf_sdio_sleep(sdiodev->bus, true); + sdio_release_host(sdiodev->func[1]); + return res; +} + +static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev) +{ + sdio_claim_host(sdiodev->func[1]); + brcmf_sdio_sleep(sdiodev->bus, false); + sdio_release_host(sdiodev->func[1]); + atomic_set(&sdiodev->freezer->freezing, 0); + complete_all(&sdiodev->freezer->resumed); +} + +bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev) +{ + return atomic_read(&sdiodev->freezer->freezing); +} + +void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev) +{ + if (!brcmf_sdiod_freezing(sdiodev)) + return; + sdiodev->freezer->frozen_count++; + wake_up(&sdiodev->freezer->thread_freeze); + wait_for_completion(&sdiodev->freezer->resumed); +} + +void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev) +{ + atomic_inc(&sdiodev->freezer->thread_count); +} + +void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev) +{ + atomic_dec(&sdiodev->freezer->thread_count); +} +#else +static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev) +{ + return 0; +} + +static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_PM_SLEEP */ + static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) { if (sdiodev->bus) { @@ -902,6 +991,8 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) sdiodev->bus = NULL; } + brcmf_sdiod_freezer_detach(sdiodev); + /* Disable Function 2 */ sdio_claim_host(sdiodev->func[2]); sdio_disable_func(sdiodev->func[2]); @@ -973,6 +1064,10 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) */ brcmf_sdiod_sgtable_alloc(sdiodev); + ret = brcmf_sdiod_freezer_attach(sdiodev); + if (ret) + goto out; + /* try to attach to the target device */ sdiodev->bus = brcmf_sdio_probe(sdiodev); if (!sdiodev->bus) { @@ -1069,9 +1164,6 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, #endif brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); - sdiodev->sleeping = false; - atomic_set(&sdiodev->suspend, false); - init_waitqueue_head(&sdiodev->idle_wait); brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); err = brcmf_sdiod_probe(sdiodev); @@ -1133,24 +1225,22 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled) #ifdef CONFIG_PM_SLEEP static int brcmf_ops_sdio_suspend(struct device *dev) { + struct sdio_func *func; struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t sdio_flags; - brcmf_dbg(SDIO, "Enter\n"); + func = container_of(dev, struct sdio_func, dev); + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + if (func->num != SDIO_FUNC_1) + return 0; + bus_if = dev_get_drvdata(dev); sdiodev = bus_if->bus_priv.sdio; - /* wait for watchdog to go idle */ - if (wait_event_timeout(sdiodev->idle_wait, sdiodev->sleeping, - msecs_to_jiffies(3 * BRCMF_WD_POLL_MS)) == 0) { - brcmf_err("bus still active\n"); - return -EBUSY; - } - /* disable watchdog */ + brcmf_sdiod_freezer_on(sdiodev); brcmf_sdio_wd_timer(sdiodev->bus, 0); - atomic_set(&sdiodev->suspend, true); if (sdiodev->wowl_enabled) { sdio_flags = MMC_PM_KEEP_POWER; @@ -1168,12 +1258,13 @@ static int brcmf_ops_sdio_resume(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct sdio_func *func = container_of(dev, struct sdio_func, dev); - brcmf_dbg(SDIO, "Enter\n"); - if (sdiodev->pdata && sdiodev->pdata->oob_irq_supported) - disable_irq_wake(sdiodev->pdata->oob_irq_nr); - brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); - atomic_set(&sdiodev->suspend, false); + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + if (func->num != SDIO_FUNC_2) + return 0; + + brcmf_sdiod_freezer_off(sdiodev); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 823c4cbdb2ce0b..ef913b8a5fa89a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -515,6 +515,7 @@ struct brcmf_sdio { bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ + bool sleeping; u8 tx_hdrlen; /* sdio bus header length for tx packet */ bool txglom; /* host tx glomming enable flag */ @@ -1013,12 +1014,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), - (bus->sdiodev->sleeping ? "SLEEP" : "WAKE")); + (bus->sleeping ? "SLEEP" : "WAKE")); /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ - if (sleep == bus->sdiodev->sleeping) + if (sleep == bus->sleeping) goto end; /* Going to sleep */ @@ -1065,9 +1066,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - bus->sdiodev->sleeping = sleep; - if (sleep) - wake_up(&bus->sdiodev->idle_wait); + bus->sleeping = sleep; brcmf_dbg(SDIO, "new state %s\n", (sleep ? "SLEEP" : "WAKE")); done: @@ -2603,21 +2602,6 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) return ret; } -static int brcmf_sdio_pm_resume_wait(struct brcmf_sdio_dev *sdiodev) -{ -#ifdef CONFIG_PM_SLEEP - int retry; - - /* Wait for possible resume to complete */ - retry = 0; - while ((atomic_read(&sdiodev->suspend)) && (retry++ != 50)) - msleep(20); - if (atomic_read(&sdiodev->suspend)) - return -EIO; -#endif - return 0; -} - static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; @@ -2628,9 +2612,6 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_sdio_pm_resume_wait(bus->sdiodev)) - return; - sdio_claim_host(bus->sdiodev->func[1]); /* If waiting for HTAVAIL, check status */ @@ -2862,11 +2843,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) qcount[prec] = pktq_plen(&bus->txq, prec); #endif - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); - } - + brcmf_sdio_trigger_dpc(bus); return ret; } @@ -2964,11 +2941,8 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) bus->ctrl_frame_buf = msg; bus->ctrl_frame_len = msglen; bus->ctrl_frame_stat = true; - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); - } + brcmf_sdio_trigger_dpc(bus); wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, msecs_to_jiffies(CTL_DONE_TIMEOUT)); @@ -3548,6 +3522,14 @@ static int brcmf_sdio_bus_preinit(struct device *dev) return err; } +void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) +{ + if (atomic_read(&bus->dpc_tskcnt) == 0) { + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); + } +} + void brcmf_sdio_isr(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -3602,9 +3584,8 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) SDIO_CCCR_INTx, NULL); sdio_release_host(bus->sdiodev->func[1]); - intstatus = - devpend & (INTR_STATUS_FUNC1 | - INTR_STATUS_FUNC2); + intstatus = devpend & (INTR_STATUS_FUNC1 | + INTR_STATUS_FUNC2); } /* If there is something, make like the ISR and @@ -3667,6 +3648,11 @@ static void brcmf_sdio_dataworker(struct work_struct *work) atomic_set(&bus->dpc_tskcnt, 0); brcmf_sdio_dpc(bus); } + if (brcmf_sdiod_freezing(bus->sdiodev)) { + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); + brcmf_sdiod_try_freeze(bus->sdiodev); + brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DATA); + } } static void @@ -3944,13 +3930,19 @@ static int brcmf_sdio_watchdog_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; + int wait; allow_signal(SIGTERM); /* Run until signal received */ + brcmf_sdiod_freezer_count(bus->sdiodev); while (1) { if (kthread_should_stop()) break; - if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { + brcmf_sdiod_freezer_uncount(bus->sdiodev); + wait = wait_for_completion_interruptible(&bus->watchdog_wait); + brcmf_sdiod_freezer_count(bus->sdiodev); + brcmf_sdiod_try_freeze(bus->sdiodev); + if (!wait) { brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; @@ -4089,6 +4081,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; + struct workqueue_struct *wq; brcmf_dbg(TRACE, "Enter\n"); @@ -4117,12 +4110,16 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; } - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); - bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); - if (bus->brcmf_wq == NULL) { + /* single-threaded workqueue */ + wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, + dev_name(&sdiodev->func[1]->dev)); + if (!wq) { brcmf_err("insufficient memory to create txworkqueue\n"); goto fail; } + brcmf_sdiod_freezer_count(sdiodev); + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); + bus->brcmf_wq = wq; /* attempt to attach to the dongle */ if (!(brcmf_sdio_probe_attach(bus))) { @@ -4143,7 +4140,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) /* Initialize watchdog thread */ init_completion(&bus->watchdog_wait); bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, - bus, "brcmf_watchdog"); + bus, "brcmf_wdog/%s", + dev_name(&sdiodev->func[1]->dev)); if (IS_ERR(bus->watchdog_tsk)) { pr_warn("brcmf_watchdog thread failed to start\n"); bus->watchdog_tsk = NULL; @@ -4303,3 +4301,15 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) bus->save_ms = wdtick; } } + +int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep) +{ + int ret; + + sdio_claim_host(bus->sdiodev->func[1]); + ret = brcmf_sdio_bus_sleep(bus, sleep, false); + sdio_release_host(bus->sdiodev->func[1]); + + return ret; +} + diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index c0c2b80b300e37..5585a67ef3ccd8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -175,15 +175,13 @@ struct brcmf_sdreg { }; struct brcmf_sdio; +struct brcmf_sdiod_freezer; struct brcmf_sdio_dev { struct sdio_func *func[SDIO_MAX_FUNCS]; u8 num_funcs; /* Supported funcs on client */ u32 sbwad; /* Save backplane window address */ struct brcmf_sdio *bus; - atomic_t suspend; /* suspend flag */ - bool sleeping; - wait_queue_head_t idle_wait; struct device *dev; struct brcmf_bus *bus_if; struct brcmfmac_sdio_platform_data *pdata; @@ -201,6 +199,7 @@ struct brcmf_sdio_dev { char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; bool wowl_enabled; enum brcmf_sdiod_state state; + struct brcmf_sdiod_freezer *freezer; }; /* sdio core registers */ @@ -343,6 +342,28 @@ int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, /* Issue an abort to the specified function */ int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state); +#ifdef CONFIG_PM_SLEEP +bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev); +#else +static inline bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev) +{ + return false; +} +static inline void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev) +{ +} +static inline void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev) +{ +} +static inline void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev) +{ +} +#endif /* CONFIG_PM_SLEEP */ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); void brcmf_sdio_remove(struct brcmf_sdio *bus); @@ -350,8 +371,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); - -void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, - enum brcmf_sdiod_state state); +int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); +void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); #endif /* _BRCM_SDH_H_ */ From c0569c9d9f9d4e2fed924fc6da4117d372cbf9a2 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Feb 2015 18:36:46 +0100 Subject: [PATCH 0253/1983] brcmfmac: Dont sleep when ctrl frames to transmit. Upstream-commit: a7b134a7676edf68d05fe44f0a045c14cadd66b2 The SDIO watchdog will put the device in sleep mode when there is no activity for some time and nothing to do anymore. This check is incomplete and should also check if there is a control frame to transmit. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index ef913b8a5fa89a..9f59158122cf8c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -1027,6 +1027,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) /* Don't sleep if something is pending */ if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || + bus->ctrl_frame_stat || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus))) { From 329ea72a0fe84633c1f3097c551cdc9391617eaa Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Feb 2015 18:36:47 +0100 Subject: [PATCH 0254/1983] brcmfmac: Fix escan timer causing oops. Upstream-commit: 661fa95ddb9e4c9bf6b3ec575a92dd837ec72ace In some rare circumstances the escan protection timer can expire before the setup completed (due to long timeouts on IOCTL). This patch avoids this situation by setting the timer after the setup completed correctly. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index b25cf2201287c1..73ba3d4fa47b48 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -1050,10 +1050,6 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - /* Arm scan timeout timer */ - mod_timer(&cfg->escan_timeout, jiffies + - WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); - escan_req = false; if (request) { /* scan bss */ @@ -1112,12 +1108,14 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, } } + /* Arm scan timeout timer */ + mod_timer(&cfg->escan_timeout, jiffies + + WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); + return 0; scan_out: clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - if (timer_pending(&cfg->escan_timeout)) - del_timer_sync(&cfg->escan_timeout); cfg->scan_request = NULL; return err; } From 571c1a3056b39dc826ba3fe7fcd4ea0a29fdce2b Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Fri, 6 Mar 2015 16:18:41 +0100 Subject: [PATCH 0255/1983] brcmfmac: Perform bound checking on vendor command buffer Upstream-commit: 3f1615340acea54e21f4b9d4d65921540dca84b2 A short or malformed vendor command buffer could cause reads outside the command buffer. Cc: stable@vger.kernel.org # v3.19 Signed-off-by: Pontus Fuchs [arend@broadcom.com: slightly modified debug trace output] Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 50cdf7090198b3..8eff2753abadeb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -39,13 +39,22 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, void *dcmd_buf = NULL, *wr_pointer; u16 msglen, maxmsglen = PAGE_SIZE - 0x100; - brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, - cmdhdr->len); + if (len < sizeof(*cmdhdr)) { + brcmf_err("vendor command too short: %d\n", len); + return -EINVAL; + } vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); ifp = vif->ifp; - len -= sizeof(struct brcmf_vndr_dcmd_hdr); + brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd); + + if (cmdhdr->offset > len) { + brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len); + return -EINVAL; + } + + len -= cmdhdr->offset; ret_len = cmdhdr->len; if (ret_len > 0 || len > 0) { if (len > BRCMF_DCMD_MAXLEN) { From 918c0ff9959dd4f072358f92c73c17cd937eaafe Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Mar 2015 18:40:38 +0100 Subject: [PATCH 0256/1983] brcmfmac: Fix oops when SDIO device is removed. Upstream-commit: de6878c8354d1524940055fe1b802c799f4fc318 On removal of SDIO card both functions of card will be getting a remove call. When the first is hanging in ctrl frame xmit then the second will cause oops. This patch fixes the xmit ctrl handling in case of serious errors and also limits the handling for remove to function 1 only. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index c438ccdb6ed821..ffb0e2d382ea8b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1194,7 +1194,7 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); brcmf_dbg(SDIO, "Function: %d\n", func->num); - if (func->num != 1 && func->num != 2) + if (func->num != 1) return; bus_if = dev_get_drvdata(&func->dev); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 9f59158122cf8c..cd66acb0841866 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -2740,6 +2740,11 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) if ((bus->sdiodev->state != BRCMF_SDIOD_DATA) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); atomic_set(&bus->intstatus, 0); + if (bus->ctrl_frame_stat) { + bus->ctrl_frame_err = -ENODEV; + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } } else if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && From 2bf350f3ceccd425c27241343dfa498a01b4bf94 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Mar 2015 18:40:39 +0100 Subject: [PATCH 0257/1983] brcmfmac: Simplify watchdog sleep. Upstream-commit: b441ba8dc34136dc418d85299757514c3a08913d The watchdog thread is used to put the SDIO bus to sleep when the system is idling. This patch simplifies the way it is determined when sleep can be entered. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/sdio.c | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index cd66acb0841866..e0637a0bdf825a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -485,10 +485,9 @@ struct brcmf_sdio { #endif /* DEBUG */ uint clkstate; /* State of sd and backplane clock(s) */ - bool activity; /* Activity flag for clock down */ s32 idletime; /* Control for activity timeout */ - s32 idlecount; /* Activity timeout counter */ - s32 idleclock; /* How to set bus driver when idle */ + s32 idlecount; /* Activity timeout counter */ + s32 idleclock; /* How to set bus driver when idle */ bool rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ bool alp_only; /* Don't use HT clock (ALP only) */ @@ -511,6 +510,7 @@ struct brcmf_sdio { struct workqueue_struct *brcmf_wq; struct work_struct datawork; atomic_t dpc_tskcnt; + atomic_t dpc_running; bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; @@ -959,13 +959,8 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) brcmf_dbg(SDIO, "Enter\n"); /* Early exit if we're already there */ - if (bus->clkstate == target) { - if (target == CLK_AVAIL) { - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - bus->activity = true; - } + if (bus->clkstate == target) return 0; - } switch (target) { case CLK_AVAIL: @@ -975,7 +970,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) /* Now request HT Avail on the backplane */ brcmf_sdio_htclk(bus, true, pendok); brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - bus->activity = true; break; case CLK_SDONLY: @@ -1024,17 +1018,6 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) /* Going to sleep */ if (sleep) { - /* Don't sleep if something is pending */ - if (atomic_read(&bus->intstatus) || - atomic_read(&bus->ipend) > 0 || - bus->ctrl_frame_stat || - (!atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) { - err = -EBUSY; - goto done; - } - clkcsr = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); @@ -1045,11 +1028,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) SBSDIO_ALP_AVAIL_REQ, &err); } err = brcmf_sdio_kso_control(bus, false); - /* disable watchdog */ - if (!err) - brcmf_sdio_wd_timer(bus, 0); } else { - bus->idlecount = 0; err = brcmf_sdio_kso_control(bus, true); } if (err) { @@ -3566,7 +3545,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) queue_work(bus->brcmf_wq, &bus->datawork); } -static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) +static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) { brcmf_dbg(TIMER, "Enter\n"); @@ -3627,22 +3606,21 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ - if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { - if (++bus->idlecount >= bus->idletime) { + if ((atomic_read(&bus->dpc_tskcnt) == 0) && + (atomic_read(&bus->dpc_running) == 0) && + (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { + bus->idlecount++; + if (bus->idlecount > bus->idletime) { + brcmf_dbg(SDIO, "idle\n"); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); bus->idlecount = 0; - if (bus->activity) { - bus->activity = false; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - } else { - brcmf_dbg(SDIO, "idle\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } + brcmf_sdio_bus_sleep(bus, true, false); + sdio_release_host(bus->sdiodev->func[1]); } + } else { + bus->idlecount = 0; } - - return (atomic_read(&bus->ipend) > 0); } static void brcmf_sdio_dataworker(struct work_struct *work) @@ -3651,8 +3629,11 @@ static void brcmf_sdio_dataworker(struct work_struct *work) datawork); while (atomic_read(&bus->dpc_tskcnt)) { + atomic_set(&bus->dpc_running, 1); atomic_set(&bus->dpc_tskcnt, 0); brcmf_sdio_dpc(bus); + bus->idlecount = 0; + atomic_set(&bus->dpc_running, 0); } if (brcmf_sdiod_freezing(bus->sdiodev)) { brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); @@ -4154,6 +4135,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) } /* Initialize DPC thread */ atomic_set(&bus->dpc_tskcnt, 0); + atomic_set(&bus->dpc_running, 0); /* Assign bus interface call back */ bus->sdiodev->bus_if->dev = bus->sdiodev->dev; From 5b9b3cfa5457ae2a2ad759c37c094ef3ddb6cd39 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Mar 2015 18:40:40 +0100 Subject: [PATCH 0258/1983] brcmfmac: Fix possible race-condition. Upstream-commit: 449e58b85c0051117bf8428777b4ae38e098506a SDIO is using a "shared" variable to handoff ctl frames to DPC and to see when they are done. In a timeout situation this can lead to erroneous situation where DPC started to handle the ctl frame while the timeout expired. This patch will fix this by adding locking around the shared variable. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/sdio.c | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index e0637a0bdf825a..0404a2fc2f0e95 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -2700,11 +2700,13 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && data_ok(bus)) { sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, - bus->ctrl_frame_len); + if (bus->ctrl_frame_stat) { + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + bus->ctrl_frame_err = err; + bus->ctrl_frame_stat = false; + } sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_err = err; - bus->ctrl_frame_stat = false; brcmf_sdio_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ @@ -2720,9 +2722,13 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_err("failed backplane access over SDIO, halting operation\n"); atomic_set(&bus->intstatus, 0); if (bus->ctrl_frame_stat) { - bus->ctrl_frame_err = -ENODEV; - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + bus->ctrl_frame_err = -ENODEV; + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + sdio_release_host(bus->sdiodev->func[1]); } } else if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || @@ -2930,15 +2936,20 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) brcmf_sdio_trigger_dpc(bus); wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, msecs_to_jiffies(CTL_DONE_TIMEOUT)); - - if (!bus->ctrl_frame_stat) { + ret = 0; + if (bus->ctrl_frame_stat) { + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->ctrl_frame_stat) { + brcmf_dbg(SDIO, "ctrl_frame timeout\n"); + bus->ctrl_frame_stat = false; + ret = -ETIMEDOUT; + } + sdio_release_host(bus->sdiodev->func[1]); + } + if (!ret) { brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", bus->ctrl_frame_err); ret = bus->ctrl_frame_err; - } else { - brcmf_dbg(SDIO, "ctrl_frame timeout\n"); - bus->ctrl_frame_stat = false; - ret = -ETIMEDOUT; } if (ret) From 4c8e575f0feaa43691e6e3f306236c16c1dcf50c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Fri, 6 Mar 2015 18:40:41 +0100 Subject: [PATCH 0259/1983] brcmfmac: Fix race condition in msgbuf ioctl processing. Upstream-commit: d375bc8a85a49bf4d2897f59fab4d4afb34d5d44 Msgbuf is using a wait_event_timeout to wait for the response on an ioctl. The wakeup routine uses waitqueue_active to see if wait_event_timeout has been called. There is a chance that the response arrives before wait_event_timeout is called, this will result in situation that wait_event_timeout never gets woken again and assumed result will be a timeout. This patch removes that errornous situation by always setting the ctl_completed var before checking for queue active. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 6262612dec450b..4ec9811f49c877 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -481,10 +481,9 @@ static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf) { - if (waitqueue_active(&msgbuf->ioctl_resp_wait)) { - msgbuf->ctl_completed = true; + msgbuf->ctl_completed = true; + if (waitqueue_active(&msgbuf->ioctl_resp_wait)) wake_up(&msgbuf->ioctl_resp_wait); - } } From 01f18c9cfa5533f29b171b85b72e2ea1ca77c2bd Mon Sep 17 00:00:00 2001 From: Syed Asifful Dayyan Date: Fri, 6 Mar 2015 18:40:42 +0100 Subject: [PATCH 0260/1983] brcmfmac: Add support for BCM4345 SDIO chipset. Upstream-commit: 9c51026509d7fd11d84e0035008e1a9768960f2b These changes add support for BCM4345 SDIO chipset. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend Van Spriel Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Syed Asifful Dayyan Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/chip.c | 4 ++++ drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 +++++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 1 + include/linux/mmc/sdio_ids.h | 1 + 5 files changed, 12 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index ffb0e2d382ea8b..43995308307d04 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1096,6 +1096,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 04d2ca0d87d60b..e679edca081d21 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -491,6 +491,10 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) case BRCM_CC_43362_CHIP_ID: ci->pub.ramsize = 0x3c000; break; + case BRCM_CC_4345_CHIP_ID: + ci->pub.ramsize = 0xc8000; + ci->pub.rambase = 0x198000; + break; case BRCM_CC_4339_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 0404a2fc2f0e95..602592083914d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -617,6 +617,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4345_FIRMWARE_NAME "brcm/brcmfmac4345-sdio.bin" +#define BCM4345_NVRAM_NAME "brcm/brcmfmac4345-sdio.txt" #define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" #define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" @@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4345_NVRAM_NAME); MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4354_NVRAM_NAME); @@ -669,6 +673,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 2124a17d0bfdda..b599e7e411486d 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -37,6 +37,7 @@ #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_4345_CHIP_ID 0x4345 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 99680796371677..91397858dc9586 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -33,6 +33,7 @@ #define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 #define SDIO_DEVICE_ID_BROADCOM_43362 0xa962 +#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 #define SDIO_VENDOR_ID_INTEL 0x0089 From d31084966ce1b1668d77b6b321ccba48cb08edcc Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:27 +0100 Subject: [PATCH 0261/1983] brcmfmac: remove duplication of ramsize info Upstream-commit: 55b59fba0f0c700493a63157db8e3ef4300908dc Removing the ramsize from the brcmf_sdio structure to avoid duplication. The information is available in brcmf_chip structure. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 602592083914d3..a1fb03367abc57 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -432,8 +432,6 @@ struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ struct brcmf_chip *ci; /* Chip info struct */ - u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ - u32 hostintmask; /* Copy of Host Interrupt Mask */ atomic_t intstatus; /* Intstatus bits (events) pending */ atomic_t fcstate; /* State of dongle flow-control */ @@ -1075,7 +1073,7 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, struct sdpcm_shared_le sh_le; __le32 addr_le; - shaddr = bus->ci->rambase + bus->ramsize - 4; + shaddr = bus->ci->rambase + bus->ci->ramsize - 4; /* * Read last word in socram to determine @@ -3871,13 +3869,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); - /* Get info on the SOCRAM cores... */ - bus->ramsize = bus->ci->ramsize; - if (!(bus->ramsize)) { - brcmf_err("failed to find SOCRAM memory!\n"); - goto fail; - } - /* Set card control so an SDIO card reset does a WLAN backplane reset */ reg_val = brcmf_sdiod_regrb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCTRL, &err); From 1d84b4cc604c8fbb1a40dad9d88dc3398b4605a1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:28 +0100 Subject: [PATCH 0262/1983] brcmfmac: always perform cores checks Upstream-commit: 5ded1c251f8e6c93012ef007e2bade61bff20c7e Instead of checking the cores in the chip only if CONFIG_BRCMDBG is selected perform the check always and extend it with more sanity checking. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index e679edca081d21..cb08f4ecec99c2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -419,13 +419,13 @@ static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, return &core->pub; } -#ifdef DEBUG /* safety check for chipinfo */ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) { struct brcmf_core_priv *core; bool need_socram = false; bool has_socram = false; + bool cpu_found = false; int idx = 1; list_for_each_entry(core, &ci->cores, list) { @@ -435,12 +435,14 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) switch (core->pub.id) { case BCMA_CORE_ARM_CM3: + cpu_found = true; need_socram = true; break; case BCMA_CORE_INTERNAL_MEM: has_socram = true; break; case BCMA_CORE_ARM_CR4: + cpu_found = true; if (ci->pub.rambase == 0) { brcmf_err("RAM base not provided with ARM CR4 core\n"); return -ENOMEM; @@ -451,19 +453,21 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) } } + if (!cpu_found) { + brcmf_err("CPU core not detected\n"); + return -ENXIO; + } /* check RAM core presence for ARM CM3 core */ if (need_socram && !has_socram) { brcmf_err("RAM core not provided with ARM CM3 core\n"); return -ENODEV; } + if (!ci->pub.ramsize) { + brcmf_err("RAM size is undetermined\n"); + return -ENOMEM; + } return 0; } -#else /* DEBUG */ -static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) -{ - return 0; -} -#endif static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) { From b369de9c125094937d138a63f053c633093ca1e1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:29 +0100 Subject: [PATCH 0263/1983] brcmfmac: rename chip download functions Upstream-commit: d380ebc9b6fb552345de3051d9b64e963eaadf46 The functions brcmf_chip_[enter/exit]_download() are not exclusively used for firmware download so rename these more appropriate. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/chip.c | 26 +++++++++---------- .../net/wireless/brcm80211/brcmfmac/chip.h | 8 +++--- .../net/wireless/brcm80211/brcmfmac/pcie.c | 10 +++---- .../net/wireless/brcm80211/brcmfmac/sdio.c | 17 ++++++------ 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index cb08f4ecec99c2..d68e37d3a42fac 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -807,7 +807,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, err = -EINVAL; if (WARN_ON(!ops->prepare)) err = -EINVAL; - if (WARN_ON(!ops->exit_dl)) + if (WARN_ON(!ops->activate)) err = -EINVAL; if (err < 0) return ERR_PTR(-EINVAL); @@ -905,7 +905,7 @@ void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, } static void -brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) +brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; @@ -919,7 +919,7 @@ brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) brcmf_chip_resetcore(core, 0, 0, 0); } -static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) +static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) { struct brcmf_core *core; @@ -929,7 +929,7 @@ static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) return false; } - chip->ops->exit_dl(chip->ctx, &chip->pub, 0); + chip->ops->activate(chip->ctx, &chip->pub, 0); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); brcmf_chip_resetcore(core, 0, 0, 0); @@ -938,7 +938,7 @@ static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) } static inline void -brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) +brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; @@ -951,11 +951,11 @@ brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) D11_BCMA_IOCTL_PHYCLOCKEN); } -static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) +static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) { struct brcmf_core *core; - chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec); + chip->ops->activate(chip->ctx, &chip->pub, rstvec); /* restore ARM */ core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); @@ -964,7 +964,7 @@ static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) return true; } -void brcmf_chip_enter_download(struct brcmf_chip *pub) +void brcmf_chip_set_passive(struct brcmf_chip *pub) { struct brcmf_chip_priv *chip; struct brcmf_core *arm; @@ -974,14 +974,14 @@ void brcmf_chip_enter_download(struct brcmf_chip *pub) chip = container_of(pub, struct brcmf_chip_priv, pub); arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) { - brcmf_chip_cr4_enterdl(chip); + brcmf_chip_cr4_set_passive(chip); return; } - brcmf_chip_cm3_enterdl(chip); + brcmf_chip_cm3_set_passive(chip); } -bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) +bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) { struct brcmf_chip_priv *chip; struct brcmf_core *arm; @@ -991,9 +991,9 @@ bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) chip = container_of(pub, struct brcmf_chip_priv, pub); arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) - return brcmf_chip_cr4_exitdl(chip, rstvec); + return brcmf_chip_cr4_set_active(chip, rstvec); - return brcmf_chip_cm3_exitdl(chip); + return brcmf_chip_cm3_set_active(chip); } bool brcmf_chip_sr_capable(struct brcmf_chip *pub) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h index c32908da90c853..7b7b62938ca368 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -64,7 +64,7 @@ struct brcmf_core { * @write32: write 32-bit value over bus. * @prepare: prepare bus for core configuration. * @setup: bus-specific core setup. - * @exit_dl: exit download state. + * @active: chip becomes active. * The callback should use the provided @rstvec when non-zero. */ struct brcmf_buscore_ops { @@ -72,7 +72,7 @@ struct brcmf_buscore_ops { void (*write32)(void *ctx, u32 addr, u32 value); int (*prepare)(void *ctx); int (*setup)(void *ctx, struct brcmf_chip *chip); - void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec); + void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); }; struct brcmf_chip *brcmf_chip_attach(void *ctx, @@ -84,8 +84,8 @@ bool brcmf_chip_iscoreup(struct brcmf_core *core); void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, u32 postreset); -void brcmf_chip_enter_download(struct brcmf_chip *ci); -bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec); +void brcmf_chip_set_passive(struct brcmf_chip *ci); +bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); bool brcmf_chip_sr_capable(struct brcmf_chip *pub); #endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 61c053a729be01..81e544f2f2fe1f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -509,7 +509,7 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) { - brcmf_chip_enter_download(devinfo->ci); + brcmf_chip_set_passive(devinfo->ci); if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); @@ -536,7 +536,7 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, brcmf_chip_resetcore(core, 0, 0, 0); } - return !brcmf_chip_exit_download(devinfo->ci, resetintr); + return !brcmf_chip_set_active(devinfo->ci, resetintr); } @@ -1566,8 +1566,8 @@ static int brcmf_pcie_buscoreprep(void *ctx) } -static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, - u32 rstvec) +static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, + u32 rstvec) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; @@ -1577,7 +1577,7 @@ static void brcmf_pcie_buscore_exitdl(void *ctx, struct brcmf_chip *chip, static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, - .exit_dl = brcmf_pcie_buscore_exitdl, + .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index a1fb03367abc57..ce8ffa2b219db8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -3357,7 +3357,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - brcmf_chip_enter_download(bus->ci); + brcmf_chip_set_passive(bus->ci); rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); @@ -3378,7 +3378,7 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, } /* Take arm out of reset */ - if (!brcmf_chip_exit_download(bus->ci, rstvec)) { + if (!brcmf_chip_set_active(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } @@ -3771,8 +3771,8 @@ static int brcmf_sdio_buscoreprep(void *ctx) return 0; } -static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, - u32 rstvec) +static void brcmf_sdio_buscore_activate(void *ctx, struct brcmf_chip *chip, + u32 rstvec) { struct brcmf_sdio_dev *sdiodev = ctx; struct brcmf_core *core; @@ -3815,7 +3815,7 @@ static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { .prepare = brcmf_sdio_buscoreprep, - .exit_dl = brcmf_sdio_buscore_exitdl, + .activate = brcmf_sdio_buscore_activate, .read32 = brcmf_sdio_buscore_read32, .write32 = brcmf_sdio_buscore_write32, }; @@ -4239,12 +4239,11 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is - * 'quiet'. This is done by putting it in - * download_state which essentially resets - * all necessary cores. + * 'passive'. This is done by resetting all + * necessary cores. */ msleep(20); - brcmf_chip_enter_download(bus->ci); + brcmf_chip_set_passive(bus->ci); brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); } From df6fb1b899d12158b3bd312565e3fa2ecb96e749 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:30 +0100 Subject: [PATCH 0264/1983] brcmfmac: assure device is ready for download after brcmf_chip_attach() Upstream-commit: 38f29e1f42b114105e1ffefba0bc183e1e144d0b Make the brcmf_chip_attach() function responsible for putting the device in a state where it is accessible for firmware download. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 8 ++------ drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 -- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 3 --- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index d68e37d3a42fac..c11137e290b13b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -786,12 +786,6 @@ static int brcmf_chip_setup(struct brcmf_chip_priv *chip) if (chip->ops->setup) ret = chip->ops->setup(chip->ctx, pub); - /* - * Make sure any on-chip ARM is off (in case strapping is wrong), - * or downloaded code was already running. - */ - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); return ret; } @@ -833,6 +827,8 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, if (err < 0) goto fail; + /* assure chip is passive for download */ + brcmf_chip_set_passive(&chip->pub); return &chip->pub; fail: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 81e544f2f2fe1f..aa7a4dfb8beb77 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -509,8 +509,6 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) { - brcmf_chip_set_passive(devinfo->ci); - if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index ce8ffa2b219db8..822857cc398d12 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -3356,9 +3356,6 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - /* Keep arm in reset */ - brcmf_chip_set_passive(bus->ci); - rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); From b1785e754b52b4ea6f8d2ec425594a61f697ce4f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:31 +0100 Subject: [PATCH 0265/1983] brcmfmac: extract ram size info from internal memory registers Upstream-commit: 0da32ba4ee667ef3c837ee4ebaa230f6f826a822 Instead of hard-coded memory sizes it is possible to obtain that information from the internal memory registers. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/chip.c | 260 ++++++++++++++---- .../net/wireless/brcm80211/brcmfmac/chip.h | 4 +- 2 files changed, 217 insertions(+), 47 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index c11137e290b13b..734172570054d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -100,9 +100,6 @@ #define BCM4329_CORE_SOCRAM_BASE 0x18003000 /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 -#define BCM4329_RAMSIZE 0x48000 -/* bcm43143 */ -#define BCM43143_RAMSIZE 0x70000 #define CORE_SB(base, field) \ (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) @@ -150,6 +147,78 @@ struct sbconfig { u32 sbidhigh; /* identification */ }; +/* bankidx and bankinfo reg defines corerev >= 8 */ +#define SOCRAM_BANKINFO_RETNTRAM_MASK 0x00010000 +#define SOCRAM_BANKINFO_SZMASK 0x0000007f +#define SOCRAM_BANKIDX_ROM_MASK 0x00000100 + +#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 +/* socram bankinfo memtype */ +#define SOCRAM_MEMTYPE_RAM 0 +#define SOCRAM_MEMTYPE_R0M 1 +#define SOCRAM_MEMTYPE_DEVRAM 2 + +#define SOCRAM_BANKINFO_SZBASE 8192 +#define SRCI_LSS_MASK 0x00f00000 +#define SRCI_LSS_SHIFT 20 +#define SRCI_SRNB_MASK 0xf0 +#define SRCI_SRNB_SHIFT 4 +#define SRCI_SRBSZ_MASK 0xf +#define SRCI_SRBSZ_SHIFT 0 +#define SR_BSZ_BASE 14 + +struct sbsocramregs { + u32 coreinfo; + u32 bwalloc; + u32 extracoreinfo; + u32 biststat; + u32 bankidx; + u32 standbyctrl; + + u32 errlogstatus; /* rev 6 */ + u32 errlogaddr; /* rev 6 */ + /* used for patching rev 3 & 5 */ + u32 cambankidx; + u32 cambankstandbyctrl; + u32 cambankpatchctrl; + u32 cambankpatchtblbaseaddr; + u32 cambankcmdreg; + u32 cambankdatareg; + u32 cambankmaskreg; + u32 PAD[1]; + u32 bankinfo; /* corev 8 */ + u32 bankpda; + u32 PAD[14]; + u32 extmemconfig; + u32 extmemparitycsr; + u32 extmemparityerrdata; + u32 extmemparityerrcnt; + u32 extmemwrctrlandsize; + u32 PAD[84]; + u32 workaround; + u32 pwrctl; /* corerev >= 2 */ + u32 PAD[133]; + u32 sr_control; /* corerev >= 15 */ + u32 sr_status; /* corerev >= 15 */ + u32 sr_address; /* corerev >= 15 */ + u32 sr_data; /* corerev >= 15 */ +}; + +#define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) + +#define ARMCR4_CAP (0x04) +#define ARMCR4_BANKIDX (0x40) +#define ARMCR4_BANKINFO (0x44) +#define ARMCR4_BANKPDA (0x4C) + +#define ARMCR4_TCBBNB_MASK 0xf0 +#define ARMCR4_TCBBNB_SHIFT 4 +#define ARMCR4_TCBANB_MASK 0xf +#define ARMCR4_TCBANB_SHIFT 0 + +#define ARMCR4_BSZ_MASK 0x3f +#define ARMCR4_BSZ_MULT 8192 + struct brcmf_core_priv { struct brcmf_core pub; u32 wrapbase; @@ -443,10 +512,6 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) break; case BCMA_CORE_ARM_CR4: cpu_found = true; - if (ci->pub.rambase == 0) { - brcmf_err("RAM base not provided with ARM CR4 core\n"); - return -ENOMEM; - } break; default: break; @@ -462,60 +527,160 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) brcmf_err("RAM core not provided with ARM CM3 core\n"); return -ENODEV; } - if (!ci->pub.ramsize) { - brcmf_err("RAM size is undetermined\n"); - return -ENOMEM; - } return 0; } -static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +static u32 brcmf_chip_core_read32(struct brcmf_core_priv *core, u16 reg) { - switch (ci->pub.chip) { - case BRCM_CC_4329_CHIP_ID: - ci->pub.ramsize = BCM4329_RAMSIZE; - break; - case BRCM_CC_43143_CHIP_ID: - ci->pub.ramsize = BCM43143_RAMSIZE; - break; - case BRCM_CC_43241_CHIP_ID: - ci->pub.ramsize = 0x90000; - break; - case BRCM_CC_4330_CHIP_ID: - ci->pub.ramsize = 0x48000; - break; + return core->chip->ops->read32(core->chip->ctx, core->pub.base + reg); +} + +static void brcmf_chip_core_write32(struct brcmf_core_priv *core, + u16 reg, u32 val) +{ + core->chip->ops->write32(core->chip->ctx, core->pub.base + reg, val); +} + +static bool brcmf_chip_socram_banksize(struct brcmf_core_priv *core, u8 idx, + u32 *banksize) +{ + u32 bankinfo; + u32 bankidx = (SOCRAM_MEMTYPE_RAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + + bankidx |= idx; + brcmf_chip_core_write32(core, SOCRAMREGOFFS(bankidx), bankidx); + bankinfo = brcmf_chip_core_read32(core, SOCRAMREGOFFS(bankinfo)); + *banksize = (bankinfo & SOCRAM_BANKINFO_SZMASK) + 1; + *banksize *= SOCRAM_BANKINFO_SZBASE; + return !!(bankinfo & SOCRAM_BANKINFO_RETNTRAM_MASK); +} + +static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, + u32 *srsize) +{ + u32 coreinfo; + uint nb, banksize, lss; + bool retent; + int i; + + *ramsize = 0; + *srsize = 0; + + if (WARN_ON(sr->pub.rev < 4)) + return; + + if (!brcmf_chip_iscoreup(&sr->pub)) + brcmf_chip_resetcore(&sr->pub, 0, 0, 0); + + /* Get info for determining size */ + coreinfo = brcmf_chip_core_read32(sr, SOCRAMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + if ((sr->pub.rev <= 7) || (sr->pub.rev == 12)) { + banksize = (coreinfo & SRCI_SRBSZ_MASK); + lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; + if (lss != 0) + nb--; + *ramsize = nb * (1 << (banksize + SR_BSZ_BASE)); + if (lss != 0) + *ramsize += (1 << ((lss - 1) + SR_BSZ_BASE)); + } else { + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) { + retent = brcmf_chip_socram_banksize(sr, i, &banksize); + *ramsize += banksize; + if (retent) + *srsize += banksize; + } + } + + /* hardcoded save&restore memory sizes */ + switch (sr->chip->pub.chip) { case BRCM_CC_4334_CHIP_ID: - case BRCM_CC_43340_CHIP_ID: - ci->pub.ramsize = 0x80000; - break; - case BRCM_CC_4335_CHIP_ID: - ci->pub.ramsize = 0xc0000; - ci->pub.rambase = 0x180000; + if (sr->chip->pub.chiprev < 2) + *srsize = (32 * 1024); break; - case BRCM_CC_43362_CHIP_ID: - ci->pub.ramsize = 0x3c000; + default: break; + } +} + +/** Return the TCM-RAM size of the ARMCR4 core. */ +static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) +{ + u32 corecap; + u32 memsize = 0; + u32 nab; + u32 nbb; + u32 totb; + u32 bxinfo; + u32 idx; + + corecap = brcmf_chip_core_read32(cr4, ARMCR4_CAP); + + nab = (corecap & ARMCR4_TCBANB_MASK) >> ARMCR4_TCBANB_SHIFT; + nbb = (corecap & ARMCR4_TCBBNB_MASK) >> ARMCR4_TCBBNB_SHIFT; + totb = nab + nbb; + + for (idx = 0; idx < totb; idx++) { + brcmf_chip_core_write32(cr4, ARMCR4_BANKIDX, idx); + bxinfo = brcmf_chip_core_read32(cr4, ARMCR4_BANKINFO); + memsize += ((bxinfo & ARMCR4_BSZ_MASK) + 1) * ARMCR4_BSZ_MULT; + } + + return memsize; +} + +static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) +{ + switch (ci->pub.chip) { case BRCM_CC_4345_CHIP_ID: - ci->pub.ramsize = 0xc8000; - ci->pub.rambase = 0x198000; - break; + return 0x198000; + case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: case BRCM_CC_43567_CHIP_ID: case BRCM_CC_43569_CHIP_ID: case BRCM_CC_43570_CHIP_ID: - ci->pub.ramsize = 0xc0000; - ci->pub.rambase = 0x180000; - break; case BRCM_CC_43602_CHIP_ID: - ci->pub.ramsize = 0xf0000; - ci->pub.rambase = 0x180000; - break; + return 0x180000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; } + return 0; +} + +static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +{ + struct brcmf_core_priv *mem_core; + struct brcmf_core *mem; + + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_ARM_CR4); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, pub); + ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM); + mem_core = container_of(mem, struct brcmf_core_priv, pub); + brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, + &ci->pub.srsize); + } + brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", + ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, + ci->pub.srsize, ci->pub.srsize); + + if (!ci->pub.ramsize) { + brcmf_err("RAM size is undetermined\n"); + return -ENOMEM; + } + return 0; } static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, @@ -668,6 +833,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) struct brcmf_core *core; u32 regdata; u32 socitype; + int ret; /* Get CC core rev * Chipid is assume to be at offset 0 from SI_ENUM_BASE @@ -720,9 +886,13 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) return -ENODEV; } - brcmf_chip_get_raminfo(ci); + ret = brcmf_chip_cores_check(ci); + if (ret) + return ret; - return brcmf_chip_cores_check(ci); + /* assure chip is passive for core access */ + brcmf_chip_set_passive(&ci->pub); + return brcmf_chip_get_raminfo(ci); } static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) @@ -827,8 +997,6 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, if (err < 0) goto fail; - /* assure chip is passive for download */ - brcmf_chip_set_passive(&chip->pub); return &chip->pub; fail: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h index 7b7b62938ca368..60dcb38fc77a3f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -30,7 +30,8 @@ * @pmucaps: PMU capabilities. * @pmurev: PMU revision. * @rambase: RAM base address (only applicable for ARM CR4 chips). - * @ramsize: amount of RAM on chip. + * @ramsize: amount of RAM on chip including retention. + * @srsize: amount of retention RAM on chip. * @name: string representation of the chip identifier. */ struct brcmf_chip { @@ -41,6 +42,7 @@ struct brcmf_chip { u32 pmurev; u32 rambase; u32 ramsize; + u32 srsize; char name[8]; }; From c96a57a1c3c6a5684cad4006f260d9f88d7daed9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:32 +0100 Subject: [PATCH 0266/1983] brcmfmac: take save&restore memory into account for SDIO shared info Upstream-commit: 9819a9024eabee7bb793bbbb3f3833dfaf222dc9 The firmware provides pointer to SDIO shared information at end of RAM during firmware initialization. End of RAM is obviously determined by the actual ram size, but part of that may be used for save&restore memory. In that case another location in RAM will hold the pointer. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/sdio.c | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 822857cc398d12..386d875235db51 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -1067,44 +1067,47 @@ static inline bool brcmf_sdio_valid_shared_address(u32 addr) static int brcmf_sdio_readshared(struct brcmf_sdio *bus, struct sdpcm_shared *sh) { - u32 addr; + u32 addr = 0; int rv; u32 shaddr = 0; struct sdpcm_shared_le sh_le; __le32 addr_le; - shaddr = bus->ci->rambase + bus->ci->ramsize - 4; + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); /* * Read last word in socram to determine * address of sdpcm_shared structure */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); + shaddr = bus->ci->rambase + bus->ci->ramsize - 4; + if (!bus->ci->rambase && brcmf_chip_sr_capable(bus->ci)) + shaddr -= bus->ci->srsize; + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, + (u8 *)&addr_le, 4); if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + goto fail; /* * Check if addr is valid. * NVRAM length at the end of memory should have been overwritten. */ + addr = le32_to_cpu(addr_le); if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; + brcmf_err("invalid sdpcm_shared address 0x%08X\n", addr); + rv = -EINVAL; + goto fail; } + brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); + /* Read hndrte_shared structure */ rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, sizeof(struct sdpcm_shared_le)); if (rv < 0) - return rv; + goto fail; + + sdio_release_host(bus->sdiodev->func[1]); /* Endianness */ sh->flags = le32_to_cpu(sh_le.flags); @@ -1121,8 +1124,13 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, sh->flags & SDPCM_SHARED_VERSION_MASK); return -EPROTO; } - return 0; + +fail: + brcmf_err("unable to obtain sdpcm_shared info: rv=%d (addr=0x%x)\n", + rv, addr); + sdio_release_host(bus->sdiodev->func[1]); + return rv; } static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) From b645f4f52e91aa5587a55292c62048465e86bae7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 11 Mar 2015 16:11:33 +0100 Subject: [PATCH 0267/1983] brcmfmac: fix watchdog timer regression Upstream-commit: cf45932a74d4d07c2d05c4e871f2ea5bc0e5891c The watchdog timer is used to put the device in a low-power mode when it is idle for some time. This timer is stopped during that mode and should be restarted upon activity. This has been broken by commit d4150fced0365 ("brcmfmac: Simplify watchdog sleep."). This patch restores the behaviour as it was before that commit. Reported-by: Pontus Fuchs Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 386d875235db51..632e2458597c72 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -972,7 +972,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) brcmf_sdio_sdclk(bus, true); /* Now request HT Avail on the backplane */ brcmf_sdio_htclk(bus, true, pendok); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); break; case CLK_SDONLY: @@ -984,7 +983,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) else brcmf_err("request for %d -> %d\n", bus->clkstate, target); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); break; case CLK_NONE: @@ -993,7 +991,6 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) brcmf_sdio_htclk(bus, false, false); /* Now remove the SD clock */ brcmf_sdio_sdclk(bus, false); - brcmf_sdio_wd_timer(bus, 0); break; } #ifdef DEBUG @@ -1048,6 +1045,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmf_sdio_clkctl(bus, CLK_NONE, pendok); } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); } bus->sleeping = sleep; brcmf_dbg(SDIO, "new state %s\n", @@ -4242,6 +4240,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus->ci) { if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is * 'passive'. This is done by resetting all From a4dbbccab64c9ffb9f88da457767f8cc87a38fe3 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 18 Mar 2015 13:25:21 +0100 Subject: [PATCH 0268/1983] brcmfmac: avoid runtime-pm for sdio host controller Upstream-commit: 063d51776bd60ebf2587682eb394851c792e58d9 Several host controllers supporting runtime-pm are causing issues with our sdio wireless cards because they disable the sdio interrupt upon going into runtime suspend. This patch avoids that by doing a pm_runtime_forbid() call during the probe. Tested with Sony Vaio Duo 13 which uses sdhci-acpi host controller. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 43995308307d04..9667b6aabc00d1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -1006,6 +1007,7 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) sg_free_table(&sdiodev->sgtable); sdiodev->sbwad = 0; + pm_runtime_allow(sdiodev->func[1]->card->host->parent); return 0; } @@ -1074,7 +1076,7 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) ret = -ENODEV; goto out; } - + pm_runtime_forbid(host->parent); out: if (ret) brcmf_sdiod_remove(sdiodev); From 9354618aaebbf2d22006ce9b54d09850d8a7fdaf Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 18 Mar 2015 13:25:22 +0100 Subject: [PATCH 0269/1983] brcmfmac: Add necessary memory barriers for SDIO. Upstream-commit: 2c64e16d1ff1228df837508ab32e8a060cb06907 SDIO uses a thread to handle all communication with the device, for this data is exchanged between threads. This data needs proper memory barriers to make sure that data "exchange" is going correct. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/sdio.c | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 632e2458597c72..96d076158e34e7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -507,8 +507,8 @@ struct brcmf_sdio { struct workqueue_struct *brcmf_wq; struct work_struct datawork; - atomic_t dpc_tskcnt; - atomic_t dpc_running; + bool dpc_triggered; + bool dpc_running; bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; @@ -2713,6 +2713,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, bus->ctrl_frame_len); bus->ctrl_frame_err = err; + wmb(); bus->ctrl_frame_stat = false; } sdio_release_host(bus->sdiodev->func[1]); @@ -2734,6 +2735,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) sdio_claim_host(bus->sdiodev->func[1]); if (bus->ctrl_frame_stat) { bus->ctrl_frame_err = -ENODEV; + wmb(); bus->ctrl_frame_stat = false; brcmf_sdio_wait_event_wakeup(bus); } @@ -2744,7 +2746,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus))) { - atomic_inc(&bus->dpc_tskcnt); + bus->dpc_triggered = true; } } @@ -2940,6 +2942,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) /* Send from dpc */ bus->ctrl_frame_buf = msg; bus->ctrl_frame_len = msglen; + wmb(); bus->ctrl_frame_stat = true; brcmf_sdio_trigger_dpc(bus); @@ -2958,6 +2961,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) if (!ret) { brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", bus->ctrl_frame_err); + rmb(); ret = bus->ctrl_frame_err; } @@ -3526,8 +3530,8 @@ static int brcmf_sdio_bus_preinit(struct device *dev) void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) { - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); + if (!bus->dpc_triggered) { + bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } } @@ -3558,7 +3562,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) if (!bus->intr) brcmf_err("isr w/o interrupt configured!\n"); - atomic_inc(&bus->dpc_tskcnt); + bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } @@ -3578,7 +3582,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) if (!bus->intr || (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { - if (atomic_read(&bus->dpc_tskcnt) == 0) { + if (!bus->dpc_triggered) { u8 devpend; sdio_claim_host(bus->sdiodev->func[1]); @@ -3596,7 +3600,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) bus->sdcnt.pollcnt++; atomic_set(&bus->ipend, 1); - atomic_inc(&bus->dpc_tskcnt); + bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } } @@ -3623,17 +3627,21 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ - if ((atomic_read(&bus->dpc_tskcnt) == 0) && - (atomic_read(&bus->dpc_running) == 0) && - (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { - bus->idlecount++; - if (bus->idlecount > bus->idletime) { - brcmf_dbg(SDIO, "idle\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); + if (!bus->dpc_triggered) { + rmb(); + if ((!bus->dpc_running) && (bus->idletime > 0) && + (bus->clkstate == CLK_AVAIL)) { + bus->idlecount++; + if (bus->idlecount > bus->idletime) { + brcmf_dbg(SDIO, "idle\n"); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_wd_timer(bus, 0); + bus->idlecount = 0; + brcmf_sdio_bus_sleep(bus, true, false); + sdio_release_host(bus->sdiodev->func[1]); + } + } else { bus->idlecount = 0; - brcmf_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); } } else { bus->idlecount = 0; @@ -3645,13 +3653,14 @@ static void brcmf_sdio_dataworker(struct work_struct *work) struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, datawork); - while (atomic_read(&bus->dpc_tskcnt)) { - atomic_set(&bus->dpc_running, 1); - atomic_set(&bus->dpc_tskcnt, 0); + bus->dpc_running = true; + wmb(); + while (ACCESS_ONCE(bus->dpc_triggered)) { + bus->dpc_triggered = false; brcmf_sdio_dpc(bus); bus->idlecount = 0; - atomic_set(&bus->dpc_running, 0); } + bus->dpc_running = false; if (brcmf_sdiod_freezing(bus->sdiodev)) { brcmf_sdiod_change_state(bus->sdiodev, BRCMF_SDIOD_DOWN); brcmf_sdiod_try_freeze(bus->sdiodev); @@ -4144,8 +4153,8 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->watchdog_tsk = NULL; } /* Initialize DPC thread */ - atomic_set(&bus->dpc_tskcnt, 0); - atomic_set(&bus->dpc_running, 0); + bus->dpc_triggered = false; + bus->dpc_running = false; /* Assign bus interface call back */ bus->sdiodev->bus_if->dev = bus->sdiodev->dev; From 3f23a97e96c3b1ba7c57a4055240fb1bbfed396f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 18 Mar 2015 13:25:23 +0100 Subject: [PATCH 0270/1983] brcmfmac: Update msgbuf commonring size for improved throughput. Upstream-commit: a74196bb67c4f21a8308d26ba3bbdd22d766f5a4 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h index 77a51b8c1e1208..3d513e407e3d5d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.h @@ -17,11 +17,11 @@ #ifdef CONFIG_BRCMFMAC_PROTO_MSGBUF -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 20 -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 256 -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 20 +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_MAX_ITEM 64 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT_MAX_ITEM 512 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE_MAX_ITEM 64 #define BRCMF_D2H_MSGRING_TX_COMPLETE_MAX_ITEM 1024 -#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 256 +#define BRCMF_D2H_MSGRING_RX_COMPLETE_MAX_ITEM 512 #define BRCMF_H2D_TXFLOWRING_MAX_ITEM 512 #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT_ITEMSIZE 40 From 5e5700400a4e539130b844f91da1f97899e4c4f4 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 18 Mar 2015 13:25:24 +0100 Subject: [PATCH 0271/1983] brcmfmac: Remove unnecessary new-line in pcie console logging. Upstream-commit: ef5671d29df558405f454c606c639db3337548e2 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index aa7a4dfb8beb77..ec426f38ef9622 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -651,10 +651,9 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) console->log_str[console->log_idx] = ch; console->log_idx++; } - if (ch == '\n') { console->log_str[console->log_idx] = 0; - brcmf_dbg(PCIE, "CONSOLE: %s\n", console->log_str); + brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str); console->log_idx = 0; } } From 1cb3dbbcba440513f49cf8fbb6b5a19be5c8cd39 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 18 Mar 2015 13:25:25 +0100 Subject: [PATCH 0272/1983] brcmfmac: add MODULE_FIRMWARE() macros for bcm4356 PCIe device Upstream-commit: 7fca40eb003009e09614fb70edb6e48851f3fefc The BCM4356 PCIe wireless device was added recently but overlooked the fact that the MODULE_FIRMWARE() macros were missing for the firmwares needed by this device. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index ec426f38ef9622..d4066e880dfc5c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -189,6 +189,8 @@ MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); From 31b94d99278e54d2a129fee39463850a880543d7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 18 Mar 2015 13:25:26 +0100 Subject: [PATCH 0273/1983] brcmfmac: add support for BCM43430 SDIO chipset Upstream-commit: 25911556283e5093fca235d7ba03faae7ed5dbf2 This patch added support for the BCM43430 802.11n SDIO chipset. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/chip.c | 18 ++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 5 +++++ .../wireless/brcm80211/include/brcm_hw_ids.h | 1 + .../wireless/brcm80211/include/chipcommon.h | 9 ++++++++- include/linux/mmc/sdio_ids.h | 1 + 6 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9667b6aabc00d1..9b508bd3b83925 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1098,6 +1098,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 734172570054d3..ab2fac8b2760a8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -600,6 +600,12 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, if (sr->chip->pub.chiprev < 2) *srsize = (32 * 1024); break; + case BRCM_CC_43430_CHIP_ID: + /* assume sr for now as we can not check + * firmware sr capability at this point. + */ + *srsize = (64 * 1024); + break; default: break; } @@ -1072,6 +1078,7 @@ static void brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; + struct brcmf_core_priv *sr; brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); @@ -1081,6 +1088,13 @@ brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) D11_BCMA_IOCTL_PHYCLOCKEN); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); brcmf_chip_resetcore(core, 0, 0, 0); + + /* disable bank #3 remap for this device */ + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + sr = container_of(core, struct brcmf_core_priv, pub); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); + brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); + } } static bool brcmf_chip_cm3_set_active(struct brcmf_chip_priv *chip) @@ -1188,6 +1202,10 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) addr = CORE_CC_REG(base, chipcontrol_data); reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; + case BRCM_CC_43430_CHIP_ID: + addr = CORE_CC_REG(base, sr_control1); + reg = chip->ops->read32(chip->ctx, addr); + return reg != 0; default: addr = CORE_CC_REG(base, pmucapabilities_ext); reg = chip->ops->read32(chip->ctx, addr); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 96d076158e34e7..4a0fefe6879cf0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -615,6 +615,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" #define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" +#define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" #define BCM4345_FIRMWARE_NAME "brcm/brcmfmac4345-sdio.bin" #define BCM4345_NVRAM_NAME "brcm/brcmfmac4345-sdio.txt" #define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" @@ -640,6 +642,8 @@ MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43362_NVRAM_NAME); MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43430_NVRAM_NAME); MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4345_NVRAM_NAME); MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); @@ -671,6 +675,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, { BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index b599e7e411486d..4efdd51af9c8fd 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -37,6 +37,7 @@ #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_43430_CHIP_ID 43430 #define BRCM_CC_4345_CHIP_ID 0x4345 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 diff --git a/drivers/net/wireless/brcm80211/include/chipcommon.h b/drivers/net/wireless/brcm80211/include/chipcommon.h index d242333b7559fc..e1fd499930a03a 100644 --- a/drivers/net/wireless/brcm80211/include/chipcommon.h +++ b/drivers/net/wireless/brcm80211/include/chipcommon.h @@ -183,7 +183,14 @@ struct chipcregs { u8 uart1lsr; u8 uart1msr; u8 uart1scratch; - u32 PAD[126]; + u32 PAD[62]; + + /* save/restore, corerev >= 48 */ + u32 sr_capability; /* 0x500 */ + u32 sr_control0; /* 0x504 */ + u32 sr_control1; /* 0x508 */ + u32 gpio_control; /* 0x50C */ + u32 PAD[60]; /* PMU registers (corerev >= 20) */ u32 pmucontrol; /* 0x600 */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 91397858dc9586..83430f2ea757df 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -33,6 +33,7 @@ #define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d #define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 #define SDIO_DEVICE_ID_BROADCOM_43362 0xa962 +#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6 #define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 From cf43b4320ec632fe3fe3f68f73b733c3cd3b0092 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 18 Mar 2015 13:25:27 +0100 Subject: [PATCH 0274/1983] brcmfmac: only support the BCM43455/7 device Upstream-commit: 228a71763d9f24637a6db2d1712f1e1a21a80ec6 Recently support was added for the BCM4345 SDIO chipset by commit 9c51026509d7 ("brcmfmac: Add support for BCM4345 SDIO chipset") however this was verified using a BCM43455 device, which is a more recent revision of the chip. This patch assure that older revisions are not probed as they would fail. Reviewed-by: Hante Meuleman Reviewed-by: Syed Asifful Dayyan Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 4a0fefe6879cf0..7374bd1ac62ff3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -617,8 +617,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" #define BCM43430_FIRMWARE_NAME "brcm/brcmfmac43430-sdio.bin" #define BCM43430_NVRAM_NAME "brcm/brcmfmac43430-sdio.txt" -#define BCM4345_FIRMWARE_NAME "brcm/brcmfmac4345-sdio.bin" -#define BCM4345_NVRAM_NAME "brcm/brcmfmac4345-sdio.txt" +#define BCM43455_FIRMWARE_NAME "brcm/brcmfmac43455-sdio.bin" +#define BCM43455_NVRAM_NAME "brcm/brcmfmac43455-sdio.txt" #define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" #define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" @@ -644,8 +644,8 @@ MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4339_NVRAM_NAME); MODULE_FIRMWARE(BCM43430_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43430_NVRAM_NAME); -MODULE_FIRMWARE(BCM4345_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4345_NVRAM_NAME); +MODULE_FIRMWARE(BCM43455_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43455_NVRAM_NAME); MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4354_NVRAM_NAME); @@ -676,7 +676,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, { BRCM_CC_43430_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43430) }, - { BRCM_CC_4345_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4345) }, + { BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43455) }, { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; From 754747e34055f9e3a6d8c3cdf9fc06ceeabb7e95 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 18 Mar 2015 13:25:28 +0100 Subject: [PATCH 0275/1983] brcmfmac: remove support for unreleased BCM4354 PCIe Upstream-commit: 55ab9a72bd324648987063e283ab3d4228efdaef There are no known BCM4354 PCIe devices released so removing support from the driver until proven otherwise. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index d4066e880dfc5c..1831ecd0813e95 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -47,8 +47,6 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" #define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" -#define BRCMF_PCIE_4354_FW_NAME "brcm/brcmfmac4354-pcie.bin" -#define BRCMF_PCIE_4354_NVRAM_NAME "brcm/brcmfmac4354-pcie.txt" #define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" #define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" #define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" @@ -187,8 +185,6 @@ enum brcmf_pcie_state { MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4354_FW_NAME); -MODULE_FIRMWARE(BRCMF_PCIE_4354_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); @@ -1327,10 +1323,6 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_43602_FW_NAME; nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; break; - case BRCM_CC_4354_CHIP_ID: - fw_name = BRCMF_PCIE_4354_FW_NAME; - nvram_name = BRCMF_PCIE_4354_NVRAM_NAME; - break; case BRCM_CC_4356_CHIP_ID: fw_name = BRCMF_PCIE_4356_FW_NAME; nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; @@ -1855,7 +1847,6 @@ static int brcmf_pcie_resume(struct pci_dev *pdev) PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } static struct pci_device_id brcmf_pcie_devid_table[] = { - BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), From ef5a74197789414313590dacdca113428133c627 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 20 Mar 2015 22:18:17 +0100 Subject: [PATCH 0276/1983] brcmfmac: disable MBSS feature for BCM43362 Upstream-commit: f93a25b38cbd840f26c9fd2dd8a6611a57b259b7 The BCM43362 firmware falsely reports it is capable of providing MBSS. As a result AP mode no longer works for this device. Therefor disable MBSS in the driver for this chipset. Cc: stable@vger.kernel.org # 3.19.y Reported-by: Jorg Krause Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index defb7a44e0bc1f..7748a1ccf14fdf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -126,7 +126,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); + if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) + brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); /* set chip related quirks */ switch (drvr->bus_if->chip) { From 9e9b14334133bc2881cacb8096849002e527e25e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 26 May 2015 13:19:46 +0200 Subject: [PATCH 0277/1983] brcmfmac: avoid null pointer access when brcmf_msgbuf_get_pktid() fails Upstream-commit: 7d072b404c5d8f1e0b62b6bc488dfeaa61bd2d8d The function brcmf_msgbuf_get_pktid() may return a NULL pointer so the callers should check the return pointer before accessing it to avoid the crash below (see [1]): brcmfmac: brcmf_msgbuf_get_pktid: Invalid packet id 273 (not in use) BUG: unable to handle kernel NULL pointer dereference at 0000000000000080 IP: [] skb_pull+0x5/0x50 PGD 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: pci_stub vboxpci(O) vboxnetflt(O) vboxnetadp(O) vboxdrv(O) snd_hda_codec_hdmi bnep mousedev hid_generic ushwmon msr ext4 crc16 mbcache jbd2 sd_mod uas usb_storage ahci libahci libata scsi_mod xhci_pci xhci_hcd usbcore usb_common CPU: 0 PID: 1661 Comm: irq/61-brcmf_pc Tainted: G O 4.0.1-MacbookPro-ARCH #1 Hardware name: Apple Inc. MacBookPro12,1/Mac-E43C1C25D4880AD6, BIOS MBP121.88Z.0167.B02.1503241251 03/24/2015 task: ffff880264203cc0 ti: ffff88025ffe4000 task.ti: ffff88025ffe4000 RIP: 0010:[] [] skb_pull+0x5/0x50 RSP: 0018:ffff88025ffe7d40 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ffff88008a33c000 RCX: 0000000000000044 RDX: 0000000000000000 RSI: 000000000000004a RDI: 0000000000000000 RBP: ffff88025ffe7da8 R08: 0000000000000096 R09: 000000000000004a R10: 0000000000000000 R11: 000000000000048e R12: ffff88025ff14f00 R13: 0000000000000000 R14: ffff880263b48200 R15: ffff88008a33c000 FS: 0000000000000000(0000) GS:ffff88026ec00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000080 CR3: 000000000180b000 CR4: 00000000003407f0 Stack: ffffffffa06aed74 ffff88025ffe7dc8 ffff880263b48270 ffff880263b48278 05ea88020000004a 0002ffff81014635 000000001720b2f6 ffff88026ec116c0 ffff880263b48200 0000000000010000 ffff880263b4ae00 ffff880264203cc0 Call Trace: [] ? brcmf_msgbuf_process_rx+0x404/0x480 [brcmfmac] [] ? irq_finalize_oneshot.part.30+0xf0/0xf0 [] brcmf_proto_msgbuf_rx_trigger+0x35/0xf0 [brcmfmac] [] brcmf_pcie_isr_thread_v2+0x8a/0x130 [brcmfmac] [] irq_thread_fn+0x20/0x50 [] irq_thread+0x13f/0x170 [] ? wake_threads_waitq+0x30/0x30 [] ? irq_thread_dtor+0xb0/0xb0 [] kthread+0xd8/0xf0 [] ? kthread_create_on_node+0x1c0/0x1c0 [] ret_from_fork+0x58/0x90 [] ? kthread_create_on_node+0x1c0/0x1c0 Code: 01 83 e2 f7 88 50 01 48 83 c4 08 5b 5d f3 c3 0f 1f 80 00 00 00 00 83 e2 f7 88 50 01 c3 66 0f 1f 84 00 00 00 00 00 0f 1f RIP [] skb_pull+0x5/0x50 RSP CR2: 0000000000000080 ---[ end trace b074c0f90e7c997d ]--- [1] http://mid.gmane.org/20150430193259.GA5630@googlemail.com Cc: # v3.18, v3.19, v4.0, v4.1 Reported-by: Michael Hornung Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 4ec9811f49c877..65efb146898844 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -511,11 +511,9 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, msgbuf->rx_pktids, msgbuf->ioctl_resp_pktid); if (msgbuf->ioctl_resp_ret_len != 0) { - if (!skb) { - brcmf_err("Invalid packet id idx recv'd %d\n", - msgbuf->ioctl_resp_pktid); + if (!skb) return -EBADF; - } + memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? len : msgbuf->ioctl_resp_ret_len); } @@ -874,10 +872,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) flowid -= BRCMF_NROF_H2D_COMMON_MSGRINGS; skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, idx); - if (!skb) { - brcmf_err("Invalid packet id idx recv'd %d\n", idx); + if (!skb) return; - } set_bit(flowid, msgbuf->txstatus_done_map); commonring = msgbuf->flowrings[flowid]; @@ -1156,6 +1152,8 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) skb = brcmf_msgbuf_get_pktid(msgbuf->drvr->bus_if->dev, msgbuf->rx_pktids, idx); + if (!skb) + return; if (data_offset) skb_pull(skb, data_offset); From 594da631070b003da58525facd4a3257a4496100 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 14 Apr 2015 20:10:24 +0200 Subject: [PATCH 0278/1983] brcmfmac: use static superset of channels for wiphy bands Upstream-commit: 9e5e7bed4c64274e491352131e4863eb4b687fff The driver was constructing a list of channels per wiphy band by querying the device. This list is not what the hardware is able to do as it is already filtered by the country setting in the device. As user-space may change the country this would require updating the channel list which is not recommended [1]. This patch introduces a superset of channels. The individual channels are disabled appropriately by querying the device. [1] http://mid.gmane.org/1426706320.3001.21.camel@sipsolutions.net Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/cfg80211.c | 193 ++++++++++-------- 1 file changed, 106 insertions(+), 87 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 73ba3d4fa47b48..3fc7ef84d37e71 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -129,13 +129,47 @@ static struct ieee80211_rate __wl_rates[] = { RATETAB_ENT(BRCM_RATE_54M, 0), }; -#define wl_a_rates (__wl_rates + 4) -#define wl_a_rates_size 8 #define wl_g_rates (__wl_rates + 0) -#define wl_g_rates_size 12 +#define wl_g_rates_size ARRAY_SIZE(__wl_rates) +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size (wl_g_rates_size - 4) + +#define CHAN2G(_channel, _freq) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), + CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), + CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), + CHAN2G(13, 2472), CHAN2G(14, 2484) +}; + +static struct ieee80211_channel __wl_5ghz_channels[] = { + CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), + CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), + CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), + CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), + CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), + CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) +}; /* Band templates duplicated per wiphy. The channel info - * is filled in after querying the device. + * above is added to the band during setup. */ static const struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, @@ -143,7 +177,7 @@ static const struct ieee80211_supported_band __wl_band_2ghz = { .n_bitrates = wl_g_rates_size, }; -static const struct ieee80211_supported_band __wl_band_5ghz_a = { +static const struct ieee80211_supported_band __wl_band_5ghz = { .band = IEEE80211_BAND_5GHZ, .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, @@ -5247,40 +5281,6 @@ brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, return err; } -/* Filter the list of channels received from firmware counting only - * the 20MHz channels. The wiphy band data only needs those which get - * flagged to indicate if they can take part in higher bandwidth. - */ -static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, - struct brcmf_chanspec_list *chlist, - u32 chcnt[]) -{ - u32 total = le32_to_cpu(chlist->count); - struct brcmu_chan ch; - int i; - - for (i = 0; i < total; i++) { - ch.chspec = (u16)le32_to_cpu(chlist->element[i]); - cfg->d11inf.decchspec(&ch); - - /* Firmware gives a ordered list. We skip non-20MHz - * channels is 2G. For 5G we can abort upon reaching - * a non-20MHz channel in the list. - */ - if (ch.bw != BRCMU_CHAN_BW_20) { - if (ch.band == BRCMU_CHAN_BAND_5G) - break; - else - continue; - } - - if (ch.band == BRCMU_CHAN_BAND_2G) - chcnt[0] += 1; - else if (ch.band == BRCMU_CHAN_BAND_5G) - chcnt[1] += 1; - } -} - static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, struct brcmu_chan *ch) { @@ -5316,7 +5316,6 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, u32 i, j; u32 total; u32 chaninfo; - u32 chcnt[2] = { 0, 0 }; u32 index; pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); @@ -5333,42 +5332,15 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, goto fail_pbuf; } - brcmf_count_20mhz_channels(cfg, list, chcnt); wiphy = cfg_to_wiphy(cfg); - if (chcnt[0]) { - band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), - GFP_KERNEL); - if (band == NULL) { - err = -ENOMEM; - goto fail_pbuf; - } - band->channels = kcalloc(chcnt[0], sizeof(*channel), - GFP_KERNEL); - if (band->channels == NULL) { - kfree(band); - err = -ENOMEM; - goto fail_pbuf; - } - band->n_channels = 0; - wiphy->bands[IEEE80211_BAND_2GHZ] = band; - } - if (chcnt[1]) { - band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a), - GFP_KERNEL); - if (band == NULL) { - err = -ENOMEM; - goto fail_band2g; - } - band->channels = kcalloc(chcnt[1], sizeof(*channel), - GFP_KERNEL); - if (band->channels == NULL) { - kfree(band); - err = -ENOMEM; - goto fail_band2g; - } - band->n_channels = 0; - wiphy->bands[IEEE80211_BAND_5GHZ] = band; - } + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; total = le32_to_cpu(list->count); for (i = 0; i < total; i++) { @@ -5383,6 +5355,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); continue; } + if (!band) + continue; if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; @@ -5410,9 +5384,9 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, } else if (ch.bw == BRCMU_CHAN_BW_40) { brcmf_update_bw40_channel_flag(&channel[index], &ch); } else { - /* disable other bandwidths for now as mentioned - * order assure they are enabled for subsequent - * chanspecs. + /* enable the channel and disable other bandwidths + * for now as mentioned order assure they are enabled + * for subsequent chanspecs. */ channel[index].flags = IEEE80211_CHAN_NO_HT40 | IEEE80211_CHAN_NO_80MHZ; @@ -5431,16 +5405,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, IEEE80211_CHAN_NO_IR; } } - if (index == band->n_channels) - band->n_channels++; } - kfree(pbuf); - return 0; -fail_band2g: - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); - wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; fail_pbuf: kfree(pbuf); return err; @@ -5773,7 +5739,12 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { + struct ieee80211_supported_band *band; struct ieee80211_iface_combination ifc_combo; + __le32 bandlist[3]; + u32 n_bands; + int err, i; + wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; @@ -5815,7 +5786,52 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) brcmf_wiphy_wowl_params(wiphy); - return brcmf_setup_wiphybands(wiphy); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, + sizeof(bandlist)); + if (err) { + brcmf_err("could not obtain band info: err=%d\n", err); + return err; + } + /* first entry in bandlist is number of bands */ + n_bands = le32_to_cpu(bandlist[0]); + for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { + if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { + band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_2ghz_channels, + sizeof(__wl_2ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); + wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { + band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_5ghz_channels, + sizeof(__wl_5ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); + wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } + } + err = brcmf_setup_wiphybands(wiphy); + return err; } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) @@ -6005,6 +6021,9 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, static void brcmf_free_wiphy(struct wiphy *wiphy) { + if (!wiphy) + return; + kfree(wiphy->iface_combinations); if (wiphy->bands[IEEE80211_BAND_2GHZ]) { kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); From ac28bffb63533ecb7cd7a3da3d5dd43f3e5f3db7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 14 Apr 2015 20:10:25 +0200 Subject: [PATCH 0279/1983] brcmfmac: update wiphy band information upon updating regulatory domain Upstream-commit: ab686970abe624d07ae006f008d485f60761315f When change the country code the available channels may change. So the wiphy bands should be updated accordingly. Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 3fc7ef84d37e71..957759dd0b7cf0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -6016,7 +6016,11 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, memset(&ccreq, 0, sizeof(ccreq)); ccreq.rev = cpu_to_le32(-1); memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); - brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); + if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { + brcmf_err("firmware rejected country setting\n"); + return; + } + brcmf_setup_wiphybands(wiphy); } static void brcmf_free_wiphy(struct wiphy *wiphy) From 9d772afe65fb8419e265fce6390460f55d65261e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 14 Apr 2015 20:10:26 +0200 Subject: [PATCH 0280/1983] brcmfmac: add description for feature flags Upstream-commit: 117a887cd6c2f699110efc2b3040625789511495 Some feature flags were not described in the header file. Adding the description. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/feature.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index f5832e077bb799..4bf40ff327d6ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -19,7 +19,9 @@ /* * Features: * + * MBSS: multiple BSSID support (eg. guest network in AP mode). * MCHAN: multi-channel for concurrent P2P. + * WOWL: Wake-On-WLAN. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ From b77ff5104edf6927e2f9daff7e2006b7e795ae02 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 14 Apr 2015 20:10:27 +0200 Subject: [PATCH 0281/1983] brcmfmac: make scheduled scan support conditional Upstream-commit: 9fa5b107d7c5edbb36aa25ff0d02c66c431dae1f The scheduled scan support depends on firmware supporting the PNO feature. This feature is optional so add a feature flag for this in the driver and announce scheduled scan support accordingly. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 3 ++- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 1 + drivers/net/wireless/brcm80211/brcmfmac/feature.h | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 957759dd0b7cf0..bea9bb94cf6f3c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -5777,7 +5777,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; - brcmf_wiphy_pno_params(wiphy); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) + brcmf_wiphy_pno_params(wiphy); /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 7748a1ccf14fdf..2c5fad3a3aa225 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -124,6 +124,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) struct brcmf_if *ifp = drvr->iflist[0]; brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index 4bf40ff327d6ec..546962525cd2a1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -21,11 +21,13 @@ * * MBSS: multiple BSSID support (eg. guest network in AP mode). * MCHAN: multi-channel for concurrent P2P. + * PNO: preferred network offload. * WOWL: Wake-On-WLAN. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ BRCMF_FEAT_DEF(MCHAN) \ + BRCMF_FEAT_DEF(PNO) \ BRCMF_FEAT_DEF(WOWL) /* * Quirks: From eb7f909add1d1abffb3608ab024376ba3996135e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 14 Apr 2015 20:10:29 +0200 Subject: [PATCH 0282/1983] brcmfmac: process interrupt regardless sdiod state Upstream-commit: 620490e57876e5e7883278aa88bd1745c30540d1 When the sdio bus state is not ready to process we abort the interrupt service routine. This is not wanted as it keeps the interrupt source active. Better clear the interrupt source. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 7374bd1ac62ff3..1027b3769ad0db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -3550,10 +3550,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (bus->sdiodev->state != BRCMF_SDIOD_DATA) { - brcmf_err("bus is down. we have nothing to do\n"); - return; - } /* Count the interrupt call */ bus->sdcnt.intrcount++; if (in_interrupt()) From 492f5691be96e6c038070d35205e2fdcb371637f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 14 Apr 2015 20:10:30 +0200 Subject: [PATCH 0283/1983] brcmfmac: fix sdio suspend and resume Upstream-commit: f5c0cdd7e9483ba8f9afe79bc64538cbab7e5c68 commit 330b4e4be937 ("brcmfmac: Add wowl support for SDIO devices.") changed the behaviour by removing the MMC_PM_KEEP_POWER flag for non-wowl scenario, which needs to be restored. Another necessary change is to mark the card as being non-removable. With this in place the suspend resume test passes successfully doing: # echo devices > /sys/power/pm_test # echo mem > /sys/power/state Note that power may still be switched off when system is going in S3 state. Reported-by: Fu, Zhonghui < Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Acked-by: Ulf Hansson Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9b508bd3b83925..8a69544485a93d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1011,6 +1011,14 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) return 0; } +static void brcmf_sdiod_host_fixup(struct mmc_host *host) +{ + /* runtime-pm powers off the device */ + pm_runtime_forbid(host->parent); + /* avoid removal detection upon resume */ + host->caps |= MMC_CAP_NONREMOVABLE; +} + static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) { struct sdio_func *func; @@ -1076,7 +1084,7 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) ret = -ENODEV; goto out; } - pm_runtime_forbid(host->parent); + brcmf_sdiod_host_fixup(host); out: if (ret) brcmf_sdiod_remove(sdiodev); @@ -1246,15 +1254,15 @@ static int brcmf_ops_sdio_suspend(struct device *dev) brcmf_sdiod_freezer_on(sdiodev); brcmf_sdio_wd_timer(sdiodev->bus, 0); + sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->wowl_enabled) { - sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->pdata->oob_irq_supported) enable_irq_wake(sdiodev->pdata->oob_irq_nr); else - sdio_flags = MMC_PM_WAKE_SDIO_IRQ; - if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) - brcmf_err("Failed to set pm_flags %x\n", sdio_flags); + sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; } + if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) + brcmf_err("Failed to set pm_flags %x\n", sdio_flags); return 0; } From 71e662a545da988e231a6f32f4158d5ff828f4b1 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 14 Apr 2015 20:10:33 +0200 Subject: [PATCH 0284/1983] brcmfmac: Add support for multiple PCIE devices in nvram. Upstream-commit: 7eba6611e5e06d68934c450f0ae8ad462b44e07e With PCIE it is possible to support multiple devices with the same device type. They all load the same nvram file. In order to support this the nvram can specify which part of the nvram is for which pcie device. This patch adds support for these new types of nvram files. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/firmware.c | 189 +++++++++++++++++- .../wireless/brcm80211/brcmfmac/firmware.h | 6 + .../net/wireless/brcm80211/brcmfmac/pcie.c | 15 +- 3 files changed, 197 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 9cb99152ad1753..8ff31ffa4a412e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -23,6 +23,10 @@ #include "debug.h" #include "firmware.h" +#define BRCMF_FW_MAX_NVRAM_SIZE 64000 +#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ +#define BRCMF_FW_NVRAM_PCIEDEV_LEN 9 /* pcie/1/4/ */ + char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; module_param_string(firmware_path, brcmf_firmware_path, BRCMF_FW_PATH_LEN, 0440); @@ -46,6 +50,8 @@ enum nvram_parser_state { * @column: current column in line. * @pos: byte offset in input buffer. * @entry: start position of key,value entry. + * @multi_dev_v1: detect pcie multi device v1 (compressed). + * @multi_dev_v2: detect pcie multi device v2. */ struct nvram_parser { enum nvram_parser_state state; @@ -56,6 +62,8 @@ struct nvram_parser { u32 column; u32 pos; u32 entry; + bool multi_dev_v1; + bool multi_dev_v2; }; static bool is_nvram_char(char c) @@ -108,6 +116,10 @@ static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) st = COMMENT; else st = VALUE; + if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0) + nvp->multi_dev_v1 = true; + if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0) + nvp->multi_dev_v2 = true; } else if (!is_nvram_char(c)) { brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", nvp->line, nvp->column); @@ -133,6 +145,8 @@ brcmf_nvram_handle_value(struct nvram_parser *nvp) ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; skv = (u8 *)&nvp->fwnv->data[nvp->entry]; cplen = ekv - skv; + if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE) + return END; /* copy to output buffer */ memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); nvp->nvram_len += cplen; @@ -180,10 +194,18 @@ static enum nvram_parser_state static int brcmf_init_nvram_parser(struct nvram_parser *nvp, const struct firmware *nv) { + size_t size; + memset(nvp, 0, sizeof(*nvp)); nvp->fwnv = nv; + /* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */ + if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE) + size = BRCMF_FW_MAX_NVRAM_SIZE; + else + size = nv->size; /* Alloc for extra 0 byte + roundup by 4 + length field */ - nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); + size += 1 + 3 + sizeof(u32); + nvp->nvram = kzalloc(size, GFP_KERNEL); if (!nvp->nvram) return -ENOMEM; @@ -192,12 +214,136 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, return 0; } +/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v1 is the version where nvram is stored + * compressed and "devpath" maps to index for valid entries. + */ +static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + u32 i, j; + bool found; + u8 *nvram; + u8 id; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* min length: devpath0=pcie/1/4/ + 0:x=y */ + if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6) + goto fail; + + /* First search for the devpathX and see if it is the configuration + * for domain_nr/bus_nr. Search complete nvp + */ + found = false; + i = 0; + while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { + /* Format: devpathX=pcie/Y/Z/ + * Y = domain_nr, Z = bus_nr, X = virtual ID + */ + if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) && + (strncmp(&nvp->nvram[i + 8], "=pcie/", 6) == 0)) { + if (((nvp->nvram[i + 14] - '0') == domain_nr) && + ((nvp->nvram[i + 16] - '0') == bus_nr)) { + id = nvp->nvram[i + 7] - '0'; + found = true; + break; + } + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + if (!found) + goto fail; + + /* Now copy all valid entries, release old nvram and assign new one */ + i = 0; + j = 0; + while (i < nvp->nvram_len) { + if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) { + i += 2; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; + +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + +/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v2 is the version where nvram is stored + * uncompressed, all relevant valid entries are identified by + * pcie/domain_nr/bus_nr: + */ +static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + u32 i, j; + u8 *nvram; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* Copy all valid entries, release old nvram and assign new one. + * Valid entries are of type pcie/X/Y/ where X = domain_nr and + * Y = bus_nr. + */ + i = 0; + j = 0; + while (i < nvp->nvram_len - BRCMF_FW_NVRAM_PCIEDEV_LEN) { + if ((strncmp(&nvp->nvram[i], "pcie/", 5) == 0) && + (nvp->nvram[i + 6] == '/') && (nvp->nvram[i + 8] == '/') && + ((nvp->nvram[i + 5] - '0') == domain_nr) && + ((nvp->nvram[i + 7] - '0') == bus_nr)) { + i += BRCMF_FW_NVRAM_PCIEDEV_LEN; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + /* brcmf_nvram_strip :Takes a buffer of "=\n" lines read from a fil * and ending in a NUL. Removes carriage returns, empty lines, comment lines, * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. * End of buffer is completed with token identifying length of buffer. */ -static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length, + u16 domain_nr, u16 bus_nr) { struct nvram_parser nvp; u32 pad; @@ -212,6 +358,16 @@ static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) if (nvp.state == END) break; } + if (nvp.multi_dev_v1) + brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr); + else if (nvp.multi_dev_v2) + brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr); + + if (nvp.nvram_len == 0) { + kfree(nvp.nvram); + return NULL; + } + pad = nvp.nvram_len; *new_length = roundup(nvp.nvram_len + 1, 4); while (pad != *new_length) { @@ -239,6 +395,8 @@ struct brcmf_fw { u16 flags; const struct firmware *code; const char *nvram_name; + u16 domain_nr; + u16 bus_nr; void (*done)(struct device *dev, const struct firmware *fw, void *nvram_image, u32 nvram_len); }; @@ -254,7 +412,8 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) goto fail; if (fw) { - nvram = brcmf_fw_nvram_strip(fw, &nvram_length); + nvram = brcmf_fw_nvram_strip(fw, &nvram_length, + fwctx->domain_nr, fwctx->bus_nr); release_firmware(fw); if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) goto fail; @@ -309,11 +468,12 @@ static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) kfree(fwctx); } -int brcmf_fw_get_firmwares(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len)) +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr) { struct brcmf_fw *fwctx; @@ -333,8 +493,21 @@ int brcmf_fw_get_firmwares(struct device *dev, u16 flags, fwctx->done = fw_cb; if (flags & BRCMF_FW_REQUEST_NVRAM) fwctx->nvram_name = nvram; + fwctx->domain_nr = domain_nr; + fwctx->bus_nr = bus_nr; return request_firmware_nowait(THIS_MODULE, true, code, dev, GFP_KERNEL, fwctx, brcmf_fw_request_code_done); } + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, + 0); +} + diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 4d3482356b77b0..604dd48ab4e095 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram); * fails it will not use the callback, but call device_release_driver() * instead which will call the driver .remove() callback. */ +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr); int brcmf_fw_get_firmwares(struct device *dev, u16 flags, const char *code, const char *nvram, void (*fw_cb)(struct device *dev, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 1831ecd0813e95..6740ab3ee251d0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -1641,8 +1641,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct brcmf_pciedev_info *devinfo; struct brcmf_pciedev *pcie_bus_dev; struct brcmf_bus *bus; + u16 domain_nr; + u16 bus_nr; - brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device); + domain_nr = pci_domain_nr(pdev->bus) + 1; + bus_nr = pdev->bus->number; + brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device, + domain_nr, bus_nr); ret = -ENOMEM; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); @@ -1691,10 +1696,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto fail_bus; - ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM | - BRCMF_FW_REQ_NV_OPTIONAL, - devinfo->fw_name, devinfo->nvram_name, - brcmf_pcie_setup); + ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup, domain_nr, bus_nr); if (ret == 0) return 0; fail_bus: From 3b4eef5eb1dde3760d153e61541973add01033a3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 May 2015 12:59:19 +0300 Subject: [PATCH 0285/1983] brcmfmac: cleanup a sizeof() Upstream-commit: f88ad05f6755badf539924f44dcadbe8e64e04de "flowrings" and "*flowrings" are both pointers so this always returns sizeof(void *) and the current code works fine. But "*flowrings" is intended here and static checkers complain, so lets change it. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 6740ab3ee251d0..31d66a7b11aaf4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -1609,7 +1609,7 @@ static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, bus->msgbuf->commonrings[i] = &devinfo->shared.commonrings[i]->commonring; - flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(flowrings), + flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), GFP_KERNEL); if (!flowrings) goto fail; From 5d812896645de7bb5a2b62afaceaf11f3be1d241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 7 May 2015 14:13:03 +0200 Subject: [PATCH 0286/1983] brcmfmac: check result of USB firmware request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream-commit: beb4e0fcd663cc7cf1c113bf7eae639a6c6126a2 This prevents silence failures with driver waiting (infinitely) for a callback. Signed-off-by: Rafał Miłecki Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 5df6aa72cc2db3..daba86d881bc1a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1270,8 +1270,13 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->chiprev = bus_pub->chiprev; /* request firmware here */ - brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, - brcmf_usb_probe_phase2); + ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), + NULL, brcmf_usb_probe_phase2); + if (ret) { + brcmf_err("firmware request failed: %d\n", ret); + goto fail; + } + return 0; fail: From 92e0f87e98f2f26a2089c407222a71b645014dcf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 12 May 2015 23:54:25 +0200 Subject: [PATCH 0287/1983] brcmfmac: avoid gcc-5.1 warning Upstream-commit: 331cfe544912b8d008ea792a97fbc4e92cdfdb65 gcc-5.0 gained a new warning in the fwsignal portion of the brcmfmac driver: drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c: In function 'brcmf_fws_txs_process': drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c:1478:8: warning: 'skb' may be used uninitialized in this function [-Wmaybe-uninitialized] This is a false positive, and marking the brcmf_fws_hanger_poppkt function as 'static inline' makes the warning go away. I have checked the object file output and while a little code gets moved around, the size of the binary remains identical. Signed-off-by: Arnd Bergmann Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index f0dda0ecd23b43..5017eaa4af45fd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -635,7 +635,7 @@ static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, return 0; } -static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, +static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, u32 slot_id, struct sk_buff **pktout, bool remove_item) { From d54927ac6a0a66ed76a11616cd33f72731290931 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 20 May 2015 14:09:47 +0200 Subject: [PATCH 0288/1983] brcmfmac: allow device tree node without 'interrupts' property Upstream-commit: d557b34e06731596a3355526d7dab23660f2c2db As described in the device tree bindings for 'brcm,bcm4329-fmac' nodes, the interrupts property is optional. So adding a check for the presence of this property before attempting to parse and map the interrupt. If not present or parsing fails return and fallback to in-band sdio interrupt. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/of.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index c824570ddea320..03f35e0c52ca54 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -39,10 +39,16 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) if (!sdiodev->pdata) return; + if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) + sdiodev->pdata->drive_strength = val; + + /* make sure there are interrupts defined in the node */ + if (!of_find_property(np, "interrupts", NULL)) + return; + irq = irq_of_parse_and_map(np, 0); if (!irq) { brcmf_err("interrupt could not be mapped\n"); - devm_kfree(dev, sdiodev->pdata); return; } irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); @@ -50,7 +56,4 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) sdiodev->pdata->oob_irq_supported = true; sdiodev->pdata->oob_irq_nr = irq; sdiodev->pdata->oob_irq_flags = irqf; - - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdiodev->pdata->drive_strength = val; } From c71ad0bd06b2c6fbc91d7b589860bdd223c87d2b Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 20 May 2015 14:09:48 +0200 Subject: [PATCH 0289/1983] brcmfmac: Improve throughput by scheduling msbug flow worker. Upstream-commit: 4aadad04c57efb26bb8b63808c17b94c2f258941 The tx flow worker in msgbuf gets scheduled at tx till a certain threshold has been reached. Then the tx completes will take over the scheduling. When amsdu and ampdu is used the frames are transferred wireless in a very bulky fashion, in combination with this scheduling algorithm and buffer limiters in the stack this can result in limited throughput. This change causes the flow worker to be scheduled more frequently from tx. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 5 +++-- drivers/net/wireless/brcm80211/brcmfmac/flowring.h | 4 ++-- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 910fbb561469e8..e3516f04ee9ca7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -249,8 +249,8 @@ void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) } -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb) +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) { struct brcmf_flowring_ring *ring; @@ -271,6 +271,7 @@ void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) brcmf_flowring_block(flow, flowid, false); } + return skb_queue_len(&ring->skblist); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index a34cd394c616c0..5551861a44bc42 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -64,8 +64,8 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb); +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, struct sk_buff *skb); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 65efb146898844..205685146e773c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -73,7 +73,7 @@ #define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 #define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 -#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 64 +#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96 #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 struct msgbuf_common_hdr { @@ -795,6 +795,8 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, struct brcmf_flowring *flow = msgbuf->flow; struct ethhdr *eh = (struct ethhdr *)(skb->data); u32 flowid; + u32 queue_count; + bool force; flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); if (flowid == BRCMF_FLOWRING_INVALID_ID) { @@ -802,8 +804,9 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, if (flowid == BRCMF_FLOWRING_INVALID_ID) return -ENOMEM; } - brcmf_flowring_enqueue(flow, flowid, skb); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false); + queue_count = brcmf_flowring_enqueue(flow, flowid, skb); + force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force); return 0; } From 4a3406276ba8a2fa0b5039873be7adae30b1625b Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 20 May 2015 14:09:49 +0200 Subject: [PATCH 0290/1983] brcmfmac: remove pci shared structure rev4 support Upstream-commit: 1569a85c9483cff39cfe6dfc5c0cab70f84603f5 All pcie full dongle chips supported by fmac are using rev 5+ shared structure. This patch removes the rev4 related code. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Reviewed-by: Arend Van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 31d66a7b11aaf4..c36bd5df25da6d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -110,10 +110,9 @@ enum brcmf_pcie_state { BRCMF_PCIE_MB_INT_D2H3_DB0 | \ BRCMF_PCIE_MB_INT_D2H3_DB1) -#define BRCMF_PCIE_MIN_SHARED_VERSION 4 +#define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION 5 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF -#define BRCMF_PCIE_SHARED_TXPUSH_SUPPORT 0x4000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 #define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 @@ -1276,11 +1275,6 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, brcmf_err("Unsupported PCIE version %d\n", version); return -EINVAL; } - if (shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT) { - brcmf_err("Unsupported legacy TX mode 0x%x\n", - shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT); - return -EINVAL; - } addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); From 93658a9bff4ff094baead4817c1ae47f135ea907 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 20 May 2015 14:09:50 +0200 Subject: [PATCH 0291/1983] brcmfmac: remove dummy cache flush/invalidate function Upstream-commit: 2c99c5d38e6a35761d79a301eeef7e9cf3951f4e brcmf_dma_flush and brcmf_dma_invalidate_cache are not necessary and have never been implemented. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Reviewed-by: Arend Van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/brcm80211/brcmfmac/commonring.c | 18 ------------------ .../net/wireless/brcm80211/brcmfmac/msgbuf.c | 11 ----------- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 11 ----------- 3 files changed, 40 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c index 77656c711bedbf..26c65872dae3b1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -22,17 +22,6 @@ #include "core.h" #include "commonring.h" - -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - * SEE ALSO msgbuf.c - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, int (*cr_ring_bell)(void *ctx), int (*cr_update_rptr)(void *ctx), @@ -206,14 +195,9 @@ int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) address = commonring->buf_addr; address += (commonring->f_ptr * commonring->item_len); if (commonring->f_ptr > commonring->w_ptr) { - brcmf_dma_flush(address, - (commonring->depth - commonring->f_ptr) * - commonring->item_len); address = commonring->buf_addr; commonring->f_ptr = 0; } - brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * - commonring->item_len); commonring->f_ptr = commonring->w_ptr; @@ -258,8 +242,6 @@ void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring, if (commonring->r_ptr == commonring->depth) commonring->r_ptr = 0; - brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); - return ret_addr; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 205685146e773c..1b47de067d25cb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -278,16 +278,6 @@ struct brcmf_msgbuf_pktids { struct brcmf_msgbuf_pktid *array; }; - -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); @@ -462,7 +452,6 @@ static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, memcpy(msgbuf->ioctbuf, buf, buf_len); else memset(msgbuf->ioctbuf, 0, buf_len); - brcmf_dma_flush(ioctl_buf, buf_len); err = brcmf_commonring_write_complete(commonring); brcmf_commonring_unlock(commonring); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index c36bd5df25da6d..df01f5a3205098 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -272,15 +272,6 @@ static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { }; -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) { @@ -1170,7 +1161,6 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) goto fail; memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); - brcmf_dma_flush(devinfo->shared.scratch, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; @@ -1188,7 +1178,6 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) goto fail; memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); - brcmf_dma_flush(devinfo->shared.ringupd, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; From 271cfd6197c3797f0cdfd3c77dbb6f6279a383e5 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 20 May 2015 14:09:51 +0200 Subject: [PATCH 0292/1983] brcmfmac: add support for dma indices feature Upstream-commit: 492da36c37ba31b63e0399159e733bd1d8e59d39 PCIe full dongle firmware can support a dma indices feature with which firmware can update/fetch the read/write indices of message buffer rings on both host to dongle and dongle to host directions. The support is announced by firmware through shared flags. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/brcm80211/brcmfmac/pcie.c | 140 +++++++++++++++--- 1 file changed, 119 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index df01f5a3205098..d289289fb21c91 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -113,6 +113,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION 5 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF +#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 +#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 #define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 @@ -144,6 +146,10 @@ enum brcmf_pcie_state { #define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 #define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 #define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 +#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20 +#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28 +#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36 +#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44 #define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 #define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 @@ -243,6 +249,13 @@ struct brcmf_pciedev_info { bool mbdata_completed; bool irq_allocated; bool wowl_enabled; + u8 dma_idx_sz; + void *idxbuf; + u32 idxbuf_sz; + dma_addr_t idxbuf_dmahandle; + u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); + void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value); }; struct brcmf_pcie_ringbuf { @@ -319,6 +332,25 @@ brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, } +static u16 +brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + return (*(address)); +} + + +static void +brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + *(address) = value; +} + + static u32 brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { @@ -864,7 +896,7 @@ static int brcmf_pcie_ring_mb_write_rptr(void *ctx) brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, commonring->w_ptr, ring->id); - brcmf_pcie_write_tcm16(devinfo, ring->r_idx_addr, commonring->r_ptr); + devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr); return 0; } @@ -882,7 +914,7 @@ static int brcmf_pcie_ring_mb_write_wptr(void *ctx) brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, commonring->r_ptr, ring->id); - brcmf_pcie_write_tcm16(devinfo, ring->w_idx_addr, commonring->w_ptr); + devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr); return 0; } @@ -911,7 +943,7 @@ static int brcmf_pcie_ring_mb_update_rptr(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - commonring->r_ptr = brcmf_pcie_read_tcm16(devinfo, ring->r_idx_addr); + commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr); brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, commonring->w_ptr, ring->id); @@ -929,7 +961,7 @@ static int brcmf_pcie_ring_mb_update_wptr(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - commonring->w_ptr = brcmf_pcie_read_tcm16(devinfo, ring->w_idx_addr); + commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr); brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, commonring->r_ptr, ring->id); @@ -1034,6 +1066,13 @@ static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) } kfree(devinfo->shared.flowrings); devinfo->shared.flowrings = NULL; + if (devinfo->idxbuf) { + dma_free_coherent(&devinfo->pdev->dev, + devinfo->idxbuf_sz, + devinfo->idxbuf, + devinfo->idxbuf_dmahandle); + devinfo->idxbuf = NULL; + } } @@ -1049,19 +1088,72 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) u32 addr; u32 ring_mem_ptr; u32 i; + u64 address; + u32 bufsz; u16 max_sub_queues; + u8 idx_offset; ring_addr = devinfo->shared.ring_info_addr; brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); + addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; + max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + + if (devinfo->dma_idx_sz != 0) { + bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * + devinfo->dma_idx_sz * 2; + devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, + &devinfo->idxbuf_dmahandle, + GFP_KERNEL); + if (!devinfo->idxbuf) + devinfo->dma_idx_sz = 0; + } - addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; - d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; - d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; - h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; - h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + if (devinfo->dma_idx_sz == 0) { + addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; + d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; + d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; + h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; + h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + idx_offset = sizeof(u32); + devinfo->write_ptr = brcmf_pcie_write_tcm16; + devinfo->read_ptr = brcmf_pcie_read_tcm16; + brcmf_dbg(PCIE, "Using TCM indices\n"); + } else { + memset(devinfo->idxbuf, 0, bufsz); + devinfo->idxbuf_sz = bufsz; + idx_offset = devinfo->dma_idx_sz; + devinfo->write_ptr = brcmf_pcie_write_idx; + devinfo->read_ptr = brcmf_pcie_read_idx; + + h2d_w_idx_ptr = 0; + addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; + address = (u64)devinfo->idxbuf_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; + address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + brcmf_dbg(PCIE, "Using host memory indices\n"); + } addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); @@ -1075,8 +1167,8 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring->id = i; devinfo->shared.commonrings[i] = ring; - h2d_w_idx_ptr += sizeof(u32); - h2d_r_idx_ptr += sizeof(u32); + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; ring_mem_ptr += BRCMF_RING_MEM_SZ; } @@ -1090,13 +1182,11 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring->id = i; devinfo->shared.commonrings[i] = ring; - d2h_w_idx_ptr += sizeof(u32); - d2h_r_idx_ptr += sizeof(u32); + d2h_w_idx_ptr += idx_offset; + d2h_r_idx_ptr += idx_offset; ring_mem_ptr += BRCMF_RING_MEM_SZ; } - addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; - max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); devinfo->shared.nrof_flowrings = max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), @@ -1120,15 +1210,15 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring); ring->w_idx_addr = h2d_w_idx_ptr; ring->r_idx_addr = h2d_r_idx_ptr; - h2d_w_idx_ptr += sizeof(u32); - h2d_r_idx_ptr += sizeof(u32); + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; } devinfo->shared.flowrings = rings; return 0; fail: - brcmf_err("Allocating commonring buffers failed\n"); + brcmf_err("Allocating ring buffers failed\n"); brcmf_pcie_release_ringbuffers(devinfo); return -ENOMEM; } @@ -1265,6 +1355,14 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, return -EINVAL; } + /* check firmware support dma indicies */ + if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) { + if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX) + devinfo->dma_idx_sz = sizeof(u16); + else + devinfo->dma_idx_sz = sizeof(u32); + } + addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; shared->max_rxbufpost = brcmf_pcie_read_tcm16(devinfo, addr); if (shared->max_rxbufpost == 0) From 2071da50ea0bd966cf821ed00dd842dd55552b43 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 28 May 2015 16:10:30 +0200 Subject: [PATCH 0293/1983] brcmfmac: only report if_add errors if acting on them We always get an error message on boot because we add the main interface then enable events and get the event the interface was added so we try to add it again. Only report the error if we are going to try and destroy the existing interface to try and continue. Also move the Event ignored error to be a debug EVENT message as it is just a NOOP that is handled by the caller. --- drivers/net/wireless/brcm80211/brcmfmac/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index 369d2f8bcd811a..3b9d364e176c60 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -791,15 +791,15 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, * in case we missed the BRCMF_E_IF_DEL event. */ if (ifp) { - brcmf_err("ERROR: netdev:%s already exists\n", - ifp->ndev->name); if (ifidx) { + brcmf_err("ERROR: netdev:%s already exists, deleting old interface\n", + ifp->ndev->name); netif_stop_queue(ifp->ndev); unregister_netdev(ifp->ndev); free_netdev(ifp->ndev); drvr->iflist[bssidx] = NULL; } else { - brcmf_err("ignore IF event\n"); + brcmf_dbg(EVENT, "ignore IF event\n"); return ERR_PTR(-EINVAL); } } From c90089f320db15024346172e234e8665910eba95 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 29 May 2015 06:16:37 +0200 Subject: [PATCH 0294/1983] brcmfmac: disable mbss for BCM4330 chipset With this enabled the AP only shows up as BRCM_TEST_AP so I am assuming that this feature is also not support on this chipset. disabling it. --- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 2c5fad3a3aa225..004ce146baacad 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -127,7 +127,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) + if ((drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) && + (drvr->bus_if->chip != BRCM_CC_4330_CHIP_ID)) brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); /* set chip related quirks */ From 29531a76f3a0bb377d61a0bbce1aeb387350a841 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 7 Jun 2015 09:55:09 +0200 Subject: [PATCH 0295/1983] brcmfmac: Ignore world wide roaming country code The brcm firmwares don't support the '00' world wide roaming country code. This is unsupported rather than invalid so just silently return rather than passing lots of errors to userspace. --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index bea9bb94cf6f3c..5cb82013c56d27 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -6008,7 +6008,15 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, req->alpha2[0], req->alpha2[1]); - /* ignore non-ISO3166 country codes */ + /* ignore non-ISO3166 country codes + * don't report an error on 00 the world roaming + * designator as the firmware don't support it + * but there is no reason to pass that info to userspace + */ + + if (req->alpha2[0] == '0' && req->alpha2[1] == '0') + return; + for (i = 0; i < sizeof(req->alpha2); i++) if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { brcmf_err("not a ISO3166 code\n"); From ae0fd7a192f9e507b7adbe0730edc9fba7a88f6f Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 13 Jul 2014 22:21:10 +0200 Subject: [PATCH 0296/1983] mmc: add support for power-on sequencing through DT This patch enables support for power-on sequencing of SDIO peripherals through DT. In general, it's quite common that wifi modules and other similar peripherals have several signals in addition to the SDIO interface that needs wiggling before the module will power on. It's common to have a reference clock, one or several power rails and one or several lines for reset/enable type functions. The binding as written today introduces a number of reset gpios, a regulator and a clock specifier. The code will handle up to 2 gpio reset lines, but it's trivial to increase to more than that if needed at some point. Implementation-wise, the MMC core has been changed to handle this during host power up, before the host interface is powered on. I have not yet implemented the power-down side, I wanted people to have a chance for reporting back w.r.t. issues (or comments on the bindings) first. I have not tested the regulator portion, since the system and module I'm working on doesn't need one (Samsung Chromebook with Marvell 8797-based wifi). Testing of those portions (and reporting back) would be appreciated. Signed-off-by: Olof Johansson Signed-off-by: Russell King --- Documentation/devicetree/bindings/mmc/mmc.txt | 11 +++++ drivers/mmc/core/core.c | 42 +++++++++++++++++++ drivers/mmc/core/host.c | 30 ++++++++++++- include/linux/mmc/host.h | 5 +++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 458b57f199afe6..962e0eec1fcfaf 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -5,6 +5,8 @@ these definitions. Interpreted by the OF core: - reg: Registers location and length. - interrupts: Interrupts used by the MMC controller. +- clocks: Clocks needed for the host controller, if any. +- clock-names: Goes with clocks above. Card detection: If no property below is supplied, host native card detect is used. @@ -30,6 +32,15 @@ Optional properties: - cap-sdio-irq: enable SDIO IRQ signalling on this interface - full-pwr-cycle: full power cycle of the card is supported +Card power and reset control: +The following properties can be specified for cases where the MMC +peripheral needs additional reset, regulator and clock lines. It is for +example common for WiFi/BT adapters to have these separate from the main +MMC bus: + - card-reset-gpios: Specify GPIOs for card reset (reset active low) + - card-external-vcc-supply: Regulator to drive (independent) card VCC + - clock with name "card_ext_clock": External clock provided to the card + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line polarity properties, we have to fix the meaning of the "normal" and "inverted" line levels. We choose to follow the SDHCI standard, which specifies both those diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index e324d7bd29e534..069e9c43b54b30 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -13,12 +13,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -1520,6 +1522,43 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) mmc_host_clk_release(host); } +static void mmc_card_power_up(struct mmc_host *host) +{ + int i; + struct gpio_desc **gds = host->card_reset_gpios; + + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + if (gds[i]) { + dev_dbg(host->parent, "Asserting reset line %d", i); + gpiod_set_value(gds[i], 1); + } + } + + if (host->card_regulator) { + dev_dbg(host->parent, "Enabling external regulator"); + if (regulator_enable(host->card_regulator)) + dev_err(host->parent, "Failed to enable external regulator"); + } + + if (host->card_clk) { + dev_dbg(host->parent, "Enabling external clock"); + clk_prepare_enable(host->card_clk); + } + + /* 2ms delay to let clocks and power settle */ + mmc_delay(20); + + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + if (gds[i]) { + dev_dbg(host->parent, "Deasserting reset line %d", i); + gpiod_set_value(gds[i], 0); + } + } + + /* 2ms delay to after reset release */ + mmc_delay(20); +} + /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. @@ -1536,6 +1575,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) if (host->ios.power_mode == MMC_POWER_ON) return; + /* Power up the card/module first, if needed */ + mmc_card_power_up(host); + mmc_host_clk_hold(host); host->ios.vdd = fls(ocr) - 1; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b7079afc1ad57c..b28bf7b6c23ed2 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -12,14 +12,18 @@ * MMC host class device management */ +#include +#include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -312,7 +316,7 @@ int mmc_of_parse(struct mmc_host *host) u32 bus_width; bool explicit_inv_wp, gpio_inv_wp = false; enum of_gpio_flags flags; - int len, ret, gpio; + int i, len, ret, gpio; if (!host->parent || !host->parent->of_node) return 0; @@ -415,6 +419,30 @@ int mmc_of_parse(struct mmc_host *host) if (explicit_inv_wp ^ gpio_inv_wp) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + /* Parse card power/reset/clock control */ + if (of_find_property(np, "card-reset-gpios", NULL)) { + struct gpio_desc *gpd; + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + gpd = devm_gpiod_get_index(host->parent, "card-reset", i); + if (IS_ERR(gpd)) + break; + gpiod_direction_output(gpd, 0); + host->card_reset_gpios[i] = gpd; + } + + gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios)); + if (!IS_ERR(gpd)) { + dev_warn(host->parent, "More reset gpios than we can handle"); + gpiod_put(gpd); + } + } + + host->card_clk = of_clk_get_by_name(np, "card_ext_clock"); + if (IS_ERR(host->card_clk)) + host->card_clk = NULL; + + host->card_regulator = regulator_get(host->parent, "card-external-vcc"); + if (of_find_property(np, "cap-sd-highspeed", &len)) host->caps |= MMC_CAP_SD_HIGHSPEED; if (of_find_property(np, "cap-mmc-highspeed", &len)) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index bfd0769159eaf2..7352f5cc89159e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -298,6 +298,11 @@ struct mmc_host { unsigned long clkgate_delay; #endif + /* card specific properties to deal with power and reset */ + struct regulator *card_regulator; /* External VCC needed by the card */ + struct gpio_desc *card_reset_gpios[2]; /* External resets, active low */ + struct clk *card_clk; /* External clock needed by the card */ + /* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned short max_segs; /* see blk_queue_max_segments */ From 0487463823d5723bf658fb82034dffa0602bd460 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 13 Jul 2014 22:21:10 +0200 Subject: [PATCH 0297/1983] mmc: dw_mmc: call mmc_of_parse to fill in common options The shared of parse function fills in common options for capabilities, etc, but it needs to be called from each driver that wants to make use of it. dw_mmc was missing the call. Signed-off-by: Olof Johansson Signed-off-by: Russell King --- drivers/mmc/host/dw_mmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index caed9d53e8fa4c..5d59c92d246f3f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2147,6 +2147,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (!mmc) return -ENOMEM; + mmc_of_parse(mmc); + slot = mmc_priv(mmc); slot->id = id; slot->mmc = mmc; From d6ec9ac24f0e6c2b63411caf98fb15fa322f53cb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 22:21:10 +0200 Subject: [PATCH 0298/1983] mmc: fix power-on sequencing for esdhc-imx driver There's no reason that this can't be applied to all MMC interfaces as it's all core code, so tie this into the core rather than it being optional for each host. Signed-off-by: Russell King --- drivers/mmc/core/host.c | 90 +++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b28bf7b6c23ed2..0e77e59df8d409 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -316,7 +316,7 @@ int mmc_of_parse(struct mmc_host *host) u32 bus_width; bool explicit_inv_wp, gpio_inv_wp = false; enum of_gpio_flags flags; - int i, len, ret, gpio; + int len, ret, gpio; if (!host->parent || !host->parent->of_node) return 0; @@ -419,30 +419,6 @@ int mmc_of_parse(struct mmc_host *host) if (explicit_inv_wp ^ gpio_inv_wp) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - /* Parse card power/reset/clock control */ - if (of_find_property(np, "card-reset-gpios", NULL)) { - struct gpio_desc *gpd; - for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { - gpd = devm_gpiod_get_index(host->parent, "card-reset", i); - if (IS_ERR(gpd)) - break; - gpiod_direction_output(gpd, 0); - host->card_reset_gpios[i] = gpd; - } - - gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios)); - if (!IS_ERR(gpd)) { - dev_warn(host->parent, "More reset gpios than we can handle"); - gpiod_put(gpd); - } - } - - host->card_clk = of_clk_get_by_name(np, "card_ext_clock"); - if (IS_ERR(host->card_clk)) - host->card_clk = NULL; - - host->card_regulator = regulator_get(host->parent, "card-external-vcc"); - if (of_find_property(np, "cap-sd-highspeed", &len)) host->caps |= MMC_CAP_SD_HIGHSPEED; if (of_find_property(np, "cap-mmc-highspeed", &len)) @@ -469,6 +445,66 @@ EXPORT_SYMBOL(mmc_of_parse); int mmc_max_reserved_idx(void); +static int mmc_of_parse_child(struct mmc_host *host) +{ + struct device_node *np; + struct clk *clk; + int i; + + if (!host->parent || !host->parent->of_node) + return 0; + + np = host->parent->of_node; + + host->card_regulator = regulator_get(host->parent, "card-external-vcc"); + if (IS_ERR(host->card_regulator)) { + if (PTR_ERR(host->card_regulator) == -EPROBE_DEFER) + return PTR_ERR(host->card_regulator); + host->card_regulator = NULL; + } + + /* Parse card power/reset/clock control */ + if (of_find_property(np, "card-reset-gpios", NULL)) { + struct gpio_desc *gpd; + int level = 0; + + /* + * If the regulator is enabled, then we can hold the + * card in reset with an active high resets. Otherwise, + * hold the resets low. + */ + if (host->card_regulator && regulator_is_enabled(host->card_regulator)) + level = 1; + + for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { + gpd = devm_gpiod_get_index(host->parent, "card-reset", i); + if (IS_ERR(gpd)) { + if (PTR_ERR(gpd) == -EPROBE_DEFER) + return PTR_ERR(gpd); + break; + } + gpiod_direction_output(gpd, gpiod_is_active_low(gpd) | level); + host->card_reset_gpios[i] = gpd; + } + + gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios)); + if (!IS_ERR(gpd)) { + dev_warn(host->parent, "More reset gpios than we can handle"); + gpiod_put(gpd); + } + } + + clk = of_clk_get_by_name(np, "card_ext_clock"); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return PTR_ERR(clk); + clk = NULL; + } + host->card_clk = clk; + + return 0; +} + /** * mmc_alloc_host - initialise the per-host structure. * @extra: sizeof private data structure @@ -559,6 +595,10 @@ int mmc_add_host(struct mmc_host *host) { int err; + err = mmc_of_parse_child(host); + if (err) + return err; + WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && !host->ops->enable_sdio_irq); From 7781c73675dfcc7c9585e277f9a3eed5fc4b3471 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 7 Jul 2015 18:10:33 +0200 Subject: [PATCH 0299/1983] dts: imxqdl-microsom: Fix wifi initialization The mmc wifi drivers require some device-tree patches in order to initialize properly. These patches were carried briefly upstream but have since been replaced with the proper mmc-pwr-seq patchset that handles proper power sequencing needed for certain mmc based devices. Backporting that patchset is too cumbersome so we will instead use the initial solution to the problem. --- arch/arm/boot/dts/imx6qdl-microsom.dtsi | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6qdl-microsom.dtsi b/arch/arm/boot/dts/imx6qdl-microsom.dtsi index ec6c7c7a406f0e..5f40c504afe5fb 100644 --- a/arch/arm/boot/dts/imx6qdl-microsom.dtsi +++ b/arch/arm/boot/dts/imx6qdl-microsom.dtsi @@ -52,6 +52,19 @@ regulators { compatible = "simple-bus"; + reg_brcm_osc: brcm-osc-reg { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio5 5 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_microsom_brcm_osc>; + regulator-name = "brcm_osc_reg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + reg_brcm: brcm-reg { compatible = "regulator-fixed"; enable-active-high; @@ -150,10 +163,11 @@ /* USDHC1 - Connected to optional BRCM Wifi/BT/FM */ &usdhc1 { + card-external-vcc-supply = <®_brcm>; + card-reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, <&gpio6 0 GPIO_ACTIVE_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_microsom_usdhc1>; bus-width = <4>; - mmc-pwrseq = <&usdhc1_pwrseq>; keep-power-in-suspend; non-removable; vmmc-supply = <®_brcm>; From b39f6e7b822b70a4861eeb575738aa0286981aad Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 17 Dec 2014 11:17:40 +0100 Subject: [PATCH 0300/1983] char: fsl_otp: Enable kernel config option to enable write support Write access to the fuses is very dangerous as they can only be written once. This is the first commit in locking this down a bit. Now you must explicitely enable this functionality in the kernel. --- drivers/char/Kconfig | 12 ++++++++++++ drivers/char/fsl_otp.c | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 4389d37d85157e..0b7842a670faa3 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -97,6 +97,18 @@ config FSL_OTP If unsure, it is safe to say Y. +config FSL_OTP_WRITE_ENABLE + bool "Enable writing support of OTP pages on Freescale chips" + depends on FSL_OTP + default n + help + If you say Y here, you will enable support for writing of the + OTP pages. This is dangerous by nature as you can only program + the pages once, so only enable this option when you actually + need it so as to not inadvertently clobber data. + + If unsure, say N. + config PRINTER tristate "Parallel printer support" depends on PARPORT diff --git a/drivers/char/fsl_otp.c b/drivers/char/fsl_otp.c index face297d2487bb..d14b42d3e19a13 100644 --- a/drivers/char/fsl_otp.c +++ b/drivers/char/fsl_otp.c @@ -151,6 +151,7 @@ static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr, return ret ? 0 : sprintf(buf, "0x%x\n", value); } +#ifdef CONFIG_FSL_OTP_WRITE_ENABLE static int otp_write_bits(int addr, u32 data, u32 magic) { u32 c; /* for control register */ @@ -204,6 +205,7 @@ static ssize_t fsl_otp_store(struct kobject *kobj, struct kobj_attribute *attr, clk_disable_unprepare(otp_clk); return ret ? 0 : count; } +#endif static int fsl_otp_probe(struct platform_device *pdev) { @@ -244,9 +246,13 @@ static int fsl_otp_probe(struct platform_device *pdev) for (i = 0; i < num; i++) { sysfs_attr_init(&otp_kattr[i].attr); otp_kattr[i].attr.name = desc[i]; +#ifdef CONFIG_FSL_OTP_WRITE_ENABLE otp_kattr[i].attr.mode = 0600; - otp_kattr[i].show = fsl_otp_show; otp_kattr[i].store = fsl_otp_store; +#else + otp_kattr[i].attr.mode = 0400; +#endif + otp_kattr[i].show = fsl_otp_show; attrs[i] = &otp_kattr[i].attr; } otp_attr_group->attrs = attrs; From b01cc90b75902a4a55f192455d396fe126e3c1b3 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 17 Dec 2014 15:40:37 +0100 Subject: [PATCH 0301/1983] char: fsl_otp: Add function to read otp values Instead of having all drivers mapping the otp memory and checking values provide a function so they can do it through the driver. --- drivers/char/fsl_otp.c | 25 ++++++++++++++++++------- include/linux/fsl_otp.h | 6 ++++++ 2 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 include/linux/fsl_otp.h diff --git a/drivers/char/fsl_otp.c b/drivers/char/fsl_otp.c index d14b42d3e19a13..fe12932ef07f26 100644 --- a/drivers/char/fsl_otp.c +++ b/drivers/char/fsl_otp.c @@ -30,6 +30,7 @@ #include #include #include +#include #define HW_OCOTP_CTRL 0x00000000 #define HW_OCOTP_CTRL_SET 0x00000004 @@ -125,16 +126,13 @@ static int otp_wait_busy(u32 flags) return 0; } -static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr, - char *buf) +int fsl_otp_readl(unsigned long offset, u32 *value) { - unsigned int index = attr - otp_kattr; - u32 value = 0; - int ret; + int ret = 0; ret = clk_prepare_enable(otp_clk); if (ret) - return 0; + return ret; mutex_lock(&otp_mutex); @@ -143,11 +141,24 @@ static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr, if (ret) goto out; - value = __raw_readl(otp_base + HW_OCOTP_CUST_N(index)); + *value = __raw_readl(otp_base + offset); out: mutex_unlock(&otp_mutex); clk_disable_unprepare(otp_clk); + return ret; +} +EXPORT_SYMBOL(fsl_otp_readl); + +static ssize_t fsl_otp_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + unsigned int index = attr - otp_kattr; + u32 value = 0; + int ret; + + ret = fsl_otp_readl(HW_OCOTP_CUST_N(index), &value); + return ret ? 0 : sprintf(buf, "0x%x\n", value); } diff --git a/include/linux/fsl_otp.h b/include/linux/fsl_otp.h new file mode 100644 index 00000000000000..7c30f4df5b3dc8 --- /dev/null +++ b/include/linux/fsl_otp.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_FSL_OTP_H +#define _LINUX_FSL_OTP_H + +int fsl_otp_readl(unsigned long offset, u32 *value); + +#endif From 2bb5b19a9b6b3a68ae100578991500aefe728c2e Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Nov 2014 18:32:34 +0100 Subject: [PATCH 0302/1983] thermal: imx_thermal: Add a trip point for each cpu_freq state The driver currently would only trip at 85C and then bring the SOC and GPU down to the slowest speeds until it reached 75C. Now we read the number of cpufreq states and spread out trip points between 85C and critical temp. At each trip point we drop the top cpufrequency down and don't raise it again until the temp is lower than the previous zones trip point. --- drivers/thermal/imx_thermal.c | 64 ++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index db37eec4f6e8e9..2e3b4298c45e10 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -57,13 +57,6 @@ #define OCOTP_ANA1 0x04e0 -/* The driver supports 1 passive trip point and 1 critical trip point */ -enum imx_thermal_trip { - IMX_TRIP_PASSIVE, - IMX_TRIP_CRITICAL, - IMX_TRIP_NUM, -}; - /* * It defines the temperature in millicelsius for passive trip point * that will trigger cooling action when crossed. @@ -93,12 +86,15 @@ static struct thermal_soc_data thermal_imx6sx_data = { .version = TEMPMON_V2, }; +#define IMX_TRIP_PASSIVE 0 + struct imx_thermal_data { struct thermal_zone_device *tz; struct thermal_cooling_device *cdev[2]; enum thermal_device_mode mode; struct regmap *tempmon; u32 c1, c2; /* See formula in imx_get_sensor_data() */ + unsigned long num_passive_trips; unsigned long temp_passive; unsigned long temp_critical; unsigned long alarm_temp; @@ -141,6 +137,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) struct imx_thermal_data *data = tz->devdata; struct regmap *map = data->tempmon; unsigned int n_meas; + unsigned long cur_state, temp_passive_delta; bool wait; u32 val; @@ -186,10 +183,17 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) /* See imx_get_sensor_data() for formula derivation */ *temp = data->c2 - n_meas * data->c1; + data->cdev[0]->ops->get_cur_state(data->cdev[0], &cur_state); + temp_passive_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; + /* Update alarm value to next higher trip point */ - if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive) + if (data->alarm_temp < data->temp_critical && cur_state < data->num_passive_trips) + imx_set_alarm_temp(data, data->temp_passive + ((cur_state + 1) * temp_passive_delta)); + + if (data->alarm_temp < data->temp_critical && *temp >= data->temp_passive + (data->num_passive_trips * temp_passive_delta)) imx_set_alarm_temp(data, data->temp_critical); - if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) { + + if (data->alarm_temp > data->temp_passive && *temp < data->temp_passive) { imx_set_alarm_temp(data, data->temp_passive); dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", data->alarm_temp / 1000); @@ -258,7 +262,8 @@ static int imx_set_mode(struct thermal_zone_device *tz, static int imx_get_trip_type(struct thermal_zone_device *tz, int trip, enum thermal_trip_type *type) { - *type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE : + struct imx_thermal_data *data = tz->devdata; + *type = (trip < data->num_passive_trips) ? THERMAL_TRIP_PASSIVE : THERMAL_TRIP_CRITICAL; return 0; } @@ -276,9 +281,11 @@ static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip, unsigned long *temp) { struct imx_thermal_data *data = tz->devdata; + unsigned long temp_passive_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; - *temp = (trip == IMX_TRIP_PASSIVE) ? data->temp_passive : - data->temp_critical; + *temp = (trip < data->num_passive_trips) ? + data->temp_passive + (trip * temp_passive_delta) : + data->temp_critical; return 0; } @@ -287,12 +294,15 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip, { struct imx_thermal_data *data = tz->devdata; - if (trip == IMX_TRIP_CRITICAL) { + if (trip > IMX_TRIP_PASSIVE) { data->temp_critical = temp; if (data->socdata->version == TEMPMON_V2) imx_set_panic_temp(data, temp); } + if (trip == IMX_TRIP_PASSIVE) + return -EPERM; + if (trip == IMX_TRIP_PASSIVE) { if (temp > IMX_TEMP_PASSIVE) return -EINVAL; @@ -306,17 +316,29 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip, static int imx_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) { + struct imx_thermal_data *data = tz->devdata; int ret; - unsigned long trip_temp; + unsigned long trip_temp, cur_state, temp_passive_delta; ret = imx_get_trip_temp(tz, trip, &trip_temp); if (ret < 0) return ret; - if (tz->temperature >= (trip_temp - IMX_TEMP_PASSIVE_COOL_DELTA)) - *trend = THERMAL_TREND_RAISE_FULL; - else - *trend = THERMAL_TREND_DROP_FULL; + data->cdev[0]->ops->get_cur_state(data->cdev[0], &cur_state); + temp_passive_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; + + if (tz->temperature > tz->last_temperature && tz->temperature > (data->temp_passive + (cur_state * temp_passive_delta))) { + *trend = THERMAL_TREND_RAISING; + } else if (tz->temperature < tz->last_temperature && cur_state) { + if (tz->temperature <= (data->temp_passive - temp_passive_delta)) + *trend = THERMAL_TREND_DROP_FULL; + else if (tz->temperature <= (data->temp_passive + ((cur_state - 1) * temp_passive_delta))) + *trend = THERMAL_TREND_DROPPING; + else + *trend = THERMAL_TREND_STABLE; + } else { + *trend = THERMAL_TREND_STABLE; + } return 0; } @@ -595,6 +617,8 @@ static int imx_thermal_probe(struct platform_device *pdev) } data->cdev[1] = devfreq_cooling_register(); + data->cdev[0]->ops->get_max_state(data->cdev[0], &data->num_passive_trips); + if (IS_ERR(data->cdev[1])) { ret = PTR_ERR(data->cdev[1]); dev_err(&pdev->dev, @@ -603,8 +627,8 @@ static int imx_thermal_probe(struct platform_device *pdev) } data->tz = thermal_zone_device_register("imx_thermal_zone", - IMX_TRIP_NUM, - (1 << IMX_TRIP_NUM) - 1, data, + data->num_passive_trips + 1, + BIT(IMX_TRIP_PASSIVE), data, &imx_tz_ops, NULL, IMX_PASSIVE_DELAY, IMX_POLLING_DELAY); From c40daca09ec6cda0a5ba96f38793517c4f6c8060 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Nov 2014 18:40:46 +0100 Subject: [PATCH 0303/1983] thermal: step_wise: Add and fix debug output Add some additional debug output to track the changes being detected by the driver. Also fix the typing of some variables to verify that they print out properly in the debug output. --- drivers/thermal/step_wise.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index ee52ab7d373013..034e3109e10c11 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -70,10 +70,12 @@ static unsigned long get_target_state(struct thermal_instance *instance, if (next_target < instance->lower) next_target = instance->lower; } + dev_dbg(&cdev->device, "THERMAL_TREND_RAISING: next_target=%ld\n", next_target); break; case THERMAL_TREND_RAISE_FULL: if (throttle) next_target = instance->upper; + dev_dbg(&cdev->device, "THERMAL_TREND_RAISE_FULL: next_target=%ld\n", next_target); break; case THERMAL_TREND_DROPPING: if (cur_state == instance->lower) { @@ -84,6 +86,7 @@ static unsigned long get_target_state(struct thermal_instance *instance, if (next_target > instance->upper) next_target = instance->upper; } + dev_dbg(&cdev->device, "THERMAL_TREND_DROPPING: next_target=%ld\n", next_target); break; case THERMAL_TREND_DROP_FULL: if (cur_state == instance->lower) { @@ -91,6 +94,7 @@ static unsigned long get_target_state(struct thermal_instance *instance, next_target = THERMAL_NO_TARGET; } else next_target = instance->lower; + dev_dbg(&cdev->device, "THERMAL_TREND_DROP_FULL: next_target=%ld\n", next_target); break; default: break; @@ -117,7 +121,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) enum thermal_trend trend; struct thermal_instance *instance; bool throttle = false; - int old_target; + unsigned long old_target; if (trip == THERMAL_TRIPS_NONE) { trip_temp = tz->forced_passive; @@ -143,8 +147,8 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) old_target = instance->target; instance->target = get_target_state(instance, trend, throttle); - dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n", - old_target, (int)instance->target); + dev_dbg(&instance->cdev->device, "old_target=%ld, target=%ld\n", + old_target, instance->target); /* Activate a passive thermal instance */ if (old_target == THERMAL_NO_TARGET && From f34de102c03aae1aadd485b26c708847a62b50eb Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Nov 2014 18:43:06 +0100 Subject: [PATCH 0304/1983] thermal: devfreq_cooling: Allow max_states to be variable This allows max_state to be set at the time the devfreq cooling device is instantiated. This allows events to match the cpufreq cooling device and gives tighter grained control over the devices listening. Currently I am using state 5 as the critical event number. This could be dynamic as well but since only the imx_thermal and galcore use this driver I am okay hardcoding it for testing. --- drivers/thermal/device_cooling.c | 16 +++++++++++----- include/linux/device_cooling.h | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/device_cooling.c b/drivers/thermal/device_cooling.c index 1c223a88bc1aa6..f33bd8f2e731bd 100644 --- a/drivers/thermal/device_cooling.c +++ b/drivers/thermal/device_cooling.c @@ -16,13 +16,12 @@ struct devfreq_cooling_device { int id; struct thermal_cooling_device *cool_dev; unsigned int devfreq_state; + unsigned int max_state; }; static DEFINE_IDR(devfreq_idr); static DEFINE_MUTEX(devfreq_cooling_lock); -#define MAX_STATE 1 - static BLOCKING_NOTIFIER_HEAD(devfreq_cooling_chain_head); int register_devfreq_cooling_notifier(struct notifier_block *nb) @@ -51,8 +50,13 @@ static int devfreq_set_cur_state(struct thermal_cooling_device *cdev, { struct devfreq_cooling_device *devfreq_device = cdev->devdata; int ret; + unsigned long notify_state; - ret = devfreq_cooling_notifier_call_chain(state); + if (state >= devfreq_device->max_state) + notify_state = 5; + else + notify_state = state; + ret = devfreq_cooling_notifier_call_chain(notify_state); if (ret) return -EINVAL; devfreq_device->devfreq_state = state; @@ -63,7 +67,8 @@ static int devfreq_set_cur_state(struct thermal_cooling_device *cdev, static int devfreq_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = MAX_STATE; + struct devfreq_cooling_device *devfreq_device = cdev->devdata; + *state = devfreq_device->max_state; return 0; } @@ -105,7 +110,7 @@ static void release_idr(struct idr *idr, int id) mutex_unlock(&devfreq_cooling_lock); } -struct thermal_cooling_device *devfreq_cooling_register(void) +struct thermal_cooling_device *devfreq_cooling_register(unsigned long max_state) { struct thermal_cooling_device *cool_dev; struct devfreq_cooling_device *devfreq_dev = NULL; @@ -135,6 +140,7 @@ struct thermal_cooling_device *devfreq_cooling_register(void) } devfreq_dev->cool_dev = cool_dev; devfreq_dev->devfreq_state = 0; + devfreq_dev->max_state = max_state; return cool_dev; } diff --git a/include/linux/device_cooling.h b/include/linux/device_cooling.h index ae40a451e4fa92..79e2434b517d91 100644 --- a/include/linux/device_cooling.h +++ b/include/linux/device_cooling.h @@ -15,7 +15,7 @@ #ifdef CONFIG_DEVICE_THERMAL int register_devfreq_cooling_notifier(struct notifier_block *nb); int unregister_devfreq_cooling_notifier(struct notifier_block *nb); -struct thermal_cooling_device *devfreq_cooling_register(void); +struct thermal_cooling_device *devfreq_cooling_register(unsigned long max_state); void devfreq_cooling_unregister(struct thermal_cooling_device *cdev); #else static inline @@ -31,7 +31,7 @@ int unregister_devfreq_cooling_notifier(struct notifier_block *nb) } static inline -struct thermal_cooling_device *devfreq_cooling_register(void) +struct thermal_cooling_device *devfreq_cooling_register(unsigned long max_state) { return NULL; } From d580d42339d4478716411d4e40339ee407a908f4 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Nov 2014 18:46:28 +0100 Subject: [PATCH 0305/1983] thermal: imx_thermal: Set max_events for devfreq_cooling Tell the devfreq device how many cooling states it will have. --- drivers/thermal/imx_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 2e3b4298c45e10..244f5b2ea6232d 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -616,9 +616,9 @@ static int imx_thermal_probe(struct platform_device *pdev) return ret; } - data->cdev[1] = devfreq_cooling_register(); data->cdev[0]->ops->get_max_state(data->cdev[0], &data->num_passive_trips); + data->cdev[1] = devfreq_cooling_register(data->num_passive_trips + 1); if (IS_ERR(data->cdev[1])) { ret = PTR_ERR(data->cdev[1]); dev_err(&pdev->dev, From 5e5534256c51030acc54aba84bf9a49d8c778773 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 17 Dec 2014 15:45:30 +0100 Subject: [PATCH 0306/1983] thermal: imx: Rework driver for better flexibility - read grade of chip from otp and set critical based on this value. - base the default passive trip points based on the number of cpu frequency points and the critical thermal temperature. - allow the user to change the trip_point0 up to a maximum of 85c and then scale the trip points based on that. --- drivers/thermal/Kconfig | 1 + drivers/thermal/imx_thermal.c | 122 ++++++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 34 deletions(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 10d26e491d05ce..e9c80915b45739 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -119,6 +119,7 @@ config IMX_THERMAL depends on CPU_THERMAL depends on MFD_SYSCON depends on OF + select FSL_OTP help Support for Temperature Monitor (TEMPMON) found on Freescale i.MX SoCs. It supports one critical trip point and one passive trip point. The diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 244f5b2ea6232d..b73c7222492105 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -57,15 +58,24 @@ #define OCOTP_ANA1 0x04e0 +#define OCOTP_TEMP_GRADE 0x480 +#define OCOTP_TEMP_GRADE_SHIFT 5 +#define OCOTP_TEMP_GRADE_AUT 0x3 +#define OCOTP_TEMP_GRADE_IND 0x2 +#define OCOTP_TEMP_GRADE_EXT 0x1 +#define OCOTP_TEMP_GRADE_COM 0x0 + /* * It defines the temperature in millicelsius for passive trip point * that will trigger cooling action when crossed. */ -#define IMX_TEMP_PASSIVE 85000 -#define IMX_TEMP_PASSIVE_COOL_DELTA 10000 +#define IMX_TRIP_PASSIVE 0 + +#define IMX_TEMP_MAX_PASSIVE 85000 +#define IMX_TEMP_MIN_TRIP_DELTA 6000 -#define IMX_POLLING_DELAY 2000 /* millisecond */ -#define IMX_PASSIVE_DELAY 1000 +#define IMX_POLLING_DELAY 3000 /* millisecond */ +#define IMX_PASSIVE_DELAY 2000 #define FACTOR0 10000000 #define FACTOR1 15976 @@ -94,9 +104,10 @@ struct imx_thermal_data { enum thermal_device_mode mode; struct regmap *tempmon; u32 c1, c2; /* See formula in imx_get_sensor_data() */ - unsigned long num_passive_trips; unsigned long temp_passive; unsigned long temp_critical; + unsigned long num_passive_trips; + unsigned long temp_zone_delta; unsigned long alarm_temp; unsigned long last_temp; bool irq_enabled; @@ -137,7 +148,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) struct imx_thermal_data *data = tz->devdata; struct regmap *map = data->tempmon; unsigned int n_meas; - unsigned long cur_state, temp_passive_delta; + unsigned long cur_state; bool wait; u32 val; @@ -184,13 +195,17 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) *temp = data->c2 - n_meas * data->c1; data->cdev[0]->ops->get_cur_state(data->cdev[0], &cur_state); - temp_passive_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; /* Update alarm value to next higher trip point */ - if (data->alarm_temp < data->temp_critical && cur_state < data->num_passive_trips) - imx_set_alarm_temp(data, data->temp_passive + ((cur_state + 1) * temp_passive_delta)); + if ((data->temp_passive < data->alarm_temp) && + (data->alarm_temp < data->temp_critical) && + (cur_state < data->num_passive_trips)) { + imx_set_alarm_temp(data, data->temp_passive + ((cur_state + 1) * data->temp_zone_delta)); + dev_dbg(&tz->device, "thermal alarm on: T < %lu\n", + data->alarm_temp / 1000); + } - if (data->alarm_temp < data->temp_critical && *temp >= data->temp_passive + (data->num_passive_trips * temp_passive_delta)) + if (data->alarm_temp < data->temp_critical && *temp >= data->temp_passive + (data->num_passive_trips * data->temp_zone_delta)) imx_set_alarm_temp(data, data->temp_critical); if (data->alarm_temp > data->temp_passive && *temp < data->temp_passive) { @@ -281,10 +296,9 @@ static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip, unsigned long *temp) { struct imx_thermal_data *data = tz->devdata; - unsigned long temp_passive_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; *temp = (trip < data->num_passive_trips) ? - data->temp_passive + (trip * temp_passive_delta) : + data->temp_passive + (trip * data->temp_zone_delta) : data->temp_critical; return 0; } @@ -295,19 +309,19 @@ static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip, struct imx_thermal_data *data = tz->devdata; if (trip > IMX_TRIP_PASSIVE) { - data->temp_critical = temp; - if (data->socdata->version == TEMPMON_V2) + if (data->socdata->version == TEMPMON_V2) { + data->temp_critical = temp; imx_set_panic_temp(data, temp); + } else + return -EPERM; } - if (trip == IMX_TRIP_PASSIVE) - return -EPERM; - if (trip == IMX_TRIP_PASSIVE) { - if (temp > IMX_TEMP_PASSIVE) + if (temp > IMX_TEMP_MAX_PASSIVE) return -EINVAL; data->temp_passive = temp; imx_set_alarm_temp(data, temp); + data->temp_zone_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; } return 0; @@ -318,21 +332,22 @@ static int imx_get_trend(struct thermal_zone_device *tz, { struct imx_thermal_data *data = tz->devdata; int ret; - unsigned long trip_temp, cur_state, temp_passive_delta; + unsigned long trip_temp, cur_state; ret = imx_get_trip_temp(tz, trip, &trip_temp); if (ret < 0) return ret; data->cdev[0]->ops->get_cur_state(data->cdev[0], &cur_state); - temp_passive_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; - if (tz->temperature > tz->last_temperature && tz->temperature > (data->temp_passive + (cur_state * temp_passive_delta))) { + if (tz->temperature > tz->last_temperature && + tz->temperature > (data->temp_passive + (cur_state * data->temp_zone_delta))) { *trend = THERMAL_TREND_RAISING; } else if (tz->temperature < tz->last_temperature && cur_state) { - if (tz->temperature <= (data->temp_passive - temp_passive_delta)) + if (tz->temperature <= (data->temp_passive - data->temp_zone_delta)) *trend = THERMAL_TREND_DROP_FULL; - else if (tz->temperature <= (data->temp_passive + ((cur_state - 1) * temp_passive_delta))) + else if (tz->temperature <= (data->temp_passive + + ((cur_state - 1) * data->temp_zone_delta))) *trend = THERMAL_TREND_DROPPING; else *trend = THERMAL_TREND_STABLE; @@ -453,21 +468,58 @@ static int imx_get_sensor_data(struct platform_device *pdev) data->c1 = temp64; data->c2 = n1 * data->c1 + 1000 * t1; - /* - * Set the default passive cooling trip point to IMX_TEMP_PASSIVE. - * Can be changed from userspace. - */ - data->temp_passive = IMX_TEMP_PASSIVE; + return 0; +} - /* - * Set the default critical trip point to 20 C higher - * than passive trip point. Can be changed from userspace. - */ - data->temp_critical = IMX_TEMP_PASSIVE + 20 * 1000; +static void imx_set_thermal_defaults(struct imx_thermal_data *data) +{ + int ret; + u32 val; - return 0; + ret = fsl_otp_readl(OCOTP_TEMP_GRADE, &val); + + if (ret) { + /* + * Set the default passive cooling trip point, + * can be changed from userspace. + */ + data->temp_passive = IMX_TEMP_MAX_PASSIVE; + + /* + * The maximum die temperature set to 20 C higher than + * IMX_TEMP_MAX_PASSIVE. + */ + data->temp_critical = 1000 * 20 + data->temp_passive; + data->temp_zone_delta = (data->temp_critical - data->temp_passive) / data->num_passive_trips; + } else { + val >>= OCOTP_TEMP_GRADE_SHIFT; + val &= 0x3; + + switch (val) { + case OCOTP_TEMP_GRADE_AUT: + data->temp_critical = 125000; + break; + case OCOTP_TEMP_GRADE_IND: + case OCOTP_TEMP_GRADE_EXT: + data->temp_critical = 105000; + break; + case OCOTP_TEMP_GRADE_COM: + default: + data->temp_critical = 95000; + break; + } + data->temp_passive = data->temp_critical - (IMX_TEMP_MIN_TRIP_DELTA * data->num_passive_trips); + data->temp_zone_delta = IMX_TEMP_MIN_TRIP_DELTA; + } + + pr_debug("THERMAL DEFAULTS: passive: %lu \ + critical %lu trip_points: %lu \ + zone_delta: %lu\n", + data->temp_passive, data->temp_critical, + data->num_passive_trips, data->temp_zone_delta); } + static irqreturn_t imx_thermal_alarm_irq(int irq, void *dev) { struct imx_thermal_data *data = dev; @@ -626,6 +678,8 @@ static int imx_thermal_probe(struct platform_device *pdev) return ret; } + imx_set_thermal_defaults(data); + data->tz = thermal_zone_device_register("imx_thermal_zone", data->num_passive_trips + 1, BIT(IMX_TRIP_PASSIVE), data, From 42390ba595e3494f2ebfc5a92536391cf5f8fec2 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 23 Feb 2014 18:38:09 +0100 Subject: [PATCH 0307/1983] rtc: pcf8523: Auto-detect the proper capacitance Instead of forcing the capacitance to high for all hardware, or adding another setting to device-tree we auto-detect the proper capacitance for the oscillator. First we check the oscillator for stability based on its power on state, if that fails switch to the other capacitance and check for stability. The oscillator can take up to 2 seconds to stabilize. Signed-off-by: Jon Nettleton --- drivers/rtc/rtc-pcf8523.c | 89 +++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 5c8f8226c8485a..d61d1330444677 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -82,24 +83,96 @@ static int pcf8523_write(struct i2c_client *client, u8 reg, u8 value) return 0; } -static int pcf8523_select_capacitance(struct i2c_client *client, bool high) +static int pcf8523_rtc_check_oscillator(struct i2c_client *client) +{ + u8 start = REG_SECONDS, regs[7]; + struct i2c_msg msgs[2]; + int err; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &start; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(regs); + msgs[1].buf = regs; + + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err < 0) + return err; + + if (regs[0] & REG_SECONDS_OS) { + /* + * If the oscillator was stopped, try to clear the flag. Upon + * power-up the flag is always set, but if we cannot clear it + * the oscillator isn't running properly for some reason. The + * sensible thing therefore is to return an error, signalling + * that the clock cannot be assumed to be correct. + */ + + regs[0] &= ~REG_SECONDS_OS; + + err = pcf8523_write(client, REG_SECONDS, regs[0]); + if (err < 0) + return err; + + err = pcf8523_read(client, REG_SECONDS, ®s[0]); + if (err < 0) + return err; + + if (regs[0] & REG_SECONDS_OS) + return -EAGAIN; + } + + return 0; +} + +static int pcf8523_switch_capacitance(struct i2c_client *client) { u8 value; int err; err = pcf8523_read(client, REG_CONTROL1, &value); if (err < 0) - return err; + goto out; - if (!high) - value &= ~REG_CONTROL1_CAP_SEL; - else - value |= REG_CONTROL1_CAP_SEL; + value ^= REG_CONTROL1_CAP_SEL; err = pcf8523_write(client, REG_CONTROL1, value); + +out: + return err; +} + +static int pcf8523_enable_oscillator(struct i2c_client *client) +{ + int err, loop; + + loop = 0; + while (loop < 200) { + err = pcf8523_rtc_check_oscillator(client); + if (!err) + return 0; + loop++; + msleep(10); + } + + err = pcf8523_switch_capacitance(client); if (err < 0) - return err; + goto out; + + loop = 0; + while (loop < 200) { + err = pcf8523_rtc_check_oscillator(client); + if (!err) + return 0; + loop++; + msleep(10); + } +out: return err; } @@ -299,7 +372,7 @@ static int pcf8523_probe(struct i2c_client *client, if (!pcf) return -ENOMEM; - err = pcf8523_select_capacitance(client, true); + err = pcf8523_enable_oscillator(client); if (err < 0) return err; From 500a96e15eb89b13a000d063b1fd9695327e27aa Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 26 Feb 2014 10:15:47 +0100 Subject: [PATCH 0308/1983] rtc: pcf8523: Warn on possible oscillator problems Be nice to the operator and let them know of possible reasons that the rtc device may not be working properly. Signed-off-by: Russell King Signed-off-by: Jon Nettleton --- drivers/rtc/rtc-pcf8523.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index d61d1330444677..2452a33375d731 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -363,6 +363,7 @@ static int pcf8523_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pcf8523 *pcf; + u8 value; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) @@ -372,10 +373,20 @@ static int pcf8523_probe(struct i2c_client *client, if (!pcf) return -ENOMEM; - err = pcf8523_enable_oscillator(client); + /* Check whether the RTC reports battery low */ + err = pcf8523_read(client, REG_CONTROL3, &value); if (err < 0) return err; + if (value & REG_CONTROL3_BLF) + dev_warn(&client->dev, "RTC reports battery is low\n"); + + err = pcf8523_enable_oscillator(client); + if (err < 0) { + dev_warn(&client->dev, "RTC reports oscillator is not running\n"); + return err; + } + err = pcf8523_set_pm(client, 0); if (err < 0) return err; From ba09d2563e50e7020d826fa124af6b1fa0a2ad7f Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Feb 2014 10:19:46 +0100 Subject: [PATCH 0309/1983] rtc: pcf8523: Simplify the oscillator check and enable code This code was doing way more than it needed to. Simplify it so it is easier to understand. --- drivers/rtc/rtc-pcf8523.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 2452a33375d731..0813bdd69ef206 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -85,25 +85,14 @@ static int pcf8523_write(struct i2c_client *client, u8 reg, u8 value) static int pcf8523_rtc_check_oscillator(struct i2c_client *client) { - u8 start = REG_SECONDS, regs[7]; - struct i2c_msg msgs[2]; + u8 value; int err; - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = 1; - msgs[0].buf = &start; - - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = sizeof(regs); - msgs[1].buf = regs; - - err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + err = pcf8523_read(client, REG_SECONDS, &value); if (err < 0) return err; - if (regs[0] & REG_SECONDS_OS) { + if (value & REG_SECONDS_OS) { /* * If the oscillator was stopped, try to clear the flag. Upon * power-up the flag is always set, but if we cannot clear it @@ -112,17 +101,17 @@ static int pcf8523_rtc_check_oscillator(struct i2c_client *client) * that the clock cannot be assumed to be correct. */ - regs[0] &= ~REG_SECONDS_OS; + value &= ~REG_SECONDS_OS; - err = pcf8523_write(client, REG_SECONDS, regs[0]); + err = pcf8523_write(client, REG_SECONDS, value); if (err < 0) return err; - err = pcf8523_read(client, REG_SECONDS, ®s[0]); + err = pcf8523_read(client, REG_SECONDS, &value); if (err < 0) return err; - if (regs[0] & REG_SECONDS_OS) + if (value & REG_SECONDS_OS) return -EAGAIN; } From 7897fa1dbb508b61b6bc5c1e5768928bcbcba346 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 20 Apr 2015 14:03:25 +0800 Subject: [PATCH 0310/1983] rtc: pcf8523: start calibration at 12.5 pF capacitance Some boards come to a stable state at 7 pF, even though they have a 12.5 pF crystal. This patch provides a device-tree option that allows you to specify the auto-calibration process should start at 12.5 pF mode rather than the default 7 pF on this board. Co-Authored-By: Jon Nettleton Signed-off-by: Sean Cross Signed-off-by: Jon Nettleton --- .../devicetree/bindings/rtc/nxp,pcf8523.txt | 14 ++++++++ drivers/rtc/rtc-pcf8523.c | 32 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt new file mode 100644 index 00000000000000..b453c68319f673 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf8523.txt @@ -0,0 +1,14 @@ +NXP Semiconductors PCF8523 Real Time Clock + +Required properties: +- compatible: should be: "nxp,pcf8523" +- reg: i2c address +- nxp,12p5_pf: force autodetection to start at 12.5 pf instead of 7pf + +Example: + +rtc: pcf8523@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + nxp,12p5_pf; +}; diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 0813bdd69ef206..c56f18fbe1b13e 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -118,6 +118,28 @@ static int pcf8523_rtc_check_oscillator(struct i2c_client *client) return 0; } +#ifdef CONFIG_OF +static int pcf8523_set_12p5_pf(struct i2c_client *client) +{ + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL1, &value); + if (err < 0) + goto out; + + if (value & REG_CONTROL1_CAP_SEL) + return 0; + + value |= REG_CONTROL1_CAP_SEL; + + err = pcf8523_write(client, REG_CONTROL1, value); + +out: + return err; +} +#endif + static int pcf8523_switch_capacitance(struct i2c_client *client) { u8 value; @@ -351,6 +373,9 @@ static const struct rtc_class_ops pcf8523_rtc_ops = { static int pcf8523_probe(struct i2c_client *client, const struct i2c_device_id *id) { +#ifdef CONFIG_OF + struct device_node *np = client->dev.of_node; +#endif struct pcf8523 *pcf; u8 value; int err; @@ -370,6 +395,13 @@ static int pcf8523_probe(struct i2c_client *client, if (value & REG_CONTROL3_BLF) dev_warn(&client->dev, "RTC reports battery is low\n"); +#ifdef CONFIG_OF + if (of_property_read_bool(np, "nxp,12p5_pf")) { + dev_info(&client->dev, "starting calibration at 12.5 pf\n"); + pcf8523_set_12p5_pf(client); + } +#endif + err = pcf8523_enable_oscillator(client); if (err < 0) { dev_warn(&client->dev, "RTC reports oscillator is not running\n"); From b98420f489341c266a71469fad03d7f374f0eaf5 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 08:40:30 +0200 Subject: [PATCH 0311/1983] dts: cbi/hb: Use new nxp,12p5_pf flag for the rtc. Our rtc has a 12.5 pf crystal on it but will come to stability at 7pf which is the default setting. This causes the clock to run fast. Therefore use this setting to try the 12.5 pf value first for calibration. --- arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 1 + arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi index 1a5cf7b5a2f677..f1d11be3c01e8c 100644 --- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi @@ -141,6 +141,7 @@ rtc: pcf8523@68 { compatible = "nxp,pcf8523"; reg = <0x68>; + nxp,12p5_pf; }; }; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index 04ad5991a6f026..efc0829ae43ca6 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -186,6 +186,7 @@ rtc: pcf8523@68 { compatible = "nxp,pcf8523"; reg = <0x68>; + nxp,12p5_pf; }; /* Pro baseboard model */ From 43e0ce07ee0a8075b8f27e0f4acfd97e9fd24650 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 08:38:25 +0200 Subject: [PATCH 0312/1983] cpufreq: imx: reorder init to avoid crash at boot init mutex and register pm_handler before registering the driver to avoid messages like this. Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 805 [#1] PREEMPT SMP ARM Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.14.44-01875-gb025cd5-dirty #224 task: d8080000 ti: d8062000 task.ti: d8062000 PC is at __mutex_lock_slowpath+0x148/0x258 LR is at _raw_spin_lock+0x10/0x50 pc : [] lr : [] psr: a0000113 sp : d8063b68 ip : d8063b6c fp : 00000000 r10: c0eee130 r9 : d8062028 r8 : d8080000 r7 : 00000002 r6 : ffffffff r5 : c0eee12c r4 : c0eee128 r3 : 00000000 r2 : d8063b6c r1 : 00000000 r0 : 00000002 --- drivers/cpufreq/imx6q-cpufreq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index e62ef8a6108b9f..dd3942894b43f9 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -406,15 +406,15 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) if (ret > 0) transition_latency += ret * 1000; + mutex_init(&set_cpufreq_lock); + register_pm_notifier(&imx6_cpufreq_pm_notifier); + ret = cpufreq_register_driver(&imx6q_cpufreq_driver); if (ret) { dev_err(cpu_dev, "failed register driver: %d\n", ret); goto free_freq_table; } - mutex_init(&set_cpufreq_lock); - register_pm_notifier(&imx6_cpufreq_pm_notifier); - of_node_put(np); return 0; From 7ae39727732b5bd100a3fa0f72581e9db2564963 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 7 Aug 2012 12:29:46 -0700 Subject: [PATCH 0313/1983] CHROMIUM: cpufreq: ondemand: io_is_busy should be 1 for ARM Be conservative for now and only set it for V7 and newer versions. BUG=chrome-os-partner:11403 TEST=build, boot, check boot time. Change-Id: Ia4460efadf106c75608aac85a1f90d773af46cf2 Signed-off-by: Olof Johansson Reviewed-on: https://gerrit.chromium.org/gerrit/29435 Reviewed-by: Sameer Nanda --- drivers/cpufreq/cpufreq_ondemand.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 18d40918909200..3e8699f842b2b4 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -50,7 +50,8 @@ static void ondemand_powersave_bias_init_cpu(int cpu) * efficient idling at a higher frequency/voltage is. * Pavel Machek says this is not so for various generations of AMD and old * Intel systems. - * Mike Chan (android.com) claims this is also not true for ARM. + * ARM systems v7 and later generally good idle power management as well, so + * treat them the same. * Because of this, whitelist specific known (series) of CPUs by default, and * leave all others up to the user. */ @@ -64,6 +65,9 @@ static int should_io_be_busy(void) boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model >= 15) return 1; +#endif +#if defined(CONFIG_ARM) && defined(CPU_V7) + return 1; #endif return 0; } From c95099eeb9cd6169773abac2fa76cc969f2e51c0 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Tue, 3 Sep 2013 16:50:42 +0200 Subject: [PATCH 0314/1983] block: cgroups, kconfig, build bits for BFQ-v7r8-3.14 Update Kconfig.iosched and do the related Makefile changes to include kernel configuration options for BFQ. Also add the bfqio controller to the cgroups subsystem. Signed-off-by: Paolo Valente Signed-off-by: Arianna Avanzini --- block/Kconfig.iosched | 32 ++++++++++++++++++++++++++++++++ block/Makefile | 1 + include/linux/cgroup_subsys.h | 4 ++++ 3 files changed, 37 insertions(+) diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index 421bef9c4c48d2..0ee5f0f78b7b1a 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -39,6 +39,27 @@ config CFQ_GROUP_IOSCHED ---help--- Enable group IO scheduling in CFQ. +config IOSCHED_BFQ + tristate "BFQ I/O scheduler" + default n + ---help--- + The BFQ I/O scheduler tries to distribute bandwidth among + all processes according to their weights. + It aims at distributing the bandwidth as desired, independently of + the disk parameters and with any workload. It also tries to + guarantee low latency to interactive and soft real-time + applications. If compiled built-in (saying Y here), BFQ can + be configured to support hierarchical scheduling. + +config CGROUP_BFQIO + bool "BFQ hierarchical scheduling support" + depends on CGROUPS && IOSCHED_BFQ=y + default n + ---help--- + Enable hierarchical scheduling in BFQ, using the cgroups + filesystem interface. The name of the subsystem will be + bfqio. + choice prompt "Default I/O scheduler" default DEFAULT_CFQ @@ -52,6 +73,16 @@ choice config DEFAULT_CFQ bool "CFQ" if IOSCHED_CFQ=y + config DEFAULT_BFQ + bool "BFQ" if IOSCHED_BFQ=y + help + Selects BFQ as the default I/O scheduler which will be + used by default for all block devices. + The BFQ I/O scheduler aims at distributing the bandwidth + as desired, independently of the disk parameters and with + any workload. It also tries to guarantee low latency to + interactive and soft real-time applications. + config DEFAULT_NOOP bool "No-op" @@ -61,6 +92,7 @@ config DEFAULT_IOSCHED string default "deadline" if DEFAULT_DEADLINE default "cfq" if DEFAULT_CFQ + default "bfq" if DEFAULT_BFQ default "noop" if DEFAULT_NOOP endmenu diff --git a/block/Makefile b/block/Makefile index 20645e88fb572e..cbd83fb36dd952 100644 --- a/block/Makefile +++ b/block/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_BLK_DEV_THROTTLING) += blk-throttle.o obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o +obj-$(CONFIG_IOSCHED_BFQ) += bfq-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 7b99d717411d2b..4e8c0ff5c8a22b 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -39,6 +39,10 @@ SUBSYS(net_cls) SUBSYS(blkio) #endif +#if IS_SUBSYS_ENABLED(CONFIG_CGROUP_BFQIO) +SUBSYS(bfqio) +#endif + #if IS_SUBSYS_ENABLED(CONFIG_CGROUP_PERF) SUBSYS(perf) #endif From 7d00980d321502f9330cd9ff276e0482cd257be0 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Thu, 9 May 2013 19:10:02 +0200 Subject: [PATCH 0315/1983] block: introduce the BFQ-v7r8 I/O sched for 3.14 Add the BFQ-v7r8 I/O scheduler to 3.14. The general structure is borrowed from CFQ, as much of the code for handling I/O contexts. Over time, several useful features have been ported from CFQ as well (details in the changelog in README.BFQ). A (bfq_)queue is associated to each task doing I/O on a device, and each time a scheduling decision has to be made a queue is selected and served until it expires. - Slices are given in the service domain: tasks are assigned budgets, measured in number of sectors. Once got the disk, a task must however consume its assigned budget within a configurable maximum time (by default, the maximum possible value of the budgets is automatically computed to comply with this timeout). This allows the desired latency vs "throughput boosting" tradeoff to be set. - Budgets are scheduled according to a variant of WF2Q+, implemented using an augmented rb-tree to take eligibility into account while preserving an O(log N) overall complexity. - A low-latency tunable is provided; if enabled, both interactive and soft real-time applications are guaranteed a very low latency. - Latency guarantees are preserved also in the presence of NCQ. - Also with flash-based devices, a high throughput is achieved while still preserving latency guarantees. - BFQ features Early Queue Merge (EQM), a sort of fusion of the cooperating-queue-merging and the preemption mechanisms present in CFQ. EQM is in fact a unified mechanism that tries to get a sequential read pattern, and hence a high throughput, with any set of processes performing interleaved I/O over a contiguous sequence of sectors. - BFQ supports full hierarchical scheduling, exporting a cgroups interface. Since each node has a full scheduler, each group can be assigned its own weight. - If the cgroups interface is not used, only I/O priorities can be assigned to processes, with ioprio values mapped to weights with the relation weight = IOPRIO_BE_NR - ioprio. - ioprio classes are served in strict priority order, i.e., lower priority queues are not served as long as there are higher priority queues. Among queues in the same class the bandwidth is distributed in proportion to the weight of each queue. A very thin extra bandwidth is however guaranteed to the Idle class, to prevent it from starving. Signed-off-by: Paolo Valente Signed-off-by: Arianna Avanzini --- block/bfq-cgroup.c | 938 +++++++++++ block/bfq-ioc.c | 36 + block/bfq-iosched.c | 3898 +++++++++++++++++++++++++++++++++++++++++++ block/bfq-sched.c | 1208 ++++++++++++++ block/bfq.h | 771 +++++++++ 5 files changed, 6851 insertions(+) create mode 100644 block/bfq-cgroup.c create mode 100644 block/bfq-ioc.c create mode 100644 block/bfq-iosched.c create mode 100644 block/bfq-sched.c create mode 100644 block/bfq.h diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c new file mode 100644 index 00000000000000..798bb177989044 --- /dev/null +++ b/block/bfq-cgroup.c @@ -0,0 +1,938 @@ +/* + * BFQ: CGROUPS support. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ + * file. + */ + +#ifdef CONFIG_CGROUP_BFQIO + +static DEFINE_MUTEX(bfqio_mutex); + +static bool bfqio_is_removed(struct bfqio_cgroup *bgrp) +{ + return bgrp ? !bgrp->online : false; +} + +static struct bfqio_cgroup bfqio_root_cgroup = { + .weight = BFQ_DEFAULT_GRP_WEIGHT, + .ioprio = BFQ_DEFAULT_GRP_IOPRIO, + .ioprio_class = BFQ_DEFAULT_GRP_CLASS, +}; + +static inline void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +{ + entity->weight = entity->new_weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class; + entity->parent = bfqg->my_entity; + entity->sched_data = &bfqg->sched_data; +} + +static struct bfqio_cgroup *css_to_bfqio(struct cgroup_subsys_state *css) +{ + return css ? container_of(css, struct bfqio_cgroup, css) : NULL; +} + +/* + * Search the bfq_group for bfqd into the hash table (by now only a list) + * of bgrp. Must be called under rcu_read_lock(). + */ +static struct bfq_group *bfqio_lookup_group(struct bfqio_cgroup *bgrp, + struct bfq_data *bfqd) +{ + struct bfq_group *bfqg; + void *key; + + hlist_for_each_entry_rcu(bfqg, &bgrp->group_data, group_node) { + key = rcu_dereference(bfqg->bfqd); + if (key == bfqd) + return bfqg; + } + + return NULL; +} + +static inline void bfq_group_init_entity(struct bfqio_cgroup *bgrp, + struct bfq_group *bfqg) +{ + struct bfq_entity *entity = &bfqg->entity; + + /* + * If the weight of the entity has never been set via the sysfs + * interface, then bgrp->weight == 0. In this case we initialize + * the weight from the current ioprio value. Otherwise, the group + * weight, if set, has priority over the ioprio value. + */ + if (bgrp->weight == 0) { + entity->new_weight = bfq_ioprio_to_weight(bgrp->ioprio); + entity->new_ioprio = bgrp->ioprio; + } else { + if (bgrp->weight < BFQ_MIN_WEIGHT || + bgrp->weight > BFQ_MAX_WEIGHT) { + printk(KERN_CRIT "bfq_group_init_entity: " + "bgrp->weight %d\n", bgrp->weight); + BUG(); + } + entity->new_weight = bgrp->weight; + entity->new_ioprio = bfq_weight_to_ioprio(bgrp->weight); + } + entity->orig_weight = entity->weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class = bgrp->ioprio_class; + entity->my_sched_data = &bfqg->sched_data; + bfqg->active_entities = 0; +} + +static inline void bfq_group_set_parent(struct bfq_group *bfqg, + struct bfq_group *parent) +{ + struct bfq_entity *entity; + + BUG_ON(parent == NULL); + BUG_ON(bfqg == NULL); + + entity = &bfqg->entity; + entity->parent = parent->my_entity; + entity->sched_data = &parent->sched_data; +} + +/** + * bfq_group_chain_alloc - allocate a chain of groups. + * @bfqd: queue descriptor. + * @css: the leaf cgroup_subsys_state this chain starts from. + * + * Allocate a chain of groups starting from the one belonging to + * @cgroup up to the root cgroup. Stop if a cgroup on the chain + * to the root has already an allocated group on @bfqd. + */ +static struct bfq_group *bfq_group_chain_alloc(struct bfq_data *bfqd, + struct cgroup_subsys_state *css) +{ + struct bfqio_cgroup *bgrp; + struct bfq_group *bfqg, *prev = NULL, *leaf = NULL; + + for (; css != NULL; css = css->parent) { + bgrp = css_to_bfqio(css); + + bfqg = bfqio_lookup_group(bgrp, bfqd); + if (bfqg != NULL) { + /* + * All the cgroups in the path from there to the + * root must have a bfq_group for bfqd, so we don't + * need any more allocations. + */ + break; + } + + bfqg = kzalloc(sizeof(*bfqg), GFP_ATOMIC); + if (bfqg == NULL) + goto cleanup; + + bfq_group_init_entity(bgrp, bfqg); + bfqg->my_entity = &bfqg->entity; + + if (leaf == NULL) { + leaf = bfqg; + prev = leaf; + } else { + bfq_group_set_parent(prev, bfqg); + /* + * Build a list of allocated nodes using the bfqd + * filed, that is still unused and will be + * initialized only after the node will be + * connected. + */ + prev->bfqd = bfqg; + prev = bfqg; + } + } + + return leaf; + +cleanup: + while (leaf != NULL) { + prev = leaf; + leaf = leaf->bfqd; + kfree(prev); + } + + return NULL; +} + +/** + * bfq_group_chain_link - link an allocated group chain to a cgroup + * hierarchy. + * @bfqd: the queue descriptor. + * @css: the leaf cgroup_subsys_state to start from. + * @leaf: the leaf group (to be associated to @cgroup). + * + * Try to link a chain of groups to a cgroup hierarchy, connecting the + * nodes bottom-up, so we can be sure that when we find a cgroup in the + * hierarchy that already as a group associated to @bfqd all the nodes + * in the path to the root cgroup have one too. + * + * On locking: the queue lock protects the hierarchy (there is a hierarchy + * per device) while the bfqio_cgroup lock protects the list of groups + * belonging to the same cgroup. + */ +static void bfq_group_chain_link(struct bfq_data *bfqd, + struct cgroup_subsys_state *css, + struct bfq_group *leaf) +{ + struct bfqio_cgroup *bgrp; + struct bfq_group *bfqg, *next, *prev = NULL; + unsigned long flags; + + assert_spin_locked(bfqd->queue->queue_lock); + + for (; css != NULL && leaf != NULL; css = css->parent) { + bgrp = css_to_bfqio(css); + next = leaf->bfqd; + + bfqg = bfqio_lookup_group(bgrp, bfqd); + BUG_ON(bfqg != NULL); + + spin_lock_irqsave(&bgrp->lock, flags); + + rcu_assign_pointer(leaf->bfqd, bfqd); + hlist_add_head_rcu(&leaf->group_node, &bgrp->group_data); + hlist_add_head(&leaf->bfqd_node, &bfqd->group_list); + + spin_unlock_irqrestore(&bgrp->lock, flags); + + prev = leaf; + leaf = next; + } + + BUG_ON(css == NULL && leaf != NULL); + if (css != NULL && prev != NULL) { + bgrp = css_to_bfqio(css); + bfqg = bfqio_lookup_group(bgrp, bfqd); + bfq_group_set_parent(prev, bfqg); + } +} + +/** + * bfq_find_alloc_group - return the group associated to @bfqd in @cgroup. + * @bfqd: queue descriptor. + * @cgroup: cgroup being searched for. + * + * Return a group associated to @bfqd in @cgroup, allocating one if + * necessary. When a group is returned all the cgroups in the path + * to the root have a group associated to @bfqd. + * + * If the allocation fails, return the root group: this breaks guarantees + * but is a safe fallback. If this loss becomes a problem it can be + * mitigated using the equivalent weight (given by the product of the + * weights of the groups in the path from @group to the root) in the + * root scheduler. + * + * We allocate all the missing nodes in the path from the leaf cgroup + * to the root and we connect the nodes only after all the allocations + * have been successful. + */ +static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, + struct cgroup_subsys_state *css) +{ + struct bfqio_cgroup *bgrp = css_to_bfqio(css); + struct bfq_group *bfqg; + + bfqg = bfqio_lookup_group(bgrp, bfqd); + if (bfqg != NULL) + return bfqg; + + bfqg = bfq_group_chain_alloc(bfqd, css); + if (bfqg != NULL) + bfq_group_chain_link(bfqd, css, bfqg); + else + bfqg = bfqd->root_group; + + return bfqg; +} + +/** + * bfq_bfqq_move - migrate @bfqq to @bfqg. + * @bfqd: queue descriptor. + * @bfqq: the queue to move. + * @entity: @bfqq's entity. + * @bfqg: the group to move to. + * + * Move @bfqq to @bfqg, deactivating it from its old group and reactivating + * it on the new one. Avoid putting the entity on the old group idle tree. + * + * Must be called under the queue lock; the cgroup owning @bfqg must + * not disappear (by now this just means that we are called under + * rcu_read_lock()). + */ +static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct bfq_entity *entity, struct bfq_group *bfqg) +{ + int busy, resume; + + busy = bfq_bfqq_busy(bfqq); + resume = !RB_EMPTY_ROOT(&bfqq->sort_list); + + BUG_ON(resume && !entity->on_st); + BUG_ON(busy && !resume && entity->on_st && + bfqq != bfqd->in_service_queue); + + if (busy) { + BUG_ON(atomic_read(&bfqq->ref) < 2); + + if (!resume) + bfq_del_bfqq_busy(bfqd, bfqq, 0); + else + bfq_deactivate_bfqq(bfqd, bfqq, 0); + } else if (entity->on_st) + bfq_put_idle_entity(bfq_entity_service_tree(entity), entity); + + /* + * Here we use a reference to bfqg. We don't need a refcounter + * as the cgroup reference will not be dropped, so that its + * destroy() callback will not be invoked. + */ + entity->parent = bfqg->my_entity; + entity->sched_data = &bfqg->sched_data; + + if (busy && resume) + bfq_activate_bfqq(bfqd, bfqq); + + if (bfqd->in_service_queue == NULL && !bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); +} + +/** + * __bfq_bic_change_cgroup - move @bic to @cgroup. + * @bfqd: the queue descriptor. + * @bic: the bic to move. + * @cgroup: the cgroup to move to. + * + * Move bic to cgroup, assuming that bfqd->queue is locked; the caller + * has to make sure that the reference to cgroup is valid across the call. + * + * NOTE: an alternative approach might have been to store the current + * cgroup in bfqq and getting a reference to it, reducing the lookup + * time here, at the price of slightly more complex code. + */ +static struct bfq_group *__bfq_bic_change_cgroup(struct bfq_data *bfqd, + struct bfq_io_cq *bic, + struct cgroup_subsys_state *css) +{ + struct bfq_queue *async_bfqq = bic_to_bfqq(bic, 0); + struct bfq_queue *sync_bfqq = bic_to_bfqq(bic, 1); + struct bfq_entity *entity; + struct bfq_group *bfqg; + struct bfqio_cgroup *bgrp; + + bgrp = css_to_bfqio(css); + + bfqg = bfq_find_alloc_group(bfqd, css); + if (async_bfqq != NULL) { + entity = &async_bfqq->entity; + + if (entity->sched_data != &bfqg->sched_data) { + bic_set_bfqq(bic, NULL, 0); + bfq_log_bfqq(bfqd, async_bfqq, + "bic_change_group: %p %d", + async_bfqq, atomic_read(&async_bfqq->ref)); + bfq_put_queue(async_bfqq); + } + } + + if (sync_bfqq != NULL) { + entity = &sync_bfqq->entity; + if (entity->sched_data != &bfqg->sched_data) + bfq_bfqq_move(bfqd, sync_bfqq, entity, bfqg); + } + + return bfqg; +} + +/** + * bfq_bic_change_cgroup - move @bic to @cgroup. + * @bic: the bic being migrated. + * @cgroup: the destination cgroup. + * + * When the task owning @bic is moved to @cgroup, @bic is immediately + * moved into its new parent group. + */ +static void bfq_bic_change_cgroup(struct bfq_io_cq *bic, + struct cgroup_subsys_state *css) +{ + struct bfq_data *bfqd; + unsigned long uninitialized_var(flags); + + bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), + &flags); + if (bfqd != NULL) { + __bfq_bic_change_cgroup(bfqd, bic, css); + bfq_put_bfqd_unlock(bfqd, &flags); + } +} + +/** + * bfq_bic_update_cgroup - update the cgroup of @bic. + * @bic: the @bic to update. + * + * Make sure that @bic is enqueued in the cgroup of the current task. + * We need this in addition to moving bics during the cgroup attach + * phase because the task owning @bic could be at its first disk + * access or we may end up in the root cgroup as the result of a + * memory allocation failure and here we try to move to the right + * group. + * + * Must be called under the queue lock. It is safe to use the returned + * value even after the rcu_read_unlock() as the migration/destruction + * paths act under the queue lock too. IOW it is impossible to race with + * group migration/destruction and end up with an invalid group as: + * a) here cgroup has not yet been destroyed, nor its destroy callback + * has started execution, as current holds a reference to it, + * b) if it is destroyed after rcu_read_unlock() [after current is + * migrated to a different cgroup] its attach() callback will have + * taken care of remove all the references to the old cgroup data. + */ +static struct bfq_group *bfq_bic_update_cgroup(struct bfq_io_cq *bic) +{ + struct bfq_data *bfqd = bic_to_bfqd(bic); + struct bfq_group *bfqg; + struct cgroup_subsys_state *css; + + BUG_ON(bfqd == NULL); + + rcu_read_lock(); + css = task_css(current, bfqio_subsys_id); + bfqg = __bfq_bic_change_cgroup(bfqd, bic, css); + rcu_read_unlock(); + + return bfqg; +} + +/** + * bfq_flush_idle_tree - deactivate any entity on the idle tree of @st. + * @st: the service tree being flushed. + */ +static inline void bfq_flush_idle_tree(struct bfq_service_tree *st) +{ + struct bfq_entity *entity = st->first_idle; + + for (; entity != NULL; entity = st->first_idle) + __bfq_deactivate_entity(entity, 0); +} + +/** + * bfq_reparent_leaf_entity - move leaf entity to the root_group. + * @bfqd: the device data structure with the root group. + * @entity: the entity to move. + */ +static inline void bfq_reparent_leaf_entity(struct bfq_data *bfqd, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + BUG_ON(bfqq == NULL); + bfq_bfqq_move(bfqd, bfqq, entity, bfqd->root_group); + return; +} + +/** + * bfq_reparent_active_entities - move to the root group all active + * entities. + * @bfqd: the device data structure with the root group. + * @bfqg: the group to move from. + * @st: the service tree with the entities. + * + * Needs queue_lock to be taken and reference to be valid over the call. + */ +static inline void bfq_reparent_active_entities(struct bfq_data *bfqd, + struct bfq_group *bfqg, + struct bfq_service_tree *st) +{ + struct rb_root *active = &st->active; + struct bfq_entity *entity = NULL; + + if (!RB_EMPTY_ROOT(&st->active)) + entity = bfq_entity_of(rb_first(active)); + + for (; entity != NULL; entity = bfq_entity_of(rb_first(active))) + bfq_reparent_leaf_entity(bfqd, entity); + + if (bfqg->sched_data.in_service_entity != NULL) + bfq_reparent_leaf_entity(bfqd, + bfqg->sched_data.in_service_entity); + + return; +} + +/** + * bfq_destroy_group - destroy @bfqg. + * @bgrp: the bfqio_cgroup containing @bfqg. + * @bfqg: the group being destroyed. + * + * Destroy @bfqg, making sure that it is not referenced from its parent. + */ +static void bfq_destroy_group(struct bfqio_cgroup *bgrp, struct bfq_group *bfqg) +{ + struct bfq_data *bfqd; + struct bfq_service_tree *st; + struct bfq_entity *entity = bfqg->my_entity; + unsigned long uninitialized_var(flags); + int i; + + hlist_del(&bfqg->group_node); + + /* + * Empty all service_trees belonging to this group before + * deactivating the group itself. + */ + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) { + st = bfqg->sched_data.service_tree + i; + + /* + * The idle tree may still contain bfq_queues belonging + * to exited task because they never migrated to a different + * cgroup from the one being destroyed now. No one else + * can access them so it's safe to act without any lock. + */ + bfq_flush_idle_tree(st); + + /* + * It may happen that some queues are still active + * (busy) upon group destruction (if the corresponding + * processes have been forced to terminate). We move + * all the leaf entities corresponding to these queues + * to the root_group. + * Also, it may happen that the group has an entity + * in service, which is disconnected from the active + * tree: it must be moved, too. + * There is no need to put the sync queues, as the + * scheduler has taken no reference. + */ + bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); + if (bfqd != NULL) { + bfq_reparent_active_entities(bfqd, bfqg, st); + bfq_put_bfqd_unlock(bfqd, &flags); + } + BUG_ON(!RB_EMPTY_ROOT(&st->active)); + BUG_ON(!RB_EMPTY_ROOT(&st->idle)); + } + BUG_ON(bfqg->sched_data.next_in_service != NULL); + BUG_ON(bfqg->sched_data.in_service_entity != NULL); + + /* + * We may race with device destruction, take extra care when + * dereferencing bfqg->bfqd. + */ + bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); + if (bfqd != NULL) { + hlist_del(&bfqg->bfqd_node); + __bfq_deactivate_entity(entity, 0); + bfq_put_async_queues(bfqd, bfqg); + bfq_put_bfqd_unlock(bfqd, &flags); + } + BUG_ON(entity->tree != NULL); + + /* + * No need to defer the kfree() to the end of the RCU grace + * period: we are called from the destroy() callback of our + * cgroup, so we can be sure that no one is a) still using + * this cgroup or b) doing lookups in it. + */ + kfree(bfqg); +} + +static void bfq_end_wr_async(struct bfq_data *bfqd) +{ + struct hlist_node *tmp; + struct bfq_group *bfqg; + + hlist_for_each_entry_safe(bfqg, tmp, &bfqd->group_list, bfqd_node) + bfq_end_wr_async_queues(bfqd, bfqg); + bfq_end_wr_async_queues(bfqd, bfqd->root_group); +} + +/** + * bfq_disconnect_groups - disconnect @bfqd from all its groups. + * @bfqd: the device descriptor being exited. + * + * When the device exits we just make sure that no lookup can return + * the now unused group structures. They will be deallocated on cgroup + * destruction. + */ +static void bfq_disconnect_groups(struct bfq_data *bfqd) +{ + struct hlist_node *tmp; + struct bfq_group *bfqg; + + bfq_log(bfqd, "disconnect_groups beginning"); + hlist_for_each_entry_safe(bfqg, tmp, &bfqd->group_list, bfqd_node) { + hlist_del(&bfqg->bfqd_node); + + __bfq_deactivate_entity(bfqg->my_entity, 0); + + /* + * Don't remove from the group hash, just set an + * invalid key. No lookups can race with the + * assignment as bfqd is being destroyed; this + * implies also that new elements cannot be added + * to the list. + */ + rcu_assign_pointer(bfqg->bfqd, NULL); + + bfq_log(bfqd, "disconnect_groups: put async for group %p", + bfqg); + bfq_put_async_queues(bfqd, bfqg); + } +} + +static inline void bfq_free_root_group(struct bfq_data *bfqd) +{ + struct bfqio_cgroup *bgrp = &bfqio_root_cgroup; + struct bfq_group *bfqg = bfqd->root_group; + + bfq_put_async_queues(bfqd, bfqg); + + spin_lock_irq(&bgrp->lock); + hlist_del_rcu(&bfqg->group_node); + spin_unlock_irq(&bgrp->lock); + + /* + * No need to synchronize_rcu() here: since the device is gone + * there cannot be any read-side access to its root_group. + */ + kfree(bfqg); +} + +static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) +{ + struct bfq_group *bfqg; + struct bfqio_cgroup *bgrp; + int i; + + bfqg = kzalloc_node(sizeof(*bfqg), GFP_KERNEL, node); + if (bfqg == NULL) + return NULL; + + bfqg->entity.parent = NULL; + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) + bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; + + bgrp = &bfqio_root_cgroup; + spin_lock_irq(&bgrp->lock); + rcu_assign_pointer(bfqg->bfqd, bfqd); + hlist_add_head_rcu(&bfqg->group_node, &bgrp->group_data); + spin_unlock_irq(&bgrp->lock); + + return bfqg; +} + +#define SHOW_FUNCTION(__VAR) \ +static u64 bfqio_cgroup_##__VAR##_read(struct cgroup_subsys_state *css, \ + struct cftype *cftype) \ +{ \ + struct bfqio_cgroup *bgrp = css_to_bfqio(css); \ + u64 ret = -ENODEV; \ + \ + mutex_lock(&bfqio_mutex); \ + if (bfqio_is_removed(bgrp)) \ + goto out_unlock; \ + \ + spin_lock_irq(&bgrp->lock); \ + ret = bgrp->__VAR; \ + spin_unlock_irq(&bgrp->lock); \ + \ +out_unlock: \ + mutex_unlock(&bfqio_mutex); \ + return ret; \ +} + +SHOW_FUNCTION(weight); +SHOW_FUNCTION(ioprio); +SHOW_FUNCTION(ioprio_class); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__VAR, __MIN, __MAX) \ +static int bfqio_cgroup_##__VAR##_write(struct cgroup_subsys_state *css,\ + struct cftype *cftype, \ + u64 val) \ +{ \ + struct bfqio_cgroup *bgrp = css_to_bfqio(css); \ + struct bfq_group *bfqg; \ + int ret = -EINVAL; \ + \ + if (val < (__MIN) || val > (__MAX)) \ + return ret; \ + \ + ret = -ENODEV; \ + mutex_lock(&bfqio_mutex); \ + if (bfqio_is_removed(bgrp)) \ + goto out_unlock; \ + ret = 0; \ + \ + spin_lock_irq(&bgrp->lock); \ + bgrp->__VAR = (unsigned short)val; \ + hlist_for_each_entry(bfqg, &bgrp->group_data, group_node) { \ + /* \ + * Setting the ioprio_changed flag of the entity \ + * to 1 with new_##__VAR == ##__VAR would re-set \ + * the value of the weight to its ioprio mapping. \ + * Set the flag only if necessary. \ + */ \ + if ((unsigned short)val != bfqg->entity.new_##__VAR) { \ + bfqg->entity.new_##__VAR = (unsigned short)val; \ + /* \ + * Make sure that the above new value has been \ + * stored in bfqg->entity.new_##__VAR before \ + * setting the ioprio_changed flag. In fact, \ + * this flag may be read asynchronously (in \ + * critical sections protected by a different \ + * lock than that held here), and finding this \ + * flag set may cause the execution of the code \ + * for updating parameters whose value may \ + * depend also on bfqg->entity.new_##__VAR (in \ + * __bfq_entity_update_weight_prio). \ + * This barrier makes sure that the new value \ + * of bfqg->entity.new_##__VAR is correctly \ + * seen in that code. \ + */ \ + smp_wmb(); \ + bfqg->entity.ioprio_changed = 1; \ + } \ + } \ + spin_unlock_irq(&bgrp->lock); \ + \ +out_unlock: \ + mutex_unlock(&bfqio_mutex); \ + return ret; \ +} + +STORE_FUNCTION(weight, BFQ_MIN_WEIGHT, BFQ_MAX_WEIGHT); +STORE_FUNCTION(ioprio, 0, IOPRIO_BE_NR - 1); +STORE_FUNCTION(ioprio_class, IOPRIO_CLASS_RT, IOPRIO_CLASS_IDLE); +#undef STORE_FUNCTION + +static struct cftype bfqio_files[] = { + { + .name = "weight", + .read_u64 = bfqio_cgroup_weight_read, + .write_u64 = bfqio_cgroup_weight_write, + }, + { + .name = "ioprio", + .read_u64 = bfqio_cgroup_ioprio_read, + .write_u64 = bfqio_cgroup_ioprio_write, + }, + { + .name = "ioprio_class", + .read_u64 = bfqio_cgroup_ioprio_class_read, + .write_u64 = bfqio_cgroup_ioprio_class_write, + }, + { }, /* terminate */ +}; + +static struct cgroup_subsys_state *bfqio_create(struct cgroup_subsys_state + *parent_css) +{ + struct bfqio_cgroup *bgrp; + + if (parent_css != NULL) { + bgrp = kzalloc(sizeof(*bgrp), GFP_KERNEL); + if (bgrp == NULL) + return ERR_PTR(-ENOMEM); + } else + bgrp = &bfqio_root_cgroup; + + spin_lock_init(&bgrp->lock); + INIT_HLIST_HEAD(&bgrp->group_data); + bgrp->ioprio = BFQ_DEFAULT_GRP_IOPRIO; + bgrp->ioprio_class = BFQ_DEFAULT_GRP_CLASS; + + return &bgrp->css; +} + +/* + * We cannot support shared io contexts, as we have no means to support + * two tasks with the same ioc in two different groups without major rework + * of the main bic/bfqq data structures. By now we allow a task to change + * its cgroup only if it's the only owner of its ioc; the drawback of this + * behavior is that a group containing a task that forked using CLONE_IO + * will not be destroyed until the tasks sharing the ioc die. + */ +static int bfqio_can_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) +{ + struct task_struct *task; + struct io_context *ioc; + int ret = 0; + + cgroup_taskset_for_each(task, css, tset) { + /* + * task_lock() is needed to avoid races with + * exit_io_context() + */ + task_lock(task); + ioc = task->io_context; + if (ioc != NULL && atomic_read(&ioc->nr_tasks) > 1) + /* + * ioc == NULL means that the task is either too + * young or exiting: if it has still no ioc the + * ioc can't be shared, if the task is exiting the + * attach will fail anyway, no matter what we + * return here. + */ + ret = -EINVAL; + task_unlock(task); + if (ret) + break; + } + + return ret; +} + +static void bfqio_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) +{ + struct task_struct *task; + struct io_context *ioc; + struct io_cq *icq; + + /* + * IMPORTANT NOTE: The move of more than one process at a time to a + * new group has not yet been tested. + */ + cgroup_taskset_for_each(task, css, tset) { + ioc = get_task_io_context(task, GFP_ATOMIC, NUMA_NO_NODE); + if (ioc) { + /* + * Handle cgroup change here. + */ + rcu_read_lock(); + hlist_for_each_entry_rcu(icq, &ioc->icq_list, ioc_node) + if (!strncmp( + icq->q->elevator->type->elevator_name, + "bfq", ELV_NAME_MAX)) + bfq_bic_change_cgroup(icq_to_bic(icq), + css); + rcu_read_unlock(); + put_io_context(ioc); + } + } +} + +static void bfqio_destroy(struct cgroup_subsys_state *css) +{ + struct bfqio_cgroup *bgrp = css_to_bfqio(css); + struct hlist_node *tmp; + struct bfq_group *bfqg; + + /* + * Since we are destroying the cgroup, there are no more tasks + * referencing it, and all the RCU grace periods that may have + * referenced it are ended (as the destruction of the parent + * cgroup is RCU-safe); bgrp->group_data will not be accessed by + * anything else and we don't need any synchronization. + */ + hlist_for_each_entry_safe(bfqg, tmp, &bgrp->group_data, group_node) + bfq_destroy_group(bgrp, bfqg); + + BUG_ON(!hlist_empty(&bgrp->group_data)); + + kfree(bgrp); +} + +static int bfqio_css_online(struct cgroup_subsys_state *css) +{ + struct bfqio_cgroup *bgrp = css_to_bfqio(css); + + mutex_lock(&bfqio_mutex); + bgrp->online = true; + mutex_unlock(&bfqio_mutex); + + return 0; +} + +static void bfqio_css_offline(struct cgroup_subsys_state *css) +{ + struct bfqio_cgroup *bgrp = css_to_bfqio(css); + + mutex_lock(&bfqio_mutex); + bgrp->online = false; + mutex_unlock(&bfqio_mutex); +} + +struct cgroup_subsys bfqio_subsys = { + .name = "bfqio", + .css_alloc = bfqio_create, + .css_online = bfqio_css_online, + .css_offline = bfqio_css_offline, + .can_attach = bfqio_can_attach, + .attach = bfqio_attach, + .css_free = bfqio_destroy, + .subsys_id = bfqio_subsys_id, + .base_cftypes = bfqio_files, +}; +#else +static inline void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +{ + entity->weight = entity->new_weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class; + entity->sched_data = &bfqg->sched_data; +} + +static inline struct bfq_group * +bfq_bic_update_cgroup(struct bfq_io_cq *bic) +{ + struct bfq_data *bfqd = bic_to_bfqd(bic); + return bfqd->root_group; +} + +static inline void bfq_bfqq_move(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct bfq_entity *entity, + struct bfq_group *bfqg) +{ +} + +static void bfq_end_wr_async(struct bfq_data *bfqd) +{ + bfq_end_wr_async_queues(bfqd, bfqd->root_group); +} + +static inline void bfq_disconnect_groups(struct bfq_data *bfqd) +{ + bfq_put_async_queues(bfqd, bfqd->root_group); +} + +static inline void bfq_free_root_group(struct bfq_data *bfqd) +{ + kfree(bfqd->root_group); +} + +static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) +{ + struct bfq_group *bfqg; + int i; + + bfqg = kmalloc_node(sizeof(*bfqg), GFP_KERNEL | __GFP_ZERO, node); + if (bfqg == NULL) + return NULL; + + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) + bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; + + return bfqg; +} +#endif diff --git a/block/bfq-ioc.c b/block/bfq-ioc.c new file mode 100644 index 00000000000000..7f6b0004ca7017 --- /dev/null +++ b/block/bfq-ioc.c @@ -0,0 +1,36 @@ +/* + * BFQ: I/O context handling. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + */ + +/** + * icq_to_bic - convert iocontext queue structure to bfq_io_cq. + * @icq: the iocontext queue. + */ +static inline struct bfq_io_cq *icq_to_bic(struct io_cq *icq) +{ + /* bic->icq is the first member, %NULL will convert to %NULL */ + return container_of(icq, struct bfq_io_cq, icq); +} + +/** + * bfq_bic_lookup - search into @ioc a bic associated to @bfqd. + * @bfqd: the lookup key. + * @ioc: the io_context of the process doing I/O. + * + * Queue lock must be held. + */ +static inline struct bfq_io_cq *bfq_bic_lookup(struct bfq_data *bfqd, + struct io_context *ioc) +{ + if (ioc) + return icq_to_bic(ioc_lookup_icq(ioc, bfqd->queue)); + return NULL; +} diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c new file mode 100644 index 00000000000000..5b675b226f6466 --- /dev/null +++ b/block/bfq-iosched.c @@ -0,0 +1,3898 @@ +/* + * Budget Fair Queueing (BFQ) disk scheduler. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ + * file. + * + * BFQ is a proportional-share storage-I/O scheduling algorithm based on + * the slice-by-slice service scheme of CFQ. But BFQ assigns budgets, + * measured in number of sectors, to processes instead of time slices. The + * device is not granted to the in-service process for a given time slice, + * but until it has exhausted its assigned budget. This change from the time + * to the service domain allows BFQ to distribute the device throughput + * among processes as desired, without any distortion due to ZBR, workload + * fluctuations or other factors. BFQ uses an ad hoc internal scheduler, + * called B-WF2Q+, to schedule processes according to their budgets. More + * precisely, BFQ schedules queues associated to processes. Thanks to the + * accurate policy of B-WF2Q+, BFQ can afford to assign high budgets to + * I/O-bound processes issuing sequential requests (to boost the + * throughput), and yet guarantee a low latency to interactive and soft + * real-time applications. + * + * BFQ is described in [1], where also a reference to the initial, more + * theoretical paper on BFQ can be found. The interested reader can find + * in the latter paper full details on the main algorithm, as well as + * formulas of the guarantees and formal proofs of all the properties. + * With respect to the version of BFQ presented in these papers, this + * implementation adds a few more heuristics, such as the one that + * guarantees a low latency to soft real-time applications, and a + * hierarchical extension based on H-WF2Q+. + * + * B-WF2Q+ is based on WF2Q+, that is described in [2], together with + * H-WF2Q+, while the augmented tree used to implement B-WF2Q+ with O(log N) + * complexity derives from the one introduced with EEVDF in [3]. + * + * [1] P. Valente and M. Andreolini, ``Improving Application Responsiveness + * with the BFQ Disk I/O Scheduler'', + * Proceedings of the 5th Annual International Systems and Storage + * Conference (SYSTOR '12), June 2012. + * + * http://algogroup.unimo.it/people/paolo/disk_sched/bf1-v1-suite-results.pdf + * + * [2] Jon C.R. Bennett and H. Zhang, ``Hierarchical Packet Fair Queueing + * Algorithms,'' IEEE/ACM Transactions on Networking, 5(5):675-689, + * Oct 1997. + * + * http://www.cs.cmu.edu/~hzhang/papers/TON-97-Oct.ps.gz + * + * [3] I. Stoica and H. Abdel-Wahab, ``Earliest Eligible Virtual Deadline + * First: A Flexible and Accurate Mechanism for Proportional Share + * Resource Allocation,'' technical report. + * + * http://www.cs.berkeley.edu/~istoica/papers/eevdf-tr-95.pdf + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "bfq.h" +#include "blk.h" + +/* Expiration time of sync (0) and async (1) requests, in jiffies. */ +static const int bfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; + +/* Maximum backwards seek, in KiB. */ +static const int bfq_back_max = 16 * 1024; + +/* Penalty of a backwards seek, in number of sectors. */ +static const int bfq_back_penalty = 2; + +/* Idling period duration, in jiffies. */ +static int bfq_slice_idle = HZ / 125; + +/* Default maximum budget values, in sectors and number of requests. */ +static const int bfq_default_max_budget = 16 * 1024; +static const int bfq_max_budget_async_rq = 4; + +/* + * Async to sync throughput distribution is controlled as follows: + * when an async request is served, the entity is charged the number + * of sectors of the request, multiplied by the factor below + */ +static const int bfq_async_charge_factor = 10; + +/* Default timeout values, in jiffies, approximating CFQ defaults. */ +static const int bfq_timeout_sync = HZ / 8; +static int bfq_timeout_async = HZ / 25; + +struct kmem_cache *bfq_pool; + +/* Below this threshold (in ms), we consider thinktime immediate. */ +#define BFQ_MIN_TT 2 + +/* hw_tag detection: parallel requests threshold and min samples needed. */ +#define BFQ_HW_QUEUE_THRESHOLD 4 +#define BFQ_HW_QUEUE_SAMPLES 32 + +#define BFQQ_SEEK_THR (sector_t)(8 * 1024) +#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > BFQQ_SEEK_THR) + +/* Min samples used for peak rate estimation (for autotuning). */ +#define BFQ_PEAK_RATE_SAMPLES 32 + +/* Shift used for peak rate fixed precision calculations. */ +#define BFQ_RATE_SHIFT 16 + +/* + * By default, BFQ computes the duration of the weight raising for + * interactive applications automatically, using the following formula: + * duration = (R / r) * T, where r is the peak rate of the device, and + * R and T are two reference parameters. + * In particular, R is the peak rate of the reference device (see below), + * and T is a reference time: given the systems that are likely to be + * installed on the reference device according to its speed class, T is + * about the maximum time needed, under BFQ and while reading two files in + * parallel, to load typical large applications on these systems. + * In practice, the slower/faster the device at hand is, the more/less it + * takes to load applications with respect to the reference device. + * Accordingly, the longer/shorter BFQ grants weight raising to interactive + * applications. + * + * BFQ uses four different reference pairs (R, T), depending on: + * . whether the device is rotational or non-rotational; + * . whether the device is slow, such as old or portable HDDs, as well as + * SD cards, or fast, such as newer HDDs and SSDs. + * + * The device's speed class is dynamically (re)detected in + * bfq_update_peak_rate() every time the estimated peak rate is updated. + * + * In the following definitions, R_slow[0]/R_fast[0] and T_slow[0]/T_fast[0] + * are the reference values for a slow/fast rotational device, whereas + * R_slow[1]/R_fast[1] and T_slow[1]/T_fast[1] are the reference values for + * a slow/fast non-rotational device. Finally, device_speed_thresh are the + * thresholds used to switch between speed classes. + * Both the reference peak rates and the thresholds are measured in + * sectors/usec, left-shifted by BFQ_RATE_SHIFT. + */ +static int R_slow[2] = {1536, 10752}; +static int R_fast[2] = {17415, 34791}; +/* + * To improve readability, a conversion function is used to initialize the + * following arrays, which entails that they can be initialized only in a + * function. + */ +static int T_slow[2]; +static int T_fast[2]; +static int device_speed_thresh[2]; + +#define BFQ_SERVICE_TREE_INIT ((struct bfq_service_tree) \ + { RB_ROOT, RB_ROOT, NULL, NULL, 0, 0 }) + +#define RQ_BIC(rq) ((struct bfq_io_cq *) (rq)->elv.priv[0]) +#define RQ_BFQQ(rq) ((rq)->elv.priv[1]) + +static inline void bfq_schedule_dispatch(struct bfq_data *bfqd); + +#include "bfq-ioc.c" +#include "bfq-sched.c" +#include "bfq-cgroup.c" + +#define bfq_class_idle(bfqq) ((bfqq)->entity.ioprio_class ==\ + IOPRIO_CLASS_IDLE) +#define bfq_class_rt(bfqq) ((bfqq)->entity.ioprio_class ==\ + IOPRIO_CLASS_RT) + +#define bfq_sample_valid(samples) ((samples) > 80) + +/* + * The following macro groups conditions that need to be evaluated when + * checking if existing queues and groups form a symmetric scenario + * and therefore idling can be reduced or disabled for some of the + * queues. See the comment to the function bfq_bfqq_must_not_expire() + * for further details. + */ +#ifdef CONFIG_CGROUP_BFQIO +#define symmetric_scenario (!bfqd->active_numerous_groups && \ + !bfq_differentiated_weights(bfqd)) +#else +#define symmetric_scenario (!bfq_differentiated_weights(bfqd)) +#endif + +/* + * We regard a request as SYNC, if either it's a read or has the SYNC bit + * set (in which case it could also be a direct WRITE). + */ +static inline int bfq_bio_sync(struct bio *bio) +{ + if (bio_data_dir(bio) == READ || (bio->bi_rw & REQ_SYNC)) + return 1; + + return 0; +} + +/* + * Scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing. + */ +static inline void bfq_schedule_dispatch(struct bfq_data *bfqd) +{ + if (bfqd->queued != 0) { + bfq_log(bfqd, "schedule dispatch"); + kblockd_schedule_work(bfqd->queue, &bfqd->unplug_work); + } +} + +/* + * Lifted from AS - choose which of rq1 and rq2 that is best served now. + * We choose the request that is closesr to the head right now. Distance + * behind the head is penalized and only allowed to a certain extent. + */ +static struct request *bfq_choose_req(struct bfq_data *bfqd, + struct request *rq1, + struct request *rq2, + sector_t last) +{ + sector_t s1, s2, d1 = 0, d2 = 0; + unsigned long back_max; +#define BFQ_RQ1_WRAP 0x01 /* request 1 wraps */ +#define BFQ_RQ2_WRAP 0x02 /* request 2 wraps */ + unsigned wrap = 0; /* bit mask: requests behind the disk head? */ + + if (rq1 == NULL || rq1 == rq2) + return rq2; + if (rq2 == NULL) + return rq1; + + if (rq_is_sync(rq1) && !rq_is_sync(rq2)) + return rq1; + else if (rq_is_sync(rq2) && !rq_is_sync(rq1)) + return rq2; + if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META)) + return rq1; + else if ((rq2->cmd_flags & REQ_META) && !(rq1->cmd_flags & REQ_META)) + return rq2; + + s1 = blk_rq_pos(rq1); + s2 = blk_rq_pos(rq2); + + /* + * By definition, 1KiB is 2 sectors. + */ + back_max = bfqd->bfq_back_max * 2; + + /* + * Strict one way elevator _except_ in the case where we allow + * short backward seeks which are biased as twice the cost of a + * similar forward seek. + */ + if (s1 >= last) + d1 = s1 - last; + else if (s1 + back_max >= last) + d1 = (last - s1) * bfqd->bfq_back_penalty; + else + wrap |= BFQ_RQ1_WRAP; + + if (s2 >= last) + d2 = s2 - last; + else if (s2 + back_max >= last) + d2 = (last - s2) * bfqd->bfq_back_penalty; + else + wrap |= BFQ_RQ2_WRAP; + + /* Found required data */ + + /* + * By doing switch() on the bit mask "wrap" we avoid having to + * check two variables for all permutations: --> faster! + */ + switch (wrap) { + case 0: /* common case for CFQ: rq1 and rq2 not wrapped */ + if (d1 < d2) + return rq1; + else if (d2 < d1) + return rq2; + else { + if (s1 >= s2) + return rq1; + else + return rq2; + } + + case BFQ_RQ2_WRAP: + return rq1; + case BFQ_RQ1_WRAP: + return rq2; + case (BFQ_RQ1_WRAP|BFQ_RQ2_WRAP): /* both rqs wrapped */ + default: + /* + * Since both rqs are wrapped, + * start with the one that's further behind head + * (--> only *one* back seek required), + * since back seek takes more time than forward. + */ + if (s1 <= s2) + return rq1; + else + return rq2; + } +} + +static struct bfq_queue * +bfq_rq_pos_tree_lookup(struct bfq_data *bfqd, struct rb_root *root, + sector_t sector, struct rb_node **ret_parent, + struct rb_node ***rb_link) +{ + struct rb_node **p, *parent; + struct bfq_queue *bfqq = NULL; + + parent = NULL; + p = &root->rb_node; + while (*p) { + struct rb_node **n; + + parent = *p; + bfqq = rb_entry(parent, struct bfq_queue, pos_node); + + /* + * Sort strictly based on sector. Smallest to the left, + * largest to the right. + */ + if (sector > blk_rq_pos(bfqq->next_rq)) + n = &(*p)->rb_right; + else if (sector < blk_rq_pos(bfqq->next_rq)) + n = &(*p)->rb_left; + else + break; + p = n; + bfqq = NULL; + } + + *ret_parent = parent; + if (rb_link) + *rb_link = p; + + bfq_log(bfqd, "rq_pos_tree_lookup %llu: returning %d", + (long long unsigned)sector, + bfqq != NULL ? bfqq->pid : 0); + + return bfqq; +} + +static void bfq_rq_pos_tree_add(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct rb_node **p, *parent; + struct bfq_queue *__bfqq; + + if (bfqq->pos_root != NULL) { + rb_erase(&bfqq->pos_node, bfqq->pos_root); + bfqq->pos_root = NULL; + } + + if (bfq_class_idle(bfqq)) + return; + if (!bfqq->next_rq) + return; + + bfqq->pos_root = &bfqd->rq_pos_tree; + __bfqq = bfq_rq_pos_tree_lookup(bfqd, bfqq->pos_root, + blk_rq_pos(bfqq->next_rq), &parent, &p); + if (__bfqq == NULL) { + rb_link_node(&bfqq->pos_node, parent, p); + rb_insert_color(&bfqq->pos_node, bfqq->pos_root); + } else + bfqq->pos_root = NULL; +} + +/* + * Tell whether there are active queues or groups with differentiated weights. + */ +static inline bool bfq_differentiated_weights(struct bfq_data *bfqd) +{ + /* + * For weights to differ, at least one of the trees must contain + * at least two nodes. + */ + return (!RB_EMPTY_ROOT(&bfqd->queue_weights_tree) && + (bfqd->queue_weights_tree.rb_node->rb_left || + bfqd->queue_weights_tree.rb_node->rb_right) +#ifdef CONFIG_CGROUP_BFQIO + ) || + (!RB_EMPTY_ROOT(&bfqd->group_weights_tree) && + (bfqd->group_weights_tree.rb_node->rb_left || + bfqd->group_weights_tree.rb_node->rb_right) +#endif + ); +} + +/* + * If the weight-counter tree passed as input contains no counter for + * the weight of the input entity, then add that counter; otherwise just + * increment the existing counter. + * + * Note that weight-counter trees contain few nodes in mostly symmetric + * scenarios. For example, if all queues have the same weight, then the + * weight-counter tree for the queues may contain at most one node. + * This holds even if low_latency is on, because weight-raised queues + * are not inserted in the tree. + * In most scenarios, the rate at which nodes are created/destroyed + * should be low too. + */ +static void bfq_weights_tree_add(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* + * Do not insert if the entity is already associated with a + * counter, which happens if: + * 1) the entity is associated with a queue, + * 2) a request arrival has caused the queue to become both + * non-weight-raised, and hence change its weight, and + * backlogged; in this respect, each of the two events + * causes an invocation of this function, + * 3) this is the invocation of this function caused by the + * second event. This second invocation is actually useless, + * and we handle this fact by exiting immediately. More + * efficient or clearer solutions might possibly be adopted. + */ + if (entity->weight_counter) + return; + + while (*new) { + struct bfq_weight_counter *__counter = container_of(*new, + struct bfq_weight_counter, + weights_node); + parent = *new; + + if (entity->weight == __counter->weight) { + entity->weight_counter = __counter; + goto inc_counter; + } + if (entity->weight < __counter->weight) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + entity->weight_counter = kzalloc(sizeof(struct bfq_weight_counter), + GFP_ATOMIC); + entity->weight_counter->weight = entity->weight; + rb_link_node(&entity->weight_counter->weights_node, parent, new); + rb_insert_color(&entity->weight_counter->weights_node, root); + +inc_counter: + entity->weight_counter->num_active++; +} + +/* + * Decrement the weight counter associated with the entity, and, if the + * counter reaches 0, remove the counter from the tree. + * See the comments to the function bfq_weights_tree_add() for considerations + * about overhead. + */ +static void bfq_weights_tree_remove(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root) +{ + if (!entity->weight_counter) + return; + + BUG_ON(RB_EMPTY_ROOT(root)); + BUG_ON(entity->weight_counter->weight != entity->weight); + + BUG_ON(!entity->weight_counter->num_active); + entity->weight_counter->num_active--; + if (entity->weight_counter->num_active > 0) + goto reset_entity_pointer; + + rb_erase(&entity->weight_counter->weights_node, root); + kfree(entity->weight_counter); + +reset_entity_pointer: + entity->weight_counter = NULL; +} + +static struct request *bfq_find_next_rq(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct request *last) +{ + struct rb_node *rbnext = rb_next(&last->rb_node); + struct rb_node *rbprev = rb_prev(&last->rb_node); + struct request *next = NULL, *prev = NULL; + + BUG_ON(RB_EMPTY_NODE(&last->rb_node)); + + if (rbprev != NULL) + prev = rb_entry_rq(rbprev); + + if (rbnext != NULL) + next = rb_entry_rq(rbnext); + else { + rbnext = rb_first(&bfqq->sort_list); + if (rbnext && rbnext != &last->rb_node) + next = rb_entry_rq(rbnext); + } + + return bfq_choose_req(bfqd, next, prev, blk_rq_pos(last)); +} + +/* see the definition of bfq_async_charge_factor for details */ +static inline unsigned long bfq_serv_to_charge(struct request *rq, + struct bfq_queue *bfqq) +{ + return blk_rq_sectors(rq) * + (1 + ((!bfq_bfqq_sync(bfqq)) * (bfqq->wr_coeff == 1) * + bfq_async_charge_factor)); +} + +/** + * bfq_updated_next_req - update the queue after a new next_rq selection. + * @bfqd: the device data the queue belongs to. + * @bfqq: the queue to update. + * + * If the first request of a queue changes we make sure that the queue + * has enough budget to serve at least its first request (if the + * request has grown). We do this because if the queue has not enough + * budget for its first request, it has to go through two dispatch + * rounds to actually get it dispatched. + */ +static void bfq_updated_next_req(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + struct request *next_rq = bfqq->next_rq; + unsigned long new_budget; + + if (next_rq == NULL) + return; + + if (bfqq == bfqd->in_service_queue) + /* + * In order not to break guarantees, budgets cannot be + * changed after an entity has been selected. + */ + return; + + BUG_ON(entity->tree != &st->active); + BUG_ON(entity == entity->sched_data->in_service_entity); + + new_budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + if (entity->budget != new_budget) { + entity->budget = new_budget; + bfq_log_bfqq(bfqd, bfqq, "updated next rq: new budget %lu", + new_budget); + bfq_activate_bfqq(bfqd, bfqq); + } +} + +static inline unsigned int bfq_wr_duration(struct bfq_data *bfqd) +{ + u64 dur; + + if (bfqd->bfq_wr_max_time > 0) + return bfqd->bfq_wr_max_time; + + dur = bfqd->RT_prod; + do_div(dur, bfqd->peak_rate); + + return dur; +} + +/* Empty burst list and add just bfqq (see comments to bfq_handle_burst) */ +static inline void bfq_reset_burst_list(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_queue *item; + struct hlist_node *n; + + hlist_for_each_entry_safe(item, n, &bfqd->burst_list, burst_list_node) + hlist_del_init(&item->burst_list_node); + hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list); + bfqd->burst_size = 1; +} + +/* Add bfqq to the list of queues in current burst (see bfq_handle_burst) */ +static void bfq_add_to_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + /* Increment burst size to take into account also bfqq */ + bfqd->burst_size++; + + if (bfqd->burst_size == bfqd->bfq_large_burst_thresh) { + struct bfq_queue *pos, *bfqq_item; + struct hlist_node *n; + + /* + * Enough queues have been activated shortly after each + * other to consider this burst as large. + */ + bfqd->large_burst = true; + + /* + * We can now mark all queues in the burst list as + * belonging to a large burst. + */ + hlist_for_each_entry(bfqq_item, &bfqd->burst_list, + burst_list_node) + bfq_mark_bfqq_in_large_burst(bfqq_item); + bfq_mark_bfqq_in_large_burst(bfqq); + + /* + * From now on, and until the current burst finishes, any + * new queue being activated shortly after the last queue + * was inserted in the burst can be immediately marked as + * belonging to a large burst. So the burst list is not + * needed any more. Remove it. + */ + hlist_for_each_entry_safe(pos, n, &bfqd->burst_list, + burst_list_node) + hlist_del_init(&pos->burst_list_node); + } else /* burst not yet large: add bfqq to the burst list */ + hlist_add_head(&bfqq->burst_list_node, &bfqd->burst_list); +} + +/* + * If many queues happen to become active shortly after each other, then, + * to help the processes associated to these queues get their job done as + * soon as possible, it is usually better to not grant either weight-raising + * or device idling to these queues. In this comment we describe, firstly, + * the reasons why this fact holds, and, secondly, the next function, which + * implements the main steps needed to properly mark these queues so that + * they can then be treated in a different way. + * + * As for the terminology, we say that a queue becomes active, i.e., + * switches from idle to backlogged, either when it is created (as a + * consequence of the arrival of an I/O request), or, if already existing, + * when a new request for the queue arrives while the queue is idle. + * Bursts of activations, i.e., activations of different queues occurring + * shortly after each other, are typically caused by services or applications + * that spawn or reactivate many parallel threads/processes. Examples are + * systemd during boot or git grep. + * + * These services or applications benefit mostly from a high throughput: + * the quicker the requests of the activated queues are cumulatively served, + * the sooner the target job of these queues gets completed. As a consequence, + * weight-raising any of these queues, which also implies idling the device + * for it, is almost always counterproductive: in most cases it just lowers + * throughput. + * + * On the other hand, a burst of activations may be also caused by the start + * of an application that does not consist in a lot of parallel I/O-bound + * threads. In fact, with a complex application, the burst may be just a + * consequence of the fact that several processes need to be executed to + * start-up the application. To start an application as quickly as possible, + * the best thing to do is to privilege the I/O related to the application + * with respect to all other I/O. Therefore, the best strategy to start as + * quickly as possible an application that causes a burst of activations is + * to weight-raise all the queues activated during the burst. This is the + * exact opposite of the best strategy for the other type of bursts. + * + * In the end, to take the best action for each of the two cases, the two + * types of bursts need to be distinguished. Fortunately, this seems + * relatively easy to do, by looking at the sizes of the bursts. In + * particular, we found a threshold such that bursts with a larger size + * than that threshold are apparently caused only by services or commands + * such as systemd or git grep. For brevity, hereafter we call just 'large' + * these bursts. BFQ *does not* weight-raise queues whose activations occur + * in a large burst. In addition, for each of these queues BFQ performs or + * does not perform idling depending on which choice boosts the throughput + * most. The exact choice depends on the device and request pattern at + * hand. + * + * Turning back to the next function, it implements all the steps needed + * to detect the occurrence of a large burst and to properly mark all the + * queues belonging to it (so that they can then be treated in a different + * way). This goal is achieved by maintaining a special "burst list" that + * holds, temporarily, the queues that belong to the burst in progress. The + * list is then used to mark these queues as belonging to a large burst if + * the burst does become large. The main steps are the following. + * + * . when the very first queue is activated, the queue is inserted into the + * list (as it could be the first queue in a possible burst) + * + * . if the current burst has not yet become large, and a queue Q that does + * not yet belong to the burst is activated shortly after the last time + * at which a new queue entered the burst list, then the function appends + * Q to the burst list + * + * . if, as a consequence of the previous step, the burst size reaches + * the large-burst threshold, then + * + * . all the queues in the burst list are marked as belonging to a + * large burst + * + * . the burst list is deleted; in fact, the burst list already served + * its purpose (keeping temporarily track of the queues in a burst, + * so as to be able to mark them as belonging to a large burst in the + * previous sub-step), and now is not needed any more + * + * . the device enters a large-burst mode + * + * . if a queue Q that does not belong to the burst is activated while + * the device is in large-burst mode and shortly after the last time + * at which a queue either entered the burst list or was marked as + * belonging to the current large burst, then Q is immediately marked + * as belonging to a large burst. + * + * . if a queue Q that does not belong to the burst is activated a while + * later, i.e., not shortly after, than the last time at which a queue + * either entered the burst list or was marked as belonging to the + * current large burst, then the current burst is deemed as finished and: + * + * . the large-burst mode is reset if set + * + * . the burst list is emptied + * + * . Q is inserted in the burst list, as Q may be the first queue + * in a possible new burst (then the burst list contains just Q + * after this step). + */ +static void bfq_handle_burst(struct bfq_data *bfqd, struct bfq_queue *bfqq, + bool idle_for_long_time) +{ + /* + * If bfqq happened to be activated in a burst, but has been idle + * for at least as long as an interactive queue, then we assume + * that, in the overall I/O initiated in the burst, the I/O + * associated to bfqq is finished. So bfqq does not need to be + * treated as a queue belonging to a burst anymore. Accordingly, + * we reset bfqq's in_large_burst flag if set, and remove bfqq + * from the burst list if it's there. We do not decrement instead + * burst_size, because the fact that bfqq does not need to belong + * to the burst list any more does not invalidate the fact that + * bfqq may have been activated during the current burst. + */ + if (idle_for_long_time) { + hlist_del_init(&bfqq->burst_list_node); + bfq_clear_bfqq_in_large_burst(bfqq); + } + + /* + * If bfqq is already in the burst list or is part of a large + * burst, then there is nothing else to do. + */ + if (!hlist_unhashed(&bfqq->burst_list_node) || + bfq_bfqq_in_large_burst(bfqq)) + return; + + /* + * If bfqq's activation happens late enough, then the current + * burst is finished, and related data structures must be reset. + * + * In this respect, consider the special case where bfqq is the very + * first queue being activated. In this case, last_ins_in_burst is + * not yet significant when we get here. But it is easy to verify + * that, whether or not the following condition is true, bfqq will + * end up being inserted into the burst list. In particular the + * list will happen to contain only bfqq. And this is exactly what + * has to happen, as bfqq may be the first queue in a possible + * burst. + */ + if (time_is_before_jiffies(bfqd->last_ins_in_burst + + bfqd->bfq_burst_interval)) { + bfqd->large_burst = false; + bfq_reset_burst_list(bfqd, bfqq); + return; + } + + /* + * If we get here, then bfqq is being activated shortly after the + * last queue. So, if the current burst is also large, we can mark + * bfqq as belonging to this large burst immediately. + */ + if (bfqd->large_burst) { + bfq_mark_bfqq_in_large_burst(bfqq); + return; + } + + /* + * If we get here, then a large-burst state has not yet been + * reached, but bfqq is being activated shortly after the last + * queue. Then we add bfqq to the burst. + */ + bfq_add_to_burst(bfqd, bfqq); +} + +static void bfq_add_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_entity *entity = &bfqq->entity; + struct bfq_data *bfqd = bfqq->bfqd; + struct request *next_rq, *prev; + unsigned long old_wr_coeff = bfqq->wr_coeff; + bool interactive = false; + + bfq_log_bfqq(bfqd, bfqq, "add_request %d", rq_is_sync(rq)); + bfqq->queued[rq_is_sync(rq)]++; + bfqd->queued++; + + elv_rb_add(&bfqq->sort_list, rq); + + /* + * Check if this request is a better next-serve candidate. + */ + prev = bfqq->next_rq; + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq, bfqd->last_position); + BUG_ON(next_rq == NULL); + bfqq->next_rq = next_rq; + + /* + * Adjust priority tree position, if next_rq changes. + */ + if (prev != bfqq->next_rq) + bfq_rq_pos_tree_add(bfqd, bfqq); + + if (!bfq_bfqq_busy(bfqq)) { + bool soft_rt, + idle_for_long_time = time_is_before_jiffies( + bfqq->budget_timeout + + bfqd->bfq_wr_min_idle_time); + + if (bfq_bfqq_sync(bfqq)) { + bool already_in_burst = + !hlist_unhashed(&bfqq->burst_list_node) || + bfq_bfqq_in_large_burst(bfqq); + bfq_handle_burst(bfqd, bfqq, idle_for_long_time); + /* + * If bfqq was not already in the current burst, + * then, at this point, bfqq either has been + * added to the current burst or has caused the + * current burst to terminate. In particular, in + * the second case, bfqq has become the first + * queue in a possible new burst. + * In both cases last_ins_in_burst needs to be + * moved forward. + */ + if (!already_in_burst) + bfqd->last_ins_in_burst = jiffies; + } + + soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 && + !bfq_bfqq_in_large_burst(bfqq) && + time_is_before_jiffies(bfqq->soft_rt_next_start); + interactive = !bfq_bfqq_in_large_burst(bfqq) && + idle_for_long_time; + entity->budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + + if (!bfq_bfqq_IO_bound(bfqq)) { + if (time_before(jiffies, + RQ_BIC(rq)->ttime.last_end_request + + bfqd->bfq_slice_idle)) { + bfqq->requests_within_timer++; + if (bfqq->requests_within_timer >= + bfqd->bfq_requests_within_timer) + bfq_mark_bfqq_IO_bound(bfqq); + } else + bfqq->requests_within_timer = 0; + } + + if (!bfqd->low_latency) + goto add_bfqq_busy; + + /* + * If the queue is not being boosted and has been idle + * for enough time, start a weight-raising period + */ + if (old_wr_coeff == 1 && (interactive || soft_rt)) { + bfqq->wr_coeff = bfqd->bfq_wr_coeff; + if (interactive) + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + else + bfqq->wr_cur_max_time = + bfqd->bfq_wr_rt_max_time; + bfq_log_bfqq(bfqd, bfqq, + "wrais starting at %lu, rais_max_time %u", + jiffies, + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } else if (old_wr_coeff > 1) { + if (interactive) + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + else if (bfq_bfqq_in_large_burst(bfqq) || + (bfqq->wr_cur_max_time == + bfqd->bfq_wr_rt_max_time && + !soft_rt)) { + bfqq->wr_coeff = 1; + bfq_log_bfqq(bfqd, bfqq, + "wrais ending at %lu, rais_max_time %u", + jiffies, + jiffies_to_msecs(bfqq-> + wr_cur_max_time)); + } else if (time_before( + bfqq->last_wr_start_finish + + bfqq->wr_cur_max_time, + jiffies + + bfqd->bfq_wr_rt_max_time) && + soft_rt) { + /* + * + * The remaining weight-raising time is lower + * than bfqd->bfq_wr_rt_max_time, which + * means that the application is enjoying + * weight raising either because deemed soft- + * rt in the near past, or because deemed + * interactive a long ago. In both cases, + * resetting now the current remaining weight- + * raising time for the application to the + * weight-raising duration for soft rt + * applications would not cause any latency + * increase for the application (as the new + * duration would be higher than the remaining + * time). + * + * In addition, the application is now meeting + * the requirements for being deemed soft rt. + * In the end we can correctly and safely + * (re)charge the weight-raising duration for + * the application with the weight-raising + * duration for soft rt applications. + * + * In particular, doing this recharge now, i.e., + * before the weight-raising period for the + * application finishes, reduces the probability + * of the following negative scenario: + * 1) the weight of a soft rt application is + * raised at startup (as for any newly + * created application), + * 2) since the application is not interactive, + * at a certain time weight-raising is + * stopped for the application, + * 3) at that time the application happens to + * still have pending requests, and hence + * is destined to not have a chance to be + * deemed soft rt before these requests are + * completed (see the comments to the + * function bfq_bfqq_softrt_next_start() + * for details on soft rt detection), + * 4) these pending requests experience a high + * latency because the application is not + * weight-raised while they are pending. + */ + bfqq->last_wr_start_finish = jiffies; + bfqq->wr_cur_max_time = + bfqd->bfq_wr_rt_max_time; + } + } + if (old_wr_coeff != bfqq->wr_coeff) + entity->ioprio_changed = 1; +add_bfqq_busy: + bfqq->last_idle_bklogged = jiffies; + bfqq->service_from_backlogged = 0; + bfq_clear_bfqq_softrt_update(bfqq); + bfq_add_bfqq_busy(bfqd, bfqq); + } else { + if (bfqd->low_latency && old_wr_coeff == 1 && !rq_is_sync(rq) && + time_is_before_jiffies( + bfqq->last_wr_start_finish + + bfqd->bfq_wr_min_inter_arr_async)) { + bfqq->wr_coeff = bfqd->bfq_wr_coeff; + bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); + + bfqd->wr_busy_queues++; + entity->ioprio_changed = 1; + bfq_log_bfqq(bfqd, bfqq, + "non-idle wrais starting at %lu, rais_max_time %u", + jiffies, + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } + if (prev != bfqq->next_rq) + bfq_updated_next_req(bfqd, bfqq); + } + + if (bfqd->low_latency && + (old_wr_coeff == 1 || bfqq->wr_coeff == 1 || interactive)) + bfqq->last_wr_start_finish = jiffies; +} + +static struct request *bfq_find_rq_fmerge(struct bfq_data *bfqd, + struct bio *bio) +{ + struct task_struct *tsk = current; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq; + + bic = bfq_bic_lookup(bfqd, tsk->io_context); + if (bic == NULL) + return NULL; + + bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); + if (bfqq != NULL) + return elv_rb_find(&bfqq->sort_list, bio_end_sector(bio)); + + return NULL; +} + +static void bfq_activate_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + bfqd->rq_in_driver++; + bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq); + bfq_log(bfqd, "activate_request: new bfqd->last_position %llu", + (long long unsigned)bfqd->last_position); +} + +static inline void bfq_deactivate_request(struct request_queue *q, + struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + BUG_ON(bfqd->rq_in_driver == 0); + bfqd->rq_in_driver--; +} + +static void bfq_remove_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + const int sync = rq_is_sync(rq); + + if (bfqq->next_rq == rq) { + bfqq->next_rq = bfq_find_next_rq(bfqd, bfqq, rq); + bfq_updated_next_req(bfqd, bfqq); + } + + if (rq->queuelist.prev != &rq->queuelist) + list_del_init(&rq->queuelist); + BUG_ON(bfqq->queued[sync] == 0); + bfqq->queued[sync]--; + bfqd->queued--; + elv_rb_del(&bfqq->sort_list, rq); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { + if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->in_service_queue) + bfq_del_bfqq_busy(bfqd, bfqq, 1); + /* + * Remove queue from request-position tree as it is empty. + */ + if (bfqq->pos_root != NULL) { + rb_erase(&bfqq->pos_node, bfqq->pos_root); + bfqq->pos_root = NULL; + } + } + + if (rq->cmd_flags & REQ_META) { + BUG_ON(bfqq->meta_pending == 0); + bfqq->meta_pending--; + } +} + +static int bfq_merge(struct request_queue *q, struct request **req, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct request *__rq; + + __rq = bfq_find_rq_fmerge(bfqd, bio); + if (__rq != NULL && elv_rq_merge_ok(__rq, bio)) { + *req = __rq; + return ELEVATOR_FRONT_MERGE; + } + + return ELEVATOR_NO_MERGE; +} + +static void bfq_merged_request(struct request_queue *q, struct request *req, + int type) +{ + if (type == ELEVATOR_FRONT_MERGE && + rb_prev(&req->rb_node) && + blk_rq_pos(req) < + blk_rq_pos(container_of(rb_prev(&req->rb_node), + struct request, rb_node))) { + struct bfq_queue *bfqq = RQ_BFQQ(req); + struct bfq_data *bfqd = bfqq->bfqd; + struct request *prev, *next_rq; + + /* Reposition request in its sort_list */ + elv_rb_del(&bfqq->sort_list, req); + elv_rb_add(&bfqq->sort_list, req); + /* Choose next request to be served for bfqq */ + prev = bfqq->next_rq; + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, req, + bfqd->last_position); + BUG_ON(next_rq == NULL); + bfqq->next_rq = next_rq; + /* + * If next_rq changes, update both the queue's budget to + * fit the new request and the queue's position in its + * rq_pos_tree. + */ + if (prev != bfqq->next_rq) { + bfq_updated_next_req(bfqd, bfqq); + bfq_rq_pos_tree_add(bfqd, bfqq); + } + } +} + +static void bfq_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq), *next_bfqq = RQ_BFQQ(next); + + /* + * If next and rq belong to the same bfq_queue and next is older + * than rq, then reposition rq in the fifo (by substituting next + * with rq). Otherwise, if next and rq belong to different + * bfq_queues, never reposition rq: in fact, we would have to + * reposition it with respect to next's position in its own fifo, + * which would most certainly be too expensive with respect to + * the benefits. + */ + if (bfqq == next_bfqq && + !list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && + time_before(rq_fifo_time(next), rq_fifo_time(rq))) { + list_del_init(&rq->queuelist); + list_replace_init(&next->queuelist, &rq->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + + if (bfqq->next_rq == next) + bfqq->next_rq = rq; + + bfq_remove_request(next); +} + +/* Must be called with bfqq != NULL */ +static inline void bfq_bfqq_end_wr(struct bfq_queue *bfqq) +{ + BUG_ON(bfqq == NULL); + if (bfq_bfqq_busy(bfqq)) + bfqq->bfqd->wr_busy_queues--; + bfqq->wr_coeff = 1; + bfqq->wr_cur_max_time = 0; + /* Trigger a weight change on the next activation of the queue */ + bfqq->entity.ioprio_changed = 1; +} + +static void bfq_end_wr_async_queues(struct bfq_data *bfqd, + struct bfq_group *bfqg) +{ + int i, j; + + for (i = 0; i < 2; i++) + for (j = 0; j < IOPRIO_BE_NR; j++) + if (bfqg->async_bfqq[i][j] != NULL) + bfq_bfqq_end_wr(bfqg->async_bfqq[i][j]); + if (bfqg->async_idle_bfqq != NULL) + bfq_bfqq_end_wr(bfqg->async_idle_bfqq); +} + +static void bfq_end_wr(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq; + + spin_lock_irq(bfqd->queue->queue_lock); + + list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) + bfq_bfqq_end_wr(bfqq); + list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) + bfq_bfqq_end_wr(bfqq); + bfq_end_wr_async(bfqd); + + spin_unlock_irq(bfqd->queue->queue_lock); +} + +static int bfq_allow_merge(struct request_queue *q, struct request *rq, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq; + + /* + * Disallow merge of a sync bio into an async request. + */ + if (bfq_bio_sync(bio) && !rq_is_sync(rq)) + return 0; + + /* + * Lookup the bfqq that this bio will be queued with. Allow + * merge only if rq is queued there. + * Queue lock is held here. + */ + bic = bfq_bic_lookup(bfqd, current->io_context); + if (bic == NULL) + return 0; + + bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); + return bfqq == RQ_BFQQ(rq); +} + +static void __bfq_set_in_service_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + if (bfqq != NULL) { + bfq_mark_bfqq_must_alloc(bfqq); + bfq_mark_bfqq_budget_new(bfqq); + bfq_clear_bfqq_fifo_expire(bfqq); + + bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; + + bfq_log_bfqq(bfqd, bfqq, + "set_in_service_queue, cur-budget = %lu", + bfqq->entity.budget); + } + + bfqd->in_service_queue = bfqq; +} + +/* + * Get and set a new queue for service. + */ +static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + if (!bfqq) + bfqq = bfq_get_next_queue(bfqd); + else + bfq_get_next_queue_forced(bfqd, bfqq); + + __bfq_set_in_service_queue(bfqd, bfqq); + return bfqq; +} + +static inline sector_t bfq_dist_from_last(struct bfq_data *bfqd, + struct request *rq) +{ + if (blk_rq_pos(rq) >= bfqd->last_position) + return blk_rq_pos(rq) - bfqd->last_position; + else + return bfqd->last_position - blk_rq_pos(rq); +} + +/* + * Return true if bfqq has no request pending and rq is close enough to + * bfqd->last_position, or if rq is closer to bfqd->last_position than + * bfqq->next_rq + */ +static inline int bfq_rq_close(struct bfq_data *bfqd, struct request *rq) +{ + return bfq_dist_from_last(bfqd, rq) <= BFQQ_SEEK_THR; +} + +static struct bfq_queue *bfqq_close(struct bfq_data *bfqd) +{ + struct rb_root *root = &bfqd->rq_pos_tree; + struct rb_node *parent, *node; + struct bfq_queue *__bfqq; + sector_t sector = bfqd->last_position; + + if (RB_EMPTY_ROOT(root)) + return NULL; + + /* + * First, if we find a request starting at the end of the last + * request, choose it. + */ + __bfqq = bfq_rq_pos_tree_lookup(bfqd, root, sector, &parent, NULL); + if (__bfqq != NULL) + return __bfqq; + + /* + * If the exact sector wasn't found, the parent of the NULL leaf + * will contain the closest sector (rq_pos_tree sorted by + * next_request position). + */ + __bfqq = rb_entry(parent, struct bfq_queue, pos_node); + if (bfq_rq_close(bfqd, __bfqq->next_rq)) + return __bfqq; + + if (blk_rq_pos(__bfqq->next_rq) < sector) + node = rb_next(&__bfqq->pos_node); + else + node = rb_prev(&__bfqq->pos_node); + if (node == NULL) + return NULL; + + __bfqq = rb_entry(node, struct bfq_queue, pos_node); + if (bfq_rq_close(bfqd, __bfqq->next_rq)) + return __bfqq; + + return NULL; +} + +/* + * bfqd - obvious + * cur_bfqq - passed in so that we don't decide that the current queue + * is closely cooperating with itself. + * + * We are assuming that cur_bfqq has dispatched at least one request, + * and that bfqd->last_position reflects a position on the disk associated + * with the I/O issued by cur_bfqq. + */ +static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, + struct bfq_queue *cur_bfqq) +{ + struct bfq_queue *bfqq; + + if (bfq_class_idle(cur_bfqq)) + return NULL; + if (!bfq_bfqq_sync(cur_bfqq)) + return NULL; + if (BFQQ_SEEKY(cur_bfqq)) + return NULL; + + /* If device has only one backlogged bfq_queue, don't search. */ + if (bfqd->busy_queues == 1) + return NULL; + + /* + * We should notice if some of the queues are cooperating, e.g. + * working closely on the same area of the disk. In that case, + * we can group them together and don't waste time idling. + */ + bfqq = bfqq_close(bfqd); + if (bfqq == NULL || bfqq == cur_bfqq) + return NULL; + + /* + * Do not merge queues from different bfq_groups. + */ + if (bfqq->entity.parent != cur_bfqq->entity.parent) + return NULL; + + /* + * It only makes sense to merge sync queues. + */ + if (!bfq_bfqq_sync(bfqq)) + return NULL; + if (BFQQ_SEEKY(bfqq)) + return NULL; + + /* + * Do not merge queues of different priority classes. + */ + if (bfq_class_rt(bfqq) != bfq_class_rt(cur_bfqq)) + return NULL; + + return bfqq; +} + +/* + * If enough samples have been computed, return the current max budget + * stored in bfqd, which is dynamically updated according to the + * estimated disk peak rate; otherwise return the default max budget + */ +static inline unsigned long bfq_max_budget(struct bfq_data *bfqd) +{ + if (bfqd->budgets_assigned < 194) + return bfq_default_max_budget; + else + return bfqd->bfq_max_budget; +} + +/* + * Return min budget, which is a fraction of the current or default + * max budget (trying with 1/32) + */ +static inline unsigned long bfq_min_budget(struct bfq_data *bfqd) +{ + if (bfqd->budgets_assigned < 194) + return bfq_default_max_budget / 32; + else + return bfqd->bfq_max_budget / 32; +} + +static void bfq_arm_slice_timer(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfqd->in_service_queue; + struct bfq_io_cq *bic; + unsigned long sl; + + BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); + + /* Processes have exited, don't wait. */ + bic = bfqd->in_service_bic; + if (bic == NULL || atomic_read(&bic->icq.ioc->active_ref) == 0) + return; + + bfq_mark_bfqq_wait_request(bfqq); + + /* + * We don't want to idle for seeks, but we do want to allow + * fair distribution of slice time for a process doing back-to-back + * seeks. So allow a little bit of time for him to submit a new rq. + * + * To prevent processes with (partly) seeky workloads from + * being too ill-treated, grant them a small fraction of the + * assigned budget before reducing the waiting time to + * BFQ_MIN_TT. This happened to help reduce latency. + */ + sl = bfqd->bfq_slice_idle; + /* + * Unless the queue is being weight-raised or the scenario is + * asymmetric, grant only minimum idle time if the queue either + * has been seeky for long enough or has already proved to be + * constantly seeky. + */ + if (bfq_sample_valid(bfqq->seek_samples) && + ((BFQQ_SEEKY(bfqq) && bfqq->entity.service > + bfq_max_budget(bfqq->bfqd) / 8) || + bfq_bfqq_constantly_seeky(bfqq)) && bfqq->wr_coeff == 1 && + symmetric_scenario) + sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); + else if (bfqq->wr_coeff > 1) + sl = sl * 3; + bfqd->last_idling_start = ktime_get(); + mod_timer(&bfqd->idle_slice_timer, jiffies + sl); + bfq_log(bfqd, "arm idle: %u/%u ms", + jiffies_to_msecs(sl), jiffies_to_msecs(bfqd->bfq_slice_idle)); +} + +/* + * Set the maximum time for the in-service queue to consume its + * budget. This prevents seeky processes from lowering the disk + * throughput (always guaranteed with a time slice scheme as in CFQ). + */ +static void bfq_set_budget_timeout(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfqd->in_service_queue; + unsigned int timeout_coeff; + if (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time) + timeout_coeff = 1; + else + timeout_coeff = bfqq->entity.weight / bfqq->entity.orig_weight; + + bfqd->last_budget_start = ktime_get(); + + bfq_clear_bfqq_budget_new(bfqq); + bfqq->budget_timeout = jiffies + + bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * timeout_coeff; + + bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u", + jiffies_to_msecs(bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * + timeout_coeff)); +} + +/* + * Move request from internal lists to the request queue dispatch list. + */ +static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + /* + * For consistency, the next instruction should have been executed + * after removing the request from the queue and dispatching it. + * We execute instead this instruction before bfq_remove_request() + * (and hence introduce a temporary inconsistency), for efficiency. + * In fact, in a forced_dispatch, this prevents two counters related + * to bfqq->dispatched to risk to be uselessly decremented if bfqq + * is not in service, and then to be incremented again after + * incrementing bfqq->dispatched. + */ + bfqq->dispatched++; + bfq_remove_request(rq); + elv_dispatch_sort(q, rq); + + if (bfq_bfqq_sync(bfqq)) + bfqd->sync_flight++; +} + +/* + * Return expired entry, or NULL to just start from scratch in rbtree. + */ +static struct request *bfq_check_fifo(struct bfq_queue *bfqq) +{ + struct request *rq = NULL; + + if (bfq_bfqq_fifo_expire(bfqq)) + return NULL; + + bfq_mark_bfqq_fifo_expire(bfqq); + + if (list_empty(&bfqq->fifo)) + return NULL; + + rq = rq_entry_fifo(bfqq->fifo.next); + + if (time_before(jiffies, rq_fifo_time(rq))) + return NULL; + + return rq; +} + +/* Must be called with the queue_lock held. */ +static int bfqq_process_refs(struct bfq_queue *bfqq) +{ + int process_refs, io_refs; + + io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; + process_refs = atomic_read(&bfqq->ref) - io_refs - bfqq->entity.on_st; + BUG_ON(process_refs < 0); + return process_refs; +} + +static void bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) +{ + int process_refs, new_process_refs; + struct bfq_queue *__bfqq; + + /* + * If there are no process references on the new_bfqq, then it is + * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain + * may have dropped their last reference (not just their last process + * reference). + */ + if (!bfqq_process_refs(new_bfqq)) + return; + + /* Avoid a circular list and skip interim queue merges. */ + while ((__bfqq = new_bfqq->new_bfqq)) { + if (__bfqq == bfqq) + return; + new_bfqq = __bfqq; + } + + process_refs = bfqq_process_refs(bfqq); + new_process_refs = bfqq_process_refs(new_bfqq); + /* + * If the process for the bfqq has gone away, there is no + * sense in merging the queues. + */ + if (process_refs == 0 || new_process_refs == 0) + return; + + /* + * Merge in the direction of the lesser amount of work. + */ + if (new_process_refs >= process_refs) { + bfqq->new_bfqq = new_bfqq; + atomic_add(process_refs, &new_bfqq->ref); + } else { + new_bfqq->new_bfqq = bfqq; + atomic_add(new_process_refs, &bfqq->ref); + } + bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d", + new_bfqq->pid); +} + +static inline unsigned long bfq_bfqq_budget_left(struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + return entity->budget - entity->service; +} + +static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + BUG_ON(bfqq != bfqd->in_service_queue); + + __bfq_bfqd_reset_in_service(bfqd); + + /* + * If this bfqq is shared between multiple processes, check + * to make sure that those processes are still issuing I/Os + * within the mean seek distance. If not, it may be time to + * break the queues apart again. + */ + if (bfq_bfqq_coop(bfqq) && BFQQ_SEEKY(bfqq)) + bfq_mark_bfqq_split_coop(bfqq); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { + /* + * Overloading budget_timeout field to store the time + * at which the queue remains with no backlog; used by + * the weight-raising mechanism. + */ + bfqq->budget_timeout = jiffies; + bfq_del_bfqq_busy(bfqd, bfqq, 1); + } else { + bfq_activate_bfqq(bfqd, bfqq); + /* + * Resort priority tree of potential close cooperators. + */ + bfq_rq_pos_tree_add(bfqd, bfqq); + } +} + +/** + * __bfq_bfqq_recalc_budget - try to adapt the budget to the @bfqq behavior. + * @bfqd: device data. + * @bfqq: queue to update. + * @reason: reason for expiration. + * + * Handle the feedback on @bfqq budget. See the body for detailed + * comments. + */ +static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + enum bfqq_expiration reason) +{ + struct request *next_rq; + unsigned long budget, min_budget; + + budget = bfqq->max_budget; + min_budget = bfq_min_budget(bfqd); + + BUG_ON(bfqq != bfqd->in_service_queue); + + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last budg %lu, budg left %lu", + bfqq->entity.budget, bfq_bfqq_budget_left(bfqq)); + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last max_budg %lu, min budg %lu", + budget, bfq_min_budget(bfqd)); + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: sync %d, seeky %d", + bfq_bfqq_sync(bfqq), BFQQ_SEEKY(bfqd->in_service_queue)); + + if (bfq_bfqq_sync(bfqq)) { + switch (reason) { + /* + * Caveat: in all the following cases we trade latency + * for throughput. + */ + case BFQ_BFQQ_TOO_IDLE: + /* + * This is the only case where we may reduce + * the budget: if there is no request of the + * process still waiting for completion, then + * we assume (tentatively) that the timer has + * expired because the batch of requests of + * the process could have been served with a + * smaller budget. Hence, betting that + * process will behave in the same way when it + * becomes backlogged again, we reduce its + * next budget. As long as we guess right, + * this budget cut reduces the latency + * experienced by the process. + * + * However, if there are still outstanding + * requests, then the process may have not yet + * issued its next request just because it is + * still waiting for the completion of some of + * the still outstanding ones. So in this + * subcase we do not reduce its budget, on the + * contrary we increase it to possibly boost + * the throughput, as discussed in the + * comments to the BUDGET_TIMEOUT case. + */ + if (bfqq->dispatched > 0) /* still outstanding reqs */ + budget = min(budget * 2, bfqd->bfq_max_budget); + else { + if (budget > 5 * min_budget) + budget -= 4 * min_budget; + else + budget = min_budget; + } + break; + case BFQ_BFQQ_BUDGET_TIMEOUT: + /* + * We double the budget here because: 1) it + * gives the chance to boost the throughput if + * this is not a seeky process (which may have + * bumped into this timeout because of, e.g., + * ZBR), 2) together with charge_full_budget + * it helps give seeky processes higher + * timestamps, and hence be served less + * frequently. + */ + budget = min(budget * 2, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_BUDGET_EXHAUSTED: + /* + * The process still has backlog, and did not + * let either the budget timeout or the disk + * idling timeout expire. Hence it is not + * seeky, has a short thinktime and may be + * happy with a higher budget too. So + * definitely increase the budget of this good + * candidate to boost the disk throughput. + */ + budget = min(budget * 4, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_NO_MORE_REQUESTS: + /* + * Leave the budget unchanged. + */ + default: + return; + } + } else /* async queue */ + /* async queues get always the maximum possible budget + * (their ability to dispatch is limited by + * @bfqd->bfq_max_budget_async_rq). + */ + budget = bfqd->bfq_max_budget; + + bfqq->max_budget = budget; + + if (bfqd->budgets_assigned >= 194 && bfqd->bfq_user_max_budget == 0 && + bfqq->max_budget > bfqd->bfq_max_budget) + bfqq->max_budget = bfqd->bfq_max_budget; + + /* + * Make sure that we have enough budget for the next request. + * Since the finish time of the bfqq must be kept in sync with + * the budget, be sure to call __bfq_bfqq_expire() after the + * update. + */ + next_rq = bfqq->next_rq; + if (next_rq != NULL) + bfqq->entity.budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + else + bfqq->entity.budget = bfqq->max_budget; + + bfq_log_bfqq(bfqd, bfqq, "head sect: %u, new budget %lu", + next_rq != NULL ? blk_rq_sectors(next_rq) : 0, + bfqq->entity.budget); +} + +static unsigned long bfq_calc_max_budget(u64 peak_rate, u64 timeout) +{ + unsigned long max_budget; + + /* + * The max_budget calculated when autotuning is equal to the + * amount of sectors transfered in timeout_sync at the + * estimated peak rate. + */ + max_budget = (unsigned long)(peak_rate * 1000 * + timeout >> BFQ_RATE_SHIFT); + + return max_budget; +} + +/* + * In addition to updating the peak rate, checks whether the process + * is "slow", and returns 1 if so. This slow flag is used, in addition + * to the budget timeout, to reduce the amount of service provided to + * seeky processes, and hence reduce their chances to lower the + * throughput. See the code for more details. + */ +static int bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int compensate, enum bfqq_expiration reason) +{ + u64 bw, usecs, expected, timeout; + ktime_t delta; + int update = 0; + + if (!bfq_bfqq_sync(bfqq) || bfq_bfqq_budget_new(bfqq)) + return 0; + + if (compensate) + delta = bfqd->last_idling_start; + else + delta = ktime_get(); + delta = ktime_sub(delta, bfqd->last_budget_start); + usecs = ktime_to_us(delta); + + /* Don't trust short/unrealistic values. */ + if (usecs < 100 || usecs >= LONG_MAX) + return 0; + + /* + * Calculate the bandwidth for the last slice. We use a 64 bit + * value to store the peak rate, in sectors per usec in fixed + * point math. We do so to have enough precision in the estimate + * and to avoid overflows. + */ + bw = (u64)bfqq->entity.service << BFQ_RATE_SHIFT; + do_div(bw, (unsigned long)usecs); + + timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); + + /* + * Use only long (> 20ms) intervals to filter out spikes for + * the peak rate estimation. + */ + if (usecs > 20000) { + if (bw > bfqd->peak_rate || + (!BFQQ_SEEKY(bfqq) && + reason == BFQ_BFQQ_BUDGET_TIMEOUT)) { + bfq_log(bfqd, "measured bw =%llu", bw); + /* + * To smooth oscillations use a low-pass filter with + * alpha=7/8, i.e., + * new_rate = (7/8) * old_rate + (1/8) * bw + */ + do_div(bw, 8); + if (bw == 0) + return 0; + bfqd->peak_rate *= 7; + do_div(bfqd->peak_rate, 8); + bfqd->peak_rate += bw; + update = 1; + bfq_log(bfqd, "new peak_rate=%llu", bfqd->peak_rate); + } + + update |= bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES - 1; + + if (bfqd->peak_rate_samples < BFQ_PEAK_RATE_SAMPLES) + bfqd->peak_rate_samples++; + + if (bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES && + update) { + int dev_type = blk_queue_nonrot(bfqd->queue); + if (bfqd->bfq_user_max_budget == 0) { + bfqd->bfq_max_budget = + bfq_calc_max_budget(bfqd->peak_rate, + timeout); + bfq_log(bfqd, "new max_budget=%lu", + bfqd->bfq_max_budget); + } + if (bfqd->device_speed == BFQ_BFQD_FAST && + bfqd->peak_rate < device_speed_thresh[dev_type]) { + bfqd->device_speed = BFQ_BFQD_SLOW; + bfqd->RT_prod = R_slow[dev_type] * + T_slow[dev_type]; + } else if (bfqd->device_speed == BFQ_BFQD_SLOW && + bfqd->peak_rate > device_speed_thresh[dev_type]) { + bfqd->device_speed = BFQ_BFQD_FAST; + bfqd->RT_prod = R_fast[dev_type] * + T_fast[dev_type]; + } + } + } + + /* + * If the process has been served for a too short time + * interval to let its possible sequential accesses prevail on + * the initial seek time needed to move the disk head on the + * first sector it requested, then give the process a chance + * and for the moment return false. + */ + if (bfqq->entity.budget <= bfq_max_budget(bfqd) / 8) + return 0; + + /* + * A process is considered ``slow'' (i.e., seeky, so that we + * cannot treat it fairly in the service domain, as it would + * slow down too much the other processes) if, when a slice + * ends for whatever reason, it has received service at a + * rate that would not be high enough to complete the budget + * before the budget timeout expiration. + */ + expected = bw * 1000 * timeout >> BFQ_RATE_SHIFT; + + /* + * Caveat: processes doing IO in the slower disk zones will + * tend to be slow(er) even if not seeky. And the estimated + * peak rate will actually be an average over the disk + * surface. Hence, to not be too harsh with unlucky processes, + * we keep a budget/3 margin of safety before declaring a + * process slow. + */ + return expected > (4 * bfqq->entity.budget) / 3; +} + +/* + * To be deemed as soft real-time, an application must meet two + * requirements. First, the application must not require an average + * bandwidth higher than the approximate bandwidth required to playback or + * record a compressed high-definition video. + * The next function is invoked on the completion of the last request of a + * batch, to compute the next-start time instant, soft_rt_next_start, such + * that, if the next request of the application does not arrive before + * soft_rt_next_start, then the above requirement on the bandwidth is met. + * + * The second requirement is that the request pattern of the application is + * isochronous, i.e., that, after issuing a request or a batch of requests, + * the application stops issuing new requests until all its pending requests + * have been completed. After that, the application may issue a new batch, + * and so on. + * For this reason the next function is invoked to compute + * soft_rt_next_start only for applications that meet this requirement, + * whereas soft_rt_next_start is set to infinity for applications that do + * not. + * + * Unfortunately, even a greedy application may happen to behave in an + * isochronous way if the CPU load is high. In fact, the application may + * stop issuing requests while the CPUs are busy serving other processes, + * then restart, then stop again for a while, and so on. In addition, if + * the disk achieves a low enough throughput with the request pattern + * issued by the application (e.g., because the request pattern is random + * and/or the device is slow), then the application may meet the above + * bandwidth requirement too. To prevent such a greedy application to be + * deemed as soft real-time, a further rule is used in the computation of + * soft_rt_next_start: soft_rt_next_start must be higher than the current + * time plus the maximum time for which the arrival of a request is waited + * for when a sync queue becomes idle, namely bfqd->bfq_slice_idle. + * This filters out greedy applications, as the latter issue instead their + * next request as soon as possible after the last one has been completed + * (in contrast, when a batch of requests is completed, a soft real-time + * application spends some time processing data). + * + * Unfortunately, the last filter may easily generate false positives if + * only bfqd->bfq_slice_idle is used as a reference time interval and one + * or both the following cases occur: + * 1) HZ is so low that the duration of a jiffy is comparable to or higher + * than bfqd->bfq_slice_idle. This happens, e.g., on slow devices with + * HZ=100. + * 2) jiffies, instead of increasing at a constant rate, may stop increasing + * for a while, then suddenly 'jump' by several units to recover the lost + * increments. This seems to happen, e.g., inside virtual machines. + * To address this issue, we do not use as a reference time interval just + * bfqd->bfq_slice_idle, but bfqd->bfq_slice_idle plus a few jiffies. In + * particular we add the minimum number of jiffies for which the filter + * seems to be quite precise also in embedded systems and KVM/QEMU virtual + * machines. + */ +static inline unsigned long bfq_bfqq_softrt_next_start(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + return max(bfqq->last_idle_bklogged + + HZ * bfqq->service_from_backlogged / + bfqd->bfq_wr_max_softrt_rate, + jiffies + bfqq->bfqd->bfq_slice_idle + 4); +} + +/* + * Return the largest-possible time instant such that, for as long as possible, + * the current time will be lower than this time instant according to the macro + * time_is_before_jiffies(). + */ +static inline unsigned long bfq_infinity_from_now(unsigned long now) +{ + return now + ULONG_MAX / 2; +} + +/** + * bfq_bfqq_expire - expire a queue. + * @bfqd: device owning the queue. + * @bfqq: the queue to expire. + * @compensate: if true, compensate for the time spent idling. + * @reason: the reason causing the expiration. + * + * + * If the process associated to the queue is slow (i.e., seeky), or in + * case of budget timeout, or, finally, if it is async, we + * artificially charge it an entire budget (independently of the + * actual service it received). As a consequence, the queue will get + * higher timestamps than the correct ones upon reactivation, and + * hence it will be rescheduled as if it had received more service + * than what it actually received. In the end, this class of processes + * will receive less service in proportion to how slowly they consume + * their budgets (and hence how seriously they tend to lower the + * throughput). + * + * In contrast, when a queue expires because it has been idling for + * too much or because it exhausted its budget, we do not touch the + * amount of service it has received. Hence when the queue will be + * reactivated and its timestamps updated, the latter will be in sync + * with the actual service received by the queue until expiration. + * + * Charging a full budget to the first type of queues and the exact + * service to the others has the effect of using the WF2Q+ policy to + * schedule the former on a timeslice basis, without violating the + * service domain guarantees of the latter. + */ +static void bfq_bfqq_expire(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + int compensate, + enum bfqq_expiration reason) +{ + int slow; + BUG_ON(bfqq != bfqd->in_service_queue); + + /* Update disk peak rate for autotuning and check whether the + * process is slow (see bfq_update_peak_rate). + */ + slow = bfq_update_peak_rate(bfqd, bfqq, compensate, reason); + + /* + * As above explained, 'punish' slow (i.e., seeky), timed-out + * and async queues, to favor sequential sync workloads. + * + * Processes doing I/O in the slower disk zones will tend to be + * slow(er) even if not seeky. Hence, since the estimated peak + * rate is actually an average over the disk surface, these + * processes may timeout just for bad luck. To avoid punishing + * them we do not charge a full budget to a process that + * succeeded in consuming at least 2/3 of its budget. + */ + if (slow || (reason == BFQ_BFQQ_BUDGET_TIMEOUT && + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3)) + bfq_bfqq_charge_full_budget(bfqq); + + bfqq->service_from_backlogged += bfqq->entity.service; + + if (BFQQ_SEEKY(bfqq) && reason == BFQ_BFQQ_BUDGET_TIMEOUT && + !bfq_bfqq_constantly_seeky(bfqq)) { + bfq_mark_bfqq_constantly_seeky(bfqq); + if (!blk_queue_nonrot(bfqd->queue)) + bfqd->const_seeky_busy_in_flight_queues++; + } + + if (reason == BFQ_BFQQ_TOO_IDLE && + bfqq->entity.service <= 2 * bfqq->entity.budget / 10 ) + bfq_clear_bfqq_IO_bound(bfqq); + + if (bfqd->low_latency && bfqq->wr_coeff == 1) + bfqq->last_wr_start_finish = jiffies; + + if (bfqd->low_latency && bfqd->bfq_wr_max_softrt_rate > 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) { + /* + * If we get here, and there are no outstanding requests, + * then the request pattern is isochronous (see the comments + * to the function bfq_bfqq_softrt_next_start()). Hence we + * can compute soft_rt_next_start. If, instead, the queue + * still has outstanding requests, then we have to wait + * for the completion of all the outstanding requests to + * discover whether the request pattern is actually + * isochronous. + */ + if (bfqq->dispatched == 0) + bfqq->soft_rt_next_start = + bfq_bfqq_softrt_next_start(bfqd, bfqq); + else { + /* + * The application is still waiting for the + * completion of one or more requests: + * prevent it from possibly being incorrectly + * deemed as soft real-time by setting its + * soft_rt_next_start to infinity. In fact, + * without this assignment, the application + * would be incorrectly deemed as soft + * real-time if: + * 1) it issued a new request before the + * completion of all its in-flight + * requests, and + * 2) at that time, its soft_rt_next_start + * happened to be in the past. + */ + bfqq->soft_rt_next_start = + bfq_infinity_from_now(jiffies); + /* + * Schedule an update of soft_rt_next_start to when + * the task may be discovered to be isochronous. + */ + bfq_mark_bfqq_softrt_update(bfqq); + } + } + + bfq_log_bfqq(bfqd, bfqq, + "expire (%d, slow %d, num_disp %d, idle_win %d)", reason, + slow, bfqq->dispatched, bfq_bfqq_idle_window(bfqq)); + + /* + * Increase, decrease or leave budget unchanged according to + * reason. + */ + __bfq_bfqq_recalc_budget(bfqd, bfqq, reason); + __bfq_bfqq_expire(bfqd, bfqq); +} + +/* + * Budget timeout is not implemented through a dedicated timer, but + * just checked on request arrivals and completions, as well as on + * idle timer expirations. + */ +static int bfq_bfqq_budget_timeout(struct bfq_queue *bfqq) +{ + if (bfq_bfqq_budget_new(bfqq) || + time_before(jiffies, bfqq->budget_timeout)) + return 0; + return 1; +} + +/* + * If we expire a queue that is waiting for the arrival of a new + * request, we may prevent the fictitious timestamp back-shifting that + * allows the guarantees of the queue to be preserved (see [1] for + * this tricky aspect). Hence we return true only if this condition + * does not hold, or if the queue is slow enough to deserve only to be + * kicked off for preserving a high throughput. +*/ +static inline int bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqq->bfqd, bfqq, + "may_budget_timeout: wait_request %d left %d timeout %d", + bfq_bfqq_wait_request(bfqq), + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3, + bfq_bfqq_budget_timeout(bfqq)); + + return (!bfq_bfqq_wait_request(bfqq) || + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3) + && + bfq_bfqq_budget_timeout(bfqq); +} + +/* + * Device idling is allowed only for the queues for which this function + * returns true. For this reason, the return value of this function plays a + * critical role for both throughput boosting and service guarantees. The + * return value is computed through a logical expression. In this rather + * long comment, we try to briefly describe all the details and motivations + * behind the components of this logical expression. + * + * First, the expression is false if bfqq is not sync, or if: bfqq happened + * to become active during a large burst of queue activations, and the + * pattern of requests bfqq contains boosts the throughput if bfqq is + * expired. In fact, queues that became active during a large burst benefit + * only from throughput, as discussed in the comments to bfq_handle_burst. + * In this respect, expiring bfqq certainly boosts the throughput on NCQ- + * capable flash-based devices, whereas, on rotational devices, it boosts + * the throughput only if bfqq contains random requests. + * + * On the opposite end, if (a) bfqq is sync, (b) the above burst-related + * condition does not hold, and (c) bfqq is being weight-raised, then the + * expression always evaluates to true, as device idling is instrumental + * for preserving low-latency guarantees (see [1]). If, instead, conditions + * (a) and (b) do hold, but (c) does not, then the expression evaluates to + * true only if: (1) bfqq is I/O-bound and has a non-null idle window, and + * (2) at least one of the following two conditions holds. + * The first condition is that the device is not performing NCQ, because + * idling the device most certainly boosts the throughput if this condition + * holds and bfqq is I/O-bound and has been granted a non-null idle window. + * The second compound condition is made of the logical AND of two components. + * + * The first component is true only if there is no weight-raised busy + * queue. This guarantees that the device is not idled for a sync non- + * weight-raised queue when there are busy weight-raised queues. The former + * is then expired immediately if empty. Combined with the timestamping + * rules of BFQ (see [1] for details), this causes sync non-weight-raised + * queues to get a lower number of requests served, and hence to ask for a + * lower number of requests from the request pool, before the busy weight- + * raised queues get served again. + * + * This is beneficial for the processes associated with weight-raised + * queues, when the request pool is saturated (e.g., in the presence of + * write hogs). In fact, if the processes associated with the other queues + * ask for requests at a lower rate, then weight-raised processes have a + * higher probability to get a request from the pool immediately (or at + * least soon) when they need one. Hence they have a higher probability to + * actually get a fraction of the disk throughput proportional to their + * high weight. This is especially true with NCQ-capable drives, which + * enqueue several requests in advance and further reorder internally- + * queued requests. + * + * In the end, mistreating non-weight-raised queues when there are busy + * weight-raised queues seems to mitigate starvation problems in the + * presence of heavy write workloads and NCQ, and hence to guarantee a + * higher application and system responsiveness in these hostile scenarios. + * + * If the first component of the compound condition is instead true, i.e., + * there is no weight-raised busy queue, then the second component of the + * compound condition takes into account service-guarantee and throughput + * issues related to NCQ (recall that the compound condition is evaluated + * only if the device is detected as supporting NCQ). + * + * As for service guarantees, allowing the drive to enqueue more than one + * request at a time, and hence delegating de facto final scheduling + * decisions to the drive's internal scheduler, causes loss of control on + * the actual request service order. In this respect, when the drive is + * allowed to enqueue more than one request at a time, the service + * distribution enforced by the drive's internal scheduler is likely to + * coincide with the desired device-throughput distribution only in the + * following, perfectly symmetric, scenario: + * 1) all active queues have the same weight, + * 2) all active groups at the same level in the groups tree have the same + * weight, + * 3) all active groups at the same level in the groups tree have the same + * number of children. + * + * Even in such a scenario, sequential I/O may still receive a preferential + * treatment, but this is not likely to be a big issue with flash-based + * devices, because of their non-dramatic loss of throughput with random + * I/O. Things do differ with HDDs, for which additional care is taken, as + * explained after completing the discussion for flash-based devices. + * + * Unfortunately, keeping the necessary state for evaluating exactly the + * above symmetry conditions would be quite complex and time-consuming. + * Therefore BFQ evaluates instead the following stronger sub-conditions, + * for which it is much easier to maintain the needed state: + * 1) all active queues have the same weight, + * 2) all active groups have the same weight, + * 3) all active groups have at most one active child each. + * In particular, the last two conditions are always true if hierarchical + * support and the cgroups interface are not enabled, hence no state needs + * to be maintained in this case. + * + * According to the above considerations, the second component of the + * compound condition evaluates to true if any of the above symmetry + * sub-condition does not hold, or the device is not flash-based. Therefore, + * if also the first component is true, then idling is allowed for a sync + * queue. These are the only sub-conditions considered if the device is + * flash-based, as, for such a device, it is sensible to force idling only + * for service-guarantee issues. In fact, as for throughput, idling + * NCQ-capable flash-based devices would not boost the throughput even + * with sequential I/O; rather it would lower the throughput in proportion + * to how fast the device is. In the end, (only) if all the three + * sub-conditions hold and the device is flash-based, the compound + * condition evaluates to false and therefore no idling is performed. + * + * As already said, things change with a rotational device, where idling + * boosts the throughput with sequential I/O (even with NCQ). Hence, for + * such a device the second component of the compound condition evaluates + * to true also if the following additional sub-condition does not hold: + * the queue is constantly seeky. Unfortunately, this different behavior + * with respect to flash-based devices causes an additional asymmetry: if + * some sync queues enjoy idling and some other sync queues do not, then + * the latter get a low share of the device throughput, simply because the + * former get many requests served after being set as in service, whereas + * the latter do not. As a consequence, to guarantee the desired throughput + * distribution, on HDDs the compound expression evaluates to true (and + * hence device idling is performed) also if the following last symmetry + * condition does not hold: no other queue is benefiting from idling. Also + * this last condition is actually replaced with a simpler-to-maintain and + * stronger condition: there is no busy queue which is not constantly seeky + * (and hence may also benefit from idling). + * + * To sum up, when all the required symmetry and throughput-boosting + * sub-conditions hold, the second component of the compound condition + * evaluates to false, and hence no idling is performed. This helps to + * keep the drives' internal queues full on NCQ-capable devices, and hence + * to boost the throughput, without causing 'almost' any loss of service + * guarantees. The 'almost' follows from the fact that, if the internal + * queue of one such device is filled while all the sub-conditions hold, + * but at some point in time some sub-condition stops to hold, then it may + * become impossible to let requests be served in the new desired order + * until all the requests already queued in the device have been served. + */ +static inline bool bfq_bfqq_must_not_expire(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; +#define cond_for_seeky_on_ncq_hdd (bfq_bfqq_constantly_seeky(bfqq) && \ + bfqd->busy_in_flight_queues == \ + bfqd->const_seeky_busy_in_flight_queues) + +#define cond_for_expiring_in_burst (bfq_bfqq_in_large_burst(bfqq) && \ + bfqd->hw_tag && \ + (blk_queue_nonrot(bfqd->queue) || \ + bfq_bfqq_constantly_seeky(bfqq))) + +/* + * Condition for expiring a non-weight-raised queue (and hence not idling + * the device). + */ +#define cond_for_expiring_non_wr (bfqd->hw_tag && \ + (bfqd->wr_busy_queues > 0 || \ + (blk_queue_nonrot(bfqd->queue) || \ + cond_for_seeky_on_ncq_hdd))) + + return bfq_bfqq_sync(bfqq) && + !cond_for_expiring_in_burst && + (bfqq->wr_coeff > 1 || !symmetric_scenario || + (bfq_bfqq_IO_bound(bfqq) && bfq_bfqq_idle_window(bfqq) && + !cond_for_expiring_non_wr) + ); +} + +/* + * If the in-service queue is empty but sync, and the function + * bfq_bfqq_must_not_expire returns true, then: + * 1) the queue must remain in service and cannot be expired, and + * 2) the disk must be idled to wait for the possible arrival of a new + * request for the queue. + * See the comments to the function bfq_bfqq_must_not_expire for the reasons + * why performing device idling is the best choice to boost the throughput + * and preserve service guarantees when bfq_bfqq_must_not_expire itself + * returns true. + */ +static inline bool bfq_bfqq_must_idle(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + + return RB_EMPTY_ROOT(&bfqq->sort_list) && bfqd->bfq_slice_idle != 0 && + bfq_bfqq_must_not_expire(bfqq); +} + +/* + * Select a queue for service. If we have a current queue in service, + * check whether to continue servicing it, or retrieve and set a new one. + */ +static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq, *new_bfqq = NULL; + struct request *next_rq; + enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT; + + bfqq = bfqd->in_service_queue; + if (bfqq == NULL) + goto new_queue; + + bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue"); + + /* + * If another queue has a request waiting within our mean seek + * distance, let it run. The expire code will check for close + * cooperators and put the close queue at the front of the + * service tree. If possible, merge the expiring queue with the + * new bfqq. + */ + new_bfqq = bfq_close_cooperator(bfqd, bfqq); + if (new_bfqq != NULL && bfqq->new_bfqq == NULL) + bfq_setup_merge(bfqq, new_bfqq); + + if (bfq_may_expire_for_budg_timeout(bfqq) && + !timer_pending(&bfqd->idle_slice_timer) && + !bfq_bfqq_must_idle(bfqq)) + goto expire; + + next_rq = bfqq->next_rq; + /* + * If bfqq has requests queued and it has enough budget left to + * serve them, keep the queue, otherwise expire it. + */ + if (next_rq != NULL) { + if (bfq_serv_to_charge(next_rq, bfqq) > + bfq_bfqq_budget_left(bfqq)) { + reason = BFQ_BFQQ_BUDGET_EXHAUSTED; + goto expire; + } else { + /* + * The idle timer may be pending because we may + * not disable disk idling even when a new request + * arrives. + */ + if (timer_pending(&bfqd->idle_slice_timer)) { + /* + * If we get here: 1) at least a new request + * has arrived but we have not disabled the + * timer because the request was too small, + * 2) then the block layer has unplugged + * the device, causing the dispatch to be + * invoked. + * + * Since the device is unplugged, now the + * requests are probably large enough to + * provide a reasonable throughput. + * So we disable idling. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + } + if (new_bfqq == NULL) + goto keep_queue; + else + goto expire; + } + } + + /* + * No requests pending. However, if the in-service queue is idling + * for a new request, or has requests waiting for a completion and + * may idle after their completion, then keep it anyway. + */ + if (new_bfqq == NULL && (timer_pending(&bfqd->idle_slice_timer) || + (bfqq->dispatched != 0 && bfq_bfqq_must_not_expire(bfqq)))) { + bfqq = NULL; + goto keep_queue; + } else if (new_bfqq != NULL && timer_pending(&bfqd->idle_slice_timer)) { + /* + * Expiring the queue because there is a close cooperator, + * cancel timer. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + } + + reason = BFQ_BFQQ_NO_MORE_REQUESTS; +expire: + bfq_bfqq_expire(bfqd, bfqq, 0, reason); +new_queue: + bfqq = bfq_set_in_service_queue(bfqd, new_bfqq); + bfq_log(bfqd, "select_queue: new queue %d returned", + bfqq != NULL ? bfqq->pid : 0); +keep_queue: + return bfqq; +} + +static void bfq_update_wr_data(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + if (bfqq->wr_coeff > 1) { /* queue is being boosted */ + struct bfq_entity *entity = &bfqq->entity; + + bfq_log_bfqq(bfqd, bfqq, + "raising period dur %u/%u msec, old coeff %u, w %d(%d)", + jiffies_to_msecs(jiffies - + bfqq->last_wr_start_finish), + jiffies_to_msecs(bfqq->wr_cur_max_time), + bfqq->wr_coeff, + bfqq->entity.weight, bfqq->entity.orig_weight); + + BUG_ON(bfqq != bfqd->in_service_queue && entity->weight != + entity->orig_weight * bfqq->wr_coeff); + if (entity->ioprio_changed) + bfq_log_bfqq(bfqd, bfqq, "WARN: pending prio change"); + /* + * If the queue was activated in a burst, or + * too much time has elapsed from the beginning + * of this weight-raising, then end weight raising. + */ + if (bfq_bfqq_in_large_burst(bfqq) || + time_is_before_jiffies(bfqq->last_wr_start_finish + + bfqq->wr_cur_max_time)) { + bfqq->last_wr_start_finish = jiffies; + bfq_log_bfqq(bfqd, bfqq, + "wrais ending at %lu, rais_max_time %u", + bfqq->last_wr_start_finish, + jiffies_to_msecs(bfqq->wr_cur_max_time)); + bfq_bfqq_end_wr(bfqq); + __bfq_entity_update_weight_prio( + bfq_entity_service_tree(entity), + entity); + } + } +} + +/* + * Dispatch one request from bfqq, moving it to the request queue + * dispatch list. + */ +static int bfq_dispatch_request(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + int dispatched = 0; + struct request *rq; + unsigned long service_to_charge; + + BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); + + /* Follow expired path, else get first next available. */ + rq = bfq_check_fifo(bfqq); + if (rq == NULL) + rq = bfqq->next_rq; + service_to_charge = bfq_serv_to_charge(rq, bfqq); + + if (service_to_charge > bfq_bfqq_budget_left(bfqq)) { + /* + * This may happen if the next rq is chosen in fifo order + * instead of sector order. The budget is properly + * dimensioned to be always sufficient to serve the next + * request only if it is chosen in sector order. The reason + * is that it would be quite inefficient and little useful + * to always make sure that the budget is large enough to + * serve even the possible next rq in fifo order. + * In fact, requests are seldom served in fifo order. + * + * Expire the queue for budget exhaustion, and make sure + * that the next act_budget is enough to serve the next + * request, even if it comes from the fifo expired path. + */ + bfqq->next_rq = rq; + /* + * Since this dispatch is failed, make sure that + * a new one will be performed + */ + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); + goto expire; + } + + /* Finally, insert request into driver dispatch list. */ + bfq_bfqq_served(bfqq, service_to_charge); + bfq_dispatch_insert(bfqd->queue, rq); + + bfq_update_wr_data(bfqd, bfqq); + + bfq_log_bfqq(bfqd, bfqq, + "dispatched %u sec req (%llu), budg left %lu", + blk_rq_sectors(rq), + (long long unsigned)blk_rq_pos(rq), + bfq_bfqq_budget_left(bfqq)); + + dispatched++; + + if (bfqd->in_service_bic == NULL) { + atomic_long_inc(&RQ_BIC(rq)->icq.ioc->refcount); + bfqd->in_service_bic = RQ_BIC(rq); + } + + if (bfqd->busy_queues > 1 && ((!bfq_bfqq_sync(bfqq) && + dispatched >= bfqd->bfq_max_budget_async_rq) || + bfq_class_idle(bfqq))) + goto expire; + + return dispatched; + +expire: + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_EXHAUSTED); + return dispatched; +} + +static int __bfq_forced_dispatch_bfqq(struct bfq_queue *bfqq) +{ + int dispatched = 0; + + while (bfqq->next_rq != NULL) { + bfq_dispatch_insert(bfqq->bfqd->queue, bfqq->next_rq); + dispatched++; + } + + BUG_ON(!list_empty(&bfqq->fifo)); + return dispatched; +} + +/* + * Drain our current requests. + * Used for barriers and when switching io schedulers on-the-fly. + */ +static int bfq_forced_dispatch(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq, *n; + struct bfq_service_tree *st; + int dispatched = 0; + + bfqq = bfqd->in_service_queue; + if (bfqq != NULL) + __bfq_bfqq_expire(bfqd, bfqq); + + /* + * Loop through classes, and be careful to leave the scheduler + * in a consistent state, as feedback mechanisms and vtime + * updates cannot be disabled during the process. + */ + list_for_each_entry_safe(bfqq, n, &bfqd->active_list, bfqq_list) { + st = bfq_entity_service_tree(&bfqq->entity); + + dispatched += __bfq_forced_dispatch_bfqq(bfqq); + bfqq->max_budget = bfq_max_budget(bfqd); + + bfq_forget_idle(st); + } + + BUG_ON(bfqd->busy_queues != 0); + + return dispatched; +} + +static int bfq_dispatch_requests(struct request_queue *q, int force) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq; + int max_dispatch; + + bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); + if (bfqd->busy_queues == 0) + return 0; + + if (unlikely(force)) + return bfq_forced_dispatch(bfqd); + + bfqq = bfq_select_queue(bfqd); + if (bfqq == NULL) + return 0; + + if (bfq_class_idle(bfqq)) + max_dispatch = 1; + + if (!bfq_bfqq_sync(bfqq)) + max_dispatch = bfqd->bfq_max_budget_async_rq; + + if (!bfq_bfqq_sync(bfqq) && bfqq->dispatched >= max_dispatch) { + if (bfqd->busy_queues > 1) + return 0; + if (bfqq->dispatched >= 4 * max_dispatch) + return 0; + } + + if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) + return 0; + + bfq_clear_bfqq_wait_request(bfqq); + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + if (!bfq_dispatch_request(bfqd, bfqq)) + return 0; + + bfq_log_bfqq(bfqd, bfqq, "dispatched %s request", + bfq_bfqq_sync(bfqq) ? "sync" : "async"); + + return 1; +} + +/* + * Task holds one reference to the queue, dropped when task exits. Each rq + * in-flight on this queue also holds a reference, dropped when rq is freed. + * + * Queue lock must be held here. + */ +static void bfq_put_queue(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + + BUG_ON(atomic_read(&bfqq->ref) <= 0); + + bfq_log_bfqq(bfqd, bfqq, "put_queue: %p %d", bfqq, + atomic_read(&bfqq->ref)); + if (!atomic_dec_and_test(&bfqq->ref)) + return; + + BUG_ON(rb_first(&bfqq->sort_list) != NULL); + BUG_ON(bfqq->allocated[READ] + bfqq->allocated[WRITE] != 0); + BUG_ON(bfqq->entity.tree != NULL); + BUG_ON(bfq_bfqq_busy(bfqq)); + BUG_ON(bfqd->in_service_queue == bfqq); + + if (bfq_bfqq_sync(bfqq)) + /* + * The fact that this queue is being destroyed does not + * invalidate the fact that this queue may have been + * activated during the current burst. As a consequence, + * although the queue does not exist anymore, and hence + * needs to be removed from the burst list if there, + * the burst size has not to be decremented. + */ + hlist_del_init(&bfqq->burst_list_node); + + bfq_log_bfqq(bfqd, bfqq, "put_queue: %p freed", bfqq); + + kmem_cache_free(bfq_pool, bfqq); +} + +static void bfq_put_cooperator(struct bfq_queue *bfqq) +{ + struct bfq_queue *__bfqq, *next; + + /* + * If this queue was scheduled to merge with another queue, be + * sure to drop the reference taken on that queue (and others in + * the merge chain). See bfq_setup_merge and bfq_merge_bfqqs. + */ + __bfqq = bfqq->new_bfqq; + while (__bfqq) { + if (__bfqq == bfqq) + break; + next = __bfqq->new_bfqq; + bfq_put_queue(__bfqq); + __bfqq = next; + } +} + +static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + if (bfqq == bfqd->in_service_queue) { + __bfq_bfqq_expire(bfqd, bfqq); + bfq_schedule_dispatch(bfqd); + } + + bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, + atomic_read(&bfqq->ref)); + + bfq_put_cooperator(bfqq); + + bfq_put_queue(bfqq); +} + +static inline void bfq_init_icq(struct io_cq *icq) +{ + struct bfq_io_cq *bic = icq_to_bic(icq); + + bic->ttime.last_end_request = jiffies; +} + +static void bfq_exit_icq(struct io_cq *icq) +{ + struct bfq_io_cq *bic = icq_to_bic(icq); + struct bfq_data *bfqd = bic_to_bfqd(bic); + + if (bic->bfqq[BLK_RW_ASYNC]) { + bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_ASYNC]); + bic->bfqq[BLK_RW_ASYNC] = NULL; + } + + if (bic->bfqq[BLK_RW_SYNC]) { + bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_SYNC]); + bic->bfqq[BLK_RW_SYNC] = NULL; + } +} + +/* + * Update the entity prio values; note that the new values will not + * be used until the next (re)activation. + */ +static void bfq_set_next_ioprio_data(struct bfq_queue *bfqq, struct bfq_io_cq *bic) +{ + struct task_struct *tsk = current; + int ioprio_class; + + ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); + switch (ioprio_class) { + default: + dev_err(bfqq->bfqd->queue->backing_dev_info.dev, + "bfq: bad prio class %d\n", ioprio_class); + case IOPRIO_CLASS_NONE: + /* + * No prio set, inherit CPU scheduling settings. + */ + bfqq->entity.new_ioprio = task_nice_ioprio(tsk); + bfqq->entity.new_ioprio_class = task_nice_ioclass(tsk); + break; + case IOPRIO_CLASS_RT: + bfqq->entity.new_ioprio = IOPRIO_PRIO_DATA(bic->ioprio); + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_RT; + break; + case IOPRIO_CLASS_BE: + bfqq->entity.new_ioprio = IOPRIO_PRIO_DATA(bic->ioprio); + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_IDLE: + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_IDLE; + bfqq->entity.new_ioprio = 7; + bfq_clear_bfqq_idle_window(bfqq); + break; + } + + if (bfqq->entity.new_ioprio < 0 || + bfqq->entity.new_ioprio >= IOPRIO_BE_NR) { + printk(KERN_CRIT "bfq_set_next_ioprio_data: new_ioprio %d\n", + bfqq->entity.new_ioprio); + BUG(); + } + + bfqq->entity.new_weight = bfq_ioprio_to_weight(bfqq->entity.new_ioprio); + bfqq->entity.ioprio_changed = 1; +} + +static void bfq_check_ioprio_change(struct bfq_io_cq *bic) +{ + struct bfq_data *bfqd; + struct bfq_queue *bfqq, *new_bfqq; + struct bfq_group *bfqg; + unsigned long uninitialized_var(flags); + int ioprio = bic->icq.ioc->ioprio; + + bfqd = bfq_get_bfqd_locked(&(bic->icq.q->elevator->elevator_data), + &flags); + /* + * This condition may trigger on a newly created bic, be sure to + * drop the lock before returning. + */ + if (unlikely(bfqd == NULL) || likely(bic->ioprio == ioprio)) + goto out; + + bic->ioprio = ioprio; + + bfqq = bic->bfqq[BLK_RW_ASYNC]; + if (bfqq != NULL) { + bfqg = container_of(bfqq->entity.sched_data, struct bfq_group, + sched_data); + new_bfqq = bfq_get_queue(bfqd, bfqg, BLK_RW_ASYNC, bic, + GFP_ATOMIC); + if (new_bfqq != NULL) { + bic->bfqq[BLK_RW_ASYNC] = new_bfqq; + bfq_log_bfqq(bfqd, bfqq, + "check_ioprio_change: bfqq %p %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + } + } + + bfqq = bic->bfqq[BLK_RW_SYNC]; + if (bfqq != NULL) + bfq_set_next_ioprio_data(bfqq, bic); + +out: + bfq_put_bfqd_unlock(bfqd, &flags); +} + +static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct bfq_io_cq *bic, pid_t pid, int is_sync) +{ + RB_CLEAR_NODE(&bfqq->entity.rb_node); + INIT_LIST_HEAD(&bfqq->fifo); + INIT_HLIST_NODE(&bfqq->burst_list_node); + + atomic_set(&bfqq->ref, 0); + bfqq->bfqd = bfqd; + + if (bic) + bfq_set_next_ioprio_data(bfqq, bic); + + if (is_sync) { + if (!bfq_class_idle(bfqq)) + bfq_mark_bfqq_idle_window(bfqq); + bfq_mark_bfqq_sync(bfqq); + } + bfq_mark_bfqq_IO_bound(bfqq); + + /* Tentative initial value to trade off between thr and lat */ + bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; + bfqq->pid = pid; + + bfqq->wr_coeff = 1; + bfqq->last_wr_start_finish = 0; + /* + * Set to the value for which bfqq will not be deemed as + * soft rt when it becomes backlogged. + */ + bfqq->soft_rt_next_start = bfq_infinity_from_now(jiffies); +} + +static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, + int is_sync, + struct bfq_io_cq *bic, + gfp_t gfp_mask) +{ + struct bfq_queue *bfqq, *new_bfqq = NULL; + +retry: + /* bic always exists here */ + bfqq = bic_to_bfqq(bic, is_sync); + + /* + * Always try a new alloc if we fall back to the OOM bfqq + * originally, since it should just be a temporary situation. + */ + if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { + bfqq = NULL; + if (new_bfqq != NULL) { + bfqq = new_bfqq; + new_bfqq = NULL; + } else if (gfp_mask & __GFP_WAIT) { + spin_unlock_irq(bfqd->queue->queue_lock); + new_bfqq = kmem_cache_alloc_node(bfq_pool, + gfp_mask | __GFP_ZERO, + bfqd->queue->node); + spin_lock_irq(bfqd->queue->queue_lock); + if (new_bfqq != NULL) + goto retry; + } else { + bfqq = kmem_cache_alloc_node(bfq_pool, + gfp_mask | __GFP_ZERO, + bfqd->queue->node); + } + + if (bfqq != NULL) { + bfq_init_bfqq(bfqd, bfqq, bic, current->pid, + is_sync); + bfq_init_entity(&bfqq->entity, bfqg); + bfq_log_bfqq(bfqd, bfqq, "allocated"); + } else { + bfqq = &bfqd->oom_bfqq; + bfq_log_bfqq(bfqd, bfqq, "using oom bfqq"); + } + } + + if (new_bfqq != NULL) + kmem_cache_free(bfq_pool, new_bfqq); + + return bfqq; +} + +static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd, + struct bfq_group *bfqg, + int ioprio_class, int ioprio) +{ + switch (ioprio_class) { + case IOPRIO_CLASS_RT: + return &bfqg->async_bfqq[0][ioprio]; + case IOPRIO_CLASS_NONE: + ioprio = IOPRIO_NORM; + /* fall through */ + case IOPRIO_CLASS_BE: + return &bfqg->async_bfqq[1][ioprio]; + case IOPRIO_CLASS_IDLE: + return &bfqg->async_idle_bfqq; + default: + BUG(); + } +} + +static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, int is_sync, + struct bfq_io_cq *bic, gfp_t gfp_mask) +{ + const int ioprio = IOPRIO_PRIO_DATA(bic->ioprio); + const int ioprio_class = IOPRIO_PRIO_CLASS(bic->ioprio); + struct bfq_queue **async_bfqq = NULL; + struct bfq_queue *bfqq = NULL; + + if (!is_sync) { + async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class, + ioprio); + bfqq = *async_bfqq; + } + + if (bfqq == NULL) + bfqq = bfq_find_alloc_queue(bfqd, bfqg, is_sync, bic, gfp_mask); + + /* + * Pin the queue now that it's allocated, scheduler exit will + * prune it. + */ + if (!is_sync && *async_bfqq == NULL) { + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "get_queue, bfqq not in async: %p, %d", + bfqq, atomic_read(&bfqq->ref)); + *async_bfqq = bfqq; + } + + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, + atomic_read(&bfqq->ref)); + return bfqq; +} + +static void bfq_update_io_thinktime(struct bfq_data *bfqd, + struct bfq_io_cq *bic) +{ + unsigned long elapsed = jiffies - bic->ttime.last_end_request; + unsigned long ttime = min(elapsed, 2UL * bfqd->bfq_slice_idle); + + bic->ttime.ttime_samples = (7*bic->ttime.ttime_samples + 256) / 8; + bic->ttime.ttime_total = (7*bic->ttime.ttime_total + 256*ttime) / 8; + bic->ttime.ttime_mean = (bic->ttime.ttime_total + 128) / + bic->ttime.ttime_samples; +} + +static void bfq_update_io_seektime(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct request *rq) +{ + sector_t sdist; + u64 total; + + if (bfqq->last_request_pos < blk_rq_pos(rq)) + sdist = blk_rq_pos(rq) - bfqq->last_request_pos; + else + sdist = bfqq->last_request_pos - blk_rq_pos(rq); + + /* + * Don't allow the seek distance to get too large from the + * odd fragment, pagein, etc. + */ + if (bfqq->seek_samples == 0) /* first request, not really a seek */ + sdist = 0; + else if (bfqq->seek_samples <= 60) /* second & third seek */ + sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*1024); + else + sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*64); + + bfqq->seek_samples = (7*bfqq->seek_samples + 256) / 8; + bfqq->seek_total = (7*bfqq->seek_total + (u64)256*sdist) / 8; + total = bfqq->seek_total + (bfqq->seek_samples/2); + do_div(total, bfqq->seek_samples); + bfqq->seek_mean = (sector_t)total; + + bfq_log_bfqq(bfqd, bfqq, "dist=%llu mean=%llu", (u64)sdist, + (u64)bfqq->seek_mean); +} + +/* + * Disable idle window if the process thinks too long or seeks so much that + * it doesn't matter. + */ +static void bfq_update_idle_window(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct bfq_io_cq *bic) +{ + int enable_idle; + + /* Don't idle for async or idle io prio class. */ + if (!bfq_bfqq_sync(bfqq) || bfq_class_idle(bfqq)) + return; + + enable_idle = bfq_bfqq_idle_window(bfqq); + + if (atomic_read(&bic->icq.ioc->active_ref) == 0 || + bfqd->bfq_slice_idle == 0 || + (bfqd->hw_tag && BFQQ_SEEKY(bfqq) && + bfqq->wr_coeff == 1)) + enable_idle = 0; + else if (bfq_sample_valid(bic->ttime.ttime_samples)) { + if (bic->ttime.ttime_mean > bfqd->bfq_slice_idle && + bfqq->wr_coeff == 1) + enable_idle = 0; + else + enable_idle = 1; + } + bfq_log_bfqq(bfqd, bfqq, "update_idle_window: enable_idle %d", + enable_idle); + + if (enable_idle) + bfq_mark_bfqq_idle_window(bfqq); + else + bfq_clear_bfqq_idle_window(bfqq); +} + +/* + * Called when a new fs request (rq) is added to bfqq. Check if there's + * something we should do about it. + */ +static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct request *rq) +{ + struct bfq_io_cq *bic = RQ_BIC(rq); + + if (rq->cmd_flags & REQ_META) + bfqq->meta_pending++; + + bfq_update_io_thinktime(bfqd, bic); + bfq_update_io_seektime(bfqd, bfqq, rq); + if (!BFQQ_SEEKY(bfqq) && bfq_bfqq_constantly_seeky(bfqq)) { + bfq_clear_bfqq_constantly_seeky(bfqq); + if (!blk_queue_nonrot(bfqd->queue)) { + BUG_ON(!bfqd->const_seeky_busy_in_flight_queues); + bfqd->const_seeky_busy_in_flight_queues--; + } + } + if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 || + !BFQQ_SEEKY(bfqq)) + bfq_update_idle_window(bfqd, bfqq, bic); + + bfq_log_bfqq(bfqd, bfqq, + "rq_enqueued: idle_window=%d (seeky %d, mean %llu)", + bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq), + (long long unsigned)bfqq->seek_mean); + + bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq); + + if (bfqq == bfqd->in_service_queue && bfq_bfqq_wait_request(bfqq)) { + int small_req = bfqq->queued[rq_is_sync(rq)] == 1 && + blk_rq_sectors(rq) < 32; + int budget_timeout = bfq_bfqq_budget_timeout(bfqq); + + /* + * There is just this request queued: if the request + * is small and the queue is not to be expired, then + * just exit. + * + * In this way, if the disk is being idled to wait for + * a new request from the in-service queue, we avoid + * unplugging the device and committing the disk to serve + * just a small request. On the contrary, we wait for + * the block layer to decide when to unplug the device: + * hopefully, new requests will be merged to this one + * quickly, then the device will be unplugged and + * larger requests will be dispatched. + */ + if (small_req && !budget_timeout) + return; + + /* + * A large enough request arrived, or the queue is to + * be expired: in both cases disk idling is to be + * stopped, so clear wait_request flag and reset + * timer. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + + /* + * The queue is not empty, because a new request just + * arrived. Hence we can safely expire the queue, in + * case of budget timeout, without risking that the + * timestamps of the queue are not updated correctly. + * See [1] for more details. + */ + if (budget_timeout) + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); + + /* + * Let the request rip immediately, or let a new queue be + * selected if bfqq has just been expired. + */ + __blk_run_queue(bfqd->queue); + } +} + +static void bfq_insert_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + assert_spin_locked(bfqd->queue->queue_lock); + + bfq_add_request(rq); + + rq_set_fifo_time(rq, jiffies + bfqd->bfq_fifo_expire[rq_is_sync(rq)]); + list_add_tail(&rq->queuelist, &bfqq->fifo); + + bfq_rq_enqueued(bfqd, bfqq, rq); +} + +static void bfq_update_hw_tag(struct bfq_data *bfqd) +{ + bfqd->max_rq_in_driver = max(bfqd->max_rq_in_driver, + bfqd->rq_in_driver); + + if (bfqd->hw_tag == 1) + return; + + /* + * This sample is valid if the number of outstanding requests + * is large enough to allow a queueing behavior. Note that the + * sum is not exact, as it's not taking into account deactivated + * requests. + */ + if (bfqd->rq_in_driver + bfqd->queued < BFQ_HW_QUEUE_THRESHOLD) + return; + + if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES) + return; + + bfqd->hw_tag = bfqd->max_rq_in_driver > BFQ_HW_QUEUE_THRESHOLD; + bfqd->max_rq_in_driver = 0; + bfqd->hw_tag_samples = 0; +} + +static void bfq_completed_request(struct request_queue *q, struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + bool sync = bfq_bfqq_sync(bfqq); + + bfq_log_bfqq(bfqd, bfqq, "completed one req with %u sects left (%d)", + blk_rq_sectors(rq), sync); + + bfq_update_hw_tag(bfqd); + + BUG_ON(!bfqd->rq_in_driver); + BUG_ON(!bfqq->dispatched); + bfqd->rq_in_driver--; + bfqq->dispatched--; + + if (!bfqq->dispatched && !bfq_bfqq_busy(bfqq)) { + bfq_weights_tree_remove(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); + if (!blk_queue_nonrot(bfqd->queue)) { + BUG_ON(!bfqd->busy_in_flight_queues); + bfqd->busy_in_flight_queues--; + if (bfq_bfqq_constantly_seeky(bfqq)) { + BUG_ON(!bfqd-> + const_seeky_busy_in_flight_queues); + bfqd->const_seeky_busy_in_flight_queues--; + } + } + } + + if (sync) { + bfqd->sync_flight--; + RQ_BIC(rq)->ttime.last_end_request = jiffies; + } + + /* + * If we are waiting to discover whether the request pattern of the + * task associated with the queue is actually isochronous, and + * both requisites for this condition to hold are satisfied, then + * compute soft_rt_next_start (see the comments to the function + * bfq_bfqq_softrt_next_start()). + */ + if (bfq_bfqq_softrt_update(bfqq) && bfqq->dispatched == 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) + bfqq->soft_rt_next_start = + bfq_bfqq_softrt_next_start(bfqd, bfqq); + + /* + * If this is the in-service queue, check if it needs to be expired, + * or if we want to idle in case it has no pending requests. + */ + if (bfqd->in_service_queue == bfqq) { + if (bfq_bfqq_budget_new(bfqq)) + bfq_set_budget_timeout(bfqd); + + if (bfq_bfqq_must_idle(bfqq)) { + bfq_arm_slice_timer(bfqd); + goto out; + } else if (bfq_may_expire_for_budg_timeout(bfqq)) + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); + else if (RB_EMPTY_ROOT(&bfqq->sort_list) && + (bfqq->dispatched == 0 || + !bfq_bfqq_must_not_expire(bfqq))) + bfq_bfqq_expire(bfqd, bfqq, 0, + BFQ_BFQQ_NO_MORE_REQUESTS); + } + + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); + +out: + return; +} + +static inline int __bfq_may_queue(struct bfq_queue *bfqq) +{ + if (bfq_bfqq_wait_request(bfqq) && bfq_bfqq_must_alloc(bfqq)) { + bfq_clear_bfqq_must_alloc(bfqq); + return ELV_MQUEUE_MUST; + } + + return ELV_MQUEUE_MAY; +} + +static int bfq_may_queue(struct request_queue *q, int rw) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq; + + /* + * Don't force setup of a queue from here, as a call to may_queue + * does not necessarily imply that a request actually will be + * queued. So just lookup a possibly existing queue, or return + * 'may queue' if that fails. + */ + bic = bfq_bic_lookup(bfqd, tsk->io_context); + if (bic == NULL) + return ELV_MQUEUE_MAY; + + bfqq = bic_to_bfqq(bic, rw_is_sync(rw)); + if (bfqq != NULL) + return __bfq_may_queue(bfqq); + + return ELV_MQUEUE_MAY; +} + +/* + * Queue lock held here. + */ +static void bfq_put_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + if (bfqq != NULL) { + const int rw = rq_data_dir(rq); + + BUG_ON(!bfqq->allocated[rw]); + bfqq->allocated[rw]--; + + rq->elv.priv[0] = NULL; + rq->elv.priv[1] = NULL; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "put_request %p, %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + } +} + +static struct bfq_queue * +bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, + struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu", + (long unsigned)bfqq->new_bfqq->pid); + bic_set_bfqq(bic, bfqq->new_bfqq, 1); + bfq_mark_bfqq_coop(bfqq->new_bfqq); + bfq_put_queue(bfqq); + return bic_to_bfqq(bic, 1); +} + +/* + * Returns NULL if a new bfqq should be allocated, or the old bfqq if this + * was the last process referring to said bfqq. + */ +static struct bfq_queue * +bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqq->bfqd, bfqq, "splitting queue"); + if (bfqq_process_refs(bfqq) == 1) { + bfqq->pid = current->pid; + bfq_clear_bfqq_coop(bfqq); + bfq_clear_bfqq_split_coop(bfqq); + return bfqq; + } + + bic_set_bfqq(bic, NULL, 1); + + bfq_put_cooperator(bfqq); + + bfq_put_queue(bfqq); + return NULL; +} + +/* + * Allocate bfq data structures associated with this request. + */ +static int bfq_set_request(struct request_queue *q, struct request *rq, + struct bio *bio, gfp_t gfp_mask) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_io_cq *bic = icq_to_bic(rq->elv.icq); + const int rw = rq_data_dir(rq); + const int is_sync = rq_is_sync(rq); + struct bfq_queue *bfqq; + struct bfq_group *bfqg; + unsigned long flags; + + might_sleep_if(gfp_mask & __GFP_WAIT); + + bfq_check_ioprio_change(bic); + + spin_lock_irqsave(q->queue_lock, flags); + + if (bic == NULL) + goto queue_fail; + + bfqg = bfq_bic_update_cgroup(bic); + +new_queue: + bfqq = bic_to_bfqq(bic, is_sync); + if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { + bfqq = bfq_get_queue(bfqd, bfqg, is_sync, bic, gfp_mask); + bic_set_bfqq(bic, bfqq, is_sync); + } else { + /* + * If the queue was seeky for too long, break it apart. + */ + if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) { + bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq"); + bfqq = bfq_split_bfqq(bic, bfqq); + if (!bfqq) + goto new_queue; + } + + /* + * Check to see if this queue is scheduled to merge with + * another closely cooperating queue. The merging of queues + * happens here as it must be done in process context. + * The reference on new_bfqq was taken in merge_bfqqs. + */ + if (bfqq->new_bfqq != NULL) + bfqq = bfq_merge_bfqqs(bfqd, bic, bfqq); + } + + bfqq->allocated[rw]++; + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, + atomic_read(&bfqq->ref)); + + rq->elv.priv[0] = bic; + rq->elv.priv[1] = bfqq; + + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; + +queue_fail: + bfq_schedule_dispatch(bfqd); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 1; +} + +static void bfq_kick_queue(struct work_struct *work) +{ + struct bfq_data *bfqd = + container_of(work, struct bfq_data, unplug_work); + struct request_queue *q = bfqd->queue; + + spin_lock_irq(q->queue_lock); + __blk_run_queue(q); + spin_unlock_irq(q->queue_lock); +} + +/* + * Handler of the expiration of the timer running if the in-service queue + * is idling inside its time slice. + */ +static void bfq_idle_slice_timer(unsigned long data) +{ + struct bfq_data *bfqd = (struct bfq_data *)data; + struct bfq_queue *bfqq; + unsigned long flags; + enum bfqq_expiration reason; + + spin_lock_irqsave(bfqd->queue->queue_lock, flags); + + bfqq = bfqd->in_service_queue; + /* + * Theoretical race here: the in-service queue can be NULL or + * different from the queue that was idling if the timer handler + * spins on the queue_lock and a new request arrives for the + * current queue and there is a full dispatch cycle that changes + * the in-service queue. This can hardly happen, but in the worst + * case we just expire a queue too early. + */ + if (bfqq != NULL) { + bfq_log_bfqq(bfqd, bfqq, "slice_timer expired"); + if (bfq_bfqq_budget_timeout(bfqq)) + /* + * Also here the queue can be safely expired + * for budget timeout without wasting + * guarantees + */ + reason = BFQ_BFQQ_BUDGET_TIMEOUT; + else if (bfqq->queued[0] == 0 && bfqq->queued[1] == 0) + /* + * The queue may not be empty upon timer expiration, + * because we may not disable the timer when the + * first request of the in-service queue arrives + * during disk idling. + */ + reason = BFQ_BFQQ_TOO_IDLE; + else + goto schedule_dispatch; + + bfq_bfqq_expire(bfqd, bfqq, 1, reason); + } + +schedule_dispatch: + bfq_schedule_dispatch(bfqd); + + spin_unlock_irqrestore(bfqd->queue->queue_lock, flags); +} + +static void bfq_shutdown_timer_wq(struct bfq_data *bfqd) +{ + del_timer_sync(&bfqd->idle_slice_timer); + cancel_work_sync(&bfqd->unplug_work); +} + +static inline void __bfq_put_async_bfqq(struct bfq_data *bfqd, + struct bfq_queue **bfqq_ptr) +{ + struct bfq_group *root_group = bfqd->root_group; + struct bfq_queue *bfqq = *bfqq_ptr; + + bfq_log(bfqd, "put_async_bfqq: %p", bfqq); + if (bfqq != NULL) { + bfq_bfqq_move(bfqd, bfqq, &bfqq->entity, root_group); + bfq_log_bfqq(bfqd, bfqq, "put_async_bfqq: putting %p, %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + *bfqq_ptr = NULL; + } +} + +/* + * Release all the bfqg references to its async queues. If we are + * deallocating the group these queues may still contain requests, so + * we reparent them to the root cgroup (i.e., the only one that will + * exist for sure until all the requests on a device are gone). + */ +static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg) +{ + int i, j; + + for (i = 0; i < 2; i++) + for (j = 0; j < IOPRIO_BE_NR; j++) + __bfq_put_async_bfqq(bfqd, &bfqg->async_bfqq[i][j]); + + __bfq_put_async_bfqq(bfqd, &bfqg->async_idle_bfqq); +} + +static void bfq_exit_queue(struct elevator_queue *e) +{ + struct bfq_data *bfqd = e->elevator_data; + struct request_queue *q = bfqd->queue; + struct bfq_queue *bfqq, *n; + + bfq_shutdown_timer_wq(bfqd); + + spin_lock_irq(q->queue_lock); + + BUG_ON(bfqd->in_service_queue != NULL); + list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list) + bfq_deactivate_bfqq(bfqd, bfqq, 0); + + bfq_disconnect_groups(bfqd); + spin_unlock_irq(q->queue_lock); + + bfq_shutdown_timer_wq(bfqd); + + synchronize_rcu(); + + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + bfq_free_root_group(bfqd); + kfree(bfqd); +} + +static int bfq_init_queue(struct request_queue *q, struct elevator_type *e) +{ + struct bfq_group *bfqg; + struct bfq_data *bfqd; + struct elevator_queue *eq; + + eq = elevator_alloc(q, e); + if (eq == NULL) + return -ENOMEM; + + bfqd = kzalloc_node(sizeof(*bfqd), GFP_KERNEL, q->node); + if (bfqd == NULL) { + kobject_put(&eq->kobj); + return -ENOMEM; + } + eq->elevator_data = bfqd; + + /* + * Our fallback bfqq if bfq_find_alloc_queue() runs into OOM issues. + * Grab a permanent reference to it, so that the normal code flow + * will not attempt to free it. + */ + bfq_init_bfqq(bfqd, &bfqd->oom_bfqq, NULL, 1, 0); + atomic_inc(&bfqd->oom_bfqq.ref); + bfqd->oom_bfqq.entity.new_ioprio = BFQ_DEFAULT_QUEUE_IOPRIO; + bfqd->oom_bfqq.entity.new_ioprio_class = IOPRIO_CLASS_BE; + bfqd->oom_bfqq.entity.new_weight = + bfq_ioprio_to_weight(bfqd->oom_bfqq.entity.new_ioprio); + /* + * Trigger weight initialization, according to ioprio, at the + * oom_bfqq's first activation. The oom_bfqq's ioprio and ioprio + * class won't be changed any more. + */ + bfqd->oom_bfqq.entity.ioprio_changed = 1; + + bfqd->queue = q; + + spin_lock_irq(q->queue_lock); + q->elevator = eq; + spin_unlock_irq(q->queue_lock); + + bfqg = bfq_alloc_root_group(bfqd, q->node); + if (bfqg == NULL) { + kfree(bfqd); + kobject_put(&eq->kobj); + return -ENOMEM; + } + + bfqd->root_group = bfqg; + bfq_init_entity(&bfqd->oom_bfqq.entity, bfqd->root_group); +#ifdef CONFIG_CGROUP_BFQIO + bfqd->active_numerous_groups = 0; +#endif + + init_timer(&bfqd->idle_slice_timer); + bfqd->idle_slice_timer.function = bfq_idle_slice_timer; + bfqd->idle_slice_timer.data = (unsigned long)bfqd; + + bfqd->rq_pos_tree = RB_ROOT; + bfqd->queue_weights_tree = RB_ROOT; + bfqd->group_weights_tree = RB_ROOT; + + INIT_WORK(&bfqd->unplug_work, bfq_kick_queue); + + INIT_LIST_HEAD(&bfqd->active_list); + INIT_LIST_HEAD(&bfqd->idle_list); + INIT_HLIST_HEAD(&bfqd->burst_list); + + bfqd->hw_tag = -1; + + bfqd->bfq_max_budget = bfq_default_max_budget; + + bfqd->bfq_fifo_expire[0] = bfq_fifo_expire[0]; + bfqd->bfq_fifo_expire[1] = bfq_fifo_expire[1]; + bfqd->bfq_back_max = bfq_back_max; + bfqd->bfq_back_penalty = bfq_back_penalty; + bfqd->bfq_slice_idle = bfq_slice_idle; + bfqd->bfq_class_idle_last_service = 0; + bfqd->bfq_max_budget_async_rq = bfq_max_budget_async_rq; + bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; + bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; + + bfqd->bfq_coop_thresh = 2; + bfqd->bfq_failed_cooperations = 7000; + bfqd->bfq_requests_within_timer = 120; + + bfqd->bfq_large_burst_thresh = 11; + bfqd->bfq_burst_interval = msecs_to_jiffies(500); + + bfqd->low_latency = true; + + bfqd->bfq_wr_coeff = 20; + bfqd->bfq_wr_rt_max_time = msecs_to_jiffies(300); + bfqd->bfq_wr_max_time = 0; + bfqd->bfq_wr_min_idle_time = msecs_to_jiffies(2000); + bfqd->bfq_wr_min_inter_arr_async = msecs_to_jiffies(500); + bfqd->bfq_wr_max_softrt_rate = 7000; /* + * Approximate rate required + * to playback or record a + * high-definition compressed + * video. + */ + bfqd->wr_busy_queues = 0; + bfqd->busy_in_flight_queues = 0; + bfqd->const_seeky_busy_in_flight_queues = 0; + + /* + * Begin by assuming, optimistically, that the device peak rate is + * equal to the highest reference rate. + */ + bfqd->RT_prod = R_fast[blk_queue_nonrot(bfqd->queue)] * + T_fast[blk_queue_nonrot(bfqd->queue)]; + bfqd->peak_rate = R_fast[blk_queue_nonrot(bfqd->queue)]; + bfqd->device_speed = BFQ_BFQD_FAST; + + return 0; +} + +static void bfq_slab_kill(void) +{ + if (bfq_pool != NULL) + kmem_cache_destroy(bfq_pool); +} + +static int __init bfq_slab_setup(void) +{ + bfq_pool = KMEM_CACHE(bfq_queue, 0); + if (bfq_pool == NULL) + return -ENOMEM; + return 0; +} + +static ssize_t bfq_var_show(unsigned int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t bfq_var_store(unsigned long *var, const char *page, + size_t count) +{ + unsigned long new_val; + int ret = kstrtoul(page, 10, &new_val); + + if (ret == 0) + *var = new_val; + + return count; +} + +static ssize_t bfq_wr_max_time_show(struct elevator_queue *e, char *page) +{ + struct bfq_data *bfqd = e->elevator_data; + return sprintf(page, "%d\n", bfqd->bfq_wr_max_time > 0 ? + jiffies_to_msecs(bfqd->bfq_wr_max_time) : + jiffies_to_msecs(bfq_wr_duration(bfqd))); +} + +static ssize_t bfq_weights_show(struct elevator_queue *e, char *page) +{ + struct bfq_queue *bfqq; + struct bfq_data *bfqd = e->elevator_data; + ssize_t num_char = 0; + + num_char += sprintf(page + num_char, "Tot reqs queued %d\n\n", + bfqd->queued); + + spin_lock_irq(bfqd->queue->queue_lock); + + num_char += sprintf(page + num_char, "Active:\n"); + list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) { + num_char += sprintf(page + num_char, + "pid%d: weight %hu, nr_queued %d %d, dur %d/%u\n", + bfqq->pid, + bfqq->entity.weight, + bfqq->queued[0], + bfqq->queued[1], + jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } + + num_char += sprintf(page + num_char, "Idle:\n"); + list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) { + num_char += sprintf(page + num_char, + "pid%d: weight %hu, dur %d/%u\n", + bfqq->pid, + bfqq->entity.weight, + jiffies_to_msecs(jiffies - + bfqq->last_wr_start_finish), + jiffies_to_msecs(bfqq->wr_cur_max_time)); + } + + spin_unlock_irq(bfqd->queue->queue_lock); + + return num_char; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct bfq_data *bfqd = e->elevator_data; \ + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return bfq_var_show(__data, (page)); \ +} +SHOW_FUNCTION(bfq_fifo_expire_sync_show, bfqd->bfq_fifo_expire[1], 1); +SHOW_FUNCTION(bfq_fifo_expire_async_show, bfqd->bfq_fifo_expire[0], 1); +SHOW_FUNCTION(bfq_back_seek_max_show, bfqd->bfq_back_max, 0); +SHOW_FUNCTION(bfq_back_seek_penalty_show, bfqd->bfq_back_penalty, 0); +SHOW_FUNCTION(bfq_slice_idle_show, bfqd->bfq_slice_idle, 1); +SHOW_FUNCTION(bfq_max_budget_show, bfqd->bfq_user_max_budget, 0); +SHOW_FUNCTION(bfq_max_budget_async_rq_show, + bfqd->bfq_max_budget_async_rq, 0); +SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout[BLK_RW_SYNC], 1); +SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); +SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); +SHOW_FUNCTION(bfq_wr_coeff_show, bfqd->bfq_wr_coeff, 0); +SHOW_FUNCTION(bfq_wr_rt_max_time_show, bfqd->bfq_wr_rt_max_time, 1); +SHOW_FUNCTION(bfq_wr_min_idle_time_show, bfqd->bfq_wr_min_idle_time, 1); +SHOW_FUNCTION(bfq_wr_min_inter_arr_async_show, bfqd->bfq_wr_min_inter_arr_async, + 1); +SHOW_FUNCTION(bfq_wr_max_softrt_rate_show, bfqd->bfq_wr_max_softrt_rate, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t \ +__FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct bfq_data *bfqd = e->elevator_data; \ + unsigned long uninitialized_var(__data); \ + int ret = bfq_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(bfq_fifo_expire_sync_store, &bfqd->bfq_fifo_expire[1], 1, + INT_MAX, 1); +STORE_FUNCTION(bfq_fifo_expire_async_store, &bfqd->bfq_fifo_expire[0], 1, + INT_MAX, 1); +STORE_FUNCTION(bfq_back_seek_max_store, &bfqd->bfq_back_max, 0, INT_MAX, 0); +STORE_FUNCTION(bfq_back_seek_penalty_store, &bfqd->bfq_back_penalty, 1, + INT_MAX, 0); +STORE_FUNCTION(bfq_slice_idle_store, &bfqd->bfq_slice_idle, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_max_budget_async_rq_store, &bfqd->bfq_max_budget_async_rq, + 1, INT_MAX, 0); +STORE_FUNCTION(bfq_timeout_async_store, &bfqd->bfq_timeout[BLK_RW_ASYNC], 0, + INT_MAX, 1); +STORE_FUNCTION(bfq_wr_coeff_store, &bfqd->bfq_wr_coeff, 1, INT_MAX, 0); +STORE_FUNCTION(bfq_wr_max_time_store, &bfqd->bfq_wr_max_time, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_wr_rt_max_time_store, &bfqd->bfq_wr_rt_max_time, 0, INT_MAX, + 1); +STORE_FUNCTION(bfq_wr_min_idle_time_store, &bfqd->bfq_wr_min_idle_time, 0, + INT_MAX, 1); +STORE_FUNCTION(bfq_wr_min_inter_arr_async_store, + &bfqd->bfq_wr_min_inter_arr_async, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_wr_max_softrt_rate_store, &bfqd->bfq_wr_max_softrt_rate, 0, + INT_MAX, 0); +#undef STORE_FUNCTION + +/* do nothing for the moment */ +static ssize_t bfq_weights_store(struct elevator_queue *e, + const char *page, size_t count) +{ + return count; +} + +static inline unsigned long bfq_estimated_max_budget(struct bfq_data *bfqd) +{ + u64 timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); + + if (bfqd->peak_rate_samples >= BFQ_PEAK_RATE_SAMPLES) + return bfq_calc_max_budget(bfqd->peak_rate, timeout); + else + return bfq_default_max_budget; +} + +static ssize_t bfq_max_budget_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long uninitialized_var(__data); + int ret = bfq_var_store(&__data, (page), count); + + if (__data == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + else { + if (__data > INT_MAX) + __data = INT_MAX; + bfqd->bfq_max_budget = __data; + } + + bfqd->bfq_user_max_budget = __data; + + return ret; +} + +static ssize_t bfq_timeout_sync_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long uninitialized_var(__data); + int ret = bfq_var_store(&__data, (page), count); + + if (__data < 1) + __data = 1; + else if (__data > INT_MAX) + __data = INT_MAX; + + bfqd->bfq_timeout[BLK_RW_SYNC] = msecs_to_jiffies(__data); + if (bfqd->bfq_user_max_budget == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + + return ret; +} + +static ssize_t bfq_low_latency_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long uninitialized_var(__data); + int ret = bfq_var_store(&__data, (page), count); + + if (__data > 1) + __data = 1; + if (__data == 0 && bfqd->low_latency != 0) + bfq_end_wr(bfqd); + bfqd->low_latency = __data; + + return ret; +} + +#define BFQ_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, bfq_##name##_show, bfq_##name##_store) + +static struct elv_fs_entry bfq_attrs[] = { + BFQ_ATTR(fifo_expire_sync), + BFQ_ATTR(fifo_expire_async), + BFQ_ATTR(back_seek_max), + BFQ_ATTR(back_seek_penalty), + BFQ_ATTR(slice_idle), + BFQ_ATTR(max_budget), + BFQ_ATTR(max_budget_async_rq), + BFQ_ATTR(timeout_sync), + BFQ_ATTR(timeout_async), + BFQ_ATTR(low_latency), + BFQ_ATTR(wr_coeff), + BFQ_ATTR(wr_max_time), + BFQ_ATTR(wr_rt_max_time), + BFQ_ATTR(wr_min_idle_time), + BFQ_ATTR(wr_min_inter_arr_async), + BFQ_ATTR(wr_max_softrt_rate), + BFQ_ATTR(weights), + __ATTR_NULL +}; + +static struct elevator_type iosched_bfq = { + .ops = { + .elevator_merge_fn = bfq_merge, + .elevator_merged_fn = bfq_merged_request, + .elevator_merge_req_fn = bfq_merged_requests, + .elevator_allow_merge_fn = bfq_allow_merge, + .elevator_dispatch_fn = bfq_dispatch_requests, + .elevator_add_req_fn = bfq_insert_request, + .elevator_activate_req_fn = bfq_activate_request, + .elevator_deactivate_req_fn = bfq_deactivate_request, + .elevator_completed_req_fn = bfq_completed_request, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_init_icq_fn = bfq_init_icq, + .elevator_exit_icq_fn = bfq_exit_icq, + .elevator_set_req_fn = bfq_set_request, + .elevator_put_req_fn = bfq_put_request, + .elevator_may_queue_fn = bfq_may_queue, + .elevator_init_fn = bfq_init_queue, + .elevator_exit_fn = bfq_exit_queue, + }, + .icq_size = sizeof(struct bfq_io_cq), + .icq_align = __alignof__(struct bfq_io_cq), + .elevator_attrs = bfq_attrs, + .elevator_name = "bfq", + .elevator_owner = THIS_MODULE, +}; + +static int __init bfq_init(void) +{ + /* + * Can be 0 on HZ < 1000 setups. + */ + if (bfq_slice_idle == 0) + bfq_slice_idle = 1; + + if (bfq_timeout_async == 0) + bfq_timeout_async = 1; + + if (bfq_slab_setup()) + return -ENOMEM; + + /* + * Times to load large popular applications for the typical systems + * installed on the reference devices (see the comments before the + * definitions of the two arrays). + */ + T_slow[0] = msecs_to_jiffies(2600); + T_slow[1] = msecs_to_jiffies(1000); + T_fast[0] = msecs_to_jiffies(5500); + T_fast[1] = msecs_to_jiffies(2000); + + /* + * Thresholds that determine the switch between speed classes (see + * the comments before the definition of the array). + */ + device_speed_thresh[0] = (R_fast[0] + R_slow[0]) / 2; + device_speed_thresh[1] = (R_fast[1] + R_slow[1]) / 2; + + elv_register(&iosched_bfq); + pr_info("BFQ I/O-scheduler: v7r8"); + + return 0; +} + +static void __exit bfq_exit(void) +{ + elv_unregister(&iosched_bfq); + bfq_slab_kill(); +} + +module_init(bfq_init); +module_exit(bfq_exit); + +MODULE_AUTHOR("Fabio Checconi, Paolo Valente"); +MODULE_LICENSE("GPL"); diff --git a/block/bfq-sched.c b/block/bfq-sched.c new file mode 100644 index 00000000000000..c3430993ec9432 --- /dev/null +++ b/block/bfq-sched.c @@ -0,0 +1,1208 @@ +/* + * BFQ: Hierarchical B-WF2Q+ scheduler. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + */ + +#ifdef CONFIG_CGROUP_BFQIO +#define for_each_entity(entity) \ + for (; entity != NULL; entity = entity->parent) + +#define for_each_entity_safe(entity, parent) \ + for (; entity && ({ parent = entity->parent; 1; }); entity = parent) + +static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract, + struct bfq_data *bfqd); + +static inline void bfq_update_budget(struct bfq_entity *next_in_service) +{ + struct bfq_entity *bfqg_entity; + struct bfq_group *bfqg; + struct bfq_sched_data *group_sd; + + BUG_ON(next_in_service == NULL); + + group_sd = next_in_service->sched_data; + + bfqg = container_of(group_sd, struct bfq_group, sched_data); + /* + * bfq_group's my_entity field is not NULL only if the group + * is not the root group. We must not touch the root entity + * as it must never become an in-service entity. + */ + bfqg_entity = bfqg->my_entity; + if (bfqg_entity != NULL) + bfqg_entity->budget = next_in_service->budget; +} + +static int bfq_update_next_in_service(struct bfq_sched_data *sd) +{ + struct bfq_entity *next_in_service; + + if (sd->in_service_entity != NULL) + /* will update/requeue at the end of service */ + return 0; + + /* + * NOTE: this can be improved in many ways, such as returning + * 1 (and thus propagating upwards the update) only when the + * budget changes, or caching the bfqq that will be scheduled + * next from this subtree. By now we worry more about + * correctness than about performance... + */ + next_in_service = bfq_lookup_next_entity(sd, 0, NULL); + sd->next_in_service = next_in_service; + + if (next_in_service != NULL) + bfq_update_budget(next_in_service); + + return 1; +} + +static inline void bfq_check_next_in_service(struct bfq_sched_data *sd, + struct bfq_entity *entity) +{ + BUG_ON(sd->next_in_service != entity); +} +#else +#define for_each_entity(entity) \ + for (; entity != NULL; entity = NULL) + +#define for_each_entity_safe(entity, parent) \ + for (parent = NULL; entity != NULL; entity = parent) + +static inline int bfq_update_next_in_service(struct bfq_sched_data *sd) +{ + return 0; +} + +static inline void bfq_check_next_in_service(struct bfq_sched_data *sd, + struct bfq_entity *entity) +{ +} + +static inline void bfq_update_budget(struct bfq_entity *next_in_service) +{ +} +#endif + +/* + * Shift for timestamp calculations. This actually limits the maximum + * service allowed in one timestamp delta (small shift values increase it), + * the maximum total weight that can be used for the queues in the system + * (big shift values increase it), and the period of virtual time + * wraparounds. + */ +#define WFQ_SERVICE_SHIFT 22 + +/** + * bfq_gt - compare two timestamps. + * @a: first ts. + * @b: second ts. + * + * Return @a > @b, dealing with wrapping correctly. + */ +static inline int bfq_gt(u64 a, u64 b) +{ + return (s64)(a - b) > 0; +} + +static inline struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = NULL; + + BUG_ON(entity == NULL); + + if (entity->my_sched_data == NULL) + bfqq = container_of(entity, struct bfq_queue, entity); + + return bfqq; +} + + +/** + * bfq_delta - map service into the virtual time domain. + * @service: amount of service. + * @weight: scale factor (weight of an entity or weight sum). + */ +static inline u64 bfq_delta(unsigned long service, + unsigned long weight) +{ + u64 d = (u64)service << WFQ_SERVICE_SHIFT; + + do_div(d, weight); + return d; +} + +/** + * bfq_calc_finish - assign the finish time to an entity. + * @entity: the entity to act upon. + * @service: the service to be charged to the entity. + */ +static inline void bfq_calc_finish(struct bfq_entity *entity, + unsigned long service) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + BUG_ON(entity->weight == 0); + + entity->finish = entity->start + + bfq_delta(service, entity->weight); + + if (bfqq != NULL) { + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: serv %lu, w %d", + service, entity->weight); + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: start %llu, finish %llu, delta %llu", + entity->start, entity->finish, + bfq_delta(service, entity->weight)); + } +} + +/** + * bfq_entity_of - get an entity from a node. + * @node: the node field of the entity. + * + * Convert a node pointer to the relative entity. This is used only + * to simplify the logic of some functions and not as the generic + * conversion mechanism because, e.g., in the tree walking functions, + * the check for a %NULL value would be redundant. + */ +static inline struct bfq_entity *bfq_entity_of(struct rb_node *node) +{ + struct bfq_entity *entity = NULL; + + if (node != NULL) + entity = rb_entry(node, struct bfq_entity, rb_node); + + return entity; +} + +/** + * bfq_extract - remove an entity from a tree. + * @root: the tree root. + * @entity: the entity to remove. + */ +static inline void bfq_extract(struct rb_root *root, + struct bfq_entity *entity) +{ + BUG_ON(entity->tree != root); + + entity->tree = NULL; + rb_erase(&entity->rb_node, root); +} + +/** + * bfq_idle_extract - extract an entity from the idle tree. + * @st: the service tree of the owning @entity. + * @entity: the entity being removed. + */ +static void bfq_idle_extract(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *next; + + BUG_ON(entity->tree != &st->idle); + + if (entity == st->first_idle) { + next = rb_next(&entity->rb_node); + st->first_idle = bfq_entity_of(next); + } + + if (entity == st->last_idle) { + next = rb_prev(&entity->rb_node); + st->last_idle = bfq_entity_of(next); + } + + bfq_extract(&st->idle, entity); + + if (bfqq != NULL) + list_del(&bfqq->bfqq_list); +} + +/** + * bfq_insert - generic tree insertion. + * @root: tree root. + * @entity: entity to insert. + * + * This is used for the idle and the active tree, since they are both + * ordered by finish time. + */ +static void bfq_insert(struct rb_root *root, struct bfq_entity *entity) +{ + struct bfq_entity *entry; + struct rb_node **node = &root->rb_node; + struct rb_node *parent = NULL; + + BUG_ON(entity->tree != NULL); + + while (*node != NULL) { + parent = *node; + entry = rb_entry(parent, struct bfq_entity, rb_node); + + if (bfq_gt(entry->finish, entity->finish)) + node = &parent->rb_left; + else + node = &parent->rb_right; + } + + rb_link_node(&entity->rb_node, parent, node); + rb_insert_color(&entity->rb_node, root); + + entity->tree = root; +} + +/** + * bfq_update_min - update the min_start field of a entity. + * @entity: the entity to update. + * @node: one of its children. + * + * This function is called when @entity may store an invalid value for + * min_start due to updates to the active tree. The function assumes + * that the subtree rooted at @node (which may be its left or its right + * child) has a valid min_start value. + */ +static inline void bfq_update_min(struct bfq_entity *entity, + struct rb_node *node) +{ + struct bfq_entity *child; + + if (node != NULL) { + child = rb_entry(node, struct bfq_entity, rb_node); + if (bfq_gt(entity->min_start, child->min_start)) + entity->min_start = child->min_start; + } +} + +/** + * bfq_update_active_node - recalculate min_start. + * @node: the node to update. + * + * @node may have changed position or one of its children may have moved, + * this function updates its min_start value. The left and right subtrees + * are assumed to hold a correct min_start value. + */ +static inline void bfq_update_active_node(struct rb_node *node) +{ + struct bfq_entity *entity = rb_entry(node, struct bfq_entity, rb_node); + + entity->min_start = entity->start; + bfq_update_min(entity, node->rb_right); + bfq_update_min(entity, node->rb_left); +} + +/** + * bfq_update_active_tree - update min_start for the whole active tree. + * @node: the starting node. + * + * @node must be the deepest modified node after an update. This function + * updates its min_start using the values held by its children, assuming + * that they did not change, and then updates all the nodes that may have + * changed in the path to the root. The only nodes that may have changed + * are the ones in the path or their siblings. + */ +static void bfq_update_active_tree(struct rb_node *node) +{ + struct rb_node *parent; + +up: + bfq_update_active_node(node); + + parent = rb_parent(node); + if (parent == NULL) + return; + + if (node == parent->rb_left && parent->rb_right != NULL) + bfq_update_active_node(parent->rb_right); + else if (parent->rb_left != NULL) + bfq_update_active_node(parent->rb_left); + + node = parent; + goto up; +} + +static void bfq_weights_tree_add(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root); + +static void bfq_weights_tree_remove(struct bfq_data *bfqd, + struct bfq_entity *entity, + struct rb_root *root); + + +/** + * bfq_active_insert - insert an entity in the active tree of its + * group/device. + * @st: the service tree of the entity. + * @entity: the entity being inserted. + * + * The active tree is ordered by finish time, but an extra key is kept + * per each node, containing the minimum value for the start times of + * its children (and the node itself), so it's possible to search for + * the eligible node with the lowest finish time in logarithmic time. + */ +static void bfq_active_insert(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *node = &entity->rb_node; +#ifdef CONFIG_CGROUP_BFQIO + struct bfq_sched_data *sd = NULL; + struct bfq_group *bfqg = NULL; + struct bfq_data *bfqd = NULL; +#endif + + bfq_insert(&st->active, entity); + + if (node->rb_left != NULL) + node = node->rb_left; + else if (node->rb_right != NULL) + node = node->rb_right; + + bfq_update_active_tree(node); + +#ifdef CONFIG_CGROUP_BFQIO + sd = entity->sched_data; + bfqg = container_of(sd, struct bfq_group, sched_data); + BUG_ON(!bfqg); + bfqd = (struct bfq_data *)bfqg->bfqd; +#endif + if (bfqq != NULL) + list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list); +#ifdef CONFIG_CGROUP_BFQIO + else { /* bfq_group */ + BUG_ON(!bfqd); + bfq_weights_tree_add(bfqd, entity, &bfqd->group_weights_tree); + } + if (bfqg != bfqd->root_group) { + BUG_ON(!bfqg); + BUG_ON(!bfqd); + bfqg->active_entities++; + if (bfqg->active_entities == 2) + bfqd->active_numerous_groups++; + } +#endif +} + +/** + * bfq_ioprio_to_weight - calc a weight from an ioprio. + * @ioprio: the ioprio value to convert. + */ +static inline unsigned short bfq_ioprio_to_weight(int ioprio) +{ + BUG_ON(ioprio < 0 || ioprio >= IOPRIO_BE_NR); + return IOPRIO_BE_NR - ioprio; +} + +/** + * bfq_weight_to_ioprio - calc an ioprio from a weight. + * @weight: the weight value to convert. + * + * To preserve as mush as possible the old only-ioprio user interface, + * 0 is used as an escape ioprio value for weights (numerically) equal or + * larger than IOPRIO_BE_NR + */ +static inline unsigned short bfq_weight_to_ioprio(int weight) +{ + BUG_ON(weight < BFQ_MIN_WEIGHT || weight > BFQ_MAX_WEIGHT); + return IOPRIO_BE_NR - weight < 0 ? 0 : IOPRIO_BE_NR - weight; +} + +static inline void bfq_get_entity(struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + if (bfqq != NULL) { + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqq->bfqd, bfqq, "get_entity: %p %d", + bfqq, atomic_read(&bfqq->ref)); + } +} + +/** + * bfq_find_deepest - find the deepest node that an extraction can modify. + * @node: the node being removed. + * + * Do the first step of an extraction in an rb tree, looking for the + * node that will replace @node, and returning the deepest node that + * the following modifications to the tree can touch. If @node is the + * last node in the tree return %NULL. + */ +static struct rb_node *bfq_find_deepest(struct rb_node *node) +{ + struct rb_node *deepest; + + if (node->rb_right == NULL && node->rb_left == NULL) + deepest = rb_parent(node); + else if (node->rb_right == NULL) + deepest = node->rb_left; + else if (node->rb_left == NULL) + deepest = node->rb_right; + else { + deepest = rb_next(node); + if (deepest->rb_right != NULL) + deepest = deepest->rb_right; + else if (rb_parent(deepest) != node) + deepest = rb_parent(deepest); + } + + return deepest; +} + +/** + * bfq_active_extract - remove an entity from the active tree. + * @st: the service_tree containing the tree. + * @entity: the entity being removed. + */ +static void bfq_active_extract(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *node; +#ifdef CONFIG_CGROUP_BFQIO + struct bfq_sched_data *sd = NULL; + struct bfq_group *bfqg = NULL; + struct bfq_data *bfqd = NULL; +#endif + + node = bfq_find_deepest(&entity->rb_node); + bfq_extract(&st->active, entity); + + if (node != NULL) + bfq_update_active_tree(node); + +#ifdef CONFIG_CGROUP_BFQIO + sd = entity->sched_data; + bfqg = container_of(sd, struct bfq_group, sched_data); + BUG_ON(!bfqg); + bfqd = (struct bfq_data *)bfqg->bfqd; +#endif + if (bfqq != NULL) + list_del(&bfqq->bfqq_list); +#ifdef CONFIG_CGROUP_BFQIO + else { /* bfq_group */ + BUG_ON(!bfqd); + bfq_weights_tree_remove(bfqd, entity, + &bfqd->group_weights_tree); + } + if (bfqg != bfqd->root_group) { + BUG_ON(!bfqg); + BUG_ON(!bfqd); + BUG_ON(!bfqg->active_entities); + bfqg->active_entities--; + if (bfqg->active_entities == 1) { + BUG_ON(!bfqd->active_numerous_groups); + bfqd->active_numerous_groups--; + } + } +#endif +} + +/** + * bfq_idle_insert - insert an entity into the idle tree. + * @st: the service tree containing the tree. + * @entity: the entity to insert. + */ +static void bfq_idle_insert(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_entity *first_idle = st->first_idle; + struct bfq_entity *last_idle = st->last_idle; + + if (first_idle == NULL || bfq_gt(first_idle->finish, entity->finish)) + st->first_idle = entity; + if (last_idle == NULL || bfq_gt(entity->finish, last_idle->finish)) + st->last_idle = entity; + + bfq_insert(&st->idle, entity); + + if (bfqq != NULL) + list_add(&bfqq->bfqq_list, &bfqq->bfqd->idle_list); +} + +/** + * bfq_forget_entity - remove an entity from the wfq trees. + * @st: the service tree. + * @entity: the entity being removed. + * + * Update the device status and forget everything about @entity, putting + * the device reference to it, if it is a queue. Entities belonging to + * groups are not refcounted. + */ +static void bfq_forget_entity(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_sched_data *sd; + + BUG_ON(!entity->on_st); + + entity->on_st = 0; + st->wsum -= entity->weight; + if (bfqq != NULL) { + sd = entity->sched_data; + bfq_log_bfqq(bfqq->bfqd, bfqq, "forget_entity: %p %d", + bfqq, atomic_read(&bfqq->ref)); + bfq_put_queue(bfqq); + } +} + +/** + * bfq_put_idle_entity - release the idle tree ref of an entity. + * @st: service tree for the entity. + * @entity: the entity being released. + */ +static void bfq_put_idle_entity(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + bfq_idle_extract(st, entity); + bfq_forget_entity(st, entity); +} + +/** + * bfq_forget_idle - update the idle tree if necessary. + * @st: the service tree to act upon. + * + * To preserve the global O(log N) complexity we only remove one entry here; + * as the idle tree will not grow indefinitely this can be done safely. + */ +static void bfq_forget_idle(struct bfq_service_tree *st) +{ + struct bfq_entity *first_idle = st->first_idle; + struct bfq_entity *last_idle = st->last_idle; + + if (RB_EMPTY_ROOT(&st->active) && last_idle != NULL && + !bfq_gt(last_idle->finish, st->vtime)) { + /* + * Forget the whole idle tree, increasing the vtime past + * the last finish time of idle entities. + */ + st->vtime = last_idle->finish; + } + + if (first_idle != NULL && !bfq_gt(first_idle->finish, st->vtime)) + bfq_put_idle_entity(st, first_idle); +} + +static struct bfq_service_tree * +__bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, + struct bfq_entity *entity) +{ + struct bfq_service_tree *new_st = old_st; + + if (entity->ioprio_changed) { + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + unsigned short prev_weight, new_weight; + struct bfq_data *bfqd = NULL; + struct rb_root *root; +#ifdef CONFIG_CGROUP_BFQIO + struct bfq_sched_data *sd; + struct bfq_group *bfqg; +#endif + + if (bfqq != NULL) + bfqd = bfqq->bfqd; +#ifdef CONFIG_CGROUP_BFQIO + else { + sd = entity->my_sched_data; + bfqg = container_of(sd, struct bfq_group, sched_data); + BUG_ON(!bfqg); + bfqd = (struct bfq_data *)bfqg->bfqd; + BUG_ON(!bfqd); + } +#endif + + BUG_ON(old_st->wsum < entity->weight); + old_st->wsum -= entity->weight; + + if (entity->new_weight != entity->orig_weight) { + if (entity->new_weight < BFQ_MIN_WEIGHT || + entity->new_weight > BFQ_MAX_WEIGHT) { + printk(KERN_CRIT "update_weight_prio: " + "new_weight %d\n", + entity->new_weight); + BUG(); + } + entity->orig_weight = entity->new_weight; + entity->ioprio = + bfq_weight_to_ioprio(entity->orig_weight); + } + + entity->ioprio_class = entity->new_ioprio_class; + entity->ioprio_changed = 0; + + /* + * NOTE: here we may be changing the weight too early, + * this will cause unfairness. The correct approach + * would have required additional complexity to defer + * weight changes to the proper time instants (i.e., + * when entity->finish <= old_st->vtime). + */ + new_st = bfq_entity_service_tree(entity); + + prev_weight = entity->weight; + new_weight = entity->orig_weight * + (bfqq != NULL ? bfqq->wr_coeff : 1); + /* + * If the weight of the entity changes, remove the entity + * from its old weight counter (if there is a counter + * associated with the entity), and add it to the counter + * associated with its new weight. + */ + if (prev_weight != new_weight) { + root = bfqq ? &bfqd->queue_weights_tree : + &bfqd->group_weights_tree; + bfq_weights_tree_remove(bfqd, entity, root); + } + entity->weight = new_weight; + /* + * Add the entity to its weights tree only if it is + * not associated with a weight-raised queue. + */ + if (prev_weight != new_weight && + (bfqq ? bfqq->wr_coeff == 1 : 1)) + /* If we get here, root has been initialized. */ + bfq_weights_tree_add(bfqd, entity, root); + + new_st->wsum += entity->weight; + + if (new_st != old_st) + entity->start = new_st->vtime; + } + + return new_st; +} + +/** + * bfq_bfqq_served - update the scheduler status after selection for + * service. + * @bfqq: the queue being served. + * @served: bytes to transfer. + * + * NOTE: this can be optimized, as the timestamps of upper level entities + * are synchronized every time a new bfqq is selected for service. By now, + * we keep it to better check consistency. + */ +static void bfq_bfqq_served(struct bfq_queue *bfqq, unsigned long served) +{ + struct bfq_entity *entity = &bfqq->entity; + struct bfq_service_tree *st; + + for_each_entity(entity) { + st = bfq_entity_service_tree(entity); + + entity->service += served; + BUG_ON(entity->service > entity->budget); + BUG_ON(st->wsum == 0); + + st->vtime += bfq_delta(served, st->wsum); + bfq_forget_idle(st); + } + bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %lu secs", served); +} + +/** + * bfq_bfqq_charge_full_budget - set the service to the entity budget. + * @bfqq: the queue that needs a service update. + * + * When it's not possible to be fair in the service domain, because + * a queue is not consuming its budget fast enough (the meaning of + * fast depends on the timeout parameter), we charge it a full + * budget. In this way we should obtain a sort of time-domain + * fairness among all the seeky/slow queues. + */ +static inline void bfq_bfqq_charge_full_budget(struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "charge_full_budget"); + + bfq_bfqq_served(bfqq, entity->budget - entity->service); +} + +/** + * __bfq_activate_entity - activate an entity. + * @entity: the entity being activated. + * + * Called whenever an entity is activated, i.e., it is not active and one + * of its children receives a new request, or has to be reactivated due to + * budget exhaustion. It uses the current budget of the entity (and the + * service received if @entity is active) of the queue to calculate its + * timestamps. + */ +static void __bfq_activate_entity(struct bfq_entity *entity) +{ + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + + if (entity == sd->in_service_entity) { + BUG_ON(entity->tree != NULL); + /* + * If we are requeueing the current entity we have + * to take care of not charging to it service it has + * not received. + */ + bfq_calc_finish(entity, entity->service); + entity->start = entity->finish; + sd->in_service_entity = NULL; + } else if (entity->tree == &st->active) { + /* + * Requeueing an entity due to a change of some + * next_in_service entity below it. We reuse the + * old start time. + */ + bfq_active_extract(st, entity); + } else if (entity->tree == &st->idle) { + /* + * Must be on the idle tree, bfq_idle_extract() will + * check for that. + */ + bfq_idle_extract(st, entity); + entity->start = bfq_gt(st->vtime, entity->finish) ? + st->vtime : entity->finish; + } else { + /* + * The finish time of the entity may be invalid, and + * it is in the past for sure, otherwise the queue + * would have been on the idle tree. + */ + entity->start = st->vtime; + st->wsum += entity->weight; + bfq_get_entity(entity); + + BUG_ON(entity->on_st); + entity->on_st = 1; + } + + st = __bfq_entity_update_weight_prio(st, entity); + bfq_calc_finish(entity, entity->budget); + bfq_active_insert(st, entity); +} + +/** + * bfq_activate_entity - activate an entity and its ancestors if necessary. + * @entity: the entity to activate. + * + * Activate @entity and all the entities on the path from it to the root. + */ +static void bfq_activate_entity(struct bfq_entity *entity) +{ + struct bfq_sched_data *sd; + + for_each_entity(entity) { + __bfq_activate_entity(entity); + + sd = entity->sched_data; + if (!bfq_update_next_in_service(sd)) + /* + * No need to propagate the activation to the + * upper entities, as they will be updated when + * the in-service entity is rescheduled. + */ + break; + } +} + +/** + * __bfq_deactivate_entity - deactivate an entity from its service tree. + * @entity: the entity to deactivate. + * @requeue: if false, the entity will not be put into the idle tree. + * + * Deactivate an entity, independently from its previous state. If the + * entity was not on a service tree just return, otherwise if it is on + * any scheduler tree, extract it from that tree, and if necessary + * and if the caller did not specify @requeue, put it on the idle tree. + * + * Return %1 if the caller should update the entity hierarchy, i.e., + * if the entity was in service or if it was the next_in_service for + * its sched_data; return %0 otherwise. + */ +static int __bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + int was_in_service = entity == sd->in_service_entity; + int ret = 0; + + if (!entity->on_st) + return 0; + + BUG_ON(was_in_service && entity->tree != NULL); + + if (was_in_service) { + bfq_calc_finish(entity, entity->service); + sd->in_service_entity = NULL; + } else if (entity->tree == &st->active) + bfq_active_extract(st, entity); + else if (entity->tree == &st->idle) + bfq_idle_extract(st, entity); + else if (entity->tree != NULL) + BUG(); + + if (was_in_service || sd->next_in_service == entity) + ret = bfq_update_next_in_service(sd); + + if (!requeue || !bfq_gt(entity->finish, st->vtime)) + bfq_forget_entity(st, entity); + else + bfq_idle_insert(st, entity); + + BUG_ON(sd->in_service_entity == entity); + BUG_ON(sd->next_in_service == entity); + + return ret; +} + +/** + * bfq_deactivate_entity - deactivate an entity. + * @entity: the entity to deactivate. + * @requeue: true if the entity can be put on the idle tree + */ +static void bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd; + struct bfq_entity *parent; + + for_each_entity_safe(entity, parent) { + sd = entity->sched_data; + + if (!__bfq_deactivate_entity(entity, requeue)) + /* + * The parent entity is still backlogged, and + * we don't need to update it as it is still + * in service. + */ + break; + + if (sd->next_in_service != NULL) + /* + * The parent entity is still backlogged and + * the budgets on the path towards the root + * need to be updated. + */ + goto update; + + /* + * If we reach there the parent is no more backlogged and + * we want to propagate the dequeue upwards. + */ + requeue = 1; + } + + return; + +update: + entity = parent; + for_each_entity(entity) { + __bfq_activate_entity(entity); + + sd = entity->sched_data; + if (!bfq_update_next_in_service(sd)) + break; + } +} + +/** + * bfq_update_vtime - update vtime if necessary. + * @st: the service tree to act upon. + * + * If necessary update the service tree vtime to have at least one + * eligible entity, skipping to its start time. Assumes that the + * active tree of the device is not empty. + * + * NOTE: this hierarchical implementation updates vtimes quite often, + * we may end up with reactivated processes getting timestamps after a + * vtime skip done because we needed a ->first_active entity on some + * intermediate node. + */ +static void bfq_update_vtime(struct bfq_service_tree *st) +{ + struct bfq_entity *entry; + struct rb_node *node = st->active.rb_node; + + entry = rb_entry(node, struct bfq_entity, rb_node); + if (bfq_gt(entry->min_start, st->vtime)) { + st->vtime = entry->min_start; + bfq_forget_idle(st); + } +} + +/** + * bfq_first_active_entity - find the eligible entity with + * the smallest finish time + * @st: the service tree to select from. + * + * This function searches the first schedulable entity, starting from the + * root of the tree and going on the left every time on this side there is + * a subtree with at least one eligible (start >= vtime) entity. The path on + * the right is followed only if a) the left subtree contains no eligible + * entities and b) no eligible entity has been found yet. + */ +static struct bfq_entity *bfq_first_active_entity(struct bfq_service_tree *st) +{ + struct bfq_entity *entry, *first = NULL; + struct rb_node *node = st->active.rb_node; + + while (node != NULL) { + entry = rb_entry(node, struct bfq_entity, rb_node); +left: + if (!bfq_gt(entry->start, st->vtime)) + first = entry; + + BUG_ON(bfq_gt(entry->min_start, st->vtime)); + + if (node->rb_left != NULL) { + entry = rb_entry(node->rb_left, + struct bfq_entity, rb_node); + if (!bfq_gt(entry->min_start, st->vtime)) { + node = node->rb_left; + goto left; + } + } + if (first != NULL) + break; + node = node->rb_right; + } + + BUG_ON(first == NULL && !RB_EMPTY_ROOT(&st->active)); + return first; +} + +/** + * __bfq_lookup_next_entity - return the first eligible entity in @st. + * @st: the service tree. + * + * Update the virtual time in @st and return the first eligible entity + * it contains. + */ +static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st, + bool force) +{ + struct bfq_entity *entity, *new_next_in_service = NULL; + + if (RB_EMPTY_ROOT(&st->active)) + return NULL; + + bfq_update_vtime(st); + entity = bfq_first_active_entity(st); + BUG_ON(bfq_gt(entity->start, st->vtime)); + + /* + * If the chosen entity does not match with the sched_data's + * next_in_service and we are forcedly serving the IDLE priority + * class tree, bubble up budget update. + */ + if (unlikely(force && entity != entity->sched_data->next_in_service)) { + new_next_in_service = entity; + for_each_entity(new_next_in_service) + bfq_update_budget(new_next_in_service); + } + + return entity; +} + +/** + * bfq_lookup_next_entity - return the first eligible entity in @sd. + * @sd: the sched_data. + * @extract: if true the returned entity will be also extracted from @sd. + * + * NOTE: since we cache the next_in_service entity at each level of the + * hierarchy, the complexity of the lookup can be decreased with + * absolutely no effort just returning the cached next_in_service value; + * we prefer to do full lookups to test the consistency of * the data + * structures. + */ +static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract, + struct bfq_data *bfqd) +{ + struct bfq_service_tree *st = sd->service_tree; + struct bfq_entity *entity; + int i = 0; + + BUG_ON(sd->in_service_entity != NULL); + + if (bfqd != NULL && + jiffies - bfqd->bfq_class_idle_last_service > BFQ_CL_IDLE_TIMEOUT) { + entity = __bfq_lookup_next_entity(st + BFQ_IOPRIO_CLASSES - 1, + true); + if (entity != NULL) { + i = BFQ_IOPRIO_CLASSES - 1; + bfqd->bfq_class_idle_last_service = jiffies; + sd->next_in_service = entity; + } + } + for (; i < BFQ_IOPRIO_CLASSES; i++) { + entity = __bfq_lookup_next_entity(st + i, false); + if (entity != NULL) { + if (extract) { + bfq_check_next_in_service(sd, entity); + bfq_active_extract(st + i, entity); + sd->in_service_entity = entity; + sd->next_in_service = NULL; + } + break; + } + } + + return entity; +} + +/* + * Get next queue for service. + */ +static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) +{ + struct bfq_entity *entity = NULL; + struct bfq_sched_data *sd; + struct bfq_queue *bfqq; + + BUG_ON(bfqd->in_service_queue != NULL); + + if (bfqd->busy_queues == 0) + return NULL; + + sd = &bfqd->root_group->sched_data; + for (; sd != NULL; sd = entity->my_sched_data) { + entity = bfq_lookup_next_entity(sd, 1, bfqd); + BUG_ON(entity == NULL); + entity->service = 0; + } + + bfqq = bfq_entity_to_bfqq(entity); + BUG_ON(bfqq == NULL); + + return bfqq; +} + +/* + * Forced extraction of the given queue. + */ +static void bfq_get_next_queue_forced(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_entity *entity; + struct bfq_sched_data *sd; + + BUG_ON(bfqd->in_service_queue != NULL); + + entity = &bfqq->entity; + /* + * Bubble up extraction/update from the leaf to the root. + */ + for_each_entity(entity) { + sd = entity->sched_data; + bfq_update_budget(entity); + bfq_update_vtime(bfq_entity_service_tree(entity)); + bfq_active_extract(bfq_entity_service_tree(entity), entity); + sd->in_service_entity = entity; + sd->next_in_service = NULL; + entity->service = 0; + } + + return; +} + +static void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd) +{ + if (bfqd->in_service_bic != NULL) { + put_io_context(bfqd->in_service_bic->icq.ioc); + bfqd->in_service_bic = NULL; + } + + bfqd->in_service_queue = NULL; + del_timer(&bfqd->idle_slice_timer); +} + +static void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int requeue) +{ + struct bfq_entity *entity = &bfqq->entity; + + if (bfqq == bfqd->in_service_queue) + __bfq_bfqd_reset_in_service(bfqd); + + bfq_deactivate_entity(entity, requeue); +} + +static void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + + bfq_activate_entity(entity); +} + +/* + * Called when the bfqq no longer has requests pending, remove it from + * the service tree. + */ +static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int requeue) +{ + BUG_ON(!bfq_bfqq_busy(bfqq)); + BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); + + bfq_log_bfqq(bfqd, bfqq, "del from busy"); + + bfq_clear_bfqq_busy(bfqq); + + BUG_ON(bfqd->busy_queues == 0); + bfqd->busy_queues--; + + if (!bfqq->dispatched) { + bfq_weights_tree_remove(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); + if (!blk_queue_nonrot(bfqd->queue)) { + BUG_ON(!bfqd->busy_in_flight_queues); + bfqd->busy_in_flight_queues--; + if (bfq_bfqq_constantly_seeky(bfqq)) { + BUG_ON(!bfqd-> + const_seeky_busy_in_flight_queues); + bfqd->const_seeky_busy_in_flight_queues--; + } + } + } + if (bfqq->wr_coeff > 1) + bfqd->wr_busy_queues--; + + bfq_deactivate_bfqq(bfqd, bfqq, requeue); +} + +/* + * Called when an inactive queue receives a new request. + */ +static void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + BUG_ON(bfq_bfqq_busy(bfqq)); + BUG_ON(bfqq == bfqd->in_service_queue); + + bfq_log_bfqq(bfqd, bfqq, "add to busy"); + + bfq_activate_bfqq(bfqd, bfqq); + + bfq_mark_bfqq_busy(bfqq); + bfqd->busy_queues++; + + if (!bfqq->dispatched) { + if (bfqq->wr_coeff == 1) + bfq_weights_tree_add(bfqd, &bfqq->entity, + &bfqd->queue_weights_tree); + if (!blk_queue_nonrot(bfqd->queue)) { + bfqd->busy_in_flight_queues++; + if (bfq_bfqq_constantly_seeky(bfqq)) + bfqd->const_seeky_busy_in_flight_queues++; + } + } + if (bfqq->wr_coeff > 1) + bfqd->wr_busy_queues++; +} diff --git a/block/bfq.h b/block/bfq.h new file mode 100644 index 00000000000000..7aab979ee195c5 --- /dev/null +++ b/block/bfq.h @@ -0,0 +1,771 @@ +/* + * BFQ-v7r8 for 3.14.0: data structures and common functions prototypes. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Copyright (C) 2010 Paolo Valente + */ + +#ifndef _BFQ_H +#define _BFQ_H + +#include +#include +#include +#include + +#define BFQ_IOPRIO_CLASSES 3 +#define BFQ_CL_IDLE_TIMEOUT (HZ/5) + +#define BFQ_MIN_WEIGHT 1 +#define BFQ_MAX_WEIGHT 1000 + +#define BFQ_DEFAULT_QUEUE_IOPRIO 4 + +#define BFQ_DEFAULT_GRP_WEIGHT 10 +#define BFQ_DEFAULT_GRP_IOPRIO 0 +#define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE + +struct bfq_entity; + +/** + * struct bfq_service_tree - per ioprio_class service tree. + * @active: tree for active entities (i.e., those backlogged). + * @idle: tree for idle entities (i.e., those not backlogged, with V <= F_i). + * @first_idle: idle entity with minimum F_i. + * @last_idle: idle entity with maximum F_i. + * @vtime: scheduler virtual time. + * @wsum: scheduler weight sum; active and idle entities contribute to it. + * + * Each service tree represents a B-WF2Q+ scheduler on its own. Each + * ioprio_class has its own independent scheduler, and so its own + * bfq_service_tree. All the fields are protected by the queue lock + * of the containing bfqd. + */ +struct bfq_service_tree { + struct rb_root active; + struct rb_root idle; + + struct bfq_entity *first_idle; + struct bfq_entity *last_idle; + + u64 vtime; + unsigned long wsum; +}; + +/** + * struct bfq_sched_data - multi-class scheduler. + * @in_service_entity: entity in service. + * @next_in_service: head-of-the-line entity in the scheduler. + * @service_tree: array of service trees, one per ioprio_class. + * + * bfq_sched_data is the basic scheduler queue. It supports three + * ioprio_classes, and can be used either as a toplevel queue or as + * an intermediate queue on a hierarchical setup. + * @next_in_service points to the active entity of the sched_data + * service trees that will be scheduled next. + * + * The supported ioprio_classes are the same as in CFQ, in descending + * priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE. + * Requests from higher priority queues are served before all the + * requests from lower priority queues; among requests of the same + * queue requests are served according to B-WF2Q+. + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_sched_data { + struct bfq_entity *in_service_entity; + struct bfq_entity *next_in_service; + struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES]; +}; + +/** + * struct bfq_weight_counter - counter of the number of all active entities + * with a given weight. + * @weight: weight of the entities that this counter refers to. + * @num_active: number of active entities with this weight. + * @weights_node: weights tree member (see bfq_data's @queue_weights_tree + * and @group_weights_tree). + */ +struct bfq_weight_counter { + short int weight; + unsigned int num_active; + struct rb_node weights_node; +}; + +/** + * struct bfq_entity - schedulable entity. + * @rb_node: service_tree member. + * @weight_counter: pointer to the weight counter associated with this entity. + * @on_st: flag, true if the entity is on a tree (either the active or + * the idle one of its service_tree). + * @finish: B-WF2Q+ finish timestamp (aka F_i). + * @start: B-WF2Q+ start timestamp (aka S_i). + * @tree: tree the entity is enqueued into; %NULL if not on a tree. + * @min_start: minimum start time of the (active) subtree rooted at + * this entity; used for O(log N) lookups into active trees. + * @service: service received during the last round of service. + * @budget: budget used to calculate F_i; F_i = S_i + @budget / @weight. + * @weight: weight of the queue + * @parent: parent entity, for hierarchical scheduling. + * @my_sched_data: for non-leaf nodes in the cgroup hierarchy, the + * associated scheduler queue, %NULL on leaf nodes. + * @sched_data: the scheduler queue this entity belongs to. + * @ioprio: the ioprio in use. + * @new_weight: when a weight change is requested, the new weight value. + * @orig_weight: original weight, used to implement weight boosting + * @new_ioprio: when an ioprio change is requested, the new ioprio value. + * @ioprio_class: the ioprio_class in use. + * @new_ioprio_class: when an ioprio_class change is requested, the new + * ioprio_class value. + * @ioprio_changed: flag, true when the user requested a weight, ioprio or + * ioprio_class change. + * + * A bfq_entity is used to represent either a bfq_queue (leaf node in the + * cgroup hierarchy) or a bfq_group into the upper level scheduler. Each + * entity belongs to the sched_data of the parent group in the cgroup + * hierarchy. Non-leaf entities have also their own sched_data, stored + * in @my_sched_data. + * + * Each entity stores independently its priority values; this would + * allow different weights on different devices, but this + * functionality is not exported to userspace by now. Priorities and + * weights are updated lazily, first storing the new values into the + * new_* fields, then setting the @ioprio_changed flag. As soon as + * there is a transition in the entity state that allows the priority + * update to take place the effective and the requested priority + * values are synchronized. + * + * Unless cgroups are used, the weight value is calculated from the + * ioprio to export the same interface as CFQ. When dealing with + * ``well-behaved'' queues (i.e., queues that do not spend too much + * time to consume their budget and have true sequential behavior, and + * when there are no external factors breaking anticipation) the + * relative weights at each level of the cgroups hierarchy should be + * guaranteed. All the fields are protected by the queue lock of the + * containing bfqd. + */ +struct bfq_entity { + struct rb_node rb_node; + struct bfq_weight_counter *weight_counter; + + int on_st; + + u64 finish; + u64 start; + + struct rb_root *tree; + + u64 min_start; + + unsigned long service, budget; + unsigned short weight, new_weight; + unsigned short orig_weight; + + struct bfq_entity *parent; + + struct bfq_sched_data *my_sched_data; + struct bfq_sched_data *sched_data; + + unsigned short ioprio, new_ioprio; + unsigned short ioprio_class, new_ioprio_class; + + int ioprio_changed; +}; + +struct bfq_group; + +/** + * struct bfq_queue - leaf schedulable entity. + * @ref: reference counter. + * @bfqd: parent bfq_data. + * @new_bfqq: shared bfq_queue if queue is cooperating with + * one or more other queues. + * @pos_node: request-position tree member (see bfq_data's @rq_pos_tree). + * @pos_root: request-position tree root (see bfq_data's @rq_pos_tree). + * @sort_list: sorted list of pending requests. + * @next_rq: if fifo isn't expired, next request to serve. + * @queued: nr of requests queued in @sort_list. + * @allocated: currently allocated requests. + * @meta_pending: pending metadata requests. + * @fifo: fifo list of requests in sort_list. + * @entity: entity representing this queue in the scheduler. + * @max_budget: maximum budget allowed from the feedback mechanism. + * @budget_timeout: budget expiration (in jiffies). + * @dispatched: number of requests on the dispatch list or inside driver. + * @flags: status flags. + * @bfqq_list: node for active/idle bfqq list inside our bfqd. + * @burst_list_node: node for the device's burst list. + * @seek_samples: number of seeks sampled + * @seek_total: sum of the distances of the seeks sampled + * @seek_mean: mean seek distance + * @last_request_pos: position of the last request enqueued + * @requests_within_timer: number of consecutive pairs of request completion + * and arrival, such that the queue becomes idle + * after the completion, but the next request arrives + * within an idle time slice; used only if the queue's + * IO_bound has been cleared. + * @pid: pid of the process owning the queue, used for logging purposes. + * @last_wr_start_finish: start time of the current weight-raising period if + * the @bfq-queue is being weight-raised, otherwise + * finish time of the last weight-raising period + * @wr_cur_max_time: current max raising time for this queue + * @soft_rt_next_start: minimum time instant such that, only if a new + * request is enqueued after this time instant in an + * idle @bfq_queue with no outstanding requests, then + * the task associated with the queue it is deemed as + * soft real-time (see the comments to the function + * bfq_bfqq_softrt_next_start()). + * @last_idle_bklogged: time of the last transition of the @bfq_queue from + * idle to backlogged + * @service_from_backlogged: cumulative service received from the @bfq_queue + * since the last transition from idle to + * backlogged + * + * A bfq_queue is a leaf request queue; it can be associated with an io_context + * or more, if it is async or shared between cooperating processes. @cgroup + * holds a reference to the cgroup, to be sure that it does not disappear while + * a bfqq still references it (mostly to avoid races between request issuing and + * task migration followed by cgroup destruction). + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_queue { + atomic_t ref; + struct bfq_data *bfqd; + + /* fields for cooperating queues handling */ + struct bfq_queue *new_bfqq; + struct rb_node pos_node; + struct rb_root *pos_root; + + struct rb_root sort_list; + struct request *next_rq; + int queued[2]; + int allocated[2]; + int meta_pending; + struct list_head fifo; + + struct bfq_entity entity; + + unsigned long max_budget; + unsigned long budget_timeout; + + int dispatched; + + unsigned int flags; + + struct list_head bfqq_list; + + struct hlist_node burst_list_node; + + unsigned int seek_samples; + u64 seek_total; + sector_t seek_mean; + sector_t last_request_pos; + + unsigned int requests_within_timer; + + pid_t pid; + + /* weight-raising fields */ + unsigned long wr_cur_max_time; + unsigned long soft_rt_next_start; + unsigned long last_wr_start_finish; + unsigned int wr_coeff; + unsigned long last_idle_bklogged; + unsigned long service_from_backlogged; +}; + +/** + * struct bfq_ttime - per process thinktime stats. + * @ttime_total: total process thinktime + * @ttime_samples: number of thinktime samples + * @ttime_mean: average process thinktime + */ +struct bfq_ttime { + unsigned long last_end_request; + + unsigned long ttime_total; + unsigned long ttime_samples; + unsigned long ttime_mean; +}; + +/** + * struct bfq_io_cq - per (request_queue, io_context) structure. + * @icq: associated io_cq structure + * @bfqq: array of two process queues, the sync and the async + * @ttime: associated @bfq_ttime struct + */ +struct bfq_io_cq { + struct io_cq icq; /* must be the first member */ + struct bfq_queue *bfqq[2]; + struct bfq_ttime ttime; + int ioprio; +}; + +enum bfq_device_speed { + BFQ_BFQD_FAST, + BFQ_BFQD_SLOW, +}; + +/** + * struct bfq_data - per device data structure. + * @queue: request queue for the managed device. + * @root_group: root bfq_group for the device. + * @rq_pos_tree: rbtree sorted by next_request position, used when + * determining if two or more queues have interleaving + * requests (see bfq_close_cooperator()). + * @active_numerous_groups: number of bfq_groups containing more than one + * active @bfq_entity. + * @queue_weights_tree: rbtree of weight counters of @bfq_queues, sorted by + * weight. Used to keep track of whether all @bfq_queues + * have the same weight. The tree contains one counter + * for each distinct weight associated to some active + * and not weight-raised @bfq_queue (see the comments to + * the functions bfq_weights_tree_[add|remove] for + * further details). + * @group_weights_tree: rbtree of non-queue @bfq_entity weight counters, sorted + * by weight. Used to keep track of whether all + * @bfq_groups have the same weight. The tree contains + * one counter for each distinct weight associated to + * some active @bfq_group (see the comments to the + * functions bfq_weights_tree_[add|remove] for further + * details). + * @busy_queues: number of bfq_queues containing requests (including the + * queue in service, even if it is idling). + * @busy_in_flight_queues: number of @bfq_queues containing pending or + * in-flight requests, plus the @bfq_queue in + * service, even if idle but waiting for the + * possible arrival of its next sync request. This + * field is updated only if the device is rotational, + * but used only if the device is also NCQ-capable. + * The reason why the field is updated also for non- + * NCQ-capable rotational devices is related to the + * fact that the value of @hw_tag may be set also + * later than when busy_in_flight_queues may need to + * be incremented for the first time(s). Taking also + * this possibility into account, to avoid unbalanced + * increments/decrements, would imply more overhead + * than just updating busy_in_flight_queues + * regardless of the value of @hw_tag. + * @const_seeky_busy_in_flight_queues: number of constantly-seeky @bfq_queues + * (that is, seeky queues that expired + * for budget timeout at least once) + * containing pending or in-flight + * requests, including the in-service + * @bfq_queue if constantly seeky. This + * field is updated only if the device + * is rotational, but used only if the + * device is also NCQ-capable (see the + * comments to @busy_in_flight_queues). + * @wr_busy_queues: number of weight-raised busy @bfq_queues. + * @queued: number of queued requests. + * @rq_in_driver: number of requests dispatched and waiting for completion. + * @sync_flight: number of sync requests in the driver. + * @max_rq_in_driver: max number of reqs in driver in the last + * @hw_tag_samples completed requests. + * @hw_tag_samples: nr of samples used to calculate hw_tag. + * @hw_tag: flag set to one if the driver is showing a queueing behavior. + * @budgets_assigned: number of budgets assigned. + * @idle_slice_timer: timer set when idling for the next sequential request + * from the queue in service. + * @unplug_work: delayed work to restart dispatching on the request queue. + * @in_service_queue: bfq_queue in service. + * @in_service_bic: bfq_io_cq (bic) associated with the @in_service_queue. + * @last_position: on-disk position of the last served request. + * @last_budget_start: beginning of the last budget. + * @last_idling_start: beginning of the last idle slice. + * @peak_rate: peak transfer rate observed for a budget. + * @peak_rate_samples: number of samples used to calculate @peak_rate. + * @bfq_max_budget: maximum budget allotted to a bfq_queue before + * rescheduling. + * @group_list: list of all the bfq_groups active on the device. + * @active_list: list of all the bfq_queues active on the device. + * @idle_list: list of all the bfq_queues idle on the device. + * @bfq_fifo_expire: timeout for async/sync requests; when it expires + * requests are served in fifo order. + * @bfq_back_penalty: weight of backward seeks wrt forward ones. + * @bfq_back_max: maximum allowed backward seek. + * @bfq_slice_idle: maximum idling time. + * @bfq_user_max_budget: user-configured max budget value + * (0 for auto-tuning). + * @bfq_max_budget_async_rq: maximum budget (in nr of requests) allotted to + * async queues. + * @bfq_timeout: timeout for bfq_queues to consume their budget; used to + * to prevent seeky queues to impose long latencies to well + * behaved ones (this also implies that seeky queues cannot + * receive guarantees in the service domain; after a timeout + * they are charged for the whole allocated budget, to try + * to preserve a behavior reasonably fair among them, but + * without service-domain guarantees). + * @bfq_coop_thresh: number of queue merges after which a @bfq_queue is + * no more granted any weight-raising. + * @bfq_failed_cooperations: number of consecutive failed cooperation + * chances after which weight-raising is restored + * to a queue subject to more than bfq_coop_thresh + * queue merges. + * @bfq_requests_within_timer: number of consecutive requests that must be + * issued within the idle time slice to set + * again idling to a queue which was marked as + * non-I/O-bound (see the definition of the + * IO_bound flag for further details). + * @last_ins_in_burst: last time at which a queue entered the current + * burst of queues being activated shortly after + * each other; for more details about this and the + * following parameters related to a burst of + * activations, see the comments to the function + * @bfq_handle_burst. + * @bfq_burst_interval: reference time interval used to decide whether a + * queue has been activated shortly after + * @last_ins_in_burst. + * @burst_size: number of queues in the current burst of queue activations. + * @bfq_large_burst_thresh: maximum burst size above which the current + * queue-activation burst is deemed as 'large'. + * @large_burst: true if a large queue-activation burst is in progress. + * @burst_list: head of the burst list (as for the above fields, more details + * in the comments to the function bfq_handle_burst). + * @low_latency: if set to true, low-latency heuristics are enabled. + * @bfq_wr_coeff: maximum factor by which the weight of a weight-raised + * queue is multiplied. + * @bfq_wr_max_time: maximum duration of a weight-raising period (jiffies). + * @bfq_wr_rt_max_time: maximum duration for soft real-time processes. + * @bfq_wr_min_idle_time: minimum idle period after which weight-raising + * may be reactivated for a queue (in jiffies). + * @bfq_wr_min_inter_arr_async: minimum period between request arrivals + * after which weight-raising may be + * reactivated for an already busy queue + * (in jiffies). + * @bfq_wr_max_softrt_rate: max service-rate for a soft real-time queue, + * sectors per seconds. + * @RT_prod: cached value of the product R*T used for computing the maximum + * duration of the weight raising automatically. + * @device_speed: device-speed class for the low-latency heuristic. + * @oom_bfqq: fallback dummy bfqq for extreme OOM conditions. + * + * All the fields are protected by the @queue lock. + */ +struct bfq_data { + struct request_queue *queue; + + struct bfq_group *root_group; + struct rb_root rq_pos_tree; + +#ifdef CONFIG_CGROUP_BFQIO + int active_numerous_groups; +#endif + + struct rb_root queue_weights_tree; + struct rb_root group_weights_tree; + + int busy_queues; + int busy_in_flight_queues; + int const_seeky_busy_in_flight_queues; + int wr_busy_queues; + int queued; + int rq_in_driver; + int sync_flight; + + int max_rq_in_driver; + int hw_tag_samples; + int hw_tag; + + int budgets_assigned; + + struct timer_list idle_slice_timer; + struct work_struct unplug_work; + + struct bfq_queue *in_service_queue; + struct bfq_io_cq *in_service_bic; + + sector_t last_position; + + ktime_t last_budget_start; + ktime_t last_idling_start; + int peak_rate_samples; + u64 peak_rate; + unsigned long bfq_max_budget; + + struct hlist_head group_list; + struct list_head active_list; + struct list_head idle_list; + + unsigned int bfq_fifo_expire[2]; + unsigned int bfq_back_penalty; + unsigned int bfq_back_max; + unsigned int bfq_slice_idle; + u64 bfq_class_idle_last_service; + + unsigned int bfq_user_max_budget; + unsigned int bfq_max_budget_async_rq; + unsigned int bfq_timeout[2]; + + unsigned int bfq_coop_thresh; + unsigned int bfq_failed_cooperations; + unsigned int bfq_requests_within_timer; + + unsigned long last_ins_in_burst; + unsigned long bfq_burst_interval; + int burst_size; + unsigned long bfq_large_burst_thresh; + bool large_burst; + struct hlist_head burst_list; + + bool low_latency; + + /* parameters of the low_latency heuristics */ + unsigned int bfq_wr_coeff; + unsigned int bfq_wr_max_time; + unsigned int bfq_wr_rt_max_time; + unsigned int bfq_wr_min_idle_time; + unsigned long bfq_wr_min_inter_arr_async; + unsigned int bfq_wr_max_softrt_rate; + u64 RT_prod; + enum bfq_device_speed device_speed; + + struct bfq_queue oom_bfqq; +}; + +enum bfqq_state_flags { + BFQ_BFQQ_FLAG_busy = 0, /* has requests or is in service */ + BFQ_BFQQ_FLAG_wait_request, /* waiting for a request */ + BFQ_BFQQ_FLAG_must_alloc, /* must be allowed rq alloc */ + BFQ_BFQQ_FLAG_fifo_expire, /* FIFO checked in this slice */ + BFQ_BFQQ_FLAG_idle_window, /* slice idling enabled */ + BFQ_BFQQ_FLAG_sync, /* synchronous queue */ + BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ + BFQ_BFQQ_FLAG_IO_bound, /* + * bfqq has timed-out at least once + * having consumed at most 2/10 of + * its budget + */ + BFQ_BFQQ_FLAG_in_large_burst, /* + * bfqq activated in a large burst, + * see comments to bfq_handle_burst. + */ + BFQ_BFQQ_FLAG_constantly_seeky, /* + * bfqq has proved to be slow and + * seeky until budget timeout + */ + BFQ_BFQQ_FLAG_softrt_update, /* + * may need softrt-next-start + * update + */ + BFQ_BFQQ_FLAG_coop, /* bfqq is shared */ + BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be splitted */ +}; + +#define BFQ_BFQQ_FNS(name) \ +static inline void bfq_mark_bfqq_##name(struct bfq_queue *bfqq) \ +{ \ + (bfqq)->flags |= (1 << BFQ_BFQQ_FLAG_##name); \ +} \ +static inline void bfq_clear_bfqq_##name(struct bfq_queue *bfqq) \ +{ \ + (bfqq)->flags &= ~(1 << BFQ_BFQQ_FLAG_##name); \ +} \ +static inline int bfq_bfqq_##name(const struct bfq_queue *bfqq) \ +{ \ + return ((bfqq)->flags & (1 << BFQ_BFQQ_FLAG_##name)) != 0; \ +} + +BFQ_BFQQ_FNS(busy); +BFQ_BFQQ_FNS(wait_request); +BFQ_BFQQ_FNS(must_alloc); +BFQ_BFQQ_FNS(fifo_expire); +BFQ_BFQQ_FNS(idle_window); +BFQ_BFQQ_FNS(sync); +BFQ_BFQQ_FNS(budget_new); +BFQ_BFQQ_FNS(IO_bound); +BFQ_BFQQ_FNS(in_large_burst); +BFQ_BFQQ_FNS(constantly_seeky); +BFQ_BFQQ_FNS(coop); +BFQ_BFQQ_FNS(split_coop); +BFQ_BFQQ_FNS(softrt_update); +#undef BFQ_BFQQ_FNS + +/* Logging facilities. */ +#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq%d " fmt, (bfqq)->pid, ##args) + +#define bfq_log(bfqd, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args) + +/* Expiration reasons. */ +enum bfqq_expiration { + BFQ_BFQQ_TOO_IDLE = 0, /* + * queue has been idling for + * too long + */ + BFQ_BFQQ_BUDGET_TIMEOUT, /* budget took too long to be used */ + BFQ_BFQQ_BUDGET_EXHAUSTED, /* budget consumed */ + BFQ_BFQQ_NO_MORE_REQUESTS, /* the queue has no more requests */ +}; + +#ifdef CONFIG_CGROUP_BFQIO +/** + * struct bfq_group - per (device, cgroup) data structure. + * @entity: schedulable entity to insert into the parent group sched_data. + * @sched_data: own sched_data, to contain child entities (they may be + * both bfq_queues and bfq_groups). + * @group_node: node to be inserted into the bfqio_cgroup->group_data + * list of the containing cgroup's bfqio_cgroup. + * @bfqd_node: node to be inserted into the @bfqd->group_list list + * of the groups active on the same device; used for cleanup. + * @bfqd: the bfq_data for the device this group acts upon. + * @async_bfqq: array of async queues for all the tasks belonging to + * the group, one queue per ioprio value per ioprio_class, + * except for the idle class that has only one queue. + * @async_idle_bfqq: async queue for the idle class (ioprio is ignored). + * @my_entity: pointer to @entity, %NULL for the toplevel group; used + * to avoid too many special cases during group creation/ + * migration. + * @active_entities: number of active entities belonging to the group; + * unused for the root group. Used to know whether there + * are groups with more than one active @bfq_entity + * (see the comments to the function + * bfq_bfqq_must_not_expire()). + * + * Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup + * there is a set of bfq_groups, each one collecting the lower-level + * entities belonging to the group that are acting on the same device. + * + * Locking works as follows: + * o @group_node is protected by the bfqio_cgroup lock, and is accessed + * via RCU from its readers. + * o @bfqd is protected by the queue lock, RCU is used to access it + * from the readers. + * o All the other fields are protected by the @bfqd queue lock. + */ +struct bfq_group { + struct bfq_entity entity; + struct bfq_sched_data sched_data; + + struct hlist_node group_node; + struct hlist_node bfqd_node; + + void *bfqd; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; + struct bfq_queue *async_idle_bfqq; + + struct bfq_entity *my_entity; + + int active_entities; +}; + +/** + * struct bfqio_cgroup - bfq cgroup data structure. + * @css: subsystem state for bfq in the containing cgroup. + * @online: flag marked when the subsystem is inserted. + * @weight: cgroup weight. + * @ioprio: cgroup ioprio. + * @ioprio_class: cgroup ioprio_class. + * @lock: spinlock that protects @ioprio, @ioprio_class and @group_data. + * @group_data: list containing the bfq_group belonging to this cgroup. + * + * @group_data is accessed using RCU, with @lock protecting the updates, + * @ioprio and @ioprio_class are protected by @lock. + */ +struct bfqio_cgroup { + struct cgroup_subsys_state css; + bool online; + + unsigned short weight, ioprio, ioprio_class; + + spinlock_t lock; + struct hlist_head group_data; +}; +#else +struct bfq_group { + struct bfq_sched_data sched_data; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; + struct bfq_queue *async_idle_bfqq; +}; +#endif + +static inline struct bfq_service_tree * +bfq_entity_service_tree(struct bfq_entity *entity) +{ + struct bfq_sched_data *sched_data = entity->sched_data; + unsigned int idx = entity->ioprio_class - 1; + + BUG_ON(idx >= BFQ_IOPRIO_CLASSES); + BUG_ON(sched_data == NULL); + + return sched_data->service_tree + idx; +} + +static inline struct bfq_queue *bic_to_bfqq(struct bfq_io_cq *bic, + bool is_sync) +{ + return bic->bfqq[is_sync]; +} + +static inline void bic_set_bfqq(struct bfq_io_cq *bic, + struct bfq_queue *bfqq, bool is_sync) +{ + bic->bfqq[is_sync] = bfqq; +} + +static inline struct bfq_data *bic_to_bfqd(struct bfq_io_cq *bic) +{ + return bic->icq.q->elevator->elevator_data; +} + +/** + * bfq_get_bfqd_locked - get a lock to a bfqd using a RCU protected pointer. + * @ptr: a pointer to a bfqd. + * @flags: storage for the flags to be saved. + * + * This function allows bfqg->bfqd to be protected by the + * queue lock of the bfqd they reference; the pointer is dereferenced + * under RCU, so the storage for bfqd is assured to be safe as long + * as the RCU read side critical section does not end. After the + * bfqd->queue->queue_lock is taken the pointer is rechecked, to be + * sure that no other writer accessed it. If we raced with a writer, + * the function returns NULL, with the queue unlocked, otherwise it + * returns the dereferenced pointer, with the queue locked. + */ +static inline struct bfq_data *bfq_get_bfqd_locked(void **ptr, + unsigned long *flags) +{ + struct bfq_data *bfqd; + + rcu_read_lock(); + bfqd = rcu_dereference(*(struct bfq_data **)ptr); + + if (bfqd != NULL) { + spin_lock_irqsave(bfqd->queue->queue_lock, *flags); + if (*ptr == bfqd) + goto out; + spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); + } + + bfqd = NULL; +out: + rcu_read_unlock(); + return bfqd; +} + +static inline void bfq_put_bfqd_unlock(struct bfq_data *bfqd, + unsigned long *flags) +{ + spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); +} + +static void bfq_check_ioprio_change(struct bfq_io_cq *bic); +static void bfq_put_queue(struct bfq_queue *bfqq); +static void bfq_dispatch_insert(struct request_queue *q, struct request *rq); +static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, int is_sync, + struct bfq_io_cq *bic, gfp_t gfp_mask); +static void bfq_end_wr_async_queues(struct bfq_data *bfqd, + struct bfq_group *bfqg); +static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg); +static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq); + +#endif /* _BFQ_H */ From 282924cc0b02e533ae2678f8b3ec088457cb11ca Mon Sep 17 00:00:00 2001 From: Mauro Andreolini Date: Sat, 6 Jun 2015 23:00:27 +0200 Subject: [PATCH 0316/1983] block, bfq: add Early Queue Merge (EQM) to BFQ-v7r8 for 3.14.0 A set of processes may happen to perform interleaved reads, i.e.,requests whose union would give rise to a sequential read pattern. There are two typical cases: in the first case, processes read fixed-size chunks of data at a fixed distance from each other, while in the second case processes may read variable-size chunks at variable distances. The latter case occurs for example with QEMU, which splits the I/O generated by the guest into multiple chunks, and lets these chunks be served by a pool of cooperating processes, iteratively assigning the next chunk of I/O to the first available process. CFQ uses actual queue merging for the first type of rocesses, whereas it uses preemption to get a sequential read pattern out of the read requests performed by the second type of processes. In the end it uses two different mechanisms to achieve the same goal: boosting the throughput with interleaved I/O. This patch introduces Early Queue Merge (EQM), a unified mechanism to get a sequential read pattern with both types of processes. The main idea is checking newly arrived requests against the next request of the active queue both in case of actual request insert and in case of request merge. By doing so, both the types of processes can be handled by just merging their queues. EQM is then simpler and more compact than the pair of mechanisms used in CFQ. Finally, EQM also preserves the typical low-latency properties of BFQ, by properly restoring the weight-raising state of a queue when it gets back to a non-merged state. Signed-off-by: Mauro Andreolini Signed-off-by: Arianna Avanzini Signed-off-by: Paolo Valente --- block/bfq-iosched.c | 750 +++++++++++++++++++++++++++++++------------- block/bfq-sched.c | 28 -- block/bfq.h | 54 +++- 3 files changed, 580 insertions(+), 252 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 5b675b226f6466..f2082e46f3d2f4 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -573,6 +573,57 @@ static inline unsigned int bfq_wr_duration(struct bfq_data *bfqd) return dur; } +static inline unsigned +bfq_bfqq_cooperations(struct bfq_queue *bfqq) +{ + return bfqq->bic ? bfqq->bic->cooperations : 0; +} + +static inline void +bfq_bfqq_resume_state(struct bfq_queue *bfqq, struct bfq_io_cq *bic) +{ + if (bic->saved_idle_window) + bfq_mark_bfqq_idle_window(bfqq); + else + bfq_clear_bfqq_idle_window(bfqq); + if (bic->saved_IO_bound) + bfq_mark_bfqq_IO_bound(bfqq); + else + bfq_clear_bfqq_IO_bound(bfqq); + /* Assuming that the flag in_large_burst is already correctly set */ + if (bic->wr_time_left && bfqq->bfqd->low_latency && + !bfq_bfqq_in_large_burst(bfqq) && + bic->cooperations < bfqq->bfqd->bfq_coop_thresh) { + /* + * Start a weight raising period with the duration given by + * the raising_time_left snapshot. + */ + if (bfq_bfqq_busy(bfqq)) + bfqq->bfqd->wr_busy_queues++; + bfqq->wr_coeff = bfqq->bfqd->bfq_wr_coeff; + bfqq->wr_cur_max_time = bic->wr_time_left; + bfqq->last_wr_start_finish = jiffies; + bfqq->entity.ioprio_changed = 1; + } + /* + * Clear wr_time_left to prevent bfq_bfqq_save_state() from + * getting confused about the queue's need of a weight-raising + * period. + */ + bic->wr_time_left = 0; +} + +/* Must be called with the queue_lock held. */ +static int bfqq_process_refs(struct bfq_queue *bfqq) +{ + int process_refs, io_refs; + + io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; + process_refs = atomic_read(&bfqq->ref) - io_refs - bfqq->entity.on_st; + BUG_ON(process_refs < 0); + return process_refs; +} + /* Empty burst list and add just bfqq (see comments to bfq_handle_burst) */ static inline void bfq_reset_burst_list(struct bfq_data *bfqd, struct bfq_queue *bfqq) @@ -817,7 +868,7 @@ static void bfq_add_request(struct request *rq) bfq_rq_pos_tree_add(bfqd, bfqq); if (!bfq_bfqq_busy(bfqq)) { - bool soft_rt, + bool soft_rt, coop_or_in_burst, idle_for_long_time = time_is_before_jiffies( bfqq->budget_timeout + bfqd->bfq_wr_min_idle_time); @@ -841,11 +892,12 @@ static void bfq_add_request(struct request *rq) bfqd->last_ins_in_burst = jiffies; } + coop_or_in_burst = bfq_bfqq_in_large_burst(bfqq) || + bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh; soft_rt = bfqd->bfq_wr_max_softrt_rate > 0 && - !bfq_bfqq_in_large_burst(bfqq) && + !coop_or_in_burst && time_is_before_jiffies(bfqq->soft_rt_next_start); - interactive = !bfq_bfqq_in_large_burst(bfqq) && - idle_for_long_time; + interactive = !coop_or_in_burst && idle_for_long_time; entity->budget = max_t(unsigned long, bfqq->max_budget, bfq_serv_to_charge(next_rq, bfqq)); @@ -864,11 +916,20 @@ static void bfq_add_request(struct request *rq) if (!bfqd->low_latency) goto add_bfqq_busy; + if (bfq_bfqq_just_split(bfqq)) + goto set_ioprio_changed; + /* - * If the queue is not being boosted and has been idle - * for enough time, start a weight-raising period + * If the queue: + * - is not being boosted, + * - has been idle for enough time, + * - is not a sync queue or is linked to a bfq_io_cq (it is + * shared "for its nature" or it is not shared and its + * requests have not been redirected to a shared queue) + * start a weight-raising period. */ - if (old_wr_coeff == 1 && (interactive || soft_rt)) { + if (old_wr_coeff == 1 && (interactive || soft_rt) && + (!bfq_bfqq_sync(bfqq) || bfqq->bic != NULL)) { bfqq->wr_coeff = bfqd->bfq_wr_coeff; if (interactive) bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); @@ -882,7 +943,7 @@ static void bfq_add_request(struct request *rq) } else if (old_wr_coeff > 1) { if (interactive) bfqq->wr_cur_max_time = bfq_wr_duration(bfqd); - else if (bfq_bfqq_in_large_burst(bfqq) || + else if (coop_or_in_burst || (bfqq->wr_cur_max_time == bfqd->bfq_wr_rt_max_time && !soft_rt)) { @@ -901,18 +962,18 @@ static void bfq_add_request(struct request *rq) /* * * The remaining weight-raising time is lower - * than bfqd->bfq_wr_rt_max_time, which - * means that the application is enjoying - * weight raising either because deemed soft- - * rt in the near past, or because deemed - * interactive a long ago. In both cases, - * resetting now the current remaining weight- - * raising time for the application to the - * weight-raising duration for soft rt - * applications would not cause any latency - * increase for the application (as the new - * duration would be higher than the remaining - * time). + * than bfqd->bfq_wr_rt_max_time, which means + * that the application is enjoying weight + * raising either because deemed soft-rt in + * the near past, or because deemed interactive + * a long ago. + * In both cases, resetting now the current + * remaining weight-raising time for the + * application to the weight-raising duration + * for soft rt applications would not cause any + * latency increase for the application (as the + * new duration would be higher than the + * remaining time). * * In addition, the application is now meeting * the requirements for being deemed soft rt. @@ -947,6 +1008,7 @@ static void bfq_add_request(struct request *rq) bfqd->bfq_wr_rt_max_time; } } +set_ioprio_changed: if (old_wr_coeff != bfqq->wr_coeff) entity->ioprio_changed = 1; add_bfqq_busy: @@ -1167,90 +1229,35 @@ static void bfq_end_wr(struct bfq_data *bfqd) spin_unlock_irq(bfqd->queue->queue_lock); } -static int bfq_allow_merge(struct request_queue *q, struct request *rq, - struct bio *bio) +static inline sector_t bfq_io_struct_pos(void *io_struct, bool request) { - struct bfq_data *bfqd = q->elevator->elevator_data; - struct bfq_io_cq *bic; - struct bfq_queue *bfqq; - - /* - * Disallow merge of a sync bio into an async request. - */ - if (bfq_bio_sync(bio) && !rq_is_sync(rq)) - return 0; - - /* - * Lookup the bfqq that this bio will be queued with. Allow - * merge only if rq is queued there. - * Queue lock is held here. - */ - bic = bfq_bic_lookup(bfqd, current->io_context); - if (bic == NULL) - return 0; - - bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); - return bfqq == RQ_BFQQ(rq); -} - -static void __bfq_set_in_service_queue(struct bfq_data *bfqd, - struct bfq_queue *bfqq) -{ - if (bfqq != NULL) { - bfq_mark_bfqq_must_alloc(bfqq); - bfq_mark_bfqq_budget_new(bfqq); - bfq_clear_bfqq_fifo_expire(bfqq); - - bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; - - bfq_log_bfqq(bfqd, bfqq, - "set_in_service_queue, cur-budget = %lu", - bfqq->entity.budget); - } - - bfqd->in_service_queue = bfqq; -} - -/* - * Get and set a new queue for service. - */ -static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd, - struct bfq_queue *bfqq) -{ - if (!bfqq) - bfqq = bfq_get_next_queue(bfqd); + if (request) + return blk_rq_pos(io_struct); else - bfq_get_next_queue_forced(bfqd, bfqq); - - __bfq_set_in_service_queue(bfqd, bfqq); - return bfqq; + return ((struct bio *)io_struct)->bi_iter.bi_sector; } -static inline sector_t bfq_dist_from_last(struct bfq_data *bfqd, - struct request *rq) +static inline sector_t bfq_dist_from(sector_t pos1, + sector_t pos2) { - if (blk_rq_pos(rq) >= bfqd->last_position) - return blk_rq_pos(rq) - bfqd->last_position; + if (pos1 >= pos2) + return pos1 - pos2; else - return bfqd->last_position - blk_rq_pos(rq); + return pos2 - pos1; } -/* - * Return true if bfqq has no request pending and rq is close enough to - * bfqd->last_position, or if rq is closer to bfqd->last_position than - * bfqq->next_rq - */ -static inline int bfq_rq_close(struct bfq_data *bfqd, struct request *rq) +static inline int bfq_rq_close_to_sector(void *io_struct, bool request, + sector_t sector) { - return bfq_dist_from_last(bfqd, rq) <= BFQQ_SEEK_THR; + return bfq_dist_from(bfq_io_struct_pos(io_struct, request), sector) <= + BFQQ_SEEK_THR; } -static struct bfq_queue *bfqq_close(struct bfq_data *bfqd) +static struct bfq_queue *bfqq_close(struct bfq_data *bfqd, sector_t sector) { struct rb_root *root = &bfqd->rq_pos_tree; struct rb_node *parent, *node; struct bfq_queue *__bfqq; - sector_t sector = bfqd->last_position; if (RB_EMPTY_ROOT(root)) return NULL; @@ -1269,7 +1276,7 @@ static struct bfq_queue *bfqq_close(struct bfq_data *bfqd) * next_request position). */ __bfqq = rb_entry(parent, struct bfq_queue, pos_node); - if (bfq_rq_close(bfqd, __bfqq->next_rq)) + if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector)) return __bfqq; if (blk_rq_pos(__bfqq->next_rq) < sector) @@ -1280,7 +1287,7 @@ static struct bfq_queue *bfqq_close(struct bfq_data *bfqd) return NULL; __bfqq = rb_entry(node, struct bfq_queue, pos_node); - if (bfq_rq_close(bfqd, __bfqq->next_rq)) + if (bfq_rq_close_to_sector(__bfqq->next_rq, true, sector)) return __bfqq; return NULL; @@ -1289,14 +1296,12 @@ static struct bfq_queue *bfqq_close(struct bfq_data *bfqd) /* * bfqd - obvious * cur_bfqq - passed in so that we don't decide that the current queue - * is closely cooperating with itself. - * - * We are assuming that cur_bfqq has dispatched at least one request, - * and that bfqd->last_position reflects a position on the disk associated - * with the I/O issued by cur_bfqq. + * is closely cooperating with itself + * sector - used as a reference point to search for a close queue */ static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, - struct bfq_queue *cur_bfqq) + struct bfq_queue *cur_bfqq, + sector_t sector) { struct bfq_queue *bfqq; @@ -1316,7 +1321,7 @@ static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, * working closely on the same area of the disk. In that case, * we can group them together and don't waste time idling. */ - bfqq = bfqq_close(bfqd); + bfqq = bfqq_close(bfqd, sector); if (bfqq == NULL || bfqq == cur_bfqq) return NULL; @@ -1343,6 +1348,315 @@ static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, return bfqq; } +static struct bfq_queue * +bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) +{ + int process_refs, new_process_refs; + struct bfq_queue *__bfqq; + + /* + * If there are no process references on the new_bfqq, then it is + * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain + * may have dropped their last reference (not just their last process + * reference). + */ + if (!bfqq_process_refs(new_bfqq)) + return NULL; + + /* Avoid a circular list and skip interim queue merges. */ + while ((__bfqq = new_bfqq->new_bfqq)) { + if (__bfqq == bfqq) + return NULL; + new_bfqq = __bfqq; + } + + process_refs = bfqq_process_refs(bfqq); + new_process_refs = bfqq_process_refs(new_bfqq); + /* + * If the process for the bfqq has gone away, there is no + * sense in merging the queues. + */ + if (process_refs == 0 || new_process_refs == 0) + return NULL; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d", + new_bfqq->pid); + + /* + * Merging is just a redirection: the requests of the process + * owning one of the two queues are redirected to the other queue. + * The latter queue, in its turn, is set as shared if this is the + * first time that the requests of some process are redirected to + * it. + * + * We redirect bfqq to new_bfqq and not the opposite, because we + * are in the context of the process owning bfqq, hence we have + * the io_cq of this process. So we can immediately configure this + * io_cq to redirect the requests of the process to new_bfqq. + * + * NOTE, even if new_bfqq coincides with the in-service queue, the + * io_cq of new_bfqq is not available, because, if the in-service + * queue is shared, bfqd->in_service_bic may not point to the + * io_cq of the in-service queue. + * Redirecting the requests of the process owning bfqq to the + * currently in-service queue is in any case the best option, as + * we feed the in-service queue with new requests close to the + * last request served and, by doing so, hopefully increase the + * throughput. + */ + bfqq->new_bfqq = new_bfqq; + atomic_add(process_refs, &new_bfqq->ref); + return new_bfqq; +} + +/* + * Attempt to schedule a merge of bfqq with the currently in-service queue + * or with a close queue among the scheduled queues. + * Return NULL if no merge was scheduled, a pointer to the shared bfq_queue + * structure otherwise. + * + * The OOM queue is not allowed to participate to cooperation: in fact, since + * the requests temporarily redirected to the OOM queue could be redirected + * again to dedicated queues at any time, the state needed to correctly + * handle merging with the OOM queue would be quite complex and expensive + * to maintain. Besides, in such a critical condition as an out of memory, + * the benefits of queue merging may be little relevant, or even negligible. + */ +static struct bfq_queue * +bfq_setup_cooperator(struct bfq_data *bfqd, struct bfq_queue *bfqq, + void *io_struct, bool request) +{ + struct bfq_queue *in_service_bfqq, *new_bfqq; + + if (bfqq->new_bfqq) + return bfqq->new_bfqq; + + if (!io_struct || unlikely(bfqq == &bfqd->oom_bfqq)) + return NULL; + + in_service_bfqq = bfqd->in_service_queue; + + if (in_service_bfqq == NULL || in_service_bfqq == bfqq || + !bfqd->in_service_bic || + unlikely(in_service_bfqq == &bfqd->oom_bfqq)) + goto check_scheduled; + + if (bfq_class_idle(in_service_bfqq) || bfq_class_idle(bfqq)) + goto check_scheduled; + + if (bfq_class_rt(in_service_bfqq) != bfq_class_rt(bfqq)) + goto check_scheduled; + + if (in_service_bfqq->entity.parent != bfqq->entity.parent) + goto check_scheduled; + + if (bfq_rq_close_to_sector(io_struct, request, bfqd->last_position) && + bfq_bfqq_sync(in_service_bfqq) && bfq_bfqq_sync(bfqq)) { + new_bfqq = bfq_setup_merge(bfqq, in_service_bfqq); + if (new_bfqq != NULL) + return new_bfqq; /* Merge with in-service queue */ + } + + /* + * Check whether there is a cooperator among currently scheduled + * queues. The only thing we need is that the bio/request is not + * NULL, as we need it to establish whether a cooperator exists. + */ +check_scheduled: + new_bfqq = bfq_close_cooperator(bfqd, bfqq, + bfq_io_struct_pos(io_struct, request)); + if (new_bfqq && likely(new_bfqq != &bfqd->oom_bfqq)) + return bfq_setup_merge(bfqq, new_bfqq); + + return NULL; +} + +static inline void +bfq_bfqq_save_state(struct bfq_queue *bfqq) +{ + /* + * If bfqq->bic == NULL, the queue is already shared or its requests + * have already been redirected to a shared queue; both idle window + * and weight raising state have already been saved. Do nothing. + */ + if (bfqq->bic == NULL) + return; + if (bfqq->bic->wr_time_left) + /* + * This is the queue of a just-started process, and would + * deserve weight raising: we set wr_time_left to the full + * weight-raising duration to trigger weight-raising when + * and if the queue is split and the first request of the + * queue is enqueued. + */ + bfqq->bic->wr_time_left = bfq_wr_duration(bfqq->bfqd); + else if (bfqq->wr_coeff > 1) { + unsigned long wr_duration = + jiffies - bfqq->last_wr_start_finish; + /* + * It may happen that a queue's weight raising period lasts + * longer than its wr_cur_max_time, as weight raising is + * handled only when a request is enqueued or dispatched (it + * does not use any timer). If the weight raising period is + * about to end, don't save it. + */ + if (bfqq->wr_cur_max_time <= wr_duration) + bfqq->bic->wr_time_left = 0; + else + bfqq->bic->wr_time_left = + bfqq->wr_cur_max_time - wr_duration; + /* + * The bfq_queue is becoming shared or the requests of the + * process owning the queue are being redirected to a shared + * queue. Stop the weight raising period of the queue, as in + * both cases it should not be owned by an interactive or + * soft real-time application. + */ + bfq_bfqq_end_wr(bfqq); + } else + bfqq->bic->wr_time_left = 0; + bfqq->bic->saved_idle_window = bfq_bfqq_idle_window(bfqq); + bfqq->bic->saved_IO_bound = bfq_bfqq_IO_bound(bfqq); + bfqq->bic->saved_in_large_burst = bfq_bfqq_in_large_burst(bfqq); + bfqq->bic->was_in_burst_list = !hlist_unhashed(&bfqq->burst_list_node); + bfqq->bic->cooperations++; + bfqq->bic->failed_cooperations = 0; +} + +static inline void +bfq_get_bic_reference(struct bfq_queue *bfqq) +{ + /* + * If bfqq->bic has a non-NULL value, the bic to which it belongs + * is about to begin using a shared bfq_queue. + */ + if (bfqq->bic) + atomic_long_inc(&bfqq->bic->icq.ioc->refcount); +} + +static void +bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, + struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) +{ + bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu", + (long unsigned)new_bfqq->pid); + /* Save weight raising and idle window of the merged queues */ + bfq_bfqq_save_state(bfqq); + bfq_bfqq_save_state(new_bfqq); + if (bfq_bfqq_IO_bound(bfqq)) + bfq_mark_bfqq_IO_bound(new_bfqq); + bfq_clear_bfqq_IO_bound(bfqq); + /* + * Grab a reference to the bic, to prevent it from being destroyed + * before being possibly touched by a bfq_split_bfqq(). + */ + bfq_get_bic_reference(bfqq); + bfq_get_bic_reference(new_bfqq); + /* + * Merge queues (that is, let bic redirect its requests to new_bfqq) + */ + bic_set_bfqq(bic, new_bfqq, 1); + bfq_mark_bfqq_coop(new_bfqq); + /* + * new_bfqq now belongs to at least two bics (it is a shared queue): + * set new_bfqq->bic to NULL. bfqq either: + * - does not belong to any bic any more, and hence bfqq->bic must + * be set to NULL, or + * - is a queue whose owning bics have already been redirected to a + * different queue, hence the queue is destined to not belong to + * any bic soon and bfqq->bic is already NULL (therefore the next + * assignment causes no harm). + */ + new_bfqq->bic = NULL; + bfqq->bic = NULL; + bfq_put_queue(bfqq); +} + +static inline void bfq_bfqq_increase_failed_cooperations(struct bfq_queue *bfqq) +{ + struct bfq_io_cq *bic = bfqq->bic; + struct bfq_data *bfqd = bfqq->bfqd; + + if (bic && bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh) { + bic->failed_cooperations++; + if (bic->failed_cooperations >= bfqd->bfq_failed_cooperations) + bic->cooperations = 0; + } +} + +static int bfq_allow_merge(struct request_queue *q, struct request *rq, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_io_cq *bic; + struct bfq_queue *bfqq, *new_bfqq; + + /* + * Disallow merge of a sync bio into an async request. + */ + if (bfq_bio_sync(bio) && !rq_is_sync(rq)) + return 0; + + /* + * Lookup the bfqq that this bio will be queued with. Allow + * merge only if rq is queued there. + * Queue lock is held here. + */ + bic = bfq_bic_lookup(bfqd, current->io_context); + if (bic == NULL) + return 0; + + bfqq = bic_to_bfqq(bic, bfq_bio_sync(bio)); + /* + * We take advantage of this function to perform an early merge + * of the queues of possible cooperating processes. + */ + if (bfqq != NULL) { + new_bfqq = bfq_setup_cooperator(bfqd, bfqq, bio, false); + if (new_bfqq != NULL) { + bfq_merge_bfqqs(bfqd, bic, bfqq, new_bfqq); + /* + * If we get here, the bio will be queued in the + * shared queue, i.e., new_bfqq, so use new_bfqq + * to decide whether bio and rq can be merged. + */ + bfqq = new_bfqq; + } else + bfq_bfqq_increase_failed_cooperations(bfqq); + } + + return bfqq == RQ_BFQQ(rq); +} + +static void __bfq_set_in_service_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + if (bfqq != NULL) { + bfq_mark_bfqq_must_alloc(bfqq); + bfq_mark_bfqq_budget_new(bfqq); + bfq_clear_bfqq_fifo_expire(bfqq); + + bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; + + bfq_log_bfqq(bfqd, bfqq, + "set_in_service_queue, cur-budget = %lu", + bfqq->entity.budget); + } + + bfqd->in_service_queue = bfqq; +} + +/* + * Get and set a new queue for service. + */ +static struct bfq_queue *bfq_set_in_service_queue(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfq_get_next_queue(bfqd); + + __bfq_set_in_service_queue(bfqd, bfqq); + return bfqq; +} + /* * If enough samples have been computed, return the current max budget * stored in bfqd, which is dynamically updated according to the @@ -1488,61 +1802,6 @@ static struct request *bfq_check_fifo(struct bfq_queue *bfqq) return rq; } -/* Must be called with the queue_lock held. */ -static int bfqq_process_refs(struct bfq_queue *bfqq) -{ - int process_refs, io_refs; - - io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; - process_refs = atomic_read(&bfqq->ref) - io_refs - bfqq->entity.on_st; - BUG_ON(process_refs < 0); - return process_refs; -} - -static void bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) -{ - int process_refs, new_process_refs; - struct bfq_queue *__bfqq; - - /* - * If there are no process references on the new_bfqq, then it is - * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain - * may have dropped their last reference (not just their last process - * reference). - */ - if (!bfqq_process_refs(new_bfqq)) - return; - - /* Avoid a circular list and skip interim queue merges. */ - while ((__bfqq = new_bfqq->new_bfqq)) { - if (__bfqq == bfqq) - return; - new_bfqq = __bfqq; - } - - process_refs = bfqq_process_refs(bfqq); - new_process_refs = bfqq_process_refs(new_bfqq); - /* - * If the process for the bfqq has gone away, there is no - * sense in merging the queues. - */ - if (process_refs == 0 || new_process_refs == 0) - return; - - /* - * Merge in the direction of the lesser amount of work. - */ - if (new_process_refs >= process_refs) { - bfqq->new_bfqq = new_bfqq; - atomic_add(process_refs, &new_bfqq->ref); - } else { - new_bfqq->new_bfqq = bfqq; - atomic_add(new_process_refs, &bfqq->ref); - } - bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d", - new_bfqq->pid); -} - static inline unsigned long bfq_bfqq_budget_left(struct bfq_queue *bfqq) { struct bfq_entity *entity = &bfqq->entity; @@ -2269,7 +2528,7 @@ static inline bool bfq_bfqq_must_idle(struct bfq_queue *bfqq) */ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) { - struct bfq_queue *bfqq, *new_bfqq = NULL; + struct bfq_queue *bfqq; struct request *next_rq; enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT; @@ -2279,17 +2538,6 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) bfq_log_bfqq(bfqd, bfqq, "select_queue: already in-service queue"); - /* - * If another queue has a request waiting within our mean seek - * distance, let it run. The expire code will check for close - * cooperators and put the close queue at the front of the - * service tree. If possible, merge the expiring queue with the - * new bfqq. - */ - new_bfqq = bfq_close_cooperator(bfqd, bfqq); - if (new_bfqq != NULL && bfqq->new_bfqq == NULL) - bfq_setup_merge(bfqq, new_bfqq); - if (bfq_may_expire_for_budg_timeout(bfqq) && !timer_pending(&bfqd->idle_slice_timer) && !bfq_bfqq_must_idle(bfqq)) @@ -2328,10 +2576,7 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) bfq_clear_bfqq_wait_request(bfqq); del_timer(&bfqd->idle_slice_timer); } - if (new_bfqq == NULL) - goto keep_queue; - else - goto expire; + goto keep_queue; } } @@ -2340,40 +2585,30 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) * for a new request, or has requests waiting for a completion and * may idle after their completion, then keep it anyway. */ - if (new_bfqq == NULL && (timer_pending(&bfqd->idle_slice_timer) || - (bfqq->dispatched != 0 && bfq_bfqq_must_not_expire(bfqq)))) { + if (timer_pending(&bfqd->idle_slice_timer) || + (bfqq->dispatched != 0 && bfq_bfqq_must_not_expire(bfqq))) { bfqq = NULL; goto keep_queue; - } else if (new_bfqq != NULL && timer_pending(&bfqd->idle_slice_timer)) { - /* - * Expiring the queue because there is a close cooperator, - * cancel timer. - */ - bfq_clear_bfqq_wait_request(bfqq); - del_timer(&bfqd->idle_slice_timer); } reason = BFQ_BFQQ_NO_MORE_REQUESTS; expire: bfq_bfqq_expire(bfqd, bfqq, 0, reason); new_queue: - bfqq = bfq_set_in_service_queue(bfqd, new_bfqq); + bfqq = bfq_set_in_service_queue(bfqd); bfq_log(bfqd, "select_queue: new queue %d returned", bfqq != NULL ? bfqq->pid : 0); keep_queue: return bfqq; } -static void bfq_update_wr_data(struct bfq_data *bfqd, - struct bfq_queue *bfqq) +static void bfq_update_wr_data(struct bfq_data *bfqd, struct bfq_queue *bfqq) { - if (bfqq->wr_coeff > 1) { /* queue is being boosted */ - struct bfq_entity *entity = &bfqq->entity; - + struct bfq_entity *entity = &bfqq->entity; + if (bfqq->wr_coeff > 1) { /* queue is being weight-raised */ bfq_log_bfqq(bfqd, bfqq, "raising period dur %u/%u msec, old coeff %u, w %d(%d)", - jiffies_to_msecs(jiffies - - bfqq->last_wr_start_finish), + jiffies_to_msecs(jiffies - bfqq->last_wr_start_finish), jiffies_to_msecs(bfqq->wr_cur_max_time), bfqq->wr_coeff, bfqq->entity.weight, bfqq->entity.orig_weight); @@ -2382,12 +2617,16 @@ static void bfq_update_wr_data(struct bfq_data *bfqd, entity->orig_weight * bfqq->wr_coeff); if (entity->ioprio_changed) bfq_log_bfqq(bfqd, bfqq, "WARN: pending prio change"); + /* * If the queue was activated in a burst, or * too much time has elapsed from the beginning - * of this weight-raising, then end weight raising. + * of this weight-raising period, or the queue has + * exceeded the acceptable number of cooperations, + * then end weight raising. */ if (bfq_bfqq_in_large_burst(bfqq) || + bfq_bfqq_cooperations(bfqq) >= bfqd->bfq_coop_thresh || time_is_before_jiffies(bfqq->last_wr_start_finish + bfqq->wr_cur_max_time)) { bfqq->last_wr_start_finish = jiffies; @@ -2396,11 +2635,13 @@ static void bfq_update_wr_data(struct bfq_data *bfqd, bfqq->last_wr_start_finish, jiffies_to_msecs(bfqq->wr_cur_max_time)); bfq_bfqq_end_wr(bfqq); - __bfq_entity_update_weight_prio( - bfq_entity_service_tree(entity), - entity); } } + /* Update weight both if it must be raised and if it must be lowered */ + if ((entity->weight > entity->orig_weight) != (bfqq->wr_coeff > 1)) + __bfq_entity_update_weight_prio( + bfq_entity_service_tree(entity), + entity); } /* @@ -2647,6 +2888,25 @@ static inline void bfq_init_icq(struct io_cq *icq) struct bfq_io_cq *bic = icq_to_bic(icq); bic->ttime.last_end_request = jiffies; + /* + * A newly created bic indicates that the process has just + * started doing I/O, and is probably mapping into memory its + * executable and libraries: it definitely needs weight raising. + * There is however the possibility that the process performs, + * for a while, I/O close to some other process. EQM intercepts + * this behavior and may merge the queue corresponding to the + * process with some other queue, BEFORE the weight of the queue + * is raised. Merged queues are not weight-raised (they are assumed + * to belong to processes that benefit only from high throughput). + * If the merge is basically the consequence of an accident, then + * the queue will be split soon and will get back its old weight. + * It is then important to write down somewhere that this queue + * does need weight raising, even if it did not make it to get its + * weight raised before being merged. To this purpose, we overload + * the field raising_time_left and assign 1 to it, to mark the queue + * as needing weight raising. + */ + bic->wr_time_left = 1; } static void bfq_exit_icq(struct io_cq *icq) @@ -2660,6 +2920,13 @@ static void bfq_exit_icq(struct io_cq *icq) } if (bic->bfqq[BLK_RW_SYNC]) { + /* + * If the bic is using a shared queue, put the reference + * taken on the io_context when the bic started using a + * shared bfq_queue. + */ + if (bfq_bfqq_coop(bic->bfqq[BLK_RW_SYNC])) + put_io_context(icq->ioc); bfq_exit_bfqq(bfqd, bic->bfqq[BLK_RW_SYNC]); bic->bfqq[BLK_RW_SYNC] = NULL; } @@ -2952,6 +3219,10 @@ static void bfq_update_idle_window(struct bfq_data *bfqd, if (!bfq_bfqq_sync(bfqq) || bfq_class_idle(bfqq)) return; + /* Idle window just restored, statistics are meaningless. */ + if (bfq_bfqq_just_split(bfqq)) + return; + enable_idle = bfq_bfqq_idle_window(bfqq); if (atomic_read(&bic->icq.ioc->active_ref) == 0 || @@ -2999,6 +3270,7 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 || !BFQQ_SEEKY(bfqq)) bfq_update_idle_window(bfqd, bfqq, bic); + bfq_clear_bfqq_just_split(bfqq); bfq_log_bfqq(bfqd, bfqq, "rq_enqueued: idle_window=%d (seeky %d, mean %llu)", @@ -3059,12 +3331,47 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, static void bfq_insert_request(struct request_queue *q, struct request *rq) { struct bfq_data *bfqd = q->elevator->elevator_data; - struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_queue *bfqq = RQ_BFQQ(rq), *new_bfqq; assert_spin_locked(bfqd->queue->queue_lock); + /* + * An unplug may trigger a requeue of a request from the device + * driver: make sure we are in process context while trying to + * merge two bfq_queues. + */ + if (!in_interrupt()) { + new_bfqq = bfq_setup_cooperator(bfqd, bfqq, rq, true); + if (new_bfqq != NULL) { + if (bic_to_bfqq(RQ_BIC(rq), 1) != bfqq) + new_bfqq = bic_to_bfqq(RQ_BIC(rq), 1); + /* + * Release the request's reference to the old bfqq + * and make sure one is taken to the shared queue. + */ + new_bfqq->allocated[rq_data_dir(rq)]++; + bfqq->allocated[rq_data_dir(rq)]--; + atomic_inc(&new_bfqq->ref); + bfq_put_queue(bfqq); + if (bic_to_bfqq(RQ_BIC(rq), 1) == bfqq) + bfq_merge_bfqqs(bfqd, RQ_BIC(rq), + bfqq, new_bfqq); + rq->elv.priv[1] = new_bfqq; + bfqq = new_bfqq; + } else + bfq_bfqq_increase_failed_cooperations(bfqq); + } + bfq_add_request(rq); + /* + * Here a newly-created bfq_queue has already started a weight-raising + * period: clear raising_time_left to prevent bfq_bfqq_save_state() + * from assigning it a full weight-raising period. See the detailed + * comments about this field in bfq_init_icq(). + */ + if (bfqq->bic != NULL) + bfqq->bic->wr_time_left = 0; rq_set_fifo_time(rq, jiffies + bfqd->bfq_fifo_expire[rq_is_sync(rq)]); list_add_tail(&rq->queuelist, &bfqq->fifo); @@ -3226,18 +3533,6 @@ static void bfq_put_request(struct request *rq) } } -static struct bfq_queue * -bfq_merge_bfqqs(struct bfq_data *bfqd, struct bfq_io_cq *bic, - struct bfq_queue *bfqq) -{ - bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu", - (long unsigned)bfqq->new_bfqq->pid); - bic_set_bfqq(bic, bfqq->new_bfqq, 1); - bfq_mark_bfqq_coop(bfqq->new_bfqq); - bfq_put_queue(bfqq); - return bic_to_bfqq(bic, 1); -} - /* * Returns NULL if a new bfqq should be allocated, or the old bfqq if this * was the last process referring to said bfqq. @@ -3246,6 +3541,9 @@ static struct bfq_queue * bfq_split_bfqq(struct bfq_io_cq *bic, struct bfq_queue *bfqq) { bfq_log_bfqq(bfqq->bfqd, bfqq, "splitting queue"); + + put_io_context(bic->icq.ioc); + if (bfqq_process_refs(bfqq) == 1) { bfqq->pid = current->pid; bfq_clear_bfqq_coop(bfqq); @@ -3274,6 +3572,7 @@ static int bfq_set_request(struct request_queue *q, struct request *rq, struct bfq_queue *bfqq; struct bfq_group *bfqg; unsigned long flags; + bool split = false; might_sleep_if(gfp_mask & __GFP_WAIT); @@ -3291,25 +3590,26 @@ static int bfq_set_request(struct request_queue *q, struct request *rq, if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { bfqq = bfq_get_queue(bfqd, bfqg, is_sync, bic, gfp_mask); bic_set_bfqq(bic, bfqq, is_sync); + if (split && is_sync) { + if ((bic->was_in_burst_list && bfqd->large_burst) || + bic->saved_in_large_burst) + bfq_mark_bfqq_in_large_burst(bfqq); + else { + bfq_clear_bfqq_in_large_burst(bfqq); + if (bic->was_in_burst_list) + hlist_add_head(&bfqq->burst_list_node, + &bfqd->burst_list); + } + } } else { - /* - * If the queue was seeky for too long, break it apart. - */ + /* If the queue was seeky for too long, break it apart. */ if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) { bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq"); bfqq = bfq_split_bfqq(bic, bfqq); + split = true; if (!bfqq) goto new_queue; } - - /* - * Check to see if this queue is scheduled to merge with - * another closely cooperating queue. The merging of queues - * happens here as it must be done in process context. - * The reference on new_bfqq was taken in merge_bfqqs. - */ - if (bfqq->new_bfqq != NULL) - bfqq = bfq_merge_bfqqs(bfqd, bic, bfqq); } bfqq->allocated[rw]++; @@ -3320,6 +3620,26 @@ static int bfq_set_request(struct request_queue *q, struct request *rq, rq->elv.priv[0] = bic; rq->elv.priv[1] = bfqq; + /* + * If a bfq_queue has only one process reference, it is owned + * by only one bfq_io_cq: we can set the bic field of the + * bfq_queue to the address of that structure. Also, if the + * queue has just been split, mark a flag so that the + * information is available to the other scheduler hooks. + */ + if (likely(bfqq != &bfqd->oom_bfqq) && bfqq_process_refs(bfqq) == 1) { + bfqq->bic = bic; + if (split) { + bfq_mark_bfqq_just_split(bfqq); + /* + * If the queue has just been split from a shared + * queue, restore the idle window and the possible + * weight raising period. + */ + bfq_bfqq_resume_state(bfqq, bic); + } + } + spin_unlock_irqrestore(q->queue_lock, flags); return 0; diff --git a/block/bfq-sched.c b/block/bfq-sched.c index c3430993ec9432..d0890c6d4c1140 100644 --- a/block/bfq-sched.c +++ b/block/bfq-sched.c @@ -1085,34 +1085,6 @@ static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) return bfqq; } -/* - * Forced extraction of the given queue. - */ -static void bfq_get_next_queue_forced(struct bfq_data *bfqd, - struct bfq_queue *bfqq) -{ - struct bfq_entity *entity; - struct bfq_sched_data *sd; - - BUG_ON(bfqd->in_service_queue != NULL); - - entity = &bfqq->entity; - /* - * Bubble up extraction/update from the leaf to the root. - */ - for_each_entity(entity) { - sd = entity->sched_data; - bfq_update_budget(entity); - bfq_update_vtime(bfq_entity_service_tree(entity)); - bfq_active_extract(bfq_entity_service_tree(entity), entity); - sd->in_service_entity = entity; - sd->next_in_service = NULL; - entity->service = 0; - } - - return; -} - static void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd) { if (bfqd->in_service_bic != NULL) { diff --git a/block/bfq.h b/block/bfq.h index 7aab979ee195c5..bdc6bd18c12888 100644 --- a/block/bfq.h +++ b/block/bfq.h @@ -218,18 +218,21 @@ struct bfq_group; * idle @bfq_queue with no outstanding requests, then * the task associated with the queue it is deemed as * soft real-time (see the comments to the function - * bfq_bfqq_softrt_next_start()). + * bfq_bfqq_softrt_next_start()) * @last_idle_bklogged: time of the last transition of the @bfq_queue from * idle to backlogged * @service_from_backlogged: cumulative service received from the @bfq_queue * since the last transition from idle to * backlogged + * @bic: pointer to the bfq_io_cq owning the bfq_queue, set to %NULL if the + * queue is shared * - * A bfq_queue is a leaf request queue; it can be associated with an io_context - * or more, if it is async or shared between cooperating processes. @cgroup - * holds a reference to the cgroup, to be sure that it does not disappear while - * a bfqq still references it (mostly to avoid races between request issuing and - * task migration followed by cgroup destruction). + * A bfq_queue is a leaf request queue; it can be associated with an + * io_context or more, if it is async or shared between cooperating + * processes. @cgroup holds a reference to the cgroup, to be sure that it + * does not disappear while a bfqq still references it (mostly to avoid + * races between request issuing and task migration followed by cgroup + * destruction). * All the fields are protected by the queue lock of the containing bfqd. */ struct bfq_queue { @@ -269,6 +272,7 @@ struct bfq_queue { unsigned int requests_within_timer; pid_t pid; + struct bfq_io_cq *bic; /* weight-raising fields */ unsigned long wr_cur_max_time; @@ -298,12 +302,42 @@ struct bfq_ttime { * @icq: associated io_cq structure * @bfqq: array of two process queues, the sync and the async * @ttime: associated @bfq_ttime struct + * @wr_time_left: snapshot of the time left before weight raising ends + * for the sync queue associated to this process; this + * snapshot is taken to remember this value while the weight + * raising is suspended because the queue is merged with a + * shared queue, and is used to set @raising_cur_max_time + * when the queue is split from the shared queue and its + * weight is raised again + * @saved_idle_window: same purpose as the previous field for the idle + * window + * @saved_IO_bound: same purpose as the previous two fields for the I/O + * bound classification of a queue + * @saved_in_large_burst: same purpose as the previous fields for the + * value of the field keeping the queue's belonging + * to a large burst + * @was_in_burst_list: true if the queue belonged to a burst list + * before its merge with another cooperating queue + * @cooperations: counter of consecutive successful queue merges underwent + * by any of the process' @bfq_queues + * @failed_cooperations: counter of consecutive failed queue merges of any + * of the process' @bfq_queues */ struct bfq_io_cq { struct io_cq icq; /* must be the first member */ struct bfq_queue *bfqq[2]; struct bfq_ttime ttime; int ioprio; + + unsigned int wr_time_left; + bool saved_idle_window; + bool saved_IO_bound; + + bool saved_in_large_burst; + bool was_in_burst_list; + + unsigned int cooperations; + unsigned int failed_cooperations; }; enum bfq_device_speed { @@ -536,7 +570,7 @@ enum bfqq_state_flags { BFQ_BFQQ_FLAG_idle_window, /* slice idling enabled */ BFQ_BFQQ_FLAG_sync, /* synchronous queue */ BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ - BFQ_BFQQ_FLAG_IO_bound, /* + BFQ_BFQQ_FLAG_IO_bound, /* * bfqq has timed-out at least once * having consumed at most 2/10 of * its budget @@ -549,12 +583,13 @@ enum bfqq_state_flags { * bfqq has proved to be slow and * seeky until budget timeout */ - BFQ_BFQQ_FLAG_softrt_update, /* + BFQ_BFQQ_FLAG_softrt_update, /* * may need softrt-next-start * update */ BFQ_BFQQ_FLAG_coop, /* bfqq is shared */ - BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be splitted */ + BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be split */ + BFQ_BFQQ_FLAG_just_split, /* queue has just been split */ }; #define BFQ_BFQQ_FNS(name) \ @@ -583,6 +618,7 @@ BFQ_BFQQ_FNS(in_large_burst); BFQ_BFQQ_FNS(constantly_seeky); BFQ_BFQQ_FNS(coop); BFQ_BFQQ_FNS(split_coop); +BFQ_BFQQ_FNS(just_split); BFQ_BFQQ_FNS(softrt_update); #undef BFQ_BFQQ_FNS From ede75b99eafad07d7734c51076d6525622c56648 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 10:38:48 +0200 Subject: [PATCH 0317/1983] dts: hb/cbi: Add support for FSL drivers This makes the changes needed for an upstream derived device-tree file to work with the freescale tree's drivers. Specifically we implement this for the SolidRun based hardware. --- arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 71 ++++++++++++++++++--- arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 28 ++++---- arch/arm/boot/dts/imx6qdl-microsom.dtsi | 1 + 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi index f1d11be3c01e8c..3457b1a90435ea 100644 --- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi @@ -48,6 +48,11 @@ aliases { mmc0 = &usdhc2; mmc1 = &usdhc1; + mxcfb0 = &mxcfb1; + }; + + chosen { + bootargs = "quiet console=ttymxc0,115200 root=/dev/mmcblk0p2 rw"; }; ir_recv: ir-receiver { @@ -55,6 +60,30 @@ gpios = <&gpio3 9 1>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_cubox_i_ir>; + linux,rc-map-name = "rc-rc6-mce"; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-0 = <&pinctrl_gpio_key>; + pinctrl-names = "default"; + + button_0 { + label = "Button 0"; + gpios = <&gpio3 8 GPIO_ACTIVE_LOW>; + linux,code = ; + }; }; pwmleds { @@ -112,17 +141,34 @@ spdif-out; }; - gpio-keys { - compatible = "gpio-keys"; - pinctrl-0 = <&pinctrl_gpio_key>; - pinctrl-names = "default"; + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; +}; - button_0 { - label = "Button 0"; - gpios = <&gpio3 8 GPIO_ACTIVE_LOW>; - linux,code = ; - }; - }; +&hdmi_core { + ipu_id = <0>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cubox_i_hdmi>; + status = "okay"; }; &i2c2 { @@ -130,6 +176,11 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_cubox_i_i2c2>; status = "okay"; + + ddc: imx6_hdmi_i2c@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; }; &i2c3 { diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index efc0829ae43ca6..1b9f62f4a81d6e 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -62,6 +62,17 @@ linux,rc-map-name = "rc-rc6-mce"; }; + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + regulators { compatible = "simple-bus"; @@ -70,6 +81,7 @@ regulator-name = "3P3V"; regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; + regulator-always-on; }; reg_usbh1_vbus: usb-h1-vbus { @@ -133,15 +145,9 @@ hdmi-controller = <&hdmi_audio>; }; - mxcfb1: fb@0 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "hdmi"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "okay"; + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; }; }; @@ -191,11 +197,11 @@ /* Pro baseboard model */ sgtl5000: sgtl5000@0a { - clocks = <&clks IMX6QDL_CLK_CKO>; compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clocks = <&clks IMX6QDL_CLK_CKO>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_sgtl5000>; - reg = <0x0a>; VDDA-supply = <®_3p3v>; VDDIO-supply = <®_3p3v>; }; diff --git a/arch/arm/boot/dts/imx6qdl-microsom.dtsi b/arch/arm/boot/dts/imx6qdl-microsom.dtsi index 5f40c504afe5fb..93d54fdcacdbe1 100644 --- a/arch/arm/boot/dts/imx6qdl-microsom.dtsi +++ b/arch/arm/boot/dts/imx6qdl-microsom.dtsi @@ -170,6 +170,7 @@ bus-width = <4>; keep-power-in-suspend; non-removable; + no-1-8-v; vmmc-supply = <®_brcm>; status = "okay"; }; From 329b5fcccbf85dc1d39ab49bed6e69e5c8e8dc4b Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 13 Feb 2013 16:02:23 -0700 Subject: [PATCH 0318/1983] mxc_v4l2_capture: fix VIDIOC_ENUMSTD, return -EINVAL if index > 0 --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 3926baa7289a03..e3a7a377b5a54c 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -2250,6 +2250,10 @@ static long mxc_v4l_do_ioctl(struct file *file, case VIDIOC_ENUMSTD: { struct v4l2_standard *e = arg; pr_debug(" case VIDIOC_ENUMSTD\n"); + if (e->index > 0) { + retval = -EINVAL; + break; + } *e = cam->standard; break; } From 7008bf2df7a41d4ffc7ab7a589777e0730a8c084 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 22 Oct 2013 12:44:51 -0700 Subject: [PATCH 0319/1983] fix camera power down Signed-off-by: Troy Kisky --- .../platform/mxc/capture/mxc_v4l2_capture.c | 41 ++++++++++++++++--- .../platform/mxc/capture/mxc_v4l2_capture.h | 2 + 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index e3a7a377b5a54c..09ce68cfb78192 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1566,6 +1566,37 @@ static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) return retval; } +static void power_down_callback(struct work_struct *work) +{ + cam_data *cam = container_of(work, struct _cam_data, power_down_work.work); + + down(&cam->busy_lock); + if (!cam->open_count) { + vidioc_int_s_power(cam->sensor, 0); + cam->power_on = 0; + } + up(&cam->busy_lock); +} + +/* cam->busy_lock is held */ +void power_up_camera(cam_data *cam) +{ + if (cam->power_on) { + cancel_delayed_work(&cam->power_down_work); + return; + } + vidioc_int_s_power(cam->sensor, 1); + vidioc_int_init(cam->sensor); + vidioc_int_dev_init(cam->sensor); + cam->power_on = 1; +} + + +void power_off_camera(cam_data *cam) +{ + schedule_delayed_work(&cam->power_down_work, (HZ * 2)); +} + /*! * V4L interface - open function * @@ -1709,9 +1740,7 @@ static int mxc_v4l_open(struct file *file) cam_fmt.fmt.pix.pixelformat, csi_param); clk_prepare_enable(sensor->sensor_clk); - vidioc_int_s_power(cam->sensor, 1); - vidioc_int_init(cam->sensor); - vidioc_int_dev_init(cam->sensor); + power_up_camera(cam); } file->private_data = dev; @@ -1768,7 +1797,6 @@ static int mxc_v4l_close(struct file *file) } if (--cam->open_count == 0) { - vidioc_int_s_power(cam->sensor, 0); clk_disable_unprepare(sensor->sensor_clk); wait_event_interruptible(cam->power_queue, cam->low_power == false); @@ -1793,6 +1821,7 @@ static int mxc_v4l_close(struct file *file) wake_up_interruptible(&cam->enc_queue); mxc_free_frames(cam); cam->enc_counter++; + power_off_camera(cam); } up(&cam->busy_lock); @@ -2675,6 +2704,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) init_MUTEX(&cam->param_lock); init_MUTEX(&cam->busy_lock); + INIT_DELAYED_WORK(&cam->power_down_work, power_down_callback); cam->video_dev = video_device_alloc(); if (cam->video_dev == NULL) @@ -2957,7 +2987,8 @@ static int mxc_v4l2_resume(struct platform_device *pdev) wake_up_interruptible(&cam->power_queue); if (cam->sensor && cam->open_count) { - vidioc_int_s_power(cam->sensor, 1); + if ((cam->overlay_on == true) || (cam->capture_on == true)) + vidioc_int_s_power(cam->sensor, 1); if (!cam->mclk_on[cam->mclk_source]) { ipu_csi_enable_mclk_if(cam->ipu, CSI_MCLK_I2C, diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 9294dc66070789..d2365113c36107 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -114,6 +114,8 @@ typedef struct _cam_data { struct semaphore busy_lock; int open_count; + struct delayed_work power_down_work; + int power_on; /* params lock for this camera */ struct semaphore param_lock; From 91d5772526a2dbccb9eb2208a2c6903bd81f4bb7 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 25 Oct 2013 15:29:04 -0700 Subject: [PATCH 0320/1983] ov5640_mipi: add remove *2 and add error checking to OV5640_get_sysclk --- .../media/platform/mxc/capture/ov5640_mipi.c | 84 ++++++++++++------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 4b190769031d9b..06a91a7b411518 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -685,7 +685,7 @@ static void ov5640_standby(s32 enable) gpio_set_value(pwn_gpio, 1); else gpio_set_value(pwn_gpio, 0); - + pr_debug("ov5640_mipi_camera_powerdown: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio); msleep(2); } @@ -785,7 +785,7 @@ static s32 ov5640_write_reg(u16 reg, u8 val) __func__, reg, val); return -1; } - + pr_debug("reg=%x,val=%x\n", reg, val); return 0; } @@ -827,43 +827,62 @@ void OV5640_stream_off(void) ov5640_write_reg(0x4202, 0x0f); } +static const int sclk_rdiv_map[] = {1, 2, 4, 8}; int OV5640_get_sysclk(void) { /* calculate sysclk */ - int xvclk = ov5640_data.mclk / 10000; - int temp1, temp2; - int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv; - int Bit_div2x = 1, sclk_rdiv, sysclk; + int tmp; + unsigned Multiplier, PreDiv, SysDiv, Pll_rdiv, Bit_div2x = 1; + unsigned div, sclk_rdiv, sysclk; u8 temp; - int sclk_rdiv_map[] = {1, 2, 4, 8}; - - temp1 = ov5640_read_reg(0x3034, &temp); - temp2 = temp1 & 0x0f; - if (temp2 == 8 || temp2 == 10) - Bit_div2x = temp2 / 2; - - temp1 = ov5640_read_reg(0x3035, &temp); - SysDiv = temp1>>4; + tmp = ov5640_read_reg(0x3034, &temp); + if (tmp < 0) + return tmp; + tmp &= 0x0f; + if (tmp == 8 || tmp == 10) + Bit_div2x = tmp / 2; + + tmp = ov5640_read_reg(0x3035, &temp); + if (tmp < 0) + return tmp; + SysDiv = tmp >> 4; if (SysDiv == 0) - SysDiv = 16; - - temp1 = ov5640_read_reg(0x3036, &temp); - Multiplier = temp1; - - temp1 = ov5640_read_reg(0x3037, &temp); - PreDiv = temp1 & 0x0f; - Pll_rdiv = ((temp1 >> 4) & 0x01) + 1; - - temp1 = ov5640_read_reg(0x3108, &temp); - temp2 = temp1 & 0x03; - sclk_rdiv = sclk_rdiv_map[temp2]; - - VCO = xvclk * Multiplier / PreDiv; - - sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv; - + SysDiv = 16; + + tmp = ov5640_read_reg(0x3036, &temp); + if (tmp < 0) + return tmp; + Multiplier = tmp; + + tmp = ov5640_read_reg(0x3037, &temp); + if (tmp < 0) + return tmp; + PreDiv = tmp & 0x0f; + Pll_rdiv = ((tmp >> 4) & 0x01) + 1; + + tmp = ov5640_read_reg(0x3108, &temp); + if (tmp < 0) + return tmp; + sclk_rdiv = sclk_rdiv_map[tmp & 0x03]; + + sysclk = ov5640_data.mclk / 10000 * Multiplier; + div = PreDiv * SysDiv * Pll_rdiv * Bit_div2x * sclk_rdiv; + if (!div) { + pr_err("%s:Error divide by 0, (%d * %d * %d * %d * %d)\n", + __func__, PreDiv, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv); + return -EINVAL; + } + if (!sysclk) { + pr_err("%s:Error 0 clk, ov5640_data.mclk=%d, Multiplier=%d\n", + __func__, ov5640_data.mclk, Multiplier); + return -EINVAL; + } + sysclk /= div; + pr_debug("%s: sysclk(%d) = %d / 10000 * %d / (%d * %d * %d * %d * %d)\n", + __func__, sysclk, ov5640_data.mclk, Multiplier, + PreDiv, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv); return sysclk; } @@ -1004,6 +1023,7 @@ int OV5640_get_light_freq(void) light_freq = 50; } else { /* 60Hz */ + light_freq = 60; } } return light_freq; From d124ada32c6e30e00f32edfca67e73ca80d2a7ce Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Sat, 26 Oct 2013 18:02:09 -0700 Subject: [PATCH 0321/1983] mxc_v4l2_capture: add ipu_id to sensor_data --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 4 ++++ drivers/media/platform/mxc/capture/mxc_v4l2_capture.h | 1 + drivers/media/platform/mxc/capture/ov5640.c | 8 ++++++++ drivers/media/platform/mxc/capture/ov5640_mipi.c | 8 ++++++++ drivers/media/platform/mxc/capture/ov5642.c | 10 ++++++++++ 5 files changed, 31 insertions(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 09ce68cfb78192..5f0669dcb2e9cf 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -3044,6 +3044,10 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) return -1; } + if (sdata->ipu_id != cam->ipu_id) { + pr_debug("%s: ipu doesn't match\n", __func__); + return -1; + } if (sdata->csi != cam->csi) { pr_debug("%s: csi doesn't match\n", __func__); return -1; diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index d2365113c36107..612e4cbce5f777 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -256,6 +256,7 @@ struct sensor_data { u32 mclk; u8 mclk_source; struct clk *sensor_clk; + int ipu_id; int csi; void (*io_init)(void); diff --git a/drivers/media/platform/mxc/capture/ov5640.c b/drivers/media/platform/mxc/capture/ov5640.c index ec8b8090201d44..73ae6b69030fc2 100644 --- a/drivers/media/platform/mxc/capture/ov5640.c +++ b/drivers/media/platform/mxc/capture/ov5640.c @@ -1817,6 +1817,7 @@ static int ov5640_probe(struct i2c_client *client, struct device *dev = &client->dev; int retval; u8 chip_id_high, chip_id_low; + struct sensor_data *sensor = &ov5640_data; /* ov5640 pinctrl */ pinctrl = devm_pinctrl_get_select_default(dev); @@ -1869,6 +1870,13 @@ static int ov5640_probe(struct i2c_client *client, return retval; } + retval = of_property_read_u32(dev->of_node, "ipu_id", + &sensor->ipu_id); + if (retval) { + dev_err(dev, "ipu_id missing or invalid\n"); + return retval; + } + retval = of_property_read_u32(dev->of_node, "csi_id", &(ov5640_data.csi)); if (retval) { diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 06a91a7b411518..ada86ff0305d33 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -2001,6 +2001,7 @@ static int ov5640_probe(struct i2c_client *client, struct device *dev = &client->dev; int retval; u8 chip_id_high, chip_id_low; + struct sensor_data *sensor = &ov5640_data; /* request power down pin */ pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); @@ -2048,6 +2049,13 @@ static int ov5640_probe(struct i2c_client *client, return retval; } + retval = of_property_read_u32(dev->of_node, "ipu_id", + &sensor->ipu_id); + if (retval) { + dev_err(dev, "ipu_id missing or invalid\n"); + return retval; + } + retval = of_property_read_u32(dev->of_node, "csi_id", &(ov5640_data.csi)); if (retval) { diff --git a/drivers/media/platform/mxc/capture/ov5642.c b/drivers/media/platform/mxc/capture/ov5642.c index be6122205c1895..818718d9b3d402 100644 --- a/drivers/media/platform/mxc/capture/ov5642.c +++ b/drivers/media/platform/mxc/capture/ov5642.c @@ -4084,6 +4084,9 @@ static int ov5642_probe(struct i2c_client *client, struct device *dev = &client->dev; int retval; u8 chip_id_high, chip_id_low; + struct reg_value *firmware_regs; + int i; + struct sensor_data *sensor = &ov5642_data; /* ov5642 pinctrl */ pinctrl = devm_pinctrl_get_select_default(dev); @@ -4138,6 +4141,13 @@ static int ov5642_probe(struct i2c_client *client, return retval; } + retval = of_property_read_u32(dev->of_node, "ipu_id", + &sensor->ipu_id); + if (retval) { + dev_err(dev, "ipu_id missing or invalid\n"); + return retval; + } + retval = of_property_read_u32(dev->of_node, "csi_id", &(ov5642_data.csi)); if (retval) { From d187773539f387e632ff5c5f3e2cead254177d74 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 7 Jan 2014 17:14:17 -0700 Subject: [PATCH 0322/1983] ov5640_mipi: print message on gpio request failure --- drivers/media/platform/mxc/capture/ov5640_mipi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index ada86ff0305d33..041a67a10ee194 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -2011,8 +2011,10 @@ static int ov5640_probe(struct i2c_client *client, } retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, "ov5640_mipi_pwdn"); - if (retval < 0) + if (retval < 0) { + dev_warn(dev, "request of pwn_gpio failed"); return retval; + } /* request reset pin */ rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); @@ -2022,8 +2024,10 @@ static int ov5640_probe(struct i2c_client *client, } retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, "ov5640_mipi_reset"); - if (retval < 0) + if (retval < 0) { + dev_warn(dev, "request of ov5640_mipi_reset failed"); return retval; + } /* Set initial values for the sensor struct. */ memset(&ov5640_data, 0, sizeof(ov5640_data)); From e7a35f84b4ba579bb3f9b183c91d4bcee85e8703 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 10 Jan 2014 19:31:11 -0700 Subject: [PATCH 0323/1983] ov5640_mipi: simplify mipi clock wait loop --- .../media/platform/mxc/capture/ov5640_mipi.c | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 041a67a10ee194..6254662ee5dc26 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -1424,37 +1424,33 @@ static int ov5640_init_mode(enum ov5640_frame_rate frame_rate, msleep(msec_wait4stable); if (mipi_csi2_info) { - unsigned int i; - - i = 0; + unsigned int i = 0; /* wait for mipi sensor ready */ - mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); - while ((mipi_reg == 0x200) && (i < 10)) { + while (1) { mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); - i++; + if (mipi_reg != 0x200) + break; + if (i++ >= 20) { + pr_err("mipi csi2 can not receive sensor clk! %x\n", mipi_reg); + return -1; + } msleep(10); } - if (i >= 10) { - pr_err("mipi csi2 can not receive sensor clk!\n"); - return -1; - } - i = 0; - /* wait for mipi stable */ - mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); - while ((mipi_reg != 0x0) && (i < 10)) { + while (1) { mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); - i++; + if (!mipi_reg) + break; + if (i++ >= 20) { + pr_err("mipi csi2 can not receive data correctly!\n"); + return -1; + } msleep(10); } - if (i >= 10) { - pr_err("mipi csi2 can not reveive data correctly!\n"); - return -1; - } } err: return retval; From dd0455cbbd8970f45ac840c7830b6c47e99b3e21 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 27 Feb 2014 15:58:06 -0700 Subject: [PATCH 0324/1983] mxc_v4l2_capture: cleanup debug messages --- .../platform/mxc/capture/mxc_v4l2_capture.c | 104 +++++++++--------- 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 5f0669dcb2e9cf..ef02783088f75d 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -240,7 +240,7 @@ static int mxc_free_frame_buf(cam_data *cam) { int i; - pr_debug("MVC: In mxc_free_frame_buf\n"); + pr_debug("%s\n", __func__); for (i = 0; i < FRAME_NUM; i++) { if (cam->frame[i].vaddress != 0) { @@ -266,8 +266,7 @@ static int mxc_allocate_frame_buf(cam_data *cam, int count) { int i; - pr_debug("In MVC:mxc_allocate_frame_buf - size=%d\n", - cam->v2f.fmt.pix.sizeimage); + pr_debug("%s: size=%d\n", __func__, cam->v2f.fmt.pix.sizeimage); for (i = 0; i < count; i++) { cam->frame[i].vaddress = @@ -276,8 +275,7 @@ static int mxc_allocate_frame_buf(cam_data *cam, int count) &cam->frame[i].paddress, GFP_DMA | GFP_KERNEL); if (cam->frame[i].vaddress == 0) { - pr_err("ERROR: v4l2 capture: " - "mxc_allocate_frame_buf failed.\n"); + pr_err("%s: failed.\n", __func__); mxc_free_frame_buf(cam); return -ENOBUFS; } @@ -305,7 +303,7 @@ static void mxc_free_frames(cam_data *cam) { int i; - pr_debug("In MVC:mxc_free_frames\n"); + pr_debug("%s\n", __func__); for (i = 0; i < FRAME_NUM; i++) cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; @@ -326,7 +324,7 @@ static void mxc_free_frames(cam_data *cam) */ static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf) { - pr_debug("In MVC:mxc_v4l2_buffer_status\n"); + pr_debug("%s\n", __func__); if (buf->index < 0 || buf->index >= FRAME_NUM) { pr_err("ERROR: v4l2 capture: mxc_v4l2_buffer_status buffers " @@ -340,13 +338,13 @@ static int mxc_v4l2_buffer_status(cam_data *cam, struct v4l2_buffer *buf) static int mxc_v4l2_release_bufs(cam_data *cam) { - pr_debug("In MVC:mxc_v4l2_release_bufs\n"); + pr_debug("%s\n", __func__); return 0; } static int mxc_v4l2_prepare_bufs(cam_data *cam, struct v4l2_buffer *buf) { - pr_debug("In MVC:mxc_v4l2_prepare_bufs\n"); + pr_debug("%s\n", __func__); if (buf->index < 0 || buf->index >= FRAME_NUM || buf->length < PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage)) { @@ -407,7 +405,7 @@ static int mxc_streamon(cam_data *cam) unsigned long lock_flags; int err = 0; - pr_debug("In MVC:mxc_streamon\n"); + pr_debug("%s\n", __func__); if (NULL == cam) { pr_err("ERROR! cam parameter is NULL\n"); @@ -493,7 +491,7 @@ static int mxc_streamoff(cam_data *cam) { int err = 0; - pr_debug("In MVC:mxc_streamoff\n"); + pr_debug("%s\n", __func__); if (cam->capture_on == false) return 0; @@ -540,7 +538,7 @@ static int verify_preview(cam_data *cam, struct v4l2_window *win) bool foregound_fb = false; mm_segment_t old_fs; - pr_debug("In MVC: verify_preview\n"); + pr_debug("%s\n", __func__); do { fbi = (struct fb_info *)registered_fb[i]; @@ -768,7 +766,7 @@ static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f) { int retval = 0; - pr_debug("In MVC: mxc_v4l2_g_fmt type=%d\n", f->type); + pr_debug("%s: type=%d\n", __func__, f->type); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -816,7 +814,7 @@ static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) int bytesperline = 0; int *width, *height; - pr_debug("In MVC: mxc_v4l2_s_fmt\n"); + pr_debug("%s\n", __func__); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -979,7 +977,7 @@ static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c) { int status = 0; - pr_debug("In MVC:mxc_v4l2_g_ctrl\n"); + pr_debug("%s\n", __func__); /* probably don't need to store the values that can be retrieved, * locally, but they are for now. */ @@ -1095,7 +1093,7 @@ static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) int tmp_rotation = IPU_ROTATE_NONE; struct sensor_data *sensor_data; - pr_debug("In MVC:mxc_v4l2_s_ctrl\n"); + pr_debug("%s\n", __func__); switch (c->id) { case V4L2_CID_HFLIP: @@ -1294,7 +1292,7 @@ static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) u32 current_fps, parm_fps; int err = 0; - pr_debug("In mxc_v4l2_s_param\n"); + pr_debug("%s\n", __func__); if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { pr_err(KERN_ERR "mxc_v4l2_s_param invalid type\n"); @@ -1429,8 +1427,7 @@ static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) */ static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e) { - pr_debug("In mxc_v4l2_s_std %Lx\n", e); - + pr_debug("%s: %Lx\n", __func__, e); if (e == V4L2_STD_PAL) { pr_debug(" Setting standard to PAL %Lx\n", V4L2_STD_PAL); cam->standard.id = V4L2_STD_PAL; @@ -1475,7 +1472,7 @@ static int mxc_v4l2_g_std(cam_data *cam, v4l2_std_id *e) { struct v4l2_format tv_fmt; - pr_debug("In mxc_v4l2_g_std\n"); + pr_debug("%s\n", __func__); if (cam->device_type == 1) { /* Use this function to get what the TV-In device detects the @@ -1515,7 +1512,7 @@ static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) struct mxc_v4l_frame *frame; unsigned long lock_flags; - pr_debug("In MVC:mxc_v4l_dqueue\n"); + pr_debug("%s\n", __func__); if (!wait_event_interruptible_timeout(cam->enc_queue, cam->enc_counter != 0, @@ -1615,18 +1612,19 @@ static int mxc_v4l_open(struct file *file) int err = 0; struct sensor_data *sensor; - pr_debug("\nIn MVC: mxc_v4l_open\n"); - pr_debug(" device name is %s\n", dev->name); - if (!cam) { - pr_err("ERROR: v4l2 capture: Internal error, " - "cam_data not found!\n"); + pr_err("%s: %s cam_data not found!\n", __func__, dev->name); return -EBADF; } - - if (cam->sensor == NULL || - cam->sensor->type != v4l2_int_type_slave) { - pr_err("ERROR: v4l2 capture: slave not found!\n"); + if (!cam->sensor) { + pr_err("%s: %s no sensor ipu%d/csi%d\n", + __func__, dev->name, cam->ipu_id, cam->csi); + return -EAGAIN; + } + if (cam->sensor->type != v4l2_int_type_slave) { + pr_err("%s: %s wrong type ipu%d/csi%d, type=%d/%d\n", + __func__, dev->name, cam->ipu_id, cam->csi, + cam->sensor->type, v4l2_int_type_slave); return -EAGAIN; } @@ -1636,6 +1634,8 @@ static int mxc_v4l_open(struct file *file) __func__); return -EBADF; } + pr_debug("%s: %s ipu%d/csi%d\n", __func__, dev->name, + cam->ipu_id, cam->csi); down(&cam->busy_lock); err = 0; @@ -1763,11 +1763,10 @@ static int mxc_v4l_close(struct file *file) int err = 0; cam_data *cam = video_get_drvdata(dev); struct sensor_data *sensor; - pr_debug("In MVC:mxc_v4l_close\n"); + pr_debug("%s\n", __func__); if (!cam) { - pr_err("ERROR: v4l2 capture: Internal error, " - "cam_data not found!\n"); + pr_err("%s: cam_data not found!\n", __func__); return -EBADF; } @@ -1939,7 +1938,7 @@ static long mxc_v4l_do_ioctl(struct file *file, int retval = 0; unsigned long lock_flags; - pr_debug("In MVC: mxc_v4l_do_ioctl %x\n", ioctlnr); + pr_debug("%s: %x ipu%d/csi%d\n", __func__, ioctlnr, cam->ipu_id, cam->csi); wait_event_interruptible(cam->power_queue, cam->low_power == false); /* make this _really_ smp-safe */ if (ioctlnr != VIDIOC_DQBUF) @@ -2461,7 +2460,7 @@ static long mxc_v4l_do_ioctl(struct file *file, static long mxc_v4l_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - pr_debug("In MVC:mxc_v4l_ioctl\n"); + pr_debug("%s\n", __func__); return video_usercopy(file, cmd, arg, mxc_v4l_do_ioctl); } @@ -2481,8 +2480,7 @@ static int mxc_mmap(struct file *file, struct vm_area_struct *vma) int res = 0; cam_data *cam = video_get_drvdata(dev); - pr_debug("In MVC:mxc_mmap\n"); - pr_debug(" pgoff=0x%lx, start=0x%lx, end=0x%lx\n", + pr_debug("%s:pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__, vma->vm_pgoff, vma->vm_start, vma->vm_end); /* make this _really_ smp-safe */ @@ -2523,7 +2521,7 @@ static unsigned int mxc_poll(struct file *file, struct poll_table_struct *wait) wait_queue_head_t *queue = NULL; int res = POLLIN | POLLRDNORM; - pr_debug("In MVC:mxc_poll\n"); + pr_debug("%s\n", __func__); if (down_interruptible(&cam->busy_lock)) return -EINTR; @@ -2581,7 +2579,7 @@ static void camera_callback(u32 mask, void *dev) if (cam == NULL) return; - pr_debug("In MVC:camera_callback\n"); + pr_debug("%s\n", __func__); spin_lock(&cam->queue_int_lock); spin_lock(&cam->dqueue_int_lock); @@ -2662,7 +2660,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) int ret = 0; struct v4l2_device *v4l2_dev; - pr_debug("In MVC: init_camera_struct\n"); + pr_debug("%s\n", __func__); ret = of_property_read_u32(np, "ipu_id", &ipu_id); if (ret) { @@ -2934,7 +2932,7 @@ static int mxc_v4l2_suspend(struct platform_device *pdev, pm_message_t state) { cam_data *cam = platform_get_drvdata(pdev); - pr_debug("In MVC:mxc_v4l2_suspend\n"); + pr_debug("%s\n", __func__); if (cam == NULL) return -1; @@ -2976,7 +2974,7 @@ static int mxc_v4l2_resume(struct platform_device *pdev) { cam_data *cam = platform_get_drvdata(pdev); - pr_debug("In MVC:mxc_v4l2_resume\n"); + pr_debug("%s\n", __func__); if (cam == NULL) return -1; @@ -3035,21 +3033,17 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) int i; struct sensor_data *sdata = slave->priv; - pr_debug("In MVC: mxc_v4l2_master_attach\n"); - pr_debug(" slave.name = %s\n", slave->name); - pr_debug(" master.name = %s\n", slave->u.slave->master->name); + pr_debug("%s:slave.name = %s, master.name = %s\n", __func__, + slave->name, slave->u.slave->master->name); if (slave == NULL) { pr_err("ERROR: v4l2 capture: slave parameter not valid.\n"); return -1; } - if (sdata->ipu_id != cam->ipu_id) { - pr_debug("%s: ipu doesn't match\n", __func__); - return -1; - } - if (sdata->csi != cam->csi) { - pr_debug("%s: csi doesn't match\n", __func__); + if ((sdata->ipu_id != cam->ipu_id) || (sdata->csi != cam->csi)) { + pr_debug("%s: ipu(%d:%d)/csi(%d:%d) doesn't match\n", __func__, + sdata->ipu_id, cam->ipu_id, sdata->csi, cam->csi); return -1; } @@ -3103,6 +3097,8 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) __func__, cam->crop_current.width, cam->crop_current.height); + pr_info("%s: ipu%d:/csi%d attached %s:%s\n", __func__, cam->ipu_id, cam->csi, + slave->name, slave->u.slave->master->name); return 0; } @@ -3114,7 +3110,7 @@ static void mxc_v4l2_master_detach(struct v4l2_int_device *slave) unsigned int i; cam_data *cam = slave->u.slave->master->priv; - pr_debug("In MVC:mxc_v4l2_master_detach\n"); + pr_debug("%s\n", __func__); if (cam->sensor_index > 1) { for (i = 0; i < cam->sensor_index; i++) { @@ -3145,7 +3141,7 @@ static __init int camera_init(void) { u8 err = 0; - pr_debug("In MVC:camera_init\n"); + pr_debug("%s\n", __func__); /* Register the device driver structure. */ err = platform_driver_register(&mxc_v4l2_driver); @@ -3163,7 +3159,7 @@ static __init int camera_init(void) */ static void __exit camera_exit(void) { - pr_debug("In MVC: camera_exit\n"); + pr_debug("%s\n", __func__); platform_driver_unregister(&mxc_v4l2_driver); } From 04596630b9fd25560f6fecb8dbca113ac9c67c4a Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 27 Feb 2014 19:50:18 -0700 Subject: [PATCH 0325/1983] ov5640_mipi: change registered name from ov5640 to ov5640_mipi to reduce confusion --- drivers/media/platform/mxc/capture/ov5640_mipi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 6254662ee5dc26..cbf4441cbdce66 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -1978,7 +1978,7 @@ static struct v4l2_int_slave ov5640_slave = { static struct v4l2_int_device ov5640_int_device = { .module = THIS_MODULE, - .name = "ov5640", + .name = "ov5640_mipi", .type = v4l2_int_type_slave, .u = { .slave = &ov5640_slave, From 92617cbb454f76baafda95d420c31445b6fd76dd Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 4 Mar 2014 19:24:08 -0700 Subject: [PATCH 0326/1983] mxc_v4l2_capture: add mxc_camera_common_lock/unlock, fix device name --- .../platform/mxc/capture/mxc_v4l2_capture.c | 18 +++++++++++++++++- .../platform/mxc/capture/mxc_v4l2_capture.h | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index ef02783088f75d..276cfb801150ed 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -2659,6 +2660,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) int ipu_id, csi_id, mclk_source; int ret = 0; struct v4l2_device *v4l2_dev; + static int camera_id; pr_debug("%s\n", __func__); @@ -2779,7 +2781,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); cam->self->module = THIS_MODULE; - sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); + sprintf(cam->self->name, "mxc_v4l2_cap%d", camera_id++); cam->self->type = v4l2_int_type_master; cam->self->u.master = &mxc_v4l2_master; @@ -3132,6 +3134,20 @@ static void mxc_v4l2_master_detach(struct v4l2_int_device *slave) vidioc_int_dev_exit(slave); } +DEFINE_MUTEX(camera_common_mutex); + +void mxc_camera_common_lock(void) +{ + mutex_lock(&camera_common_mutex); +} +EXPORT_SYMBOL(mxc_camera_common_lock); + +void mxc_camera_common_unlock(void) +{ + mutex_unlock(&camera_common_mutex); +} +EXPORT_SYMBOL(mxc_camera_common_unlock); + /*! * Entry point for the V4L2 * diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 612e4cbce5f777..9283102e8511ae 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -263,4 +263,7 @@ struct sensor_data { }; void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi); +void mxc_camera_common_lock(void); +void mxc_camera_common_unlock(void); + #endif /* __MXC_V4L2_CAPTURE_H__ */ From 0ca29bc76d4d01eadc5b8973fe777c883c9b6f27 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 6 Mar 2014 14:40:19 -0700 Subject: [PATCH 0327/1983] ipu_prp_enc: improve error message --- drivers/media/platform/mxc/capture/ipu_prp_enc.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ipu_prp_enc.c b/drivers/media/platform/mxc/capture/ipu_prp_enc.c index af419e356e6da3..33613915956984 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_enc.c @@ -382,6 +382,7 @@ static int prp_enc_enabling_tasks(void *private) { cam_data *cam = (cam_data *) private; int err = 0; + int irq; CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n"); cam->dummy_frame.vaddress = dma_alloc_coherent(0, @@ -398,15 +399,12 @@ static int prp_enc_enabling_tasks(void *private) PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; - if (cam->rotation >= IPU_ROTATE_90_RIGHT) { - err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, - prp_enc_callback, 0, "Mxc Camera", cam); - } else { - err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, - prp_enc_callback, 0, "Mxc Camera", cam); - } - if (err != 0) { - printk(KERN_ERR "Error registering rot irq\n"); + irq = (cam->rotation >= IPU_ROTATE_90_RIGHT) ? + IPU_IRQ_PRP_ENC_ROT_OUT_EOF : IPU_IRQ_PRP_ENC_OUT_EOF; + err = ipu_request_irq(cam->ipu, irq, + prp_enc_callback, 0, "Mxc Camera", cam); + if (err) { + pr_err("%s: Error requesting irq=%d\n", __func__, irq); return err; } From 94af3e5c5bf52489ccf1a26345c9e4f08d42fff6 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 6 Mar 2014 14:41:41 -0700 Subject: [PATCH 0328/1983] ipu_csi_enc: improve error message --- drivers/media/platform/mxc/capture/ipu_csi_enc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index fb3c6a24322a8f..d8d2c5aee582e6 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -253,7 +253,7 @@ static int csi_enc_enabling_tasks(void *private) err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, csi_enc_callback, 0, "Mxc Camera", cam); if (err != 0) { - printk(KERN_ERR "Error registering rot irq\n"); + pr_err("%s: Error requesting IPU_IRQ_CSI0_OUT_EOF\n", __func__); return err; } From 42d97d1bce79b8d4bd81ff99a7b2f6866b38a0db Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 6 Mar 2014 14:48:50 -0700 Subject: [PATCH 0329/1983] mxc_v4l2_capture: improve debug messages Conflicts: drivers/media/platform/mxc/capture/mxc_v4l2_capture.c --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 276cfb801150ed..2aa4d0cb021adb 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -492,11 +492,11 @@ static int mxc_streamoff(cam_data *cam) { int err = 0; - pr_debug("%s\n", __func__); - + pr_debug("%s: ipu%d/csi%d capture_on=%d %s\n", __func__, cam->ipu_id, + cam->csi, cam->capture_on, + mxc_capture_inputs[cam->current_input].name); if (cam->capture_on == false) return 0; - /* For both CSI--MEM and CSI--IC--MEM * 1. wait for idmac eof * 2. disable csi first @@ -1570,6 +1570,7 @@ static void power_down_callback(struct work_struct *work) down(&cam->busy_lock); if (!cam->open_count) { + pr_info("%s: ipu%d/csi%d\n", __func__, cam->ipu_id, cam->csi); vidioc_int_s_power(cam->sensor, 0); cam->power_on = 0; } From e7ea25c7eab8f2899f26b84c276a24bb2e129c83 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 6 Mar 2014 14:55:38 -0700 Subject: [PATCH 0330/1983] mxc_v4l2_capture: pair up ipu_enable_csi/ipu_disable_csi --- .../platform/mxc/capture/ipu_bg_overlay_sdc.c | 7 ++----- .../media/platform/mxc/capture/ipu_csi_enc.c | 7 ++----- .../platform/mxc/capture/ipu_fg_overlay_sdc.c | 7 ++----- .../media/platform/mxc/capture/ipu_prp_enc.c | 7 ++----- .../platform/mxc/capture/ipu_prp_vf_sdc.c | 7 ++----- .../platform/mxc/capture/ipu_prp_vf_sdc_bg.c | 7 ++----- drivers/media/platform/mxc/capture/ipu_still.c | 4 ++-- .../platform/mxc/capture/mxc_v4l2_capture.h | 18 ++++++++++++++++++ 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c b/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c index 4a975b54a97768..7b0b7346ef1a89 100644 --- a/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c @@ -452,9 +452,7 @@ static int bg_overlay_stop(void *private) */ static int bg_overlay_enable_csi(void *private) { - cam_data *cam = (cam_data *) private; - - return ipu_enable_csi(cam->ipu, cam->csi); + return cam_ipu_enable_csi((cam_data *)private); } /*! @@ -471,8 +469,7 @@ static int bg_overlay_disable_csi(void *private) * when disable csi, wait for idmac eof. * it requests eof irq again */ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); - - return ipu_disable_csi(cam->ipu, cam->csi); + return cam_ipu_disable_csi(cam); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index d8d2c5aee582e6..fa0091630d0055 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -319,9 +319,7 @@ static int csi_enc_disabling_tasks(void *private) */ static int csi_enc_enable_csi(void *private) { - cam_data *cam = (cam_data *) private; - - return ipu_enable_csi(cam->ipu, cam->csi); + return cam_ipu_enable_csi((cam_data *)private); } /*! @@ -338,8 +336,7 @@ static int csi_enc_disable_csi(void *private) * when disable csi, wait for idmac eof. * it requests eof irq again */ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); - - return ipu_disable_csi(cam->ipu, cam->csi); + return cam_ipu_disable_csi(cam); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c b/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c index 80136156ceafac..012a9374385a60 100644 --- a/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c @@ -537,9 +537,7 @@ static int foreground_stop(void *private) */ static int foreground_enable_csi(void *private) { - cam_data *cam = (cam_data *) private; - - return ipu_enable_csi(cam->ipu, cam->csi); + return cam_ipu_enable_csi((cam_data *)private); } /*! @@ -556,8 +554,7 @@ static int foreground_disable_csi(void *private) * when disable csi, wait for idmac eof. * it requests eof irq again */ ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); - - return ipu_disable_csi(cam->ipu, cam->csi); + return cam_ipu_disable_csi(cam); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_enc.c b/drivers/media/platform/mxc/capture/ipu_prp_enc.c index 33613915956984..9de0f54190f93d 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_enc.c @@ -479,9 +479,7 @@ static int prp_enc_disabling_tasks(void *private) */ static int prp_enc_enable_csi(void *private) { - cam_data *cam = (cam_data *) private; - - return ipu_enable_csi(cam->ipu, cam->csi); + return cam_ipu_enable_csi((cam_data *)private); } /*! @@ -499,8 +497,7 @@ static int prp_enc_disable_csi(void *private) * it requests eof irq again */ if (cam->rotation < IPU_ROTATE_90_RIGHT) ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, cam); - - return ipu_disable_csi(cam->ipu, cam->csi); + return cam_ipu_disable_csi(cam); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c index b9610f1ed820ae..7a9a4313d8da5c 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c @@ -484,9 +484,7 @@ static int prpvf_stop(void *private) */ static int prp_vf_enable_csi(void *private) { - cam_data *cam = (cam_data *) private; - - return ipu_enable_csi(cam->ipu, cam->csi); + return cam_ipu_enable_csi((cam_data *)private); } /*! @@ -504,8 +502,7 @@ static int prp_vf_disable_csi(void *private) * it requests eof irq again */ if (cam->vf_rotation < IPU_ROTATE_VERT_FLIP) ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam); - - return ipu_disable_csi(cam->ipu, cam->csi); + return cam_ipu_disable_csi(cam); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c index a24d82dfca638b..d22d214aa7ce81 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c @@ -427,9 +427,7 @@ static int prpvf_stop(void *private) */ static int prp_vf_enable_csi(void *private) { - cam_data *cam = (cam_data *) private; - - return ipu_enable_csi(cam->ipu, cam->csi); + return cam_ipu_enable_csi((cam_data *)private); } /*! @@ -446,8 +444,7 @@ static int prp_vf_disable_csi(void *private) * when disable csi, wait for idmac eof. * it requests eof irq again */ ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam); - - return ipu_disable_csi(cam->ipu, cam->csi); + return cam_ipu_disable_csi(cam); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_still.c b/drivers/media/platform/mxc/capture/ipu_still.c index b18c3cf873cc70..880a77769dee47 100644 --- a/drivers/media/platform/mxc/capture/ipu_still.c +++ b/drivers/media/platform/mxc/capture/ipu_still.c @@ -168,7 +168,7 @@ static int prp_still_start(void *private) ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0); ipu_enable_channel(cam->ipu, CSI_MEM); - ipu_enable_csi(cam->ipu, cam->csi); + cam_ipu_enable_csi(cam); #endif return err; @@ -192,7 +192,7 @@ static int prp_still_stop(void *private) ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam); #endif - ipu_disable_csi(cam->ipu, cam->csi); + cam_ipu_disable_csi(cam); ipu_disable_channel(cam->ipu, CSI_MEM, true); ipu_uninit_channel(cam->ipu, CSI_MEM); diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 9283102e8511ae..88c9190960e5ce 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -204,6 +204,7 @@ typedef struct _cam_data { /* misc status flag */ bool overlay_on; bool capture_on; + bool ipu_enable_csi_called; int overlay_pid; int capture_pid; bool low_power; @@ -266,4 +267,21 @@ void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi); void mxc_camera_common_lock(void); void mxc_camera_common_unlock(void); +static inline int cam_ipu_enable_csi(cam_data *cam) +{ + int ret = ipu_enable_csi(cam->ipu, cam->csi); + if (!ret) + cam->ipu_enable_csi_called = 1; + return ret; +} + +static inline int cam_ipu_disable_csi(cam_data *cam) +{ + if (!cam->ipu_enable_csi_called) + return 0; + cam->ipu_enable_csi_called = 0; + return ipu_disable_csi(cam->ipu, cam->csi); +} + + #endif /* __MXC_V4L2_CAPTURE_H__ */ From 33eee381e371711d0436573c0141711b5df6148f Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 6 Mar 2014 14:53:54 -0700 Subject: [PATCH 0331/1983] ipu_common: ipu_free_irq, do nothing if not owner --- drivers/mxc/ipu3/ipu_common.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 1ae9b7f9e77586..7e2ed96722731e 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -2823,14 +2823,16 @@ void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id) _ipu_get(ipu); + if (ipu->irq_list[irq].dev_id != dev_id) + return; + spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); /* disable the interrupt */ reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg &= ~IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); - if (ipu->irq_list[irq].dev_id == dev_id) - memset(&ipu->irq_list[irq], 0, sizeof(ipu->irq_list[irq])); + memset(&ipu->irq_list[irq], 0, sizeof(ipu->irq_list[irq])); spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); From b853fdb4c229c3f64701bfd278d50589dfa30e68 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 6 Mar 2014 19:17:08 -0700 Subject: [PATCH 0332/1983] add new API ipu_channel_request/disable/free --- .../platform/mxc/capture/ipu_bg_overlay_sdc.c | 10 ++-- .../media/platform/mxc/capture/ipu_csi_enc.c | 10 ++-- .../platform/mxc/capture/ipu_fg_overlay_sdc.c | 10 ++-- .../media/platform/mxc/capture/ipu_prp_enc.c | 25 +++++---- .../platform/mxc/capture/ipu_prp_vf_sdc.c | 28 +++++----- .../platform/mxc/capture/ipu_prp_vf_sdc_bg.c | 24 +++++---- .../media/platform/mxc/capture/ipu_still.c | 11 ++-- .../platform/mxc/capture/mxc_v4l2_capture.h | 2 + drivers/mxc/ipu3/ipu_common.c | 53 +++++++++++++++++++ drivers/mxc/ipu3/ipu_prv.h | 10 +++- include/linux/ipu-v3.h | 6 +++ 11 files changed, 129 insertions(+), 60 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c b/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c index 7b0b7346ef1a89..8680f45981ea5f 100644 --- a/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c @@ -230,9 +230,9 @@ static int csi_enc_setup(cam_data *cam) } pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); - err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); - if (err != 0) { - printk(KERN_ERR "ipu_init_channel %d\n", err); + err = ipu_channel_request(cam->ipu, CSI_MEM, ¶ms, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); goto out_1; } @@ -389,9 +389,9 @@ static int bg_overlay_stop(void *private) if (cam->overlay_active == false) return 0; - err = ipu_disable_channel(cam->ipu, CSI_MEM, true); + err = ipu_channel_disable(cam->ipu_chan, true); - ipu_uninit_channel(cam->ipu, CSI_MEM); + ipu_channel_free(&cam->ipu_chan); csi_buffer_num = 0; diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index fa0091630d0055..ad9371567b5076 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -160,9 +160,9 @@ static int csi_enc_setup(cam_data *cam) } #endif - err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); - if (err != 0) { - printk(KERN_ERR "ipu_init_channel %d\n", err); + err = ipu_channel_request(cam->ipu, CSI_MEM, ¶ms, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); return err; } @@ -282,9 +282,9 @@ static int csi_enc_disabling_tasks(void *private) int csi_id; #endif - err = ipu_disable_channel(cam->ipu, CSI_MEM, true); + err = ipu_channel_disable(cam->ipu_chan, true); - ipu_uninit_channel(cam->ipu, CSI_MEM); + ipu_channel_free(&cam->ipu_chan); if (cam->dummy_frame.vaddress != 0) { dma_free_coherent(0, cam->dummy_frame.buffer.length, diff --git a/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c b/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c index 012a9374385a60..597ecb69b2c743 100644 --- a/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c @@ -236,9 +236,9 @@ static int csi_enc_setup(cam_data *cam) } pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]); - err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); - if (err != 0) { - printk(KERN_ERR "ipu_init_channel %d\n", err); + err = ipu_channel_request(cam->ipu, CSI_MEM, ¶ms, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); goto out_1; } @@ -460,9 +460,9 @@ static int foreground_stop(void *private) if (cam->overlay_active == false) return 0; - err = ipu_disable_channel(cam->ipu, CSI_MEM, true); + err = ipu_channel_disable(cam->ipu_chan, true); - ipu_uninit_channel(cam->ipu, CSI_MEM); + ipu_channel_free(&cam->ipu_chan); csi_buffer_num = 0; buffer_num = 0; diff --git a/drivers/media/platform/mxc/capture/ipu_prp_enc.c b/drivers/media/platform/mxc/capture/ipu_prp_enc.c index 9de0f54190f93d..5b4673ac92ccda 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_enc.c @@ -164,9 +164,9 @@ static int prp_enc_setup(cam_data *cam) } #endif - err = ipu_init_channel(cam->ipu, CSI_PRP_ENC_MEM, &enc); - if (err != 0) { - printk(KERN_ERR "ipu_init_channel %d\n", err); + err = ipu_channel_request(cam->ipu, CSI_PRP_ENC_MEM, &enc, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); return err; } @@ -222,9 +222,9 @@ static int prp_enc_setup(cam_data *cam) return err; } - err = ipu_init_channel(cam->ipu, MEM_ROT_ENC_MEM, NULL); - if (err != 0) { - printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n"); + err = ipu_channel_request(cam->ipu, MEM_ROT_ENC_MEM, NULL, &cam->ipu_chan_rot); + if (err) { + pr_err("%s:ipu_channel_request %d for rot\n", __func__, err); return err; } @@ -427,6 +427,7 @@ static int prp_enc_disabling_tasks(void *private) { cam_data *cam = (cam_data *) private; int err = 0; + int err2 = 0; #ifdef CONFIG_MXC_MIPI_CSI2 void *mipi_csi2_info; int ipu_id; @@ -438,13 +439,11 @@ static int prp_enc_disabling_tasks(void *private) ipu_unlink_channels(cam->ipu, CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM); } - err = ipu_disable_channel(cam->ipu, CSI_PRP_ENC_MEM, true); - if (cam->rotation >= IPU_ROTATE_90_RIGHT) - err |= ipu_disable_channel(cam->ipu, MEM_ROT_ENC_MEM, true); + err = ipu_channel_disable(cam->ipu_chan, true); + err2 = ipu_channel_disable(cam->ipu_chan_rot, true); - ipu_uninit_channel(cam->ipu, CSI_PRP_ENC_MEM); - if (cam->rotation >= IPU_ROTATE_90_RIGHT) - ipu_uninit_channel(cam->ipu, MEM_ROT_ENC_MEM); + ipu_channel_free(&cam->ipu_chan); + ipu_channel_free(&cam->ipu_chan_rot); if (cam->dummy_frame.vaddress != 0) { dma_free_coherent(0, cam->dummy_frame.buffer.length, @@ -468,7 +467,7 @@ static int prp_enc_disabling_tasks(void *private) } #endif - return err; + return err ? err : err2; } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c index 7a9a4313d8da5c..e9385ffd0ec852 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c @@ -199,9 +199,11 @@ static int prpvf_start(void *private) } #endif - err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf); - if (err != 0) + err = ipu_channel_request(cam->ipu, CSI_PRP_VF_MEM, &vf, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); goto out_5; + } if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -252,9 +254,9 @@ static int prpvf_start(void *private) if (err != 0) goto out_3; - err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL); - if (err != 0) { - printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n"); + err = ipu_channel_request(cam->ipu, MEM_ROT_VF_MEM, NULL, &cam->ipu_chan_rot); + if (err) { + pr_err("%s:ipu_channel_request %d for rot\n", __func__, err); goto out_3; } @@ -359,8 +361,7 @@ static int prpvf_start(void *private) out_1: ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL); out_2: - if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) - ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); + ipu_channel_free(&cam->ipu_chan_rot); out_3: if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -377,7 +378,7 @@ static int prpvf_start(void *private) cam->vf_bufs[1] = 0; } out_4: - ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); + ipu_channel_free(&cam->ipu_chan); out_5: return err; } @@ -423,13 +424,10 @@ static int prpvf_stop(void *private) } buffer_num = 0; - ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true); - - if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) { - ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true); - ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); - } - ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); + ipu_channel_disable(cam->ipu_chan, true); + ipu_channel_disable(cam->ipu_chan_rot, true); + ipu_channel_free(&cam->ipu_chan_rot); + ipu_channel_free(&cam->ipu_chan); console_lock(); fb_blank(fbi, FB_BLANK_POWERDOWN); diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c index d22d214aa7ce81..fc027b0f075354 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c @@ -184,9 +184,11 @@ static int prpvf_start(void *private) } #endif - err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf); - if (err != 0) + err = ipu_channel_request(cam->ipu, CSI_PRP_VF_MEM, &vf, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); goto out_4; + } if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -232,9 +234,9 @@ static int prpvf_start(void *private) printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n"); goto out_3; } - err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL); - if (err != 0) { - printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n"); + err = ipu_channel_request(cam->ipu, MEM_ROT_VF_MEM, NULL, &cam->ipu_chan_rot); + if (err) { + pr_err("%s:ipu_channel_request %d for rot\n", __func__, err); goto out_3; } @@ -313,9 +315,9 @@ static int prpvf_start(void *private) out_1: ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL); out_2: - ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); + ipu_channel_free(&cam->ipu_chan_rot); out_3: - ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); + ipu_channel_free(&cam->ipu_chan); out_4: if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -366,10 +368,10 @@ static int prpvf_stop(void *private) ipu_free_irq(disp_ipu, IPU_IRQ_BG_SF_END, cam); - ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true); - ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true); - ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM); - ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM); + ipu_channel_disable(cam->ipu_chan, true); + ipu_channel_disable(cam->ipu_chan_rot, true); + ipu_channel_free(&cam->ipu_chan); + ipu_channel_free(&cam->ipu_chan_rot); #ifdef CONFIG_MXC_MIPI_CSI2 mipi_csi2_info = mipi_csi2_get_info(); diff --git a/drivers/media/platform/mxc/capture/ipu_still.c b/drivers/media/platform/mxc/capture/ipu_still.c index 880a77769dee47..e1345bb7b1424e 100644 --- a/drivers/media/platform/mxc/capture/ipu_still.c +++ b/drivers/media/platform/mxc/capture/ipu_still.c @@ -123,9 +123,11 @@ static int prp_still_start(void *private) } memset(¶ms, 0, sizeof(params)); - err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms); - if (err != 0) + err = ipu_channel_request(cam->ipu, CSI_MEM, ¶ms, &cam->ipu_chan); + if (err) { + pr_err("%s:ipu_channel_request %d\n", __func__, err); return err; + } err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, pixel_fmt, cam->v2f.fmt.pix.width, @@ -193,9 +195,8 @@ static int prp_still_stop(void *private) #endif cam_ipu_disable_csi(cam); - ipu_disable_channel(cam->ipu, CSI_MEM, true); - ipu_uninit_channel(cam->ipu, CSI_MEM); - + ipu_channel_disable(cam->ipu_chan, true); + ipu_channel_free(&cam->ipu_chan); return err; } diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 88c9190960e5ce..fce9c614e377e9 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -205,6 +205,8 @@ typedef struct _cam_data { bool overlay_on; bool capture_on; bool ipu_enable_csi_called; + struct ipu_chan *ipu_chan; + struct ipu_chan *ipu_chan_rot; int overlay_pid; int capture_pid; bool low_power; diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 7e2ed96722731e..74bd861dd7afec 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -932,6 +932,38 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel } EXPORT_SYMBOL(ipu_init_channel); +int32_t ipu_channel_request(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_channel_params_t *params, struct ipu_chan **p_ipu_chan) +{ + struct ipu_chan *ipu_chan; + unsigned channel_id = IPU_CHAN_ID(channel); + int32_t ret; + + dev_dbg(ipu->dev, "init channel = %d\n", channel_id); + *p_ipu_chan = NULL; + if (channel_id >= ARRAY_SIZE(ipu->chan)) { + dev_err(ipu->dev, "%s: ch = %d is too big!\n", __func__, + channel_id); + return -ENODEV; + } + ipu_chan = &ipu->chan[channel_id]; + if (ipu_chan->p_ipu_chan && (ipu_chan->p_ipu_chan != p_ipu_chan)) { + dev_err(ipu->dev, "%s: ch = %d is busy!\n", __func__, + channel_id); + return -EBUSY; + } + ipu_chan->p_ipu_chan = p_ipu_chan; + ipu_chan->ipu = ipu; + ipu_chan->channel = channel; + ret = ipu_init_channel(ipu, channel, params); + if (ret) + ipu_chan->p_ipu_chan = NULL; + else + *p_ipu_chan = ipu_chan; + return ret; +} +EXPORT_SYMBOL(ipu_channel_request); + /*! * This function is called to uninitialize a logical IPU channel. * @@ -1170,6 +1202,18 @@ void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel) } EXPORT_SYMBOL(ipu_uninit_channel); +void ipu_channel_free(struct ipu_chan **p_ipu_chan) +{ + struct ipu_chan *ipu_chan = *p_ipu_chan; + + *p_ipu_chan = NULL; + if (ipu_chan) { + ipu_chan->p_ipu_chan = NULL; + ipu_uninit_channel(ipu_chan->ipu, ipu_chan->channel); + } +} +EXPORT_SYMBOL(ipu_channel_free); + /*! * This function is called to initialize buffer(s) for logical IPU channel. * @@ -2472,6 +2516,15 @@ int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wai } EXPORT_SYMBOL(ipu_disable_channel); +int32_t ipu_channel_disable(struct ipu_chan *ipu_chan, bool wait_for_stop) +{ + if (ipu_chan) + if (!IS_ERR(ipu_chan)) + return ipu_disable_channel(ipu_chan->ipu, ipu_chan->channel, wait_for_stop); + return 0; +} +EXPORT_SYMBOL(ipu_channel_disable); + /*! * This function enables CSI. * diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h index 076339813809e5..ff3fee55ae4bc9 100644 --- a/drivers/mxc/ipu3/ipu_prv.h +++ b/drivers/mxc/ipu3/ipu_prv.h @@ -62,6 +62,14 @@ struct ipu_pltfm_data { bool bypass_reset; }; +struct ipu_soc; + +struct ipu_chan { + struct ipu_soc *ipu; + ipu_channel_t channel; + struct ipu_chan **p_ipu_chan; +}; + struct ipu_soc { bool online; struct ipu_pltfm_data *pdata; @@ -78,7 +86,7 @@ struct ipu_soc { int irq_sync; int irq_err; struct ipu_irq_node irq_list[IPU_IRQ_COUNT]; - + struct ipu_chan chan[32]; /*reg*/ void __iomem *cm_reg; void __iomem *idmac_reg; diff --git a/include/linux/ipu-v3.h b/include/linux/ipu-v3.h index c57d7ded4f6a79..359256982845cd 100644 --- a/include/linux/ipu-v3.h +++ b/include/linux/ipu-v3.h @@ -587,6 +587,12 @@ struct ipu_soc; struct ipu_soc *ipu_get_soc(int id); int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params); void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel); + +struct ipu_chan; +int32_t ipu_channel_request(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params, struct ipu_chan **p_ipu_chan); +void ipu_channel_free(struct ipu_chan **p_ipu_chan); +int32_t ipu_channel_disable(struct ipu_chan *ipu_chan, bool wait_for_stop); + void ipu_disable_hsp_clk(struct ipu_soc *ipu); static inline bool ipu_can_rotate_in_place(ipu_rotate_mode_t rot) From 1b28aeb4d126b0d01d80b80cd05323b49df99883 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 7 Mar 2014 13:08:38 -0700 Subject: [PATCH 0333/1983] mipi: reduce code, pair up mipi_csi2_pixelclk_enable/disable --- .../platform/mxc/capture/ipu_bg_overlay_sdc.c | 62 ++--------------- .../media/platform/mxc/capture/ipu_csi_enc.c | 63 ++---------------- .../platform/mxc/capture/ipu_fg_overlay_sdc.c | 63 ++---------------- .../media/platform/mxc/capture/ipu_prp_enc.c | 63 ++---------------- .../platform/mxc/capture/ipu_prp_vf_sdc.c | 60 ++--------------- .../platform/mxc/capture/ipu_prp_vf_sdc_bg.c | 60 ++--------------- .../platform/mxc/capture/mxc_v4l2_capture.h | 66 +++++++++++++++++++ drivers/mxc/ipu3/ipu_common.c | 20 +++--- include/linux/ipu-v3.h | 17 +++-- 9 files changed, 119 insertions(+), 355 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c b/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c index 8680f45981ea5f..7b4e3d33ae8ce4 100644 --- a/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c @@ -129,11 +129,6 @@ static int csi_enc_setup(cam_data *cam) ipu_channel_params_t params; u32 pixel_fmt; int err = 0, sensor_protocol = 0; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif if (!cam) { printk(KERN_ERR "cam private is NULL\n"); @@ -161,36 +156,9 @@ static int csi_enc_setup(cam_data *cam) printk(KERN_ERR "sensor protocol unsupported\n"); return -EINVAL; } - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - params.csi_mem.mipi_en = true; - params.csi_mem.mipi_vc = - mipi_csi2_get_virtual_channel(mipi_csi2_info); - params.csi_mem.mipi_id = - mipi_csi2_get_datatype(mipi_csi2_info); - - mipi_csi2_pixelclk_enable(mipi_csi2_info); - } else { - params.csi_mem.mipi_en = false; - params.csi_mem.mipi_vc = 0; - params.csi_mem.mipi_id = 0; - } - } else { - params.csi_mem.mipi_en = false; - params.csi_mem.mipi_vc = 0; - params.csi_mem.mipi_id = 0; - } - } -#endif + err = cam_mipi_csi2_enable(cam, ¶ms.csi_mem.mipi); + if (err) + return err; if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -379,12 +347,8 @@ static int bg_overlay_start(void *private) static int bg_overlay_stop(void *private) { int err = 0; + int err2 = 0; cam_data *cam = (cam_data *) private; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif if (cam->overlay_active == false) return 0; @@ -395,21 +359,7 @@ static int bg_overlay_stop(void *private) csi_buffer_num = 0; -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } - } -#endif - + err2 = cam_mipi_csi2_disable(cam); flush_work(&cam->csi_work_struct); cancel_work_sync(&cam->csi_work_struct); @@ -441,7 +391,7 @@ static int bg_overlay_stop(void *private) } cam->overlay_active = false; - return err; + return err ? err : err2; } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index ad9371567b5076..eb18af86aec1d7 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -69,11 +69,6 @@ static int csi_enc_setup(cam_data *cam) u32 pixel_fmt; int err = 0, sensor_protocol = 0; dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif CAMERA_TRACE("In csi_enc_setup\n"); if (!cam) { @@ -129,36 +124,9 @@ static int csi_enc_setup(cam_data *cam) printk(KERN_ERR "format not supported\n"); return -EINVAL; } - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - params.csi_mem.mipi_en = true; - params.csi_mem.mipi_vc = - mipi_csi2_get_virtual_channel(mipi_csi2_info); - params.csi_mem.mipi_id = - mipi_csi2_get_datatype(mipi_csi2_info); - - mipi_csi2_pixelclk_enable(mipi_csi2_info); - } else { - params.csi_mem.mipi_en = false; - params.csi_mem.mipi_vc = 0; - params.csi_mem.mipi_id = 0; - } - } else { - params.csi_mem.mipi_en = false; - params.csi_mem.mipi_vc = 0; - params.csi_mem.mipi_id = 0; - } - } -#endif + err = cam_mipi_csi2_enable(cam, ¶ms.csi_mem.mipi); + if (err) + return err; err = ipu_channel_request(cam->ipu, CSI_MEM, ¶ms, &cam->ipu_chan); if (err) { @@ -276,11 +244,7 @@ static int csi_enc_disabling_tasks(void *private) { cam_data *cam = (cam_data *) private; int err = 0; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif + int err2 = 0; err = ipu_channel_disable(cam->ipu_chan, true); @@ -292,23 +256,8 @@ static int csi_enc_disabling_tasks(void *private) cam->dummy_frame.paddress); cam->dummy_frame.vaddress = 0; } - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } - } -#endif - - return err; + err2 = cam_mipi_csi2_disable(cam); + return err ? err : err2; } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c b/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c index 597ecb69b2c743..54e59be281d40d 100644 --- a/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c @@ -134,11 +134,6 @@ static int csi_enc_setup(cam_data *cam) { ipu_channel_params_t params; int err = 0, sensor_protocol = 0; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif CAMERA_TRACE("In csi_enc_setup\n"); if (!cam) { @@ -167,36 +162,9 @@ static int csi_enc_setup(cam_data *cam) printk(KERN_ERR "sensor protocol unsupported\n"); return -EINVAL; } - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - params.csi_mem.mipi_en = true; - params.csi_mem.mipi_vc = - mipi_csi2_get_virtual_channel(mipi_csi2_info); - params.csi_mem.mipi_id = - mipi_csi2_get_datatype(mipi_csi2_info); - - mipi_csi2_pixelclk_enable(mipi_csi2_info); - } else { - params.csi_mem.mipi_en = false; - params.csi_mem.mipi_vc = 0; - params.csi_mem.mipi_id = 0; - } - } else { - params.csi_mem.mipi_en = false; - params.csi_mem.mipi_vc = 0; - params.csi_mem.mipi_id = 0; - } - } -#endif + err = cam_mipi_csi2_enable(cam, ¶ms.csi_mem.mipi); + if (err) + return err; if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -448,15 +416,10 @@ static int foreground_stop(void *private) { cam_data *cam = (cam_data *) private; int err = 0, i = 0; + int err2 = 0; struct fb_info *fbi = NULL; struct fb_var_screeninfo fbvar; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif - if (cam->overlay_active == false) return 0; @@ -491,21 +454,7 @@ static int foreground_stop(void *private) fbvar.nonstd = cam->fb_origin_std; fbvar.activate |= FB_ACTIVATE_FORCE; fb_set_var(fbi, &fbvar); - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } - } -#endif + err2 = cam_mipi_csi2_disable(cam); flush_work(&cam->csi_work_struct); cancel_work_sync(&cam->csi_work_struct); @@ -526,7 +475,7 @@ static int foreground_stop(void *private) } cam->overlay_active = false; - return err; + return err ? err : err2; } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_enc.c b/drivers/media/platform/mxc/capture/ipu_prp_enc.c index 5b4673ac92ccda..18fe28cc7ae934 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_enc.c @@ -71,11 +71,6 @@ static int prp_enc_setup(cam_data *cam) ipu_channel_params_t enc; int err = 0; dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif CAMERA_TRACE("In prp_enc_setup\n"); if (!cam) { @@ -133,36 +128,9 @@ static int prp_enc_setup(cam_data *cam) printk(KERN_ERR "format not supported\n"); return -EINVAL; } - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - enc.csi_prp_enc_mem.mipi_en = true; - enc.csi_prp_enc_mem.mipi_vc = - mipi_csi2_get_virtual_channel(mipi_csi2_info); - enc.csi_prp_enc_mem.mipi_id = - mipi_csi2_get_datatype(mipi_csi2_info); - - mipi_csi2_pixelclk_enable(mipi_csi2_info); - } else { - enc.csi_prp_enc_mem.mipi_en = false; - enc.csi_prp_enc_mem.mipi_vc = 0; - enc.csi_prp_enc_mem.mipi_id = 0; - } - } else { - enc.csi_prp_enc_mem.mipi_en = false; - enc.csi_prp_enc_mem.mipi_vc = 0; - enc.csi_prp_enc_mem.mipi_id = 0; - } - } -#endif + err = cam_mipi_csi2_enable(cam, &enc.csi_prp_enc_mem.mipi); + if (err) + return err; err = ipu_channel_request(cam->ipu, CSI_PRP_ENC_MEM, &enc, &cam->ipu_chan); if (err) { @@ -428,11 +396,7 @@ static int prp_enc_disabling_tasks(void *private) cam_data *cam = (cam_data *) private; int err = 0; int err2 = 0; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif + int err3 = 0; if (cam->rotation >= IPU_ROTATE_90_RIGHT) { ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam); @@ -451,23 +415,8 @@ static int prp_enc_disabling_tasks(void *private) cam->dummy_frame.paddress); cam->dummy_frame.vaddress = 0; } - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } - } -#endif - - return err ? err : err2; + err3 = cam_mipi_csi2_disable(cam); + return err ? err : (err2 ? err2 : err3); } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c index e9385ffd0ec852..e2d837a306848b 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c @@ -78,11 +78,6 @@ static int prpvf_start(void *private) u32 size = 2, temp = 0; int err = 0, i = 0; short *tmp, color; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif if (!cam) { printk(KERN_ERR "private is NULL\n"); @@ -169,35 +164,9 @@ static int prpvf_start(void *private) vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format; size = cam->win.w.width * cam->win.w.height * size; -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - vf.csi_prp_vf_mem.mipi_en = true; - vf.csi_prp_vf_mem.mipi_vc = - mipi_csi2_get_virtual_channel(mipi_csi2_info); - vf.csi_prp_vf_mem.mipi_id = - mipi_csi2_get_datatype(mipi_csi2_info); - - mipi_csi2_pixelclk_enable(mipi_csi2_info); - } else { - vf.csi_prp_vf_mem.mipi_en = false; - vf.csi_prp_vf_mem.mipi_vc = 0; - vf.csi_prp_vf_mem.mipi_id = 0; - } - } else { - vf.csi_prp_vf_mem.mipi_en = false; - vf.csi_prp_vf_mem.mipi_vc = 0; - vf.csi_prp_vf_mem.mipi_id = 0; - } - } -#endif + err = cam_mipi_csi2_enable(cam, &vf.csi_prp_vf_mem.mipi); + if (err) + return err; err = ipu_channel_request(cam->ipu, CSI_PRP_VF_MEM, &vf, &cam->ipu_chan); if (err) { @@ -395,11 +364,6 @@ static int prpvf_stop(void *private) int err = 0, i = 0; struct fb_info *fbi = NULL; struct fb_var_screeninfo fbvar; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif if (cam->overlay_active == false) return 0; @@ -439,21 +403,7 @@ static int prpvf_stop(void *private) fbvar.nonstd = cam->fb_origin_std; fbvar.activate |= FB_ACTIVATE_FORCE; fb_set_var(fbi, &fbvar); - -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } - } -#endif + err2 = cam_mipi_csi2_disable(cam); if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -471,7 +421,7 @@ static int prpvf_stop(void *private) } cam->overlay_active = false; - return err; + return err ? err : err2; } /*! diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c index fc027b0f075354..ff9d55262342f7 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c @@ -98,11 +98,6 @@ static int prpvf_start(void *private) u32 offset; u32 bpp, size = 3; int err = 0; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif if (!cam) { printk(KERN_ERR "private is NULL\n"); @@ -154,35 +149,9 @@ static int prpvf_start(void *private) vf.csi_prp_vf_mem.out_pixel_fmt = format; size = cam->win.w.width * cam->win.w.height * size; -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - vf.csi_prp_vf_mem.mipi_en = true; - vf.csi_prp_vf_mem.mipi_vc = - mipi_csi2_get_virtual_channel(mipi_csi2_info); - vf.csi_prp_vf_mem.mipi_id = - mipi_csi2_get_datatype(mipi_csi2_info); - - mipi_csi2_pixelclk_enable(mipi_csi2_info); - } else { - vf.csi_prp_vf_mem.mipi_en = false; - vf.csi_prp_vf_mem.mipi_vc = 0; - vf.csi_prp_vf_mem.mipi_id = 0; - } - } else { - vf.csi_prp_vf_mem.mipi_en = false; - vf.csi_prp_vf_mem.mipi_vc = 0; - vf.csi_prp_vf_mem.mipi_id = 0; - } - } -#endif + err = cam_mipi_csi2_enable(cam, &vf.csi_prp_vf_mem.mipi); + if (err) + return err; err = ipu_channel_request(cam->ipu, CSI_PRP_VF_MEM, &vf, &cam->ipu_chan); if (err) { @@ -356,12 +325,8 @@ static int prpvf_start(void *private) */ static int prpvf_stop(void *private) { + int err = 0; cam_data *cam = (cam_data *) private; -#ifdef CONFIG_MXC_MIPI_CSI2 - void *mipi_csi2_info; - int ipu_id; - int csi_id; -#endif if (cam->overlay_active == false) return 0; @@ -373,20 +338,7 @@ static int prpvf_stop(void *private) ipu_channel_free(&cam->ipu_chan); ipu_channel_free(&cam->ipu_chan_rot); -#ifdef CONFIG_MXC_MIPI_CSI2 - mipi_csi2_info = mipi_csi2_get_info(); - - if (mipi_csi2_info) { - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } - } -#endif + err = cam_mipi_csi2_disable(cam); if (cam->vf_bufs_vaddr[0]) { dma_free_coherent(0, cam->vf_bufs_size[0], @@ -418,7 +370,7 @@ static int prpvf_stop(void *private) buffer_num = 0; buffer_ready = 0; cam->overlay_active = false; - return 0; + return err; } /*! diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index fce9c614e377e9..1478368835b767 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "v4l2-int-device.h" @@ -205,6 +206,7 @@ typedef struct _cam_data { bool overlay_on; bool capture_on; bool ipu_enable_csi_called; + bool mipi_pixelclk_enabled; struct ipu_chan *ipu_chan; struct ipu_chan *ipu_chan_rot; int overlay_pid; @@ -285,5 +287,69 @@ static inline int cam_ipu_disable_csi(cam_data *cam) return ipu_disable_csi(cam->ipu, cam->csi); } +static inline int cam_mipi_csi2_enable(cam_data *cam, struct mipi_fields *mf) +{ +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; + + mipi_csi2_info = mipi_csi2_get_info(); + + if (!mipi_csi2_info) { +// printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", +// __func__, __FILE__); +// return -EPERM; + return 0; + } + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if (cam->ipu == ipu_get_soc(ipu_id) + && cam->csi == csi_id) { + mf->en = true; + mf->vc = mipi_csi2_get_virtual_channel(mipi_csi2_info); + mf->id = mipi_csi2_get_datatype(mipi_csi2_info); + if (!mipi_csi2_pixelclk_enable(mipi_csi2_info)) + cam->mipi_pixelclk_enabled = 1; + return 0; + } + } + mf->en = false; + mf->vc = 0; + mf->id = 0; +#endif + return 0; +} + +static inline int cam_mipi_csi2_disable(cam_data *cam) +{ +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; + + if (!cam->mipi_pixelclk_enabled) + return 0; + cam->mipi_pixelclk_enabled = 0; + mipi_csi2_info = mipi_csi2_get_info(); + + if (!mipi_csi2_info) { +// printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", +// __func__, __FILE__); +// return -EPERM; + return 0; + } + if (mipi_csi2_get_status(mipi_csi2_info)) { + ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); + csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); + + if ((cam->ipu == ipu_get_soc(ipu_id)) && (cam->csi == csi_id)) + mipi_csi2_pixelclk_disable(mipi_csi2_info); + } +#endif + return 0; +} #endif /* __MXC_V4L2_CAPTURE_H__ */ diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 74bd861dd7afec..a2a2edba866ffe 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -644,13 +644,13 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel ipu->csi_channel[params->csi_mem.csi] = channel; /*SMFC setting*/ - if (params->csi_mem.mipi_en) { + if (params->csi_mem.mipi.en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_mem.csi)); - _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc, + _ipu_smfc_init(ipu, channel, params->csi_mem.mipi.vc, params->csi_mem.csi); - _ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc, - params->csi_mem.mipi_id, params->csi_mem.csi); + _ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi.vc, + params->csi_mem.mipi.id, params->csi_mem.csi); } else { ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_mem.csi)); @@ -675,12 +675,12 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel ipu->ic_use_count++; ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel; - if (params->csi_prp_enc_mem.mipi_en) { + if (params->csi_prp_enc_mem.mipi.en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_enc_mem.csi)); _ipu_csi_set_mipi_di(ipu, - params->csi_prp_enc_mem.mipi_vc, - params->csi_prp_enc_mem.mipi_id, + params->csi_prp_enc_mem.mipi.vc, + params->csi_prp_enc_mem.mipi.id, params->csi_prp_enc_mem.csi); } else ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + @@ -716,12 +716,12 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel ipu->ic_use_count++; ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel; - if (params->csi_prp_vf_mem.mipi_en) { + if (params->csi_prp_vf_mem.mipi.en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_vf_mem.csi)); _ipu_csi_set_mipi_di(ipu, - params->csi_prp_vf_mem.mipi_vc, - params->csi_prp_vf_mem.mipi_id, + params->csi_prp_vf_mem.mipi.vc, + params->csi_prp_vf_mem.mipi.id, params->csi_prp_vf_mem.csi); } else ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + diff --git a/include/linux/ipu-v3.h b/include/linux/ipu-v3.h index 359256982845cd..1145c79030daa8 100644 --- a/include/linux/ipu-v3.h +++ b/include/linux/ipu-v3.h @@ -127,15 +127,18 @@ typedef enum { XY } display_addressing_t; +struct mipi_fields { + uint32_t id; + uint32_t vc; + bool en; +}; /*! * Union of initialization parameters for a logical channel. */ typedef union { struct { uint32_t csi; - uint32_t mipi_id; - uint32_t mipi_vc; - bool mipi_en; + struct mipi_fields mipi; bool interlaced; } csi_mem; struct { @@ -148,9 +151,7 @@ typedef union { uint32_t outh_resize_ratio; uint32_t outv_resize_ratio; uint32_t csi; - uint32_t mipi_id; - uint32_t mipi_vc; - bool mipi_en; + struct mipi_fields mipi; } csi_prp_enc_mem; struct { uint32_t in_width; @@ -189,9 +190,7 @@ typedef union { ipu_motion_sel motion_sel; enum v4l2_field field_fmt; uint32_t csi; - uint32_t mipi_id; - uint32_t mipi_vc; - bool mipi_en; + struct mipi_fields mipi; } csi_prp_vf_mem; struct { uint32_t in_width; From 835c8d47b7ac0bdf214ed8ab53d5c24558ed3ab2 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 14 Apr 2014 16:59:24 -0700 Subject: [PATCH 0334/1983] ov5640_mipi: make read_reg use only 1 i2c_transfer call Conflicts: drivers/media/platform/mxc/capture/ov5640_mipi.c --- .../media/platform/mxc/capture/ov5640_mipi.c | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index cbf4441cbdce66..4a0e114f963953 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -791,27 +791,32 @@ static s32 ov5640_write_reg(u16 reg, u8 val) static s32 ov5640_read_reg(u16 reg, u8 *val) { - u8 au8RegBuf[2] = {0}; - u8 u8RdVal = 0; - - au8RegBuf[0] = reg >> 8; - au8RegBuf[1] = reg & 0xff; - - if (2 != i2c_master_send(ov5640_data.i2c_client, au8RegBuf, 2)) { - pr_err("%s:write reg error:reg=%x\n", - __func__, reg); - return -1; - } + struct sensor_data *sensor = &ov5640_data; + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msgs[2]; + u8 buf[2]; + int ret; - if (1 != i2c_master_recv(ov5640_data.i2c_client, &u8RdVal, 1)) { - pr_err("%s:read reg error:reg=%x,val=%x\n", - __func__, reg, u8RdVal); - return -1; + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = buf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = buf; + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) { + pr_err("%s(mipi):reg=%x ret=%d\n", __func__, reg, ret); + return ret; } - - *val = u8RdVal; - - return u8RdVal; + *val = buf[0]; + pr_debug("%s(mipi):reg=%x,val=%x\n", __func__, reg, buf[0]); + return buf[0]; } static int prev_sysclk, prev_HTS; From fcf0491737e801ba2a2603fd746f1e36f89523da Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 6 May 2014 19:11:23 -0700 Subject: [PATCH 0335/1983] mxc_mipi_csi2: remove ipu/csi/v_channel, mipi camera will select this Conflicts: drivers/media/platform/mxc/capture/ov5640_mipi.c drivers/media/platform/mxc/capture/tc358743_h2c.c --- .../platform/mxc/capture/mxc_v4l2_capture.h | 49 +++++++------ .../media/platform/mxc/capture/ov5640_mipi.c | 7 +- drivers/mxc/mipi/mxc_mipi_csi2.c | 73 +------------------ drivers/mxc/mipi/mxc_mipi_csi2.h | 3 - include/linux/mipi_csi2.h | 6 -- 5 files changed, 32 insertions(+), 106 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 1478368835b767..b79fcecd19bb45 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -263,6 +263,8 @@ struct sensor_data { struct clk *sensor_clk; int ipu_id; int csi; + unsigned mipi_camera; + unsigned virtual_channel; /* Used with mipi */ void (*io_init)(void); }; @@ -291,9 +293,15 @@ static inline int cam_mipi_csi2_enable(cam_data *cam, struct mipi_fields *mf) { #ifdef CONFIG_MXC_MIPI_CSI2 void *mipi_csi2_info; - int ipu_id; - int csi_id; + struct sensor_data *sensor; + if (!cam->sensor) + return 0; + sensor = cam->sensor->priv; + if (!sensor) + return 0; + if (!sensor->mipi_camera) + return 0; mipi_csi2_info = mipi_csi2_get_info(); if (!mipi_csi2_info) { @@ -303,18 +311,12 @@ static inline int cam_mipi_csi2_enable(cam_data *cam, struct mipi_fields *mf) return 0; } if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if (cam->ipu == ipu_get_soc(ipu_id) - && cam->csi == csi_id) { - mf->en = true; - mf->vc = mipi_csi2_get_virtual_channel(mipi_csi2_info); - mf->id = mipi_csi2_get_datatype(mipi_csi2_info); - if (!mipi_csi2_pixelclk_enable(mipi_csi2_info)) - cam->mipi_pixelclk_enabled = 1; - return 0; - } + mf->en = true; + mf->vc = 0;//sensor->virtual_channel; + mf->id = mipi_csi2_get_datatype(mipi_csi2_info); + if (!mipi_csi2_pixelclk_enable(mipi_csi2_info)) + cam->mipi_pixelclk_enabled = 1; + return 0; } mf->en = false; mf->vc = 0; @@ -327,9 +329,15 @@ static inline int cam_mipi_csi2_disable(cam_data *cam) { #ifdef CONFIG_MXC_MIPI_CSI2 void *mipi_csi2_info; - int ipu_id; - int csi_id; + struct sensor_data *sensor; + if (!cam->sensor) + return 0; + sensor = cam->sensor->priv; + if (!sensor) + return 0; + if (!sensor->mipi_camera) + return 0; if (!cam->mipi_pixelclk_enabled) return 0; cam->mipi_pixelclk_enabled = 0; @@ -342,13 +350,8 @@ static inline int cam_mipi_csi2_disable(cam_data *cam) // return -EPERM; return 0; } - if (mipi_csi2_get_status(mipi_csi2_info)) { - ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info); - csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info); - - if ((cam->ipu == ipu_get_soc(ipu_id)) && (cam->csi == csi_id)) - mipi_csi2_pixelclk_disable(mipi_csi2_info); - } + if (mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_pixelclk_disable(mipi_csi2_info); #endif return 0; } diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 4a0e114f963953..1e2587cadea830 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -1412,7 +1412,7 @@ static int ov5640_init_mode(enum ov5640_frame_rate frame_rate, OV5640_set_AE_target(AE_Target); OV5640_get_light_freq(); OV5640_set_bandingfilter(); - ov5640_set_virtual_channel(ov5640_data.csi); + ov5640_set_virtual_channel(ov5640_data.virtual_channel); /* add delay to wait for sensor stable */ if (mode == ov5640_mode_QSXGA_2592_1944) { @@ -2032,6 +2032,8 @@ static int ov5640_probe(struct i2c_client *client, /* Set initial values for the sensor struct. */ memset(&ov5640_data, 0, sizeof(ov5640_data)); + + sensor->mipi_camera = 1; ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); if (IS_ERR(ov5640_data.sensor_clk)) { /* assuming clock enabled by default */ @@ -2100,12 +2102,13 @@ static int ov5640_probe(struct i2c_client *client, return -ENODEV; } + sensor->virtual_channel = sensor->csi | (sensor->ipu_id << 1); ov5640_standby(1); ov5640_int_device.priv = &ov5640_data; retval = v4l2_int_device_register(&ov5640_int_device); - clk_disable_unprepare(ov5640_data.sensor_clk); +// clk_disable_unprepare(ov5640_data.sensor_clk); pr_info("camera ov5640_mipi is found\n"); return retval; diff --git a/drivers/mxc/mipi/mxc_mipi_csi2.c b/drivers/mxc/mipi/mxc_mipi_csi2.c index df45c364f1441f..7fd154f7e6cd09 100644 --- a/drivers/mxc/mipi/mxc_mipi_csi2.c +++ b/drivers/mxc/mipi/mxc_mipi_csi2.c @@ -315,57 +315,6 @@ struct mipi_csi2_info *mipi_csi2_get_info(void) } EXPORT_SYMBOL(mipi_csi2_get_info); -/*! - * This function is called to get mipi csi2 bind ipu num. - * - * @return Returns mipi csi2 bind ipu num - */ -int mipi_csi2_get_bind_ipu(struct mipi_csi2_info *info) -{ - int ipu_id; - - _mipi_csi2_lock(info); - ipu_id = info->ipu_id; - _mipi_csi2_unlock(info); - - return ipu_id; -} -EXPORT_SYMBOL(mipi_csi2_get_bind_ipu); - -/*! - * This function is called to get mipi csi2 bind csi num. - * - * @return Returns mipi csi2 bind csi num - */ -unsigned int mipi_csi2_get_bind_csi(struct mipi_csi2_info *info) -{ - unsigned int csi_id; - - _mipi_csi2_lock(info); - csi_id = info->csi_id; - _mipi_csi2_unlock(info); - - return csi_id; -} -EXPORT_SYMBOL(mipi_csi2_get_bind_csi); - -/*! - * This function is called to get mipi csi2 virtual channel. - * - * @return Returns mipi csi2 virtual channel num - */ -unsigned int mipi_csi2_get_virtual_channel(struct mipi_csi2_info *info) -{ - unsigned int v_channel; - - _mipi_csi2_lock(info); - v_channel = info->v_channel; - _mipi_csi2_unlock(info); - - return v_channel; -} -EXPORT_SYMBOL(mipi_csi2_get_virtual_channel); - /** * This function is called by the driver framework to initialize the MIPI CSI2 * device. @@ -389,33 +338,13 @@ static int mipi_csi2_probe(struct platform_device *pdev) goto alloc_failed; } - ret = of_property_read_u32(np, "ipu_id", &(gmipi_csi2->ipu_id)); - if (ret) { - dev_err(&pdev->dev, "ipu_id missing or invalid\n"); - goto err; - } - - ret = of_property_read_u32(np, "csi_id", &(gmipi_csi2->csi_id)); - if (ret) { - dev_err(&pdev->dev, "csi_id missing or invalid\n"); - goto err; - } - - ret = of_property_read_u32(np, "v_channel", &(gmipi_csi2->v_channel)); - if (ret) { - dev_err(&pdev->dev, "v_channel missing or invalid\n"); - goto err; - } - ret = of_property_read_u32(np, "lanes", &(gmipi_csi2->lanes)); if (ret) { dev_err(&pdev->dev, "lanes missing or invalid\n"); goto err; } - if ((gmipi_csi2->ipu_id < 0) || (gmipi_csi2->ipu_id > 1) || - (gmipi_csi2->csi_id > 1) || (gmipi_csi2->v_channel > 3) || - (gmipi_csi2->lanes > 4)) { + if (gmipi_csi2->lanes > 4) { dev_err(&pdev->dev, "invalid param for mipi csi2!\n"); ret = -EINVAL; goto err; diff --git a/drivers/mxc/mipi/mxc_mipi_csi2.h b/drivers/mxc/mipi/mxc_mipi_csi2.h index 291d7e891e09de..66d777e9465188 100644 --- a/drivers/mxc/mipi/mxc_mipi_csi2.h +++ b/drivers/mxc/mipi/mxc_mipi_csi2.h @@ -29,9 +29,6 @@ /* driver private data */ struct mipi_csi2_info { bool mipi_en; - int ipu_id; - unsigned int csi_id; - unsigned int v_channel; unsigned int lanes; unsigned int datatype; struct clk *cfg_clk; diff --git a/include/linux/mipi_csi2.h b/include/linux/mipi_csi2.h index 7dc76fd293f815..35cce82c28517d 100644 --- a/include/linux/mipi_csi2.h +++ b/include/linux/mipi_csi2.h @@ -65,12 +65,6 @@ bool mipi_csi2_disable(struct mipi_csi2_info *info); bool mipi_csi2_get_status(struct mipi_csi2_info *info); -int mipi_csi2_get_bind_ipu(struct mipi_csi2_info *info); - -unsigned int mipi_csi2_get_bind_csi(struct mipi_csi2_info *info); - -unsigned int mipi_csi2_get_virtual_channel(struct mipi_csi2_info *info); - unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info); unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info, From 7f5e041ac1f93bfe2e495cabda166786e3ee8687 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 28 May 2014 14:28:56 -0700 Subject: [PATCH 0336/1983] mxc_v4l2_capture.h: remove useless checks in cam_mipi_csi2_disable --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index b79fcecd19bb45..c799c26d28c03f 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -329,15 +329,7 @@ static inline int cam_mipi_csi2_disable(cam_data *cam) { #ifdef CONFIG_MXC_MIPI_CSI2 void *mipi_csi2_info; - struct sensor_data *sensor; - if (!cam->sensor) - return 0; - sensor = cam->sensor->priv; - if (!sensor) - return 0; - if (!sensor->mipi_camera) - return 0; if (!cam->mipi_pixelclk_enabled) return 0; cam->mipi_pixelclk_enabled = 0; From 20ea6ee1b542a8df0f3d74d568917344a7744193 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 28 May 2014 14:32:02 -0700 Subject: [PATCH 0337/1983] ipu_prp_vf_sdc: define err2 to fix compile error, squash with mipi: reduce code --- drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c index e2d837a306848b..81edaa0dfeb91d 100644 --- a/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c +++ b/drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c @@ -362,6 +362,7 @@ static int prpvf_stop(void *private) { cam_data *cam = (cam_data *) private; int err = 0, i = 0; + int err2; struct fb_info *fbi = NULL; struct fb_var_screeninfo fbvar; From 733d0786cdc54f9ce50054e7e04635d35e6b6afc Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 28 May 2014 16:41:09 -0700 Subject: [PATCH 0338/1983] mxc_mipi_csi2: have mipi drivers specify lane in mipi_csi2_set_lanes Conflicts: drivers/media/platform/mxc/capture/tc358743_h2c.c --- .../media/platform/mxc/capture/ov5640_mipi.c | 2 +- drivers/mxc/mipi/mxc_mipi_csi2.c | 23 ++++--------------- drivers/mxc/mipi/mxc_mipi_csi2.h | 1 - include/linux/mipi_csi2.h | 2 +- 4 files changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 1e2587cadea830..b648dafedf13ab 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -1367,7 +1367,7 @@ static int ov5640_init_mode(enum ov5640_frame_rate frame_rate, return -1; } - mipi_csi2_set_lanes(mipi_csi2_info); + mipi_csi2_set_lanes(mipi_csi2_info, 2); /*Only reset MIPI CSI2 HW at sensor initialize*/ if (mode == ov5640_mode_INIT) diff --git a/drivers/mxc/mipi/mxc_mipi_csi2.c b/drivers/mxc/mipi/mxc_mipi_csi2.c index 7fd154f7e6cd09..45d9ec0572546c 100644 --- a/drivers/mxc/mipi/mxc_mipi_csi2.c +++ b/drivers/mxc/mipi/mxc_mipi_csi2.c @@ -141,16 +141,16 @@ EXPORT_SYMBOL(mipi_csi2_get_status); * @param info mipi csi2 hander * @return Returns setted value */ -unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info) +int mipi_csi2_set_lanes(struct mipi_csi2_info *info, unsigned lanes) { - unsigned int lanes; + if (--lanes > 3) + return -EINVAL; _mipi_csi2_lock(info); - mipi_csi2_write(info, info->lanes - 1, MIPI_CSI2_N_LANES); + mipi_csi2_write(info, lanes, MIPI_CSI2_N_LANES); lanes = mipi_csi2_read(info, MIPI_CSI2_N_LANES); _mipi_csi2_unlock(info); - - return lanes; + return ++lanes; } EXPORT_SYMBOL(mipi_csi2_set_lanes); @@ -327,7 +327,6 @@ EXPORT_SYMBOL(mipi_csi2_get_info); static int mipi_csi2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; struct resource *res; u32 mipi_csi2_dphy_ver; int ret; @@ -338,18 +337,6 @@ static int mipi_csi2_probe(struct platform_device *pdev) goto alloc_failed; } - ret = of_property_read_u32(np, "lanes", &(gmipi_csi2->lanes)); - if (ret) { - dev_err(&pdev->dev, "lanes missing or invalid\n"); - goto err; - } - - if (gmipi_csi2->lanes > 4) { - dev_err(&pdev->dev, "invalid param for mipi csi2!\n"); - ret = -EINVAL; - goto err; - } - /* initialize mutex */ mutex_init(&gmipi_csi2->mutex_lock); diff --git a/drivers/mxc/mipi/mxc_mipi_csi2.h b/drivers/mxc/mipi/mxc_mipi_csi2.h index 66d777e9465188..9a39f377c1b8e4 100644 --- a/drivers/mxc/mipi/mxc_mipi_csi2.h +++ b/drivers/mxc/mipi/mxc_mipi_csi2.h @@ -29,7 +29,6 @@ /* driver private data */ struct mipi_csi2_info { bool mipi_en; - unsigned int lanes; unsigned int datatype; struct clk *cfg_clk; struct clk *dphy_clk; diff --git a/include/linux/mipi_csi2.h b/include/linux/mipi_csi2.h index 35cce82c28517d..8a5e888554e8bf 100644 --- a/include/linux/mipi_csi2.h +++ b/include/linux/mipi_csi2.h @@ -65,7 +65,7 @@ bool mipi_csi2_disable(struct mipi_csi2_info *info); bool mipi_csi2_get_status(struct mipi_csi2_info *info); -unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info); +int mipi_csi2_set_lanes(struct mipi_csi2_info *info, unsigned lanes); unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info, unsigned int datatype); From d5c4a682c300a799ea6231fcc95fda2910a4657d Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 10 Jun 2014 18:25:07 -0700 Subject: [PATCH 0339/1983] mxc_v4l2_capture: match ipu/csi/mipi instead of only ipu/csi --- .../platform/mxc/capture/mxc_v4l2_capture.c | 63 +++++++++++++++++-- .../platform/mxc/capture/mxc_v4l2_capture.h | 2 + 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 2aa4d0cb021adb..66c737f11f0124 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -34,9 +34,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include #include @@ -1596,6 +1599,8 @@ void power_off_camera(cam_data *cam) schedule_delayed_work(&cam->power_down_work, (HZ * 2)); } +unsigned long csi_in_use; + /*! * V4L interface - open function * @@ -1613,6 +1618,7 @@ static int mxc_v4l_open(struct file *file) cam_data *cam = video_get_drvdata(dev); int err = 0; struct sensor_data *sensor; + int csi_bit; if (!cam) { pr_err("%s: %s cam_data not found!\n", __func__, dev->name); @@ -1640,11 +1646,45 @@ static int mxc_v4l_open(struct file *file) cam->ipu_id, cam->csi); down(&cam->busy_lock); + err = 0; if (signal_pending(current)) goto oops; if (cam->open_count++ == 0) { + struct regmap *gpr; + + csi_bit = (cam->ipu_id << 1) | cam->csi; + if (test_and_set_bit(csi_bit, &csi_in_use)) { + pr_err("%s: %s CSI already in use\n", __func__, dev->name); + err = -EBUSY; + cam->open_count = 0; + goto oops; + } + cam->csi_in_use = 1; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (of_machine_is_compatible("fsl,imx6q")) { + if (cam->ipu_id == cam->csi) { + unsigned shift = 19 + cam->csi; + unsigned mask = 1 << shift; + unsigned val = (cam->mipi_camera ? 0 : 1) << shift; + + regmap_update_bits(gpr, IOMUXC_GPR1, mask, val); + } + } else if (of_machine_is_compatible("fsl,imx6dl")) { + unsigned shift = cam->csi * 3; + unsigned mask = 7 << shift; + unsigned val = (cam->mipi_camera ? csi_bit : 4) << shift; + + regmap_update_bits(gpr, IOMUXC_GPR13, mask, val); + } + } else { + pr_err("%s: failed to find fsl,imx6q-iomux-gpr regmap\n", + __func__); + } + wait_event_interruptible(cam->power_queue, cam->low_power == false); @@ -1823,6 +1863,13 @@ static int mxc_v4l_close(struct file *file) mxc_free_frames(cam); cam->enc_counter++; power_off_camera(cam); + + if (cam->csi_in_use) { + int csi_bit = (cam->ipu_id << 1) | cam->csi; + + clear_bit(csi_bit, &csi_in_use); + cam->csi_in_use = 0; + } } up(&cam->busy_lock); @@ -2658,7 +2705,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) const struct of_device_id *of_id = of_match_device(mxc_v4l2_dt_ids, &pdev->dev); struct device_node *np = pdev->dev.of_node; - int ipu_id, csi_id, mclk_source; + int ipu_id, csi_id, mclk_source, mipi_camera; int ret = 0; struct v4l2_device *v4l2_dev; static int camera_id; @@ -2683,6 +2730,10 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) return ret; } + ret = of_property_read_u32(np, "mipi_camera", &mipi_camera); + if (ret) + mipi_camera = 0; + /* Default everything to 0 */ memset(cam, 0, sizeof(cam_data)); @@ -2772,6 +2823,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) cam->ipu_id = ipu_id; cam->csi = csi_id; + cam->mipi_camera = mipi_camera; cam->mclk_source = mclk_source; cam->mclk_on[cam->mclk_source] = false; @@ -3044,9 +3096,9 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) return -1; } - if ((sdata->ipu_id != cam->ipu_id) || (sdata->csi != cam->csi)) { - pr_debug("%s: ipu(%d:%d)/csi(%d:%d) doesn't match\n", __func__, - sdata->ipu_id, cam->ipu_id, sdata->csi, cam->csi); + if ((sdata->ipu_id != cam->ipu_id) || (sdata->csi != cam->csi) || (sdata->mipi_camera != cam->mipi_camera)) { + pr_info("%s: ipu(%d:%d)/csi(%d:%d)/mipi(%d:%d) doesn't match\n", __func__, + sdata->ipu_id, cam->ipu_id, sdata->csi, cam->csi, sdata->mipi_camera, cam->mipi_camera); return -1; } @@ -3100,7 +3152,8 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) __func__, cam->crop_current.width, cam->crop_current.height); - pr_info("%s: ipu%d:/csi%d attached %s:%s\n", __func__, cam->ipu_id, cam->csi, + pr_info("%s: ipu%d:/csi%d %s attached %s:%s\n", __func__, + cam->ipu_id, cam->csi, cam->mipi_camera ? "mipi" : "parallel", slave->name, slave->u.slave->master->name); return 0; } diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index c799c26d28c03f..964f7c7f5f6c59 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -215,6 +215,8 @@ typedef struct _cam_data { wait_queue_head_t power_queue; unsigned int ipu_id; unsigned int csi; + unsigned mipi_camera; + int csi_in_use; u8 mclk_source; bool mclk_on[2]; /* two mclk sources at most now */ int current_input; From ea4d0ae18c115e1a2eb14c1d4d40713e55e9acc1 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 26 Jun 2014 10:17:58 -0700 Subject: [PATCH 0340/1983] mxc_v4l2_capture: sensor_clk not required --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 66c737f11f0124..d75eeb4e6b6c0d 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1781,7 +1781,8 @@ static int mxc_v4l_open(struct file *file) cam->crop_bounds.height, cam_fmt.fmt.pix.pixelformat, csi_param); - clk_prepare_enable(sensor->sensor_clk); + if (!IS_ERR(sensor->sensor_clk)) + clk_prepare_enable(sensor->sensor_clk); power_up_camera(cam); } @@ -1838,7 +1839,8 @@ static int mxc_v4l_close(struct file *file) } if (--cam->open_count == 0) { - clk_disable_unprepare(sensor->sensor_clk); + if (!IS_ERR(sensor->sensor_clk)) + clk_disable_unprepare(sensor->sensor_clk); wait_event_interruptible(cam->power_queue, cam->low_power == false); pr_debug("mxc_v4l_close: release resource\n"); From 8d1e00737174e50ffd39ee90ef5ab9fcf628a18b Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 26 Jun 2014 12:18:28 -0700 Subject: [PATCH 0341/1983] mxc_v4l2_capture.h: add sensor width/height/left/top variables --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.h | 1 + include/uapi/linux/videodev2.h | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 964f7c7f5f6c59..319a3b1444bcee 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -247,6 +247,7 @@ struct sensor_data { struct v4l2_int_device *v4l2_int_device; struct i2c_client *i2c_client; struct v4l2_pix_format pix; + struct v4l2_sensor_dimension spix; struct v4l2_captureparm streamcap; bool on; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index b3f83e9e1037b9..5e1ed792c57148 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -139,6 +139,7 @@ enum v4l2_buf_type { #endif V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10, + V4L2_BUF_TYPE_SENSOR = 11, /* Deprecated, do not use */ V4L2_BUF_TYPE_PRIVATE = 0x80, }; @@ -284,6 +285,13 @@ struct v4l2_pix_format { __u32 priv; /* private data, depends on pixelformat */ }; +struct v4l2_sensor_dimension { + __u32 swidth; + __u32 sheight; + __u32 top; + __u32 left; +}; + /* Pixel format FOURCC depth Description */ /* RGB formats */ @@ -1705,6 +1713,7 @@ struct v4l2_format { __u32 type; union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ + struct v4l2_sensor_dimension spix; /* V4L2_BUF_TYPE_SENSOR */ struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */ From 150119d15f0bf972c7d08996e2a6056c7f3061d6 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 26 Jun 2014 13:25:22 -0700 Subject: [PATCH 0342/1983] mxc_v4l2_capture: move code to common subroutine --- .../platform/mxc/capture/mxc_v4l2_capture.c | 240 +++++++----------- 1 file changed, 90 insertions(+), 150 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index d75eeb4e6b6c0d..0945b5804d5f14 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1278,6 +1278,94 @@ static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) return ret; } +void setup_ifparm(cam_data *cam, int init_defrect) +{ + struct v4l2_format cam_fmt; + ipu_csi_signal_cfg_t csi_param; + struct v4l2_ifparm ifparm; + int swidth, sheight; + int sleft, stop; + + vidioc_int_g_ifparm(cam->sensor, &ifparm); + memset(&csi_param, 0, sizeof(csi_param)); + csi_param.csi = cam->csi; + csi_param.mclk = ifparm.u.bt656.clock_curr; + + pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr); + if (ifparm.u.bt656.clock_curr == 0) + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; + else + csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; + + csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; + + if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) { + csi_param.data_width = IPU_CSI_DATA_WIDTH_8; + } else if (ifparm.u.bt656.mode + == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) { + csi_param.data_width = IPU_CSI_DATA_WIDTH_10; + } else { + csi_param.data_width = IPU_CSI_DATA_WIDTH_8; + } + + csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; + csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; + csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct; + + /* if the capturemode changed, the size bounds will have changed. */ + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", + cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); + + csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; + + cam->crop_bounds.top = cam->crop_bounds.left = 0; + cam->crop_bounds.width = cam_fmt.fmt.pix.width; + cam->crop_bounds.height = cam_fmt.fmt.pix.height; + + /* + * Set the default current cropped resolution to be the same with + * the cropping boundary(except for tvin module). + */ + if (cam->device_type != 1) { + cam->crop_current.width = cam->crop_bounds.width; + cam->crop_current.height = cam->crop_bounds.height; + } + + if (init_defrect) { + /* This also is the max crop size for this device. */ + cam->crop_defrect.top = cam->crop_defrect.left = 0; + cam->crop_defrect.width = cam_fmt.fmt.pix.width; + cam->crop_defrect.height = cam_fmt.fmt.pix.height; + + /* At this point, this is also the current image size. */ + cam->crop_current.top = cam->crop_current.left = 0; + cam->crop_current.width = cam_fmt.fmt.pix.width; + cam->crop_current.height = cam_fmt.fmt.pix.height; + pr_debug("On Open: Input to ipu size is %d x %d\n", + cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); + pr_debug("End of %s: v2f pix widthxheight %d x %d\n", __func__, + cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); + pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", __func__, + cam->crop_bounds.width, cam->crop_bounds.height); + pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", __func__, + cam->crop_defrect.width, cam->crop_defrect.height); + pr_debug("End of %s: crop_current widthxheight %d x %d\n", __func__, + cam->crop_current.width, cam->crop_current.height); + } + /* This essentially loses the data at the left and bottom of the image + * giving a digital zoom image, if crop_current is less than the full + * size of the image. */ + ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, + cam->crop_current.height, cam->csi); + ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, + cam->crop_current.top, cam->csi); + ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, + cam->crop_bounds.height, + cam_fmt.fmt.pix.pixelformat, csi_param); +} + /*! * V4L2 - mxc_v4l2_s_param function * Allows setting of capturemode and frame rate. @@ -1289,10 +1377,7 @@ static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) */ static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) { - struct v4l2_ifparm ifparm; - struct v4l2_format cam_fmt; struct v4l2_streamparm currentparm; - ipu_csi_signal_cfg_t csi_param; u32 current_fps, parm_fps; int err = 0; @@ -1340,76 +1425,7 @@ static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) /* If resolution changed, need to re-program the CSI */ /* Get new values. */ - vidioc_int_g_ifparm(cam->sensor, &ifparm); - - csi_param.data_width = 0; - csi_param.clk_mode = 0; - csi_param.ext_vsync = 0; - csi_param.Vsync_pol = 0; - csi_param.Hsync_pol = 0; - csi_param.pixclk_pol = 0; - csi_param.data_pol = 0; - csi_param.sens_clksrc = 0; - csi_param.pack_tight = 0; - csi_param.force_eof = 0; - csi_param.data_en_pol = 0; - csi_param.data_fmt = 0; - csi_param.csi = cam->csi; - csi_param.mclk = 0; - - pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr); - if (ifparm.u.bt656.clock_curr == 0) - csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; - else - csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; - - csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; - - if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) { - csi_param.data_width = IPU_CSI_DATA_WIDTH_8; - } else if (ifparm.u.bt656.mode - == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) { - csi_param.data_width = IPU_CSI_DATA_WIDTH_10; - } else { - csi_param.data_width = IPU_CSI_DATA_WIDTH_8; - } - - csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; - csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; - csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct; - - /* if the capturemode changed, the size bounds will have changed. */ - cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); - pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", - cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); - - csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; - - cam->crop_bounds.top = cam->crop_bounds.left = 0; - cam->crop_bounds.width = cam_fmt.fmt.pix.width; - cam->crop_bounds.height = cam_fmt.fmt.pix.height; - - /* - * Set the default current cropped resolution to be the same with - * the cropping boundary(except for tvin module). - */ - if (cam->device_type != 1) { - cam->crop_current.width = cam->crop_bounds.width; - cam->crop_current.height = cam->crop_bounds.height; - } - - /* This essentially loses the data at the left and bottom of the image - * giving a digital zoom image, if crop_current is less than the full - * size of the image. */ - ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, - cam->crop_current.height, cam->csi); - ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, - cam->crop_current.top, - cam->csi); - ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, - cam->crop_bounds.height, - cam_fmt.fmt.pix.pixelformat, csi_param); + setup_ifparm(cam, 0); exit: @@ -1704,83 +1720,7 @@ static int mxc_v4l_open(struct file *file) INIT_LIST_HEAD(&cam->ready_q); INIT_LIST_HEAD(&cam->working_q); INIT_LIST_HEAD(&cam->done_q); - - vidioc_int_g_ifparm(cam->sensor, &ifparm); - - csi_param.sens_clksrc = 0; - - csi_param.clk_mode = 0; - csi_param.data_pol = 0; - csi_param.ext_vsync = 0; - - csi_param.pack_tight = 0; - csi_param.force_eof = 0; - csi_param.data_en_pol = 0; - - csi_param.mclk = ifparm.u.bt656.clock_curr; - - csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; - - if (ifparm.u.bt656.mode - == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) - csi_param.data_width = IPU_CSI_DATA_WIDTH_8; - else if (ifparm.u.bt656.mode - == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) - csi_param.data_width = IPU_CSI_DATA_WIDTH_10; - else - csi_param.data_width = IPU_CSI_DATA_WIDTH_8; - - - csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; - csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; - - csi_param.csi = cam->csi; - - cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); - - /* Reset the sizes. Needed to prevent carryover of last - * operation.*/ - cam->crop_bounds.top = cam->crop_bounds.left = 0; - cam->crop_bounds.width = cam_fmt.fmt.pix.width; - cam->crop_bounds.height = cam_fmt.fmt.pix.height; - - /* This also is the max crop size for this device. */ - cam->crop_defrect.top = cam->crop_defrect.left = 0; - cam->crop_defrect.width = cam_fmt.fmt.pix.width; - cam->crop_defrect.height = cam_fmt.fmt.pix.height; - - /* At this point, this is also the current image size. */ - cam->crop_current.top = cam->crop_current.left = 0; - cam->crop_current.width = cam_fmt.fmt.pix.width; - cam->crop_current.height = cam_fmt.fmt.pix.height; - - pr_debug("End of %s: v2f pix widthxheight %d x %d\n", - __func__, - cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height); - pr_debug("End of %s: crop_bounds widthxheight %d x %d\n", - __func__, - cam->crop_bounds.width, cam->crop_bounds.height); - pr_debug("End of %s: crop_defrect widthxheight %d x %d\n", - __func__, - cam->crop_defrect.width, cam->crop_defrect.height); - pr_debug("End of %s: crop_current widthxheight %d x %d\n", - __func__, - cam->crop_current.width, cam->crop_current.height); - - csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; - pr_debug("On Open: Input to ipu size is %d x %d\n", - cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); - ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, - cam->crop_current.height, - cam->csi); - ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, - cam->crop_current.top, - cam->csi); - ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, - cam->crop_bounds.height, - cam_fmt.fmt.pix.pixelformat, - csi_param); + setup_ifparm(cam, 1); if (!IS_ERR(sensor->sensor_clk)) clk_prepare_enable(sensor->sensor_clk); power_up_camera(cam); From ccb53cc95a5b56a30b987c6224ee57540b26af9e Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 19 Aug 2014 18:00:27 -0700 Subject: [PATCH 0343/1983] ov5640_mipi: ioctl_g_fmt_cap: check type --- .../media/platform/mxc/capture/ov5640_mipi.c | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index b648dafedf13ab..1e7e8b5f5483c1 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -1671,7 +1671,26 @@ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { struct sensor_data *sensor = s->priv; - f->fmt.pix = sensor->pix; + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->fmt.pix = sensor->pix; + pr_debug("%s: %dx%d\n", __func__, sensor->pix.width, sensor->pix.height); + break; + + case V4L2_BUF_TYPE_SENSOR: + pr_debug("%s: left=%d, top=%d, %dx%d\n", __func__, + sensor->spix.left, sensor->spix.top, + sensor->spix.swidth, sensor->spix.sheight); + f->fmt.spix = sensor->spix; + break; + + case V4L2_BUF_TYPE_PRIVATE: + break; + + default: + f->fmt.pix = sensor->pix; + break; + } return 0; } From bc093d47883cb67b93ffe8f5bbef240cd33377bc Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 4 Aug 2014 16:53:56 -0700 Subject: [PATCH 0344/1983] mxc_v4l2_capture: add setup_ifparm with parameters to support gs2971 bt656(hync/vsyn) and bt.1120(eav/sav) set active_top to 0 as this info should not be in this file --- .../platform/mxc/capture/mxc_v4l2_capture.c | 71 +++++++++++++------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 0945b5804d5f14..d1539c9cae0a62 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -190,7 +190,7 @@ static video_fmt_t video_fmts[] = { .raw_height = 525, /* SENS_FRM_HEIGHT */ .active_width = 720, /* ACT_FRM_WIDTH */ .active_height = 480, /* ACT_FRM_HEIGHT */ - .active_top = 13, + .active_top = 0, .active_left = 0, }, { /*! (B, G, H, I, N) PAL */ @@ -1292,25 +1292,45 @@ void setup_ifparm(cam_data *cam, int init_defrect) csi_param.mclk = ifparm.u.bt656.clock_curr; pr_debug(" clock_curr=mclk=%d\n", ifparm.u.bt656.clock_curr); - if (ifparm.u.bt656.clock_curr == 0) + switch (ifparm.if_type) { + case V4L2_IF_TYPE_BT1120_PROGRESSIVE_DDR: + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR; + break; + case V4L2_IF_TYPE_BT1120_PROGRESSIVE_SDR: + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR; + break; + case V4L2_IF_TYPE_BT1120_INTERLACED_DDR: + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR; + break; + case V4L2_IF_TYPE_BT1120_INTERLACED_SDR: + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR; + break; + case V4L2_IF_TYPE_BT656_PROGRESSIVE: + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; + break; + case V4L2_IF_TYPE_BT656_INTERLACED: csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; - else - csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; + break; + case V4L2_IF_TYPE_BT656: +// csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; +// break; + default: + csi_param.clk_mode = (ifparm.u.bt656.clock_curr == 0) ? + IPU_CSI_CLK_MODE_CCIR656_INTERLACED : + IPU_CSI_CLK_MODE_GATED_CLK; + } csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; - if (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT) { - csi_param.data_width = IPU_CSI_DATA_WIDTH_8; - } else if (ifparm.u.bt656.mode - == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) { - csi_param.data_width = IPU_CSI_DATA_WIDTH_10; - } else { - csi_param.data_width = IPU_CSI_DATA_WIDTH_8; - } + csi_param.data_width = + (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT) || + (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_BT_10BIT) ? + IPU_CSI_DATA_WIDTH_10 : IPU_CSI_DATA_WIDTH_8; csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct; + pr_debug("vsync_pol(%d) hsync_pol(%d) ext_vsync(%d)\n", csi_param.Vsync_pol, csi_param.Hsync_pol, csi_param.ext_vsync); /* if the capturemode changed, the size bounds will have changed. */ cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1354,16 +1374,30 @@ void setup_ifparm(cam_data *cam, int init_defrect) pr_debug("End of %s: crop_current widthxheight %d x %d\n", __func__, cam->crop_current.width, cam->crop_current.height); } + swidth = cam->crop_current.width; + sheight = cam->crop_current.height; + sleft = 0; + stop = 0; + cam_fmt.type = V4L2_BUF_TYPE_SENSOR; + cam_fmt.fmt.spix.swidth = 0; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + if (cam_fmt.fmt.spix.swidth) { + swidth = cam_fmt.fmt.spix.swidth; + sheight = cam_fmt.fmt.spix.sheight; + sleft = cam_fmt.fmt.spix.left; + stop = cam_fmt.fmt.spix.top; + } /* This essentially loses the data at the left and bottom of the image * giving a digital zoom image, if crop_current is less than the full * size of the image. */ - ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, - cam->crop_current.height, cam->csi); - ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, - cam->crop_current.top, cam->csi); + ipu_csi_window_size_crop(cam->ipu, + swidth, sheight, + cam->crop_current.width, cam->crop_current.height, + sleft + cam->crop_current.left, stop + cam->crop_current.top, + cam->csi); ipu_csi_init_interface(cam->ipu, cam->crop_bounds.width, cam->crop_bounds.height, - cam_fmt.fmt.pix.pixelformat, csi_param); + csi_param.data_fmt, csi_param); } /*! @@ -1627,9 +1661,6 @@ unsigned long csi_in_use; */ static int mxc_v4l_open(struct file *file) { - struct v4l2_ifparm ifparm; - struct v4l2_format cam_fmt; - ipu_csi_signal_cfg_t csi_param; struct video_device *dev = video_devdata(file); cam_data *cam = video_get_drvdata(dev); int err = 0; From 63eb065408d9487fab973c1253fb51bb97a15298 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 26 Jul 2012 14:29:20 +0800 Subject: [PATCH 0345/1983] ENGR00218441 MXC v4l2 capture:Fix code merge confliction This patch fixs code merge confliction after merging code from imx_3.0.35 branch to imx_3.0.35_android branch. Signed-off-by: Liu Ying --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index d1539c9cae0a62..f07b39ce17c8f2 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -351,7 +351,7 @@ static int mxc_v4l2_prepare_bufs(cam_data *cam, struct v4l2_buffer *buf) pr_debug("%s\n", __func__); if (buf->index < 0 || buf->index >= FRAME_NUM || buf->length < - PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage)) { + cam->v2f.fmt.pix.sizeimage) { pr_err("ERROR: v4l2 capture: mxc_v4l2_prepare_bufs buffers " "not allocated,index=%d, length=%d\n", buf->index, buf->length); @@ -1804,12 +1804,12 @@ static int mxc_v4l_close(struct file *file) err = stop_preview(cam); cam->overlay_on = false; } - if (cam->capture_pid == current->pid) { - err |= mxc_streamoff(cam); - wake_up_interruptible(&cam->enc_queue); - } if (--cam->open_count == 0) { + if (cam->capture_pid == current->pid) { + err |= mxc_streamoff(cam); + wake_up_interruptible(&cam->enc_queue); + } if (!IS_ERR(sensor->sensor_clk)) clk_disable_unprepare(sensor->sensor_clk); wait_event_interruptible(cam->power_queue, From 090e372c5803954a576d4a61719c2653b7d47a06 Mon Sep 17 00:00:00 2001 From: Xinyu Chen Date: Wed, 14 Dec 2011 12:05:29 +0800 Subject: [PATCH 0346/1983] ENGR00169880 v4l2 capture: reserve dummy framebuffer for 8M on probe On GB with 512MB DDR size, when we taking 5M pixel picture, the v4l2 capture will first allocate a 8M continuous memory for first frame, which may cause allocation failed. Reserve this buffer on probe when safe. Signed-off-by: Xinyu Chen --- .../media/platform/mxc/capture/ipu_csi_enc.c | 37 +++++++++++-------- .../platform/mxc/capture/mxc_v4l2_capture.c | 15 ++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index eb18af86aec1d7..d6f12aa277426f 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -203,18 +203,29 @@ static int csi_enc_enabling_tasks(void *private) int err = 0; CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n"); - cam->dummy_frame.vaddress = dma_alloc_coherent(0, - PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), - &cam->dummy_frame.paddress, - GFP_DMA | GFP_KERNEL); - if (cam->dummy_frame.vaddress == 0) { - pr_err("ERROR: v4l2 capture: Allocate dummy frame " - "failed.\n"); - return -ENOBUFS; + if (cam->dummy_frame.vaddress && + cam->dummy_frame.buffer.length + < PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage)) { + dma_free_coherent(0, cam->dummy_frame.buffer.length, + cam->dummy_frame.vaddress, + cam->dummy_frame.paddress); + cam->dummy_frame.vaddress = 0; + } + + if (!cam->dummy_frame.vaddress) { + cam->dummy_frame.vaddress = dma_alloc_coherent(0, + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + &cam->dummy_frame.paddress, + GFP_DMA | GFP_KERNEL); + if (cam->dummy_frame.vaddress == 0) { + pr_err("ERROR: v4l2 capture: Allocate dummy frame " + "failed.\n"); + return -ENOBUFS; + } + cam->dummy_frame.buffer.length = + PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); } cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE; - cam->dummy_frame.buffer.length = - PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress; ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF); @@ -250,12 +261,6 @@ static int csi_enc_disabling_tasks(void *private) ipu_channel_free(&cam->ipu_chan); - if (cam->dummy_frame.vaddress != 0) { - dma_free_coherent(0, cam->dummy_frame.buffer.length, - cam->dummy_frame.vaddress, - cam->dummy_frame.paddress); - cam->dummy_frame.vaddress = 0; - } err2 = cam_mipi_csi2_disable(cam); return err ? err : err2; } diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index f07b39ce17c8f2..9d73dfa6835456 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -2805,6 +2805,14 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) spin_lock_init(&cam->queue_int_lock); spin_lock_init(&cam->dqueue_int_lock); + cam->dummy_frame.vaddress = dma_alloc_coherent(0, + SZ_8M, &cam->dummy_frame.paddress, + GFP_DMA | GFP_KERNEL); + if (cam->dummy_frame.vaddress == 0) + pr_err("ERROR: v4l2 capture: Allocate dummy frame " + "failed.\n"); + cam->dummy_frame.buffer.length = SZ_8M; + cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); cam->self->module = THIS_MODULE; sprintf(cam->self->name, "mxc_v4l2_cap%d", camera_id++); @@ -2934,6 +2942,13 @@ static int mxc_v4l2_remove(struct platform_device *pdev) v4l2_int_device_unregister(cam->self); video_unregister_device(cam->video_dev); + if (cam->dummy_frame.vaddress != 0) { + dma_free_coherent(0, cam->dummy_frame.buffer.length, + cam->dummy_frame.vaddress, + cam->dummy_frame.paddress); + cam->dummy_frame.vaddress = 0; + } + mxc_free_frame_buf(cam); kfree(cam); From b34076022e559a9fe443266ff6a087d1a7d4f2b9 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 16 Jul 2014 10:13:02 -0700 Subject: [PATCH 0347/1983] ENGR00143324-1 v4l2_capture: add camera rotate function add four kinds of camera rotate function: rotate_none, rotate_vert rotate_horiz, rotate_180 Signed-off-by: Yuxi Sun --- .../media/platform/mxc/capture/ipu_csi_enc.c | 2 +- .../platform/mxc/capture/mxc_v4l2_capture.c | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index d6f12aa277426f..dde7e07c589825 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -138,7 +138,7 @@ static int csi_enc_setup(cam_data *cam) pixel_fmt, cam->v2f.fmt.pix.width, cam->v2f.fmt.pix.height, cam->v2f.fmt.pix.bytesperline, - IPU_ROTATE_NONE, + cam->rotation, dummy, dummy, 0, cam->offset.u_offset, cam->offset.v_offset); diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 9d73dfa6835456..0c2279fc0e98ce 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1158,6 +1158,26 @@ static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) case V4L2_MXC_ROTATE_90_LEFT: tmp_rotation = IPU_ROTATE_90_LEFT; break; + case V4L2_MXC_CAM_ROTATE_NONE: + if (vidioc_int_s_ctrl(cam->sensor, c)) { + ret = -EINVAL; + } + break; + case V4L2_MXC_CAM_ROTATE_VERT_FLIP: + if (vidioc_int_s_ctrl(cam->sensor, c)) { + ret = -EINVAL; + } + break; + case V4L2_MXC_CAM_ROTATE_HORIZ_FLIP: + if (vidioc_int_s_ctrl(cam->sensor, c)) { + ret = -EINVAL; + } + break; + case V4L2_MXC_CAM_ROTATE_180: + if (vidioc_int_s_ctrl(cam->sensor, c)) { + ret = -EINVAL; + } + break; default: ret = -EINVAL; } From d26c7f4aa68d72f7bb595dd923ecf73961610646 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 16 Jul 2014 10:27:16 -0700 Subject: [PATCH 0348/1983] mxc_v4l2_capture: add blank line to sync with android branch --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 0c2279fc0e98ce..5d232307f257e4 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -2945,6 +2945,7 @@ static int mxc_v4l2_probe(struct platform_device *pdev) static int mxc_v4l2_remove(struct platform_device *pdev) { cam_data *cam = (cam_data *)platform_get_drvdata(pdev); + if (cam->open_count) { pr_err("ERROR: v4l2 capture:camera open " "-- setting ops to NULL\n"); From 2f613cc29e16132302dfaa819f7b35065b8f1f0e Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 9 Jun 2014 12:15:44 -0700 Subject: [PATCH 0349/1983] mxc_v4l2_capture: set pack_tight for 10bit mode --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 5d232307f257e4..6f782f83c7414e 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1347,6 +1347,8 @@ void setup_ifparm(cam_data *cam, int init_defrect) (ifparm.u.bt656.mode == V4L2_IF_TYPE_BT656_MODE_BT_10BIT) ? IPU_CSI_DATA_WIDTH_10 : IPU_CSI_DATA_WIDTH_8; + csi_param.pack_tight = (csi_param.data_width == IPU_CSI_DATA_WIDTH_10) ? 1 : 0; + csi_param.Vsync_pol = ifparm.u.bt656.nobt_vs_inv; csi_param.Hsync_pol = ifparm.u.bt656.nobt_hs_inv; csi_param.ext_vsync = ifparm.u.bt656.bt_sync_correct; From d7fb2b68cb8e7378c42c740d2c655476515bdca9 Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Wed, 3 Dec 2014 15:11:42 -0700 Subject: [PATCH 0350/1983] ov5640_mipi: add support for reading/writing I2C registers Read a register by setting the register /sys/bus/i2c/devices/5-003e# echo 3002 > ov5640_reg /sys/bus/i2c/devices/5-003e# cat ov5640_reg ov5640[0x3002]=0x1c Write a register using regnum=value (in hex): ov5640[0x3002]=0x1c /sys/bus/i2c/devices/5-003e# echo 3603=1f > ov5640_reg /sys/bus/i2c/devices/5-003e# cat ov5640_reg ov5640[0x3603]=0x1f Signed-off-by: Eric Nelson --- .../platform/mxc/capture/mxc_v4l2_capture.h | 1 + .../media/platform/mxc/capture/ov5640_mipi.c | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 319a3b1444bcee..28d9b6e932eabe 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -266,6 +266,7 @@ struct sensor_data { struct clk *sensor_clk; int ipu_id; int csi; + int last_reg; unsigned mipi_camera; unsigned virtual_channel; /* Used with mipi */ diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index 1e7e8b5f5483c1..e2f3511fa4a77a 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -2009,6 +2009,38 @@ static struct v4l2_int_device ov5640_int_device = { }, }; +static ssize_t show_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val; + s32 rval = ov5640_read_reg(ov5640_data.last_reg, &val); + + return sprintf(buf, "ov5640[0x%04x]=0x%02x\n",ov5640_data.last_reg, rval); +} +static ssize_t set_reg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int regnum, value; + int num_parsed = sscanf(buf, "%04x=%02x", ®num, &value); + if (1 <= num_parsed) { + if (0xffff < (unsigned)regnum){ + pr_err("%s:invalid regnum %x\n", __func__, regnum); + return 0; + } + ov5640_data.last_reg = regnum; + } + if (2 == num_parsed) { + if (0xff < (unsigned)value) { + pr_err("%s:invalid value %x\n", __func__, value); + return 0; + } + ov5640_write_reg(ov5640_data.last_reg, value); + } + return count; +} +static DEVICE_ATTR(ov5640_reg, S_IRUGO|S_IWUGO, show_reg, set_reg); + /*! * ov5640 I2C probe function * @@ -2129,6 +2161,8 @@ static int ov5640_probe(struct i2c_client *client, // clk_disable_unprepare(ov5640_data.sensor_clk); + if (device_create_file(dev, &dev_attr_ov5640_reg)) + dev_err(dev, "%s: error creating ov5640_reg entry\n", __func__); pr_info("camera ov5640_mipi is found\n"); return retval; } From 169300340d8d71de7d6d5e495d34599f71b68bc9 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 5 Aug 2014 09:59:13 -0700 Subject: [PATCH 0351/1983] ipu_capture: add ipu_csi_window_size_crop --- drivers/mxc/ipu3/ipu_capture.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/ipu-v3.h | 3 +++ 2 files changed, 37 insertions(+) diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index b1d6fb3affd80e..f995a3d040358e 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -327,6 +327,40 @@ void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, ui } EXPORT_SYMBOL(ipu_csi_set_window_pos); +void ipu_csi_window_size_crop(struct ipu_soc *ipu, uint32_t swidth, uint32_t sheight, + uint32_t width, uint32_t height, uint32_t left, uint32_t top, uint32_t csi) +{ + uint32_t temp; + + if ((left >= (1 << 13)) || (top >= (1 << 12))) { + pr_err("%s: Error left=%x top=%x\n", __func__, left, top); + left = 0; + top = 0; + swidth = width; + sheight = height; + } + _ipu_get(ipu); + + /* + * sheight >= top + height + * swidth >= left + width, unless interlaced + * left = # of lines/field if interlaced + */ + mutex_lock(&ipu->mutex_lock); + ipu_csi_write(ipu, csi, (swidth - 1) | (sheight - 1) << 16, CSI_SENS_FRM_SIZE); + ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE); + + temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); + temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK); + temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT)); + ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); + + mutex_unlock(&ipu->mutex_lock); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_csi_window_size_crop); + /*! * _ipu_csi_horizontal_downsize_enable * Enable horizontal downsizing(decimation) by 2. diff --git a/include/linux/ipu-v3.h b/include/linux/ipu-v3.h index 1145c79030daa8..22aa529c0081b6 100644 --- a/include/linux/ipu-v3.h +++ b/include/linux/ipu-v3.h @@ -731,6 +731,9 @@ void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t heigh void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi); +void ipu_csi_window_size_crop(struct ipu_soc *ipu, uint32_t swidth, uint32_t sheight, + uint32_t width, uint32_t height, uint32_t left, uint32_t top, uint32_t csi); + uint32_t bytes_per_pixel(uint32_t fmt); bool ipu_ch_param_bad_alpha_pos(uint32_t fmt); From 2a46ccce14cdad0f3be53eb26f2b8af1e7f455e9 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Sat, 24 May 2014 18:26:46 -0700 Subject: [PATCH 0352/1983] ipu_capture: fix 861 timing mode of gs2971 --- drivers/mxc/ipu3/ipu_capture.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index f995a3d040358e..6292577f794605 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -109,6 +109,7 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; break; default: + dev_dbg(ipu->dev, "%s:pixel_fmt=%x\n", __func__, pixel_fmt); return -EINVAL; } @@ -132,12 +133,23 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, ipu_csi_write(ipu, csi, data, CSI_SENS_CONF); /* Setup sensor frame size */ - ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE); + + dev_dbg(ipu->dev, "%s: %dx%d\n", __func__, width, height); /* Set CCIR registers */ + ipu_csi_write(ipu, csi, + (cfg_param.data_width == IPU_CSI_DATA_WIDTH_10) ? + 0x03FF00000 : 0x00FF0000, CSI_CCIR_CODE_3); if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) { - ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); - ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); +/* + * bit 5-3: start of blanking line command + * bit 18-16: End of active line command + * bit 21-19: start of active line command + * 10 bit mode of GS2971 interleaves this streams, giving + * 0x3ff,0x3ff,0x000,0x000,0x000,0x000,xy,xy + * so, it cannot be used in 656 mode. It needs 1120 mode. + */ + ipu_csi_write(ipu, csi, (6 << 3) | (4 << 16), CSI_CCIR_CODE_1); } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) { if (width == 720 && height == 625) { /* PAL case */ @@ -151,9 +163,6 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 */ ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2); - - ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); - } else if (width == 720 && height == 525) { /* NTSC case */ /* @@ -166,7 +175,6 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 */ ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2); - ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); } else { dev_err(ipu->dev, "Unsupported CCIR656 interlaced " "video mode\n"); @@ -184,10 +192,10 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) { ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); - ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); _ipu_csi_ccir_err_detection_enable(ipu, csi); } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) { + ipu_csi_write(ipu, csi, (6 << 3) | (4 << 16), CSI_CCIR_CODE_1); _ipu_csi_ccir_err_detection_disable(ipu, csi); } From 5162c6f1e4a53585030469830ed3d95c25173e31 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 29 May 2014 15:59:07 -0700 Subject: [PATCH 0353/1983] ipu_capture: use expression instead of hex value --- drivers/mxc/ipu3/ipu_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index 6292577f794605..ec809c622f8a8d 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -191,7 +191,7 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) { - ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); + ipu_csi_write(ipu, csi, (6 << 3) | (4 << 16), CSI_CCIR_CODE_1); _ipu_csi_ccir_err_detection_enable(ipu, csi); } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) || (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) { From f4414ff37aae1856b6b60d3234a575a149321718 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Mon, 2 Jun 2014 10:55:34 -0700 Subject: [PATCH 0354/1983] ipu_capture: minor cleanup --- drivers/mxc/ipu3/ipu_capture.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index ec809c622f8a8d..0aca3548abc3db 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -776,6 +776,8 @@ int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) uint32_t csi_sens_conf, csi_dest; int retval = 0; + csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF); + csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; switch (channel) { case CSI_MEM0: case CSI_MEM1: @@ -791,11 +793,11 @@ int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) retval = -EINVAL; goto err; } + csi_sens_conf |= csi_dest << CSI_SENS_CONF_DATA_DEST_SHIFT; - csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF); - csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; - ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest << - CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF); + dev_dbg(ipu->dev, "%s:CSI_SENS_CONF: ipu=%p,csi=%x,data=%x\n", __func__, + ipu, csi, csi_sens_conf); + ipu_csi_write(ipu, csi, csi_sens_conf, CSI_SENS_CONF); err: return retval; } From e785f94918d471aeef6c0761e969bc804f53873a Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Sat, 7 Jun 2014 12:29:31 -0700 Subject: [PATCH 0355/1983] ipu_capture: fix NTSC/PAL detect --- drivers/mxc/ipu3/ipu_capture.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index 0aca3548abc3db..34f470d9961552 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -151,19 +151,7 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, */ ipu_csi_write(ipu, csi, (6 << 3) | (4 << 16), CSI_CCIR_CODE_1); } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) { - if (width == 720 && height == 625) { - /* PAL case */ - /* - * Field0BlankEnd = 0x6, Field0BlankStart = 0x2, - * Field0ActiveEnd = 0x4, Field0ActiveStart = 0 - */ - ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1); - /* - * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, - * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 - */ - ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2); - } else if (width == 720 && height == 525) { + if (width == 720 && height <= 525) { /* NTSC case */ /* * Field0BlankEnd = 0x7, Field0BlankStart = 0x3, @@ -175,6 +163,18 @@ ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 */ ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2); + } else if (width == 720 && height <= 625) { + /* PAL case */ + /* + * Field0BlankEnd = 0x6, Field0BlankStart = 0x2, + * Field0ActiveEnd = 0x4, Field0ActiveStart = 0 + */ + ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1); + /* + * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, + * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 + */ + ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2); } else { dev_err(ipu->dev, "Unsupported CCIR656 interlaced " "video mode\n"); From 120e932ce7580a9e5f7c220ffa3b9bfa1095785b Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 11 Jun 2014 20:46:18 -0700 Subject: [PATCH 0356/1983] ipu_capture: use compander to go from 10 bit components to 8 if memory is destination --- drivers/mxc/ipu3/ipu_capture.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c index 34f470d9961552..189e50d33e82f1 100644 --- a/drivers/mxc/ipu3/ipu_capture.c +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -775,6 +775,7 @@ int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) { uint32_t csi_sens_conf, csi_dest; int retval = 0; + uint32_t ctrl = ipu_csi_read(ipu, csi, CSI_CPD_CTRL) & ~0x13; csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF); csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; @@ -784,6 +785,32 @@ int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) case CSI_MEM2: case CSI_MEM3: csi_dest = CSI_DATA_DEST_IDMAC; + + if (((csi_sens_conf >> CSI_SENS_CONF_DATA_WIDTH_SHIFT) & 0x0f) == IPU_CSI_DATA_WIDTH_10) { + /* Send to compander before memory to reduce to 8 bits */ + /* y = Min[255, (y1[k] + (((x-x1[k])*slope[k])>>6 + 1))>>1] */ + int i; + + ctrl |= (1 << 4); + ipu_csi_write(ipu, csi, 0, CSI_CPD_OFFSET1); + ipu_csi_write(ipu, csi, 0, CSI_CPD_OFFSET2); + for (i = 0; i < 16; i += 2) { + uint32_t c = ((i+1) << (16 + 5)) | (i << (0 + 5)); + + ipu_csi_write(ipu, csi, c, CSI_CPD_RC((i >> 1))); + ipu_csi_write(ipu, csi, c, CSI_CPD_GRC((i >> 1))); + ipu_csi_write(ipu, csi, c, CSI_CPD_GBC((i >> 1))); + ipu_csi_write(ipu, csi, c, CSI_CPD_BC((i >> 1))); + } + for (i = 0; i < 16; i += 4) { + uint32_t slope = 1 << 5; + uint32_t s = (slope << 24) | (slope << 16) | (slope << 8) | (slope << 0); + ipu_csi_write(ipu, csi, s, CSI_CPD_RS((i >> 2))); + ipu_csi_write(ipu, csi, s, CSI_CPD_GRS((i >> 2))); + ipu_csi_write(ipu, csi, s, CSI_CPD_GBS((i >> 2))); + ipu_csi_write(ipu, csi, s, CSI_CPD_BS((i >> 2))); + } + } break; case CSI_PRP_ENC_MEM: case CSI_PRP_VF_MEM: @@ -797,6 +824,7 @@ int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) dev_dbg(ipu->dev, "%s:CSI_SENS_CONF: ipu=%p,csi=%x,data=%x\n", __func__, ipu, csi, csi_sens_conf); + ipu_csi_write(ipu, csi, ctrl, CSI_CPD_CTRL); ipu_csi_write(ipu, csi, csi_sens_conf, CSI_SENS_CONF); err: return retval; From e75b54b0f11d9a52c448203a607d54bf4934551e Mon Sep 17 00:00:00 2001 From: Eric Nelson Date: Tue, 14 May 2013 16:29:23 -0700 Subject: [PATCH 0357/1983] Raybiz auto-focus V3 --- .../platform/mxc/capture/mxc_v4l2_capture.c | 30 + drivers/media/platform/mxc/capture/ov5642.c | 2032 +++++++++++++++++ .../platform/mxc/capture/v4l2-int-device.h | 2 + .../staging/media/omap24xx/v4l2-int-device.h | 2 + include/uapi/linux/mxc_v4l2.h | 5 + include/uapi/linux/v4l2-controls.h | 8 + include/uapi/linux/videodev2.h | 9 + 7 files changed, 2088 insertions(+) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 6f782f83c7414e..54a0aa4adec1e8 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1077,6 +1077,16 @@ static int mxc_v4l2_g_ctrl(cam_data *cam, struct v4l2_control *c) return status; } +static int mxc_v4l2_send_command(cam_data *cam, + struct v4l2_send_command_control *c) { + int ret =0; + + if (vidioc_int_send_command(cam->sensor, c)) { + ret = -EINVAL; + } + return ret; +} + /*! * V4L2 - set_control function * V4L2_CID_PRIVATE_BASE is the extention for IPU preprocessing. @@ -1259,6 +1269,19 @@ static int mxc_v4l2_s_ctrl(cam_data *cam, struct v4l2_control *c) ipu_csi_flash_strobe(true); #endif break; + + case V4L2_CID_AUTO_FOCUS_START: { + ret = vidioc_int_s_ctrl(cam->sensor, c); + break; + } + + case V4L2_CID_AUTO_FOCUS_STOP: { + if (vidioc_int_s_ctrl(cam->sensor, c)) { + ret = -EINVAL; + } + break; + } + case V4L2_CID_MXC_SWITCH_CAM: if (cam->sensor == cam->all_sensors[c->value]) break; @@ -2479,6 +2502,12 @@ static long mxc_v4l_do_ioctl(struct file *file, } break; } + + case VIDIOC_SEND_COMMAND: { + retval = mxc_v4l2_send_command(cam, arg); + break; + } + case VIDIOC_TRY_FMT: case VIDIOC_QUERYCTRL: case VIDIOC_G_TUNER: @@ -3125,6 +3154,7 @@ static int mxc_v4l2_master_attach(struct v4l2_int_device *slave) } for (i = 0; i < cam->sensor_index; i++) { + pr_err("%s: %x\n", __func__, i); vidioc_int_dev_exit(cam->all_sensors[i]); vidioc_int_s_power(cam->all_sensors[i], 0); } diff --git a/drivers/media/platform/mxc/capture/ov5642.c b/drivers/media/platform/mxc/capture/ov5642.c index 818718d9b3d402..f059953a1a27e8 100644 --- a/drivers/media/platform/mxc/capture/ov5642.c +++ b/drivers/media/platform/mxc/capture/ov5642.c @@ -46,6 +46,50 @@ #define OV5642_XCLK_MIN 6000000 #define OV5642_XCLK_MAX 24000000 +/* OV5642 Camera Auto Focus Registers */ +#define REG_CMD_MAIN 0x3024 +#define REG_STA_FOCUS 0x3027 +#define REG_STA_ZONE 0x3026 +#define REG_CMD_TAG 0x3025 +#define REG_CMD_PARA3 0x5082 +#define REG_CMD_PARA2 0x5083 +#define REG_CMD_PARA1 0x5084 +#define REG_CMD_PARA0 0x5085 + + +/* OV5642 Auto Focus Commands and Responses */ +#define S_STARTUP 0xFA +#define S_FIRWARE 0xFF +#define S_STARTUP 0xFA +#define S_ERROR 0xFE +#define S_DRVICERR 0xEE +#define S_IDLE 0x00 +#define S_FOCUSING 0x01 +#define S_FOCUSED 0x02 +#define S_CAPTURE 0x12 +#define S_STEP 0x20 + +/*OV5642 AWB Registers*/ +#define REG_AWB_MANUAL 0x3406 +#define REG_AWB_R_GAIN_HIGH 0x3400 +#define REG_AWB_R_GAIN_LOW 0x3401 +#define REG_AWB_G_GAIN_HIGH 0x3402 +#define REG_AWB_G_GAIN_LOW 0x3403 +#define REG_AWB_B_GAIN_HIGH 0x3404 +#define REG_AWB_B_GAIN_LOW 0x3405 + +#define CMD_ENABLE_OVERLAY 0x01 +#define CMD_DISABLE_OVERLAY 0x02 +#define CMD_SINGLE_FOCUS_MODE 0x03 +#define CMD_CONST_FOCUS_MODE 0x04 +#define CMD_STEP_FOCUS_MODE 0x05 +#define CMD_PAUSE 0x06 +#define CMD_IDLE_MODE 0x08 +#define CMD_SET_ZONE_MODE 0x10 +#define CMD_UPDATE_ZONE_MODE 0x12 +#define CMD_MOTOR_MODE 0x20 +#define CMD_SCAN_MODE 0x30 + #define OV5642_CHIP_ID_HIGH_BYTE 0x300A #define OV5642_CHIP_ID_LOW_BYTE 0x300B @@ -295,6 +339,1848 @@ static struct reg_value ov5642_initial_setting[] = { {0x302b, 0x00, 0, 300}, }; +static struct reg_value ov5642_af_firmware[] = { + {0x3000, 0x20, 0, 0},{0x8000, 0x02, 0, 0},{0x8001, 0x00, 0, 0}, + {0x8002, 0x06, 0, 0},{0x8003, 0x02, 0, 0},{0x8004, 0x0b, 0, 0}, + {0x8005, 0x44, 0, 0},{0x8006, 0x78, 0, 0},{0x8007, 0x7f, 0, 0}, + {0x8008, 0xe4, 0, 0},{0x8009, 0xf6, 0, 0},{0x800a, 0xd8, 0, 0}, + {0x800b, 0xfd, 0, 0},{0x800c, 0x75, 0, 0},{0x800d, 0x81, 0, 0}, + {0x800e, 0x7d, 0, 0},{0x800f, 0x02, 0, 0},{0x8010, 0x13, 0, 0}, + {0x8011, 0xb6, 0, 0},{0x8012, 0x00, 0, 0},{0x8013, 0x02, 0, 0}, + {0x8014, 0x12, 0, 0},{0x8015, 0xe1, 0, 0},{0x8016, 0xe0, 0, 0}, + {0x8017, 0xf5, 0, 0},{0x8018, 0x71, 0, 0},{0x8019, 0xa3, 0, 0}, + {0x801a, 0xe0, 0, 0},{0x801b, 0xf5, 0, 0},{0x801c, 0x72, 0, 0}, + {0x801d, 0xae, 0, 0},{0x801e, 0x69, 0, 0},{0x801f, 0xe4, 0, 0}, + {0x8020, 0x85, 0, 0},{0x8021, 0x6a, 0, 0},{0x8022, 0x55, 0, 0}, + {0x8023, 0x8e, 0, 0},{0x8024, 0x54, 0, 0},{0x8025, 0xf5, 0, 0}, + {0x8026, 0x53, 0, 0},{0x8027, 0xf5, 0, 0},{0x8028, 0x52, 0, 0}, + {0x8029, 0xab, 0, 0},{0x802a, 0x55, 0, 0},{0x802b, 0xaa, 0, 0}, + {0x802c, 0x54, 0, 0},{0x802d, 0xa9, 0, 0},{0x802e, 0x53, 0, 0}, + {0x802f, 0xa8, 0, 0},{0x8030, 0x52, 0, 0},{0x8031, 0xaf, 0, 0}, + {0x8032, 0x2c, 0, 0},{0x8033, 0xfc, 0, 0},{0x8034, 0xfd, 0, 0}, + {0x8035, 0xfe, 0, 0},{0x8036, 0x12, 0, 0},{0x8037, 0x08, 0, 0}, + {0x8038, 0x7f, 0, 0},{0x8039, 0x8f, 0, 0},{0x803a, 0x55, 0, 0}, + {0x803b, 0x8e, 0, 0},{0x803c, 0x54, 0, 0},{0x803d, 0x8d, 0, 0}, + {0x803e, 0x53, 0, 0},{0x803f, 0x8c, 0, 0},{0x8040, 0x52, 0, 0}, + {0x8041, 0xaf, 0, 0},{0x8042, 0x55, 0, 0},{0x8043, 0xae, 0, 0}, + {0x8044, 0x54, 0, 0},{0x8045, 0xad, 0, 0},{0x8046, 0x53, 0, 0}, + {0x8047, 0xac, 0, 0},{0x8048, 0x52, 0, 0},{0x8049, 0x8f, 0, 0}, + {0x804a, 0x2b, 0, 0},{0x804b, 0x8e, 0, 0},{0x804c, 0x2a, 0, 0}, + {0x804d, 0x8d, 0, 0},{0x804e, 0x29, 0, 0},{0x804f, 0x8c, 0, 0}, + {0x8050, 0x28, 0, 0},{0x8051, 0xae, 0, 0},{0x8052, 0x6b, 0, 0}, + {0x8053, 0xe4, 0, 0},{0x8054, 0x85, 0, 0},{0x8055, 0x6c, 0, 0}, + {0x8056, 0x55, 0, 0},{0x8057, 0x8e, 0, 0},{0x8058, 0x54, 0, 0}, + {0x8059, 0xf5, 0, 0},{0x805a, 0x53, 0, 0},{0x805b, 0xf5, 0, 0}, + {0x805c, 0x52, 0, 0},{0x805d, 0xab, 0, 0},{0x805e, 0x55, 0, 0}, + {0x805f, 0xaa, 0, 0},{0x8060, 0x54, 0, 0},{0x8061, 0xa9, 0, 0}, + {0x8062, 0x53, 0, 0},{0x8063, 0xa8, 0, 0},{0x8064, 0x52, 0, 0}, + {0x8065, 0xaf, 0, 0},{0x8066, 0x2d, 0, 0},{0x8067, 0xfc, 0, 0}, + {0x8068, 0xfd, 0, 0},{0x8069, 0xfe, 0, 0},{0x806a, 0x12, 0, 0}, + {0x806b, 0x08, 0, 0},{0x806c, 0x7f, 0, 0},{0x806d, 0x8f, 0, 0}, + {0x806e, 0x55, 0, 0},{0x806f, 0x8e, 0, 0},{0x8070, 0x54, 0, 0}, + {0x8071, 0x8d, 0, 0},{0x8072, 0x53, 0, 0},{0x8073, 0x8c, 0, 0}, + {0x8074, 0x52, 0, 0},{0x8075, 0xe5, 0, 0},{0x8076, 0x2b, 0, 0}, + {0x8077, 0x25, 0, 0},{0x8078, 0x55, 0, 0},{0x8079, 0xf5, 0, 0}, + {0x807a, 0x2b, 0, 0},{0x807b, 0xe5, 0, 0},{0x807c, 0x2a, 0, 0}, + {0x807d, 0x35, 0, 0},{0x807e, 0x54, 0, 0},{0x807f, 0xf5, 0, 0}, + {0x8080, 0x2a, 0, 0},{0x8081, 0xe5, 0, 0},{0x8082, 0x29, 0, 0}, + {0x8083, 0x35, 0, 0},{0x8084, 0x53, 0, 0},{0x8085, 0xf5, 0, 0}, + {0x8086, 0x29, 0, 0},{0x8087, 0xe5, 0, 0},{0x8088, 0x28, 0, 0}, + {0x8089, 0x35, 0, 0},{0x808a, 0x52, 0, 0},{0x808b, 0xf5, 0, 0}, + {0x808c, 0x28, 0, 0},{0x808d, 0xae, 0, 0},{0x808e, 0x6d, 0, 0}, + {0x808f, 0xe4, 0, 0},{0x8090, 0x85, 0, 0},{0x8091, 0x6e, 0, 0}, + {0x8092, 0x55, 0, 0},{0x8093, 0x8e, 0, 0},{0x8094, 0x54, 0, 0}, + {0x8095, 0xf5, 0, 0},{0x8096, 0x53, 0, 0},{0x8097, 0xf5, 0, 0}, + {0x8098, 0x52, 0, 0},{0x8099, 0xab, 0, 0},{0x809a, 0x55, 0, 0}, + {0x809b, 0xaa, 0, 0},{0x809c, 0x54, 0, 0},{0x809d, 0xa9, 0, 0}, + {0x809e, 0x53, 0, 0},{0x809f, 0xa8, 0, 0},{0x80a0, 0x52, 0, 0}, + {0x80a1, 0xaf, 0, 0},{0x80a2, 0x2e, 0, 0},{0x80a3, 0xfc, 0, 0}, + {0x80a4, 0xfd, 0, 0},{0x80a5, 0xfe, 0, 0},{0x80a6, 0x12, 0, 0}, + {0x80a7, 0x08, 0, 0},{0x80a8, 0x7f, 0, 0},{0x80a9, 0x8f, 0, 0}, + {0x80aa, 0x55, 0, 0},{0x80ab, 0x8e, 0, 0},{0x80ac, 0x54, 0, 0}, + {0x80ad, 0x8d, 0, 0},{0x80ae, 0x53, 0, 0},{0x80af, 0x8c, 0, 0}, + {0x80b0, 0x52, 0, 0},{0x80b1, 0xe5, 0, 0},{0x80b2, 0x2b, 0, 0}, + {0x80b3, 0x25, 0, 0},{0x80b4, 0x55, 0, 0},{0x80b5, 0xf5, 0, 0}, + {0x80b6, 0x2b, 0, 0},{0x80b7, 0xe5, 0, 0},{0x80b8, 0x2a, 0, 0}, + {0x80b9, 0x35, 0, 0},{0x80ba, 0x54, 0, 0},{0x80bb, 0xf5, 0, 0}, + {0x80bc, 0x2a, 0, 0},{0x80bd, 0xe5, 0, 0},{0x80be, 0x29, 0, 0}, + {0x80bf, 0x35, 0, 0},{0x80c0, 0x53, 0, 0},{0x80c1, 0xf5, 0, 0}, + {0x80c2, 0x29, 0, 0},{0x80c3, 0xe5, 0, 0},{0x80c4, 0x28, 0, 0}, + {0x80c5, 0x35, 0, 0},{0x80c6, 0x52, 0, 0},{0x80c7, 0xf5, 0, 0}, + {0x80c8, 0x28, 0, 0},{0x80c9, 0xae, 0, 0},{0x80ca, 0x6f, 0, 0}, + {0x80cb, 0xe4, 0, 0},{0x80cc, 0x85, 0, 0},{0x80cd, 0x70, 0, 0}, + {0x80ce, 0x55, 0, 0},{0x80cf, 0x8e, 0, 0},{0x80d0, 0x54, 0, 0}, + {0x80d1, 0xf5, 0, 0},{0x80d2, 0x53, 0, 0},{0x80d3, 0xf5, 0, 0}, + {0x80d4, 0x52, 0, 0},{0x80d5, 0xab, 0, 0},{0x80d6, 0x55, 0, 0}, + {0x80d7, 0xaa, 0, 0},{0x80d8, 0x54, 0, 0},{0x80d9, 0xa9, 0, 0}, + {0x80da, 0x53, 0, 0},{0x80db, 0xa8, 0, 0},{0x80dc, 0x52, 0, 0}, + {0x80dd, 0xaf, 0, 0},{0x80de, 0x2f, 0, 0},{0x80df, 0xfc, 0, 0}, + {0x80e0, 0xfd, 0, 0},{0x80e1, 0xfe, 0, 0},{0x80e2, 0x12, 0, 0}, + {0x80e3, 0x08, 0, 0},{0x80e4, 0x7f, 0, 0},{0x80e5, 0x8f, 0, 0}, + {0x80e6, 0x55, 0, 0},{0x80e7, 0x8e, 0, 0},{0x80e8, 0x54, 0, 0}, + {0x80e9, 0x8d, 0, 0},{0x80ea, 0x53, 0, 0},{0x80eb, 0x8c, 0, 0}, + {0x80ec, 0x52, 0, 0},{0x80ed, 0xe5, 0, 0},{0x80ee, 0x2b, 0, 0}, + {0x80ef, 0x25, 0, 0},{0x80f0, 0x55, 0, 0},{0x80f1, 0xf5, 0, 0}, + {0x80f2, 0x2b, 0, 0},{0x80f3, 0xe5, 0, 0},{0x80f4, 0x2a, 0, 0}, + {0x80f5, 0x35, 0, 0},{0x80f6, 0x54, 0, 0},{0x80f7, 0xf5, 0, 0}, + {0x80f8, 0x2a, 0, 0},{0x80f9, 0xe5, 0, 0},{0x80fa, 0x29, 0, 0}, + {0x80fb, 0x35, 0, 0},{0x80fc, 0x53, 0, 0},{0x80fd, 0xf5, 0, 0}, + {0x80fe, 0x29, 0, 0},{0x80ff, 0xe5, 0, 0},{0x8100, 0x28, 0, 0}, + {0x8101, 0x35, 0, 0},{0x8102, 0x52, 0, 0},{0x8103, 0xf5, 0, 0}, + {0x8104, 0x28, 0, 0},{0x8105, 0xae, 0, 0},{0x8106, 0x71, 0, 0}, + {0x8107, 0xe4, 0, 0},{0x8108, 0x85, 0, 0},{0x8109, 0x72, 0, 0}, + {0x810a, 0x55, 0, 0},{0x810b, 0x8e, 0, 0},{0x810c, 0x54, 0, 0}, + {0x810d, 0xf5, 0, 0},{0x810e, 0x53, 0, 0},{0x810f, 0xf5, 0, 0}, + {0x8110, 0x52, 0, 0},{0x8111, 0xab, 0, 0},{0x8112, 0x55, 0, 0}, + {0x8113, 0xaa, 0, 0},{0x8114, 0x54, 0, 0},{0x8115, 0xa9, 0, 0}, + {0x8116, 0x53, 0, 0},{0x8117, 0xa8, 0, 0},{0x8118, 0x52, 0, 0}, + {0x8119, 0xaf, 0, 0},{0x811a, 0x30, 0, 0},{0x811b, 0xfc, 0, 0}, + {0x811c, 0xfd, 0, 0},{0x811d, 0xfe, 0, 0},{0x811e, 0x12, 0, 0}, + {0x811f, 0x08, 0, 0},{0x8120, 0x7f, 0, 0},{0x8121, 0x8f, 0, 0}, + {0x8122, 0x55, 0, 0},{0x8123, 0x8e, 0, 0},{0x8124, 0x54, 0, 0}, + {0x8125, 0x8d, 0, 0},{0x8126, 0x53, 0, 0},{0x8127, 0x8c, 0, 0}, + {0x8128, 0x52, 0, 0},{0x8129, 0xe5, 0, 0},{0x812a, 0x2b, 0, 0}, + {0x812b, 0x25, 0, 0},{0x812c, 0x55, 0, 0},{0x812d, 0xf5, 0, 0}, + {0x812e, 0x2b, 0, 0},{0x812f, 0xe5, 0, 0},{0x8130, 0x2a, 0, 0}, + {0x8131, 0x35, 0, 0},{0x8132, 0x54, 0, 0},{0x8133, 0xf5, 0, 0}, + {0x8134, 0x2a, 0, 0},{0x8135, 0xe5, 0, 0},{0x8136, 0x29, 0, 0}, + {0x8137, 0x35, 0, 0},{0x8138, 0x53, 0, 0},{0x8139, 0xf5, 0, 0}, + {0x813a, 0x29, 0, 0},{0x813b, 0xe5, 0, 0},{0x813c, 0x28, 0, 0}, + {0x813d, 0x35, 0, 0},{0x813e, 0x52, 0, 0},{0x813f, 0xf5, 0, 0}, + {0x8140, 0x28, 0, 0},{0x8141, 0x22, 0, 0},{0x8142, 0xab, 0, 0}, + {0x8143, 0x0d, 0, 0},{0x8144, 0xaa, 0, 0},{0x8145, 0x0c, 0, 0}, + {0x8146, 0xa9, 0, 0},{0x8147, 0x0b, 0, 0},{0x8148, 0xa8, 0, 0}, + {0x8149, 0x0a, 0, 0},{0x814a, 0xfc, 0, 0},{0x814b, 0xfd, 0, 0}, + {0x814c, 0xfe, 0, 0},{0x814d, 0x12, 0, 0},{0x814e, 0x08, 0, 0}, + {0x814f, 0x7f, 0, 0},{0x8150, 0x8f, 0, 0},{0x8151, 0x0d, 0, 0}, + {0x8152, 0x8e, 0, 0},{0x8153, 0x0c, 0, 0},{0x8154, 0x8d, 0, 0}, + {0x8155, 0x0b, 0, 0},{0x8156, 0x8c, 0, 0},{0x8157, 0x0a, 0, 0}, + {0x8158, 0x7b, 0, 0},{0x8159, 0x40, 0, 0},{0x815a, 0xe4, 0, 0}, + {0x815b, 0xfa, 0, 0},{0x815c, 0xf9, 0, 0},{0x815d, 0xf8, 0, 0}, + {0x815e, 0x12, 0, 0},{0x815f, 0x09, 0, 0},{0x8160, 0x0a, 0, 0}, + {0x8161, 0x8f, 0, 0},{0x8162, 0x0d, 0, 0},{0x8163, 0x8e, 0, 0}, + {0x8164, 0x0c, 0, 0},{0x8165, 0x8d, 0, 0},{0x8166, 0x0b, 0, 0}, + {0x8167, 0x8c, 0, 0},{0x8168, 0x0a, 0, 0},{0x8169, 0x22, 0, 0}, + {0x816a, 0xd2, 0, 0},{0x816b, 0x29, 0, 0},{0x816c, 0x90, 0, 0}, + {0x816d, 0x30, 0, 0},{0x816e, 0x1b, 0, 0},{0x816f, 0xe5, 0, 0}, + {0x8170, 0x25, 0, 0},{0x8171, 0xf0, 0, 0},{0x8172, 0x22, 0, 0}, + {0x8173, 0xfe, 0, 0},{0x8174, 0xe4, 0, 0},{0x8175, 0xfc, 0, 0}, + {0x8176, 0xfd, 0, 0},{0x8177, 0xe5, 0, 0},{0x8178, 0x3f, 0, 0}, + {0x8179, 0x2f, 0, 0},{0x817a, 0xf5, 0, 0},{0x817b, 0x3f, 0, 0}, + {0x817c, 0xe5, 0, 0},{0x817d, 0x3e, 0, 0},{0x817e, 0x3e, 0, 0}, + {0x817f, 0xf5, 0, 0},{0x8180, 0x3e, 0, 0},{0x8181, 0xed, 0, 0}, + {0x8182, 0x35, 0, 0},{0x8183, 0x3d, 0, 0},{0x8184, 0xf5, 0, 0}, + {0x8185, 0x3d, 0, 0},{0x8186, 0xec, 0, 0},{0x8187, 0x35, 0, 0}, + {0x8188, 0x3c, 0, 0},{0x8189, 0xf5, 0, 0},{0x818a, 0x3c, 0, 0}, + {0x818b, 0xaf, 0, 0},{0x818c, 0x3f, 0, 0},{0x818d, 0xae, 0, 0}, + {0x818e, 0x3e, 0, 0},{0x818f, 0xfc, 0, 0},{0x8190, 0xad, 0, 0}, + {0x8191, 0x3d, 0, 0},{0x8192, 0x78, 0, 0},{0x8193, 0x08, 0, 0}, + {0x8194, 0x12, 0, 0},{0x8195, 0x09, 0, 0},{0x8196, 0xaf, 0, 0}, + {0x8197, 0x8f, 0, 0},{0x8198, 0x3f, 0, 0},{0x8199, 0x8e, 0, 0}, + {0x819a, 0x3e, 0, 0},{0x819b, 0x8d, 0, 0},{0x819c, 0x3d, 0, 0}, + {0x819d, 0x8c, 0, 0},{0x819e, 0x3c, 0, 0},{0x819f, 0x22, 0, 0}, + {0x81a0, 0xe5, 0, 0},{0x81a1, 0x49, 0, 0},{0x81a2, 0x25, 0, 0}, + {0x81a3, 0x7b, 0, 0},{0x81a4, 0xf5, 0, 0},{0x81a5, 0x82, 0, 0}, + {0x81a6, 0xe4, 0, 0},{0x81a7, 0x35, 0, 0},{0x81a8, 0x48, 0, 0}, + {0x81a9, 0xf5, 0, 0},{0x81aa, 0x83, 0, 0},{0x81ab, 0xe4, 0, 0}, + {0x81ac, 0x93, 0, 0},{0x81ad, 0xff, 0, 0},{0x81ae, 0x85, 0, 0}, + {0x81af, 0x49, 0, 0},{0x81b0, 0x82, 0, 0},{0x81b1, 0x85, 0, 0}, + {0x81b2, 0x48, 0, 0},{0x81b3, 0x83, 0, 0},{0x81b4, 0xe4, 0, 0}, + {0x81b5, 0x93, 0, 0},{0x81b6, 0xfd, 0, 0},{0x81b7, 0xc3, 0, 0}, + {0x81b8, 0xef, 0, 0},{0x81b9, 0x9d, 0, 0},{0x81ba, 0xff, 0, 0}, + {0x81bb, 0xe4, 0, 0},{0x81bc, 0x94, 0, 0},{0x81bd, 0x00, 0, 0}, + {0x81be, 0x22, 0, 0},{0x81bf, 0xe5, 0, 0},{0x81c0, 0x0b, 0, 0}, + {0x81c1, 0x24, 0, 0},{0x81c2, 0x01, 0, 0},{0x81c3, 0xff, 0, 0}, + {0x81c4, 0xe4, 0, 0},{0x81c5, 0x33, 0, 0},{0x81c6, 0xfe, 0, 0}, + {0x81c7, 0x22, 0, 0},{0x81c8, 0xaf, 0, 0},{0x81c9, 0x2b, 0, 0}, + {0x81ca, 0xae, 0, 0},{0x81cb, 0x2a, 0, 0},{0x81cc, 0xad, 0, 0}, + {0x81cd, 0x29, 0, 0},{0x81ce, 0xac, 0, 0},{0x81cf, 0x28, 0, 0}, + {0x81d0, 0x78, 0, 0},{0x81d1, 0x06, 0, 0},{0x81d2, 0x12, 0, 0}, + {0x81d3, 0x09, 0, 0},{0x81d4, 0x9c, 0, 0},{0x81d5, 0x8f, 0, 0}, + {0x81d6, 0x2b, 0, 0},{0x81d7, 0x8e, 0, 0},{0x81d8, 0x2a, 0, 0}, + {0x81d9, 0x8d, 0, 0},{0x81da, 0x29, 0, 0},{0x81db, 0x8c, 0, 0}, + {0x81dc, 0x28, 0, 0},{0x81dd, 0xd3, 0, 0},{0x81de, 0xe5, 0, 0}, + {0x81df, 0x29, 0, 0},{0x81e0, 0x94, 0, 0},{0x81e1, 0x00, 0, 0}, + {0x81e2, 0xe5, 0, 0},{0x81e3, 0x28, 0, 0},{0x81e4, 0x94, 0, 0}, + {0x81e5, 0x00, 0, 0},{0x81e6, 0x22, 0, 0},{0x81e7, 0x12, 0, 0}, + {0x81e8, 0x08, 0, 0},{0x81e9, 0x7f, 0, 0},{0x81ea, 0x8f, 0, 0}, + {0x81eb, 0x68, 0, 0},{0x81ec, 0x8e, 0, 0},{0x81ed, 0x67, 0, 0}, + {0x81ee, 0x8d, 0, 0},{0x81ef, 0x66, 0, 0},{0x81f0, 0x8c, 0, 0}, + {0x81f1, 0x65, 0, 0},{0x81f2, 0xaf, 0, 0},{0x81f3, 0x68, 0, 0}, + {0x81f4, 0xae, 0, 0},{0x81f5, 0x67, 0, 0},{0x81f6, 0xad, 0, 0}, + {0x81f7, 0x66, 0, 0},{0x81f8, 0xac, 0, 0},{0x81f9, 0x65, 0, 0}, + {0x81fa, 0x22, 0, 0},{0x81fb, 0xe0, 0, 0},{0x81fc, 0x44, 0, 0}, + {0x81fd, 0x01, 0, 0},{0x81fe, 0xf0, 0, 0},{0x81ff, 0xe0, 0, 0}, + {0x8200, 0x44, 0, 0},{0x8201, 0x02, 0, 0},{0x8202, 0xf0, 0, 0}, + {0x8203, 0xe0, 0, 0},{0x8204, 0x44, 0, 0},{0x8205, 0x04, 0, 0}, + {0x8206, 0xf0, 0, 0},{0x8207, 0x22, 0, 0},{0x8208, 0xd2, 0, 0}, + {0x8209, 0x09, 0, 0},{0x820a, 0x90, 0, 0},{0x820b, 0x30, 0, 0}, + {0x820c, 0x18, 0, 0},{0x820d, 0xe5, 0, 0},{0x820e, 0x21, 0, 0}, + {0x820f, 0xf0, 0, 0},{0x8210, 0x22, 0, 0},{0x8211, 0xe4, 0, 0}, + {0x8212, 0x85, 0, 0},{0x8213, 0x11, 0, 0},{0x8214, 0x0d, 0, 0}, + {0x8215, 0x85, 0, 0},{0x8216, 0x10, 0, 0},{0x8217, 0x0c, 0, 0}, + {0x8218, 0xf5, 0, 0},{0x8219, 0x0b, 0, 0},{0x821a, 0xf5, 0, 0}, + {0x821b, 0x0a, 0, 0},{0x821c, 0xab, 0, 0},{0x821d, 0x0d, 0, 0}, + {0x821e, 0xaa, 0, 0},{0x821f, 0x0c, 0, 0},{0x8220, 0xa9, 0, 0}, + {0x8221, 0x0b, 0, 0},{0x8222, 0xa8, 0, 0},{0x8223, 0x0a, 0, 0}, + {0x8224, 0x22, 0, 0},{0x8225, 0x90, 0, 0},{0x8226, 0x30, 0, 0}, + {0x8227, 0x42, 0, 0},{0x8228, 0xe0, 0, 0},{0x8229, 0xf5, 0, 0}, + {0x822a, 0x22, 0, 0},{0x822b, 0x75, 0, 0},{0x822c, 0x51, 0, 0}, + {0x822d, 0x0a, 0, 0},{0x822e, 0x22, 0, 0},{0x822f, 0xf5, 0, 0}, + {0x8230, 0x82, 0, 0},{0x8231, 0xe4, 0, 0},{0x8232, 0x3a, 0, 0}, + {0x8233, 0xf5, 0, 0},{0x8234, 0x83, 0, 0},{0x8235, 0x02, 0, 0}, + {0x8236, 0x09, 0, 0},{0x8237, 0xc2, 0, 0},{0x8238, 0x8f, 0, 0}, + {0x8239, 0x0a, 0, 0},{0x823a, 0x74, 0, 0},{0x823b, 0x4a, 0, 0}, + {0x823c, 0x2f, 0, 0},{0x823d, 0xf8, 0, 0},{0x823e, 0xe6, 0, 0}, + {0x823f, 0x22, 0, 0},{0x8240, 0xc2, 0, 0},{0x8241, 0x07, 0, 0}, + {0x8242, 0xc2, 0, 0},{0x8243, 0x06, 0, 0},{0x8244, 0xc2, 0, 0}, + {0x8245, 0x02, 0, 0},{0x8246, 0xc2, 0, 0},{0x8247, 0x01, 0, 0}, + {0x8248, 0xc2, 0, 0},{0x8249, 0x00, 0, 0},{0x824a, 0xc2, 0, 0}, + {0x824b, 0x03, 0, 0},{0x824c, 0xd2, 0, 0},{0x824d, 0x04, 0, 0}, + {0x824e, 0x22, 0, 0},{0x824f, 0xf5, 0, 0},{0x8250, 0x82, 0, 0}, + {0x8251, 0xe4, 0, 0},{0x8252, 0x35, 0, 0},{0x8253, 0x48, 0, 0}, + {0x8254, 0xf5, 0, 0},{0x8255, 0x83, 0, 0},{0x8256, 0xe4, 0, 0}, + {0x8257, 0x22, 0, 0},{0x8258, 0x8e, 0, 0},{0x8259, 0x67, 0, 0}, + {0x825a, 0x8f, 0, 0},{0x825b, 0x68, 0, 0},{0x825c, 0x85, 0, 0}, + {0x825d, 0x68, 0, 0},{0x825e, 0x64, 0, 0},{0x825f, 0xe5, 0, 0}, + {0x8260, 0x68, 0, 0},{0x8261, 0xae, 0, 0},{0x8262, 0x67, 0, 0}, + {0x8263, 0x78, 0, 0},{0x8264, 0x06, 0, 0},{0x8265, 0x22, 0, 0}, + {0x8266, 0xe5, 0, 0},{0x8267, 0x16, 0, 0},{0x8268, 0x25, 0, 0}, + {0x8269, 0xe0, 0, 0},{0x826a, 0x25, 0, 0},{0x826b, 0xe0, 0, 0}, + {0x826c, 0x22, 0, 0},{0x826d, 0x12, 0, 0},{0x826e, 0x09, 0, 0}, + {0x826f, 0x0a, 0, 0},{0x8270, 0x8f, 0, 0},{0x8271, 0x68, 0, 0}, + {0x8272, 0x8e, 0, 0},{0x8273, 0x67, 0, 0},{0x8274, 0x8d, 0, 0}, + {0x8275, 0x66, 0, 0},{0x8276, 0x8c, 0, 0},{0x8277, 0x65, 0, 0}, + {0x8278, 0x22, 0, 0},{0x8279, 0xe4, 0, 0},{0x827a, 0x85, 0, 0}, + {0x827b, 0x0f, 0, 0},{0x827c, 0x0d, 0, 0},{0x827d, 0x85, 0, 0}, + {0x827e, 0x0e, 0, 0},{0x827f, 0x0c, 0, 0},{0x8280, 0xf5, 0, 0}, + {0x8281, 0x0b, 0, 0},{0x8282, 0xf5, 0, 0},{0x8283, 0x0a, 0, 0}, + {0x8284, 0x22, 0, 0},{0x8285, 0x90, 0, 0},{0x8286, 0x11, 0, 0}, + {0x8287, 0xdd, 0, 0},{0x8288, 0xe4, 0, 0},{0x8289, 0x93, 0, 0}, + {0x828a, 0xff, 0, 0},{0x828b, 0x90, 0, 0},{0x828c, 0x30, 0, 0}, + {0x828d, 0x0a, 0, 0},{0x828e, 0xe0, 0, 0},{0x828f, 0x22, 0, 0}, + {0x8290, 0xc2, 0, 0},{0x8291, 0x02, 0, 0},{0x8292, 0xc2, 0, 0}, + {0x8293, 0x01, 0, 0},{0x8294, 0xd2, 0, 0},{0x8295, 0x00, 0, 0}, + {0x8296, 0xc2, 0, 0},{0x8297, 0x03, 0, 0},{0x8298, 0xc2, 0, 0}, + {0x8299, 0x04, 0, 0},{0x829a, 0x22, 0, 0},{0x829b, 0x85, 0, 0}, + {0x829c, 0x0e, 0, 0},{0x829d, 0x64, 0, 0},{0x829e, 0xe5, 0, 0}, + {0x829f, 0x0e, 0, 0},{0x82a0, 0xae, 0, 0},{0x82a1, 0x0d, 0, 0}, + {0x82a2, 0x78, 0, 0},{0x82a3, 0x06, 0, 0},{0x82a4, 0x22, 0, 0}, + {0x82a5, 0xd2, 0, 0},{0x82a6, 0x02, 0, 0},{0x82a7, 0xd2, 0, 0}, + {0x82a8, 0x01, 0, 0},{0x82a9, 0xc2, 0, 0},{0x82aa, 0x00, 0, 0}, + {0x82ab, 0x22, 0, 0},{0x82ac, 0xd3, 0, 0},{0x82ad, 0xe5, 0, 0}, + {0x82ae, 0x68, 0, 0},{0x82af, 0x94, 0, 0},{0x82b0, 0xff, 0, 0}, + {0x82b1, 0xe5, 0, 0},{0x82b2, 0x67, 0, 0},{0x82b3, 0x94, 0, 0}, + {0x82b4, 0x00, 0, 0},{0x82b5, 0x22, 0, 0},{0x82b6, 0x30, 0, 0}, + {0x82b7, 0x18, 0, 0},{0x82b8, 0x50, 0, 0},{0x82b9, 0x20, 0, 0}, + {0x82ba, 0x19, 0, 0},{0x82bb, 0x4d, 0, 0},{0x82bc, 0x75, 0, 0}, + {0x82bd, 0x0a, 0, 0},{0x82be, 0x02, 0, 0},{0x82bf, 0x74, 0, 0}, + {0x82c0, 0x4a, 0, 0},{0x82c1, 0x25, 0, 0},{0x82c2, 0x0a, 0, 0}, + {0x82c3, 0xf8, 0, 0},{0x82c4, 0xe6, 0, 0},{0x82c5, 0xff, 0, 0}, + {0x82c6, 0xe5, 0, 0},{0x82c7, 0x4a, 0, 0},{0x82c8, 0xd3, 0, 0}, + {0x82c9, 0x9f, 0, 0},{0x82ca, 0x40, 0, 0},{0x82cb, 0x04, 0, 0}, + {0x82cc, 0x7f, 0, 0},{0x82cd, 0x00, 0, 0},{0x82ce, 0x80, 0, 0}, + {0x82cf, 0x02, 0, 0},{0x82d0, 0xaf, 0, 0},{0x82d1, 0x0a, 0, 0}, + {0x82d2, 0x12, 0, 0},{0x82d3, 0x02, 0, 0},{0x82d4, 0x38, 0, 0}, + {0x82d5, 0xff, 0, 0},{0x82d6, 0xe5, 0, 0},{0x82d7, 0x4b, 0, 0}, + {0x82d8, 0xd3, 0, 0},{0x82d9, 0x9f, 0, 0},{0x82da, 0x40, 0, 0}, + {0x82db, 0x04, 0, 0},{0x82dc, 0x7f, 0, 0},{0x82dd, 0x01, 0, 0}, + {0x82de, 0x80, 0, 0},{0x82df, 0x02, 0, 0},{0x82e0, 0xaf, 0, 0}, + {0x82e1, 0x0a, 0, 0},{0x82e2, 0x12, 0, 0},{0x82e3, 0x02, 0, 0}, + {0x82e4, 0x38, 0, 0},{0x82e5, 0xff, 0, 0},{0x82e6, 0xe5, 0, 0}, + {0x82e7, 0x4d, 0, 0},{0x82e8, 0xd3, 0, 0},{0x82e9, 0x9f, 0, 0}, + {0x82ea, 0x40, 0, 0},{0x82eb, 0x04, 0, 0},{0x82ec, 0x7f, 0, 0}, + {0x82ed, 0x03, 0, 0},{0x82ee, 0x80, 0, 0},{0x82ef, 0x02, 0, 0}, + {0x82f0, 0xaf, 0, 0},{0x82f1, 0x0a, 0, 0},{0x82f2, 0x12, 0, 0}, + {0x82f3, 0x02, 0, 0},{0x82f4, 0x38, 0, 0},{0x82f5, 0xff, 0, 0}, + {0x82f6, 0xe5, 0, 0},{0x82f7, 0x4e, 0, 0},{0x82f8, 0xd3, 0, 0}, + {0x82f9, 0x9f, 0, 0},{0x82fa, 0x40, 0, 0},{0x82fb, 0x04, 0, 0}, + {0x82fc, 0x7f, 0, 0},{0x82fd, 0x04, 0, 0},{0x82fe, 0x80, 0, 0}, + {0x82ff, 0x02, 0, 0},{0x8300, 0xaf, 0, 0},{0x8301, 0x0a, 0, 0}, + {0x8302, 0x12, 0, 0},{0x8303, 0x02, 0, 0},{0x8304, 0x38, 0, 0}, + {0x8305, 0xf5, 0, 0},{0x8306, 0x0b, 0, 0},{0x8307, 0x80, 0, 0}, + {0x8308, 0x06, 0, 0},{0x8309, 0x85, 0, 0},{0x830a, 0x78, 0, 0}, + {0x830b, 0x0a, 0, 0},{0x830c, 0x85, 0, 0},{0x830d, 0x4f, 0, 0}, + {0x830e, 0x0b, 0, 0},{0x830f, 0x7f, 0, 0},{0x8310, 0x01, 0, 0}, + {0x8311, 0xe4, 0, 0},{0x8312, 0xfe, 0, 0},{0x8313, 0x74, 0, 0}, + {0x8314, 0x4a, 0, 0},{0x8315, 0x25, 0, 0},{0x8316, 0x0a, 0, 0}, + {0x8317, 0xf8, 0, 0},{0x8318, 0xe6, 0, 0},{0x8319, 0xfd, 0, 0}, + {0x831a, 0xe5, 0, 0},{0x831b, 0x0b, 0, 0},{0x831c, 0xc3, 0, 0}, + {0x831d, 0x9d, 0, 0},{0x831e, 0x50, 0, 0},{0x831f, 0x04, 0, 0}, + {0x8320, 0x7d, 0, 0},{0x8321, 0x01, 0, 0},{0x8322, 0x80, 0, 0}, + {0x8323, 0x02, 0, 0},{0x8324, 0x7d, 0, 0},{0x8325, 0xff, 0, 0}, + {0x8326, 0xac, 0, 0},{0x8327, 0x0b, 0, 0},{0x8328, 0xe5, 0, 0}, + {0x8329, 0x4e, 0, 0},{0x832a, 0xb5, 0, 0},{0x832b, 0x0b, 0, 0}, + {0x832c, 0x03, 0, 0},{0x832d, 0xd3, 0, 0},{0x832e, 0x80, 0, 0}, + {0x832f, 0x01, 0, 0},{0x8330, 0xc3, 0, 0},{0x8331, 0x92, 0, 0}, + {0x8332, 0x1f, 0, 0},{0x8333, 0xe5, 0, 0},{0x8334, 0x4d, 0, 0}, + {0x8335, 0xb5, 0, 0},{0x8336, 0x0b, 0, 0},{0x8337, 0x03, 0, 0}, + {0x8338, 0xd3, 0, 0},{0x8339, 0x80, 0, 0},{0x833a, 0x01, 0, 0}, + {0x833b, 0xc3, 0, 0},{0x833c, 0x92, 0, 0},{0x833d, 0x1e, 0, 0}, + {0x833e, 0xe5, 0, 0},{0x833f, 0x4c, 0, 0},{0x8340, 0xb5, 0, 0}, + {0x8341, 0x0b, 0, 0},{0x8342, 0x03, 0, 0},{0x8343, 0xd3, 0, 0}, + {0x8344, 0x80, 0, 0},{0x8345, 0x01, 0, 0},{0x8346, 0xc3, 0, 0}, + {0x8347, 0x92, 0, 0},{0x8348, 0x1d, 0, 0},{0x8349, 0xe5, 0, 0}, + {0x834a, 0x4b, 0, 0},{0x834b, 0xb5, 0, 0},{0x834c, 0x0b, 0, 0}, + {0x834d, 0x03, 0, 0},{0x834e, 0xd3, 0, 0},{0x834f, 0x80, 0, 0}, + {0x8350, 0x01, 0, 0},{0x8351, 0xc3, 0, 0},{0x8352, 0x92, 0, 0}, + {0x8353, 0x1c, 0, 0},{0x8354, 0xe5, 0, 0},{0x8355, 0x4a, 0, 0}, + {0x8356, 0xb5, 0, 0},{0x8357, 0x0b, 0, 0},{0x8358, 0x03, 0, 0}, + {0x8359, 0xd3, 0, 0},{0x835a, 0x80, 0, 0},{0x835b, 0x01, 0, 0}, + {0x835c, 0xc3, 0, 0},{0x835d, 0x92, 0, 0},{0x835e, 0x1b, 0, 0}, + {0x835f, 0xe5, 0, 0},{0x8360, 0x30, 0, 0},{0x8361, 0xd3, 0, 0}, + {0x8362, 0x94, 0, 0},{0x8363, 0x00, 0, 0},{0x8364, 0x40, 0, 0}, + {0x8365, 0x04, 0, 0},{0x8366, 0xa2, 0, 0},{0x8367, 0x1f, 0, 0}, + {0x8368, 0x80, 0, 0},{0x8369, 0x01, 0, 0},{0x836a, 0xc3, 0, 0}, + {0x836b, 0x92, 0, 0},{0x836c, 0x1f, 0, 0},{0x836d, 0xe5, 0, 0}, + {0x836e, 0x2f, 0, 0},{0x836f, 0xd3, 0, 0},{0x8370, 0x94, 0, 0}, + {0x8371, 0x00, 0, 0},{0x8372, 0x40, 0, 0},{0x8373, 0x04, 0, 0}, + {0x8374, 0xa2, 0, 0},{0x8375, 0x1e, 0, 0},{0x8376, 0x80, 0, 0}, + {0x8377, 0x01, 0, 0},{0x8378, 0xc3, 0, 0},{0x8379, 0x92, 0, 0}, + {0x837a, 0x1e, 0, 0},{0x837b, 0xe5, 0, 0},{0x837c, 0x2e, 0, 0}, + {0x837d, 0xd3, 0, 0},{0x837e, 0x94, 0, 0},{0x837f, 0x00, 0, 0}, + {0x8380, 0x40, 0, 0},{0x8381, 0x04, 0, 0},{0x8382, 0xa2, 0, 0}, + {0x8383, 0x1d, 0, 0},{0x8384, 0x80, 0, 0},{0x8385, 0x01, 0, 0}, + {0x8386, 0xc3, 0, 0},{0x8387, 0x92, 0, 0},{0x8388, 0x1d, 0, 0}, + {0x8389, 0xe5, 0, 0},{0x838a, 0x2d, 0, 0},{0x838b, 0xd3, 0, 0}, + {0x838c, 0x94, 0, 0},{0x838d, 0x00, 0, 0},{0x838e, 0x40, 0, 0}, + {0x838f, 0x04, 0, 0},{0x8390, 0xa2, 0, 0},{0x8391, 0x1c, 0, 0}, + {0x8392, 0x80, 0, 0},{0x8393, 0x01, 0, 0},{0x8394, 0xc3, 0, 0}, + {0x8395, 0x92, 0, 0},{0x8396, 0x1c, 0, 0},{0x8397, 0xe5, 0, 0}, + {0x8398, 0x2c, 0, 0},{0x8399, 0xd3, 0, 0},{0x839a, 0x94, 0, 0}, + {0x839b, 0x00, 0, 0},{0x839c, 0x40, 0, 0},{0x839d, 0x04, 0, 0}, + {0x839e, 0xa2, 0, 0},{0x839f, 0x1b, 0, 0},{0x83a0, 0x80, 0, 0}, + {0x83a1, 0x01, 0, 0},{0x83a2, 0xc3, 0, 0},{0x83a3, 0x92, 0, 0}, + {0x83a4, 0x1b, 0, 0},{0x83a5, 0xe5, 0, 0},{0x83a6, 0x23, 0, 0}, + {0x83a7, 0x54, 0, 0},{0x83a8, 0xf8, 0, 0},{0x83a9, 0x70, 0, 0}, + {0x83aa, 0x43, 0, 0},{0x83ab, 0xbf, 0, 0},{0x83ac, 0x01, 0, 0}, + {0x83ad, 0x08, 0, 0},{0x83ae, 0xed, 0, 0},{0x83af, 0xf4, 0, 0}, + {0x83b0, 0x04, 0, 0},{0x83b1, 0xfd, 0, 0},{0x83b2, 0x7f, 0, 0}, + {0x83b3, 0x02, 0, 0},{0x83b4, 0x80, 0, 0},{0x83b5, 0x06, 0, 0}, + {0x83b6, 0xbf, 0, 0},{0x83b7, 0x02, 0, 0},{0x83b8, 0x02, 0, 0}, + {0x83b9, 0x7f, 0, 0},{0x83ba, 0x01, 0, 0},{0x83bb, 0x0e, 0, 0}, + {0x83bc, 0xd3, 0, 0},{0x83bd, 0xed, 0, 0},{0x83be, 0x64, 0, 0}, + {0x83bf, 0x80, 0, 0},{0x83c0, 0x94, 0, 0},{0x83c1, 0x80, 0, 0}, + {0x83c2, 0x40, 0, 0},{0x83c3, 0x12, 0, 0},{0x83c4, 0xec, 0, 0}, + {0x83c5, 0x2e, 0, 0},{0x83c6, 0xf5, 0, 0},{0x83c7, 0x0b, 0, 0}, + {0x83c8, 0xc3, 0, 0},{0x83c9, 0x95, 0, 0},{0x83ca, 0x7b, 0, 0}, + {0x83cb, 0x50, 0, 0},{0x83cc, 0x03, 0, 0},{0x83cd, 0x02, 0, 0}, + {0x83ce, 0x03, 0, 0},{0x83cf, 0x28, 0, 0},{0x83d0, 0x7d, 0, 0}, + {0x83d1, 0xff, 0, 0},{0x83d2, 0xe4, 0, 0},{0x83d3, 0xff, 0, 0}, + {0x83d4, 0x80, 0, 0},{0x83d5, 0x11, 0, 0},{0x83d6, 0xec, 0, 0}, + {0x83d7, 0xd3, 0, 0},{0x83d8, 0x9e, 0, 0},{0x83d9, 0x50, 0, 0}, + {0x83da, 0x0b, 0, 0},{0x83db, 0x7d, 0, 0},{0x83dc, 0x01, 0, 0}, + {0x83dd, 0xe4, 0, 0},{0x83de, 0xff, 0, 0},{0x83df, 0xec, 0, 0}, + {0x83e0, 0x2e, 0, 0},{0x83e1, 0xf5, 0, 0},{0x83e2, 0x0b, 0, 0}, + {0x83e3, 0x02, 0, 0},{0x83e4, 0x03, 0, 0},{0x83e5, 0x28, 0, 0}, + {0x83e6, 0xc3, 0, 0},{0x83e7, 0xec, 0, 0},{0x83e8, 0x9e, 0, 0}, + {0x83e9, 0xf5, 0, 0},{0x83ea, 0x0b, 0, 0},{0x83eb, 0x02, 0, 0}, + {0x83ec, 0x03, 0, 0},{0x83ed, 0x28, 0, 0},{0x83ee, 0x12, 0, 0}, + {0x83ef, 0x01, 0, 0},{0x83f0, 0xbf, 0, 0},{0x83f1, 0xe5, 0, 0}, + {0x83f2, 0x4e, 0, 0},{0x83f3, 0xb5, 0, 0},{0x83f4, 0x07, 0, 0}, + {0x83f5, 0x07, 0, 0},{0x83f6, 0xe4, 0, 0},{0x83f7, 0xb5, 0, 0}, + {0x83f8, 0x06, 0, 0},{0x83f9, 0x03, 0, 0},{0x83fa, 0xd3, 0, 0}, + {0x83fb, 0x80, 0, 0},{0x83fc, 0x02, 0, 0},{0x83fd, 0xa2, 0, 0}, + {0x83fe, 0x1f, 0, 0},{0x83ff, 0x92, 0, 0},{0x8400, 0x1f, 0, 0}, + {0x8401, 0xe5, 0, 0},{0x8402, 0x4d, 0, 0},{0x8403, 0xb5, 0, 0}, + {0x8404, 0x07, 0, 0},{0x8405, 0x07, 0, 0},{0x8406, 0xe4, 0, 0}, + {0x8407, 0xb5, 0, 0},{0x8408, 0x06, 0, 0},{0x8409, 0x03, 0, 0}, + {0x840a, 0xd3, 0, 0},{0x840b, 0x80, 0, 0},{0x840c, 0x02, 0, 0}, + {0x840d, 0xa2, 0, 0},{0x840e, 0x1e, 0, 0},{0x840f, 0x92, 0, 0}, + {0x8410, 0x1e, 0, 0},{0x8411, 0x12, 0, 0},{0x8412, 0x01, 0, 0}, + {0x8413, 0xbf, 0, 0},{0x8414, 0xe5, 0, 0},{0x8415, 0x4c, 0, 0}, + {0x8416, 0xb5, 0, 0},{0x8417, 0x07, 0, 0},{0x8418, 0x07, 0, 0}, + {0x8419, 0xe4, 0, 0},{0x841a, 0xb5, 0, 0},{0x841b, 0x06, 0, 0}, + {0x841c, 0x03, 0, 0},{0x841d, 0xd3, 0, 0},{0x841e, 0x80, 0, 0}, + {0x841f, 0x02, 0, 0},{0x8420, 0xa2, 0, 0},{0x8421, 0x1d, 0, 0}, + {0x8422, 0x92, 0, 0},{0x8423, 0x1d, 0, 0},{0x8424, 0xe5, 0, 0}, + {0x8425, 0x4b, 0, 0},{0x8426, 0xb5, 0, 0},{0x8427, 0x07, 0, 0}, + {0x8428, 0x07, 0, 0},{0x8429, 0xe4, 0, 0},{0x842a, 0xb5, 0, 0}, + {0x842b, 0x06, 0, 0},{0x842c, 0x03, 0, 0},{0x842d, 0xd3, 0, 0}, + {0x842e, 0x80, 0, 0},{0x842f, 0x02, 0, 0},{0x8430, 0xa2, 0, 0}, + {0x8431, 0x1c, 0, 0},{0x8432, 0x92, 0, 0},{0x8433, 0x1c, 0, 0}, + {0x8434, 0x12, 0, 0},{0x8435, 0x01, 0, 0},{0x8436, 0xbf, 0, 0}, + {0x8437, 0xe5, 0, 0},{0x8438, 0x4a, 0, 0},{0x8439, 0xb5, 0, 0}, + {0x843a, 0x07, 0, 0},{0x843b, 0x07, 0, 0},{0x843c, 0xe4, 0, 0}, + {0x843d, 0xb5, 0, 0},{0x843e, 0x06, 0, 0},{0x843f, 0x03, 0, 0}, + {0x8440, 0xd3, 0, 0},{0x8441, 0x80, 0, 0},{0x8442, 0x02, 0, 0}, + {0x8443, 0xa2, 0, 0},{0x8444, 0x1b, 0, 0},{0x8445, 0x92, 0, 0}, + {0x8446, 0x1b, 0, 0},{0x8447, 0xe5, 0, 0},{0x8448, 0x4e, 0, 0}, + {0x8449, 0x12, 0, 0},{0x844a, 0x01, 0, 0},{0x844b, 0xc1, 0, 0}, + {0x844c, 0xad, 0, 0},{0x844d, 0x0b, 0, 0},{0x844e, 0x7c, 0, 0}, + {0x844f, 0x00, 0, 0},{0x8450, 0xef, 0, 0},{0x8451, 0xb5, 0, 0}, + {0x8452, 0x05, 0, 0},{0x8453, 0x07, 0, 0},{0x8454, 0xec, 0, 0}, + {0x8455, 0xb5, 0, 0},{0x8456, 0x06, 0, 0},{0x8457, 0x03, 0, 0}, + {0x8458, 0xd3, 0, 0},{0x8459, 0x80, 0, 0},{0x845a, 0x02, 0, 0}, + {0x845b, 0xa2, 0, 0},{0x845c, 0x1f, 0, 0},{0x845d, 0x92, 0, 0}, + {0x845e, 0x1f, 0, 0},{0x845f, 0xe5, 0, 0},{0x8460, 0x4d, 0, 0}, + {0x8461, 0x12, 0, 0},{0x8462, 0x01, 0, 0},{0x8463, 0xc1, 0, 0}, + {0x8464, 0xef, 0, 0},{0x8465, 0xb5, 0, 0},{0x8466, 0x05, 0, 0}, + {0x8467, 0x07, 0, 0},{0x8468, 0xee, 0, 0},{0x8469, 0xb5, 0, 0}, + {0x846a, 0x04, 0, 0},{0x846b, 0x03, 0, 0},{0x846c, 0xd3, 0, 0}, + {0x846d, 0x80, 0, 0},{0x846e, 0x02, 0, 0},{0x846f, 0xa2, 0, 0}, + {0x8470, 0x1e, 0, 0},{0x8471, 0x92, 0, 0},{0x8472, 0x1e, 0, 0}, + {0x8473, 0xe5, 0, 0},{0x8474, 0x4c, 0, 0},{0x8475, 0x12, 0, 0}, + {0x8476, 0x01, 0, 0},{0x8477, 0xc1, 0, 0},{0x8478, 0xad, 0, 0}, + {0x8479, 0x0b, 0, 0},{0x847a, 0x7c, 0, 0},{0x847b, 0x00, 0, 0}, + {0x847c, 0xef, 0, 0},{0x847d, 0xb5, 0, 0},{0x847e, 0x05, 0, 0}, + {0x847f, 0x07, 0, 0},{0x8480, 0xec, 0, 0},{0x8481, 0xb5, 0, 0}, + {0x8482, 0x06, 0, 0},{0x8483, 0x03, 0, 0},{0x8484, 0xd3, 0, 0}, + {0x8485, 0x80, 0, 0},{0x8486, 0x02, 0, 0},{0x8487, 0xa2, 0, 0}, + {0x8488, 0x1d, 0, 0},{0x8489, 0x92, 0, 0},{0x848a, 0x1d, 0, 0}, + {0x848b, 0xe5, 0, 0},{0x848c, 0x4b, 0, 0},{0x848d, 0x12, 0, 0}, + {0x848e, 0x01, 0, 0},{0x848f, 0xc1, 0, 0},{0x8490, 0xef, 0, 0}, + {0x8491, 0xb5, 0, 0},{0x8492, 0x05, 0, 0},{0x8493, 0x07, 0, 0}, + {0x8494, 0xee, 0, 0},{0x8495, 0xb5, 0, 0},{0x8496, 0x04, 0, 0}, + {0x8497, 0x03, 0, 0},{0x8498, 0xd3, 0, 0},{0x8499, 0x80, 0, 0}, + {0x849a, 0x02, 0, 0},{0x849b, 0xa2, 0, 0},{0x849c, 0x1c, 0, 0}, + {0x849d, 0x92, 0, 0},{0x849e, 0x1c, 0, 0},{0x849f, 0xe5, 0, 0}, + {0x84a0, 0x4a, 0, 0},{0x84a1, 0x12, 0, 0},{0x84a2, 0x01, 0, 0}, + {0x84a3, 0xc1, 0, 0},{0x84a4, 0x7c, 0, 0},{0x84a5, 0x00, 0, 0}, + {0x84a6, 0xef, 0, 0},{0x84a7, 0xb5, 0, 0},{0x84a8, 0x0b, 0, 0}, + {0x84a9, 0x07, 0, 0},{0x84aa, 0xec, 0, 0},{0x84ab, 0xb5, 0, 0}, + {0x84ac, 0x06, 0, 0},{0x84ad, 0x03, 0, 0},{0x84ae, 0xd3, 0, 0}, + {0x84af, 0x80, 0, 0},{0x84b0, 0x02, 0, 0},{0x84b1, 0xa2, 0, 0}, + {0x84b2, 0x1b, 0, 0},{0x84b3, 0x92, 0, 0},{0x84b4, 0x1b, 0, 0}, + {0x84b5, 0xe5, 0, 0},{0x84b6, 0x30, 0, 0},{0x84b7, 0xd3, 0, 0}, + {0x84b8, 0x94, 0, 0},{0x84b9, 0x00, 0, 0},{0x84ba, 0x40, 0, 0}, + {0x84bb, 0x04, 0, 0},{0x84bc, 0xa2, 0, 0},{0x84bd, 0x1f, 0, 0}, + {0x84be, 0x80, 0, 0},{0x84bf, 0x01, 0, 0},{0x84c0, 0xc3, 0, 0}, + {0x84c1, 0x92, 0, 0},{0x84c2, 0x1f, 0, 0},{0x84c3, 0xe5, 0, 0}, + {0x84c4, 0x2f, 0, 0},{0x84c5, 0xd3, 0, 0},{0x84c6, 0x94, 0, 0}, + {0x84c7, 0x00, 0, 0},{0x84c8, 0x40, 0, 0},{0x84c9, 0x04, 0, 0}, + {0x84ca, 0xa2, 0, 0},{0x84cb, 0x1e, 0, 0},{0x84cc, 0x80, 0, 0}, + {0x84cd, 0x01, 0, 0},{0x84ce, 0xc3, 0, 0},{0x84cf, 0x92, 0, 0}, + {0x84d0, 0x1e, 0, 0},{0x84d1, 0xe5, 0, 0},{0x84d2, 0x2e, 0, 0}, + {0x84d3, 0xd3, 0, 0},{0x84d4, 0x94, 0, 0},{0x84d5, 0x00, 0, 0}, + {0x84d6, 0x40, 0, 0},{0x84d7, 0x04, 0, 0},{0x84d8, 0xa2, 0, 0}, + {0x84d9, 0x1d, 0, 0},{0x84da, 0x80, 0, 0},{0x84db, 0x01, 0, 0}, + {0x84dc, 0xc3, 0, 0},{0x84dd, 0x92, 0, 0},{0x84de, 0x1d, 0, 0}, + {0x84df, 0xe5, 0, 0},{0x84e0, 0x2d, 0, 0},{0x84e1, 0xd3, 0, 0}, + {0x84e2, 0x94, 0, 0},{0x84e3, 0x00, 0, 0},{0x84e4, 0x40, 0, 0}, + {0x84e5, 0x04, 0, 0},{0x84e6, 0xa2, 0, 0},{0x84e7, 0x1c, 0, 0}, + {0x84e8, 0x80, 0, 0},{0x84e9, 0x01, 0, 0},{0x84ea, 0xc3, 0, 0}, + {0x84eb, 0x92, 0, 0},{0x84ec, 0x1c, 0, 0},{0x84ed, 0xe5, 0, 0}, + {0x84ee, 0x2c, 0, 0},{0x84ef, 0xd3, 0, 0},{0x84f0, 0x94, 0, 0}, + {0x84f1, 0x00, 0, 0},{0x84f2, 0x40, 0, 0},{0x84f3, 0x04, 0, 0}, + {0x84f4, 0xa2, 0, 0},{0x84f5, 0x1b, 0, 0},{0x84f6, 0x80, 0, 0}, + {0x84f7, 0x01, 0, 0},{0x84f8, 0xc3, 0, 0},{0x84f9, 0x92, 0, 0}, + {0x84fa, 0x1b, 0, 0},{0x84fb, 0x85, 0, 0},{0x84fc, 0x0a, 0, 0}, + {0x84fd, 0x78, 0, 0},{0x84fe, 0xe5, 0, 0},{0x84ff, 0x7c, 0, 0}, + {0x8500, 0xb5, 0, 0},{0x8501, 0x0b, 0, 0},{0x8502, 0x03, 0, 0}, + {0x8503, 0x02, 0, 0},{0x8504, 0x15, 0, 0},{0x8505, 0x18, 0, 0}, + {0x8506, 0x85, 0, 0},{0x8507, 0x0b, 0, 0},{0x8508, 0x0c, 0, 0}, + {0x8509, 0x12, 0, 0},{0x850a, 0x0f, 0, 0},{0x850b, 0x11, 0, 0}, + {0x850c, 0xd2, 0, 0},{0x850d, 0x02, 0, 0},{0x850e, 0xc2, 0, 0}, + {0x850f, 0x01, 0, 0},{0x8510, 0xd2, 0, 0},{0x8511, 0x00, 0, 0}, + {0x8512, 0x75, 0, 0},{0x8513, 0x31, 0, 0},{0x8514, 0x03, 0, 0}, + {0x8515, 0x22, 0, 0},{0x8516, 0xe5, 0, 0},{0x8517, 0x7d, 0, 0}, + {0x8518, 0x24, 0, 0},{0x8519, 0xfe, 0, 0},{0x851a, 0x60, 0, 0}, + {0x851b, 0x27, 0, 0},{0x851c, 0x14, 0, 0},{0x851d, 0x60, 0, 0}, + {0x851e, 0x31, 0, 0},{0x851f, 0x24, 0, 0},{0x8520, 0xf8, 0, 0}, + {0x8521, 0x60, 0, 0},{0x8522, 0x3a, 0, 0},{0x8523, 0x14, 0, 0}, + {0x8524, 0x60, 0, 0},{0x8525, 0x4b, 0, 0},{0x8526, 0x14, 0, 0}, + {0x8527, 0x60, 0, 0},{0x8528, 0x59, 0, 0},{0x8529, 0x14, 0, 0}, + {0x852a, 0x60, 0, 0},{0x852b, 0x6a, 0, 0},{0x852c, 0x24, 0, 0}, + {0x852d, 0xfd, 0, 0},{0x852e, 0x70, 0, 0},{0x852f, 0x03, 0, 0}, + {0x8530, 0x02, 0, 0},{0x8531, 0x05, 0, 0},{0x8532, 0xcb, 0, 0}, + {0x8533, 0x24, 0, 0},{0x8534, 0x10, 0, 0},{0x8535, 0x60, 0, 0}, + {0x8536, 0x03, 0, 0},{0x8537, 0x02, 0, 0},{0x8538, 0x06, 0, 0}, + {0x8539, 0xc6, 0, 0},{0x853a, 0xe4, 0, 0},{0x853b, 0xf5, 0, 0}, + {0x853c, 0x0a, 0, 0},{0x853d, 0x12, 0, 0},{0x853e, 0x10, 0, 0}, + {0x853f, 0xb5, 0, 0},{0x8540, 0x02, 0, 0},{0x8541, 0x06, 0, 0}, + {0x8542, 0xaf, 0, 0},{0x8543, 0x75, 0, 0},{0x8544, 0x0a, 0, 0}, + {0x8545, 0x01, 0, 0},{0x8546, 0x12, 0, 0},{0x8547, 0x06, 0, 0}, + {0x8548, 0xc7, 0, 0},{0x8549, 0xc2, 0, 0},{0x854a, 0x3a, 0, 0}, + {0x854b, 0xd2, 0, 0},{0x854c, 0x39, 0, 0},{0x854d, 0x02, 0, 0}, + {0x854e, 0x06, 0, 0},{0x854f, 0xc0, 0, 0},{0x8550, 0x75, 0, 0}, + {0x8551, 0x0a, 0, 0},{0x8552, 0x02, 0, 0},{0x8553, 0x12, 0, 0}, + {0x8554, 0x06, 0, 0},{0x8555, 0xc7, 0, 0},{0x8556, 0xd2, 0, 0}, + {0x8557, 0x3a, 0, 0},{0x8558, 0xc2, 0, 0},{0x8559, 0x39, 0, 0}, + {0x855a, 0x02, 0, 0},{0x855b, 0x06, 0, 0},{0x855c, 0xc0, 0, 0}, + {0x855d, 0x30, 0, 0},{0x855e, 0x36, 0, 0},{0x855f, 0x0c, 0, 0}, + {0x8560, 0x20, 0, 0},{0x8561, 0x3a, 0, 0},{0x8562, 0x03, 0, 0}, + {0x8563, 0x30, 0, 0},{0x8564, 0x39, 0, 0},{0x8565, 0x06, 0, 0}, + {0x8566, 0xe4, 0, 0},{0x8567, 0xf5, 0, 0},{0x8568, 0x0a, 0, 0}, + {0x8569, 0x12, 0, 0},{0x856a, 0x12, 0, 0},{0x856b, 0x92, 0, 0}, + {0x856c, 0xd2, 0, 0},{0x856d, 0x18, 0, 0},{0x856e, 0xc2, 0, 0}, + {0x856f, 0x19, 0, 0},{0x8570, 0x22, 0, 0},{0x8571, 0x30, 0, 0}, + {0x8572, 0x36, 0, 0},{0x8573, 0x09, 0, 0},{0x8574, 0x20, 0, 0}, + {0x8575, 0x3a, 0, 0},{0x8576, 0x03, 0, 0},{0x8577, 0x30, 0, 0}, + {0x8578, 0x39, 0, 0},{0x8579, 0x03, 0, 0},{0x857a, 0x12, 0, 0}, + {0x857b, 0x06, 0, 0},{0x857c, 0xd8, 0, 0},{0x857d, 0xd2, 0, 0}, + {0x857e, 0x18, 0, 0},{0x857f, 0xd2, 0, 0},{0x8580, 0x19, 0, 0}, + {0x8581, 0x22, 0, 0},{0x8582, 0x30, 0, 0},{0x8583, 0x36, 0, 0}, + {0x8584, 0x0c, 0, 0},{0x8585, 0x20, 0, 0},{0x8586, 0x3a, 0, 0}, + {0x8587, 0x03, 0, 0},{0x8588, 0x30, 0, 0},{0x8589, 0x39, 0, 0}, + {0x858a, 0x06, 0, 0},{0x858b, 0x75, 0, 0},{0x858c, 0x0a, 0, 0}, + {0x858d, 0x02, 0, 0},{0x858e, 0x12, 0, 0},{0x858f, 0x12, 0, 0}, + {0x8590, 0x92, 0, 0},{0x8591, 0xc2, 0, 0},{0x8592, 0x18, 0, 0}, + {0x8593, 0xd2, 0, 0},{0x8594, 0x19, 0, 0},{0x8595, 0x22, 0, 0}, + {0x8596, 0x20, 0, 0},{0x8597, 0x3a, 0, 0},{0x8598, 0x06, 0, 0}, + {0x8599, 0x20, 0, 0},{0x859a, 0x39, 0, 0},{0x859b, 0x03, 0, 0}, + {0x859c, 0x02, 0, 0},{0x859d, 0x06, 0, 0},{0x859e, 0xc6, 0, 0}, + {0x859f, 0xe5, 0, 0},{0x85a0, 0x78, 0, 0},{0x85a1, 0xd3, 0, 0}, + {0x85a2, 0x94, 0, 0},{0x85a3, 0x03, 0, 0},{0x85a4, 0x40, 0, 0}, + {0x85a5, 0x04, 0, 0},{0x85a6, 0x7f, 0, 0},{0x85a7, 0x00, 0, 0}, + {0x85a8, 0x80, 0, 0},{0x85a9, 0x04, 0, 0},{0x85aa, 0xe5, 0, 0}, + {0x85ab, 0x78, 0, 0},{0x85ac, 0x04, 0, 0},{0x85ad, 0xff, 0, 0}, + {0x85ae, 0x8f, 0, 0},{0x85af, 0x78, 0, 0},{0x85b0, 0x30, 0, 0}, + {0x85b1, 0x18, 0, 0},{0x85b2, 0x06, 0, 0},{0x85b3, 0x30, 0, 0}, + {0x85b4, 0x19, 0, 0},{0x85b5, 0x03, 0, 0},{0x85b6, 0x12, 0, 0}, + {0x85b7, 0x06, 0, 0},{0x85b8, 0xd8, 0, 0},{0x85b9, 0x30, 0, 0}, + {0x85ba, 0x18, 0, 0},{0x85bb, 0x03, 0, 0},{0x85bc, 0x02, 0, 0}, + {0x85bd, 0x06, 0, 0},{0x85be, 0xc6, 0, 0},{0x85bf, 0x20, 0, 0}, + {0x85c0, 0x19, 0, 0},{0x85c1, 0x03, 0, 0},{0x85c2, 0x02, 0, 0}, + {0x85c3, 0x06, 0, 0},{0x85c4, 0xc6, 0, 0},{0x85c5, 0x75, 0, 0}, + {0x85c6, 0x0a, 0, 0},{0x85c7, 0x02, 0, 0},{0x85c8, 0x02, 0, 0}, + {0x85c9, 0x12, 0, 0},{0x85ca, 0x92, 0, 0},{0x85cb, 0xe5, 0, 0}, + {0x85cc, 0x67, 0, 0},{0x85cd, 0xd3, 0, 0},{0x85ce, 0x94, 0, 0}, + {0x85cf, 0x38, 0, 0},{0x85d0, 0x40, 0, 0},{0x85d1, 0x04, 0, 0}, + {0x85d2, 0x7f, 0, 0},{0x85d3, 0x34, 0, 0},{0x85d4, 0x80, 0, 0}, + {0x85d5, 0x02, 0, 0},{0x85d6, 0xaf, 0, 0},{0x85d7, 0x67, 0, 0}, + {0x85d8, 0x8f, 0, 0},{0x85d9, 0x67, 0, 0},{0x85da, 0xe5, 0, 0}, + {0x85db, 0x67, 0, 0},{0x85dc, 0xc3, 0, 0},{0x85dd, 0x94, 0, 0}, + {0x85de, 0x08, 0, 0},{0x85df, 0x50, 0, 0},{0x85e0, 0x04, 0, 0}, + {0x85e1, 0x7f, 0, 0},{0x85e2, 0x08, 0, 0},{0x85e3, 0x80, 0, 0}, + {0x85e4, 0x02, 0, 0},{0x85e5, 0xaf, 0, 0},{0x85e6, 0x67, 0, 0}, + {0x85e7, 0x8f, 0, 0},{0x85e8, 0x67, 0, 0},{0x85e9, 0xe5, 0, 0}, + {0x85ea, 0x68, 0, 0},{0x85eb, 0xd3, 0, 0},{0x85ec, 0x94, 0, 0}, + {0x85ed, 0x2a, 0, 0},{0x85ee, 0x40, 0, 0},{0x85ef, 0x04, 0, 0}, + {0x85f0, 0x7f, 0, 0},{0x85f1, 0x2a, 0, 0},{0x85f2, 0x80, 0, 0}, + {0x85f3, 0x02, 0, 0},{0x85f4, 0xaf, 0, 0},{0x85f5, 0x68, 0, 0}, + {0x85f6, 0x8f, 0, 0},{0x85f7, 0x68, 0, 0},{0x85f8, 0xe5, 0, 0}, + {0x85f9, 0x68, 0, 0},{0x85fa, 0xc3, 0, 0},{0x85fb, 0x94, 0, 0}, + {0x85fc, 0x06, 0, 0},{0x85fd, 0x50, 0, 0},{0x85fe, 0x04, 0, 0}, + {0x85ff, 0x7f, 0, 0},{0x8600, 0x06, 0, 0},{0x8601, 0x80, 0, 0}, + {0x8602, 0x02, 0, 0},{0x8603, 0xaf, 0, 0},{0x8604, 0x68, 0, 0}, + {0x8605, 0x8f, 0, 0},{0x8606, 0x68, 0, 0},{0x8607, 0xaf, 0, 0}, + {0x8608, 0x67, 0, 0},{0x8609, 0xef, 0, 0},{0x860a, 0x24, 0, 0}, + {0x860b, 0xf8, 0, 0},{0x860c, 0xff, 0, 0},{0x860d, 0xe4, 0, 0}, + {0x860e, 0x34, 0, 0},{0x860f, 0xff, 0, 0},{0x8610, 0xfe, 0, 0}, + {0x8611, 0xe4, 0, 0},{0x8612, 0x8f, 0, 0},{0x8613, 0x3f, 0, 0}, + {0x8614, 0x8e, 0, 0},{0x8615, 0x3e, 0, 0},{0x8616, 0xf5, 0, 0}, + {0x8617, 0x3d, 0, 0},{0x8618, 0xf5, 0, 0},{0x8619, 0x3c, 0, 0}, + {0x861a, 0xac, 0, 0},{0x861b, 0x3c, 0, 0},{0x861c, 0x12, 0, 0}, + {0x861d, 0x01, 0, 0},{0x861e, 0x90, 0, 0},{0x861f, 0xaf, 0, 0}, + {0x8620, 0x68, 0, 0},{0x8621, 0xef, 0, 0},{0x8622, 0x24, 0, 0}, + {0x8623, 0xfa, 0, 0},{0x8624, 0xff, 0, 0},{0x8625, 0xe4, 0, 0}, + {0x8626, 0x34, 0, 0},{0x8627, 0xff, 0, 0},{0x8628, 0x12, 0, 0}, + {0x8629, 0x01, 0, 0},{0x862a, 0x73, 0, 0},{0x862b, 0xaf, 0, 0}, + {0x862c, 0x67, 0, 0},{0x862d, 0xef, 0, 0},{0x862e, 0x24, 0, 0}, + {0x862f, 0x08, 0, 0},{0x8630, 0xff, 0, 0},{0x8631, 0xe4, 0, 0}, + {0x8632, 0x33, 0, 0},{0x8633, 0x12, 0, 0},{0x8634, 0x01, 0, 0}, + {0x8635, 0x73, 0, 0},{0x8636, 0xaf, 0, 0},{0x8637, 0x68, 0, 0}, + {0x8638, 0xef, 0, 0},{0x8639, 0x24, 0, 0},{0x863a, 0x06, 0, 0}, + {0x863b, 0xff, 0, 0},{0x863c, 0xe4, 0, 0},{0x863d, 0x33, 0, 0}, + {0x863e, 0xfe, 0, 0},{0x863f, 0xe4, 0, 0},{0x8640, 0xfc, 0, 0}, + {0x8641, 0xfd, 0, 0},{0x8642, 0xe5, 0, 0},{0x8643, 0x3f, 0, 0}, + {0x8644, 0x2f, 0, 0},{0x8645, 0xf5, 0, 0},{0x8646, 0x3f, 0, 0}, + {0x8647, 0xe5, 0, 0},{0x8648, 0x3e, 0, 0},{0x8649, 0x3e, 0, 0}, + {0x864a, 0xf5, 0, 0},{0x864b, 0x3e, 0, 0},{0x864c, 0xed, 0, 0}, + {0x864d, 0x35, 0, 0},{0x864e, 0x3d, 0, 0},{0x864f, 0xf5, 0, 0}, + {0x8650, 0x3d, 0, 0},{0x8651, 0xec, 0, 0},{0x8652, 0x35, 0, 0}, + {0x8653, 0x3c, 0, 0},{0x8654, 0xf5, 0, 0},{0x8655, 0x3c, 0, 0}, + {0x8656, 0xe4, 0, 0},{0x8657, 0x25, 0, 0},{0x8658, 0x3f, 0, 0}, + {0x8659, 0xf5, 0, 0},{0x865a, 0x37, 0, 0},{0x865b, 0xe4, 0, 0}, + {0x865c, 0x35, 0, 0},{0x865d, 0x3e, 0, 0},{0x865e, 0xf5, 0, 0}, + {0x865f, 0x36, 0, 0},{0x8660, 0xe4, 0, 0},{0x8661, 0x35, 0, 0}, + {0x8662, 0x3d, 0, 0},{0x8663, 0xf5, 0, 0},{0x8664, 0x35, 0, 0}, + {0x8665, 0xe5, 0, 0},{0x8666, 0x3c, 0, 0},{0x8667, 0x34, 0, 0}, + {0x8668, 0x08, 0, 0},{0x8669, 0xf5, 0, 0},{0x866a, 0x34, 0, 0}, + {0x866b, 0xe4, 0, 0},{0x866c, 0x25, 0, 0},{0x866d, 0x3f, 0, 0}, + {0x866e, 0xf5, 0, 0},{0x866f, 0x3b, 0, 0},{0x8670, 0xe4, 0, 0}, + {0x8671, 0x35, 0, 0},{0x8672, 0x3e, 0, 0},{0x8673, 0xf5, 0, 0}, + {0x8674, 0x3a, 0, 0},{0x8675, 0xe5, 0, 0},{0x8676, 0x3d, 0, 0}, + {0x8677, 0x34, 0, 0},{0x8678, 0x06, 0, 0},{0x8679, 0xf5, 0, 0}, + {0x867a, 0x39, 0, 0},{0x867b, 0xe4, 0, 0},{0x867c, 0x35, 0, 0}, + {0x867d, 0x3c, 0, 0},{0x867e, 0xf5, 0, 0},{0x867f, 0x38, 0, 0}, + {0x8680, 0xe5, 0, 0},{0x8681, 0x3f, 0, 0},{0x8682, 0x24, 0, 0}, + {0x8683, 0xfa, 0, 0},{0x8684, 0xf5, 0, 0},{0x8685, 0x43, 0, 0}, + {0x8686, 0xe5, 0, 0},{0x8687, 0x3e, 0, 0},{0x8688, 0x34, 0, 0}, + {0x8689, 0xff, 0, 0},{0x868a, 0xf5, 0, 0},{0x868b, 0x42, 0, 0}, + {0x868c, 0xe5, 0, 0},{0x868d, 0x3d, 0, 0},{0x868e, 0x34, 0, 0}, + {0x868f, 0xff, 0, 0},{0x8690, 0xf5, 0, 0},{0x8691, 0x41, 0, 0}, + {0x8692, 0xe5, 0, 0},{0x8693, 0x3c, 0, 0},{0x8694, 0x34, 0, 0}, + {0x8695, 0xff, 0, 0},{0x8696, 0xf5, 0, 0},{0x8697, 0x40, 0, 0}, + {0x8698, 0xe4, 0, 0},{0x8699, 0x25, 0, 0},{0x869a, 0x3f, 0, 0}, + {0x869b, 0xf5, 0, 0},{0x869c, 0x47, 0, 0},{0x869d, 0xe5, 0, 0}, + {0x869e, 0x3e, 0, 0},{0x869f, 0x34, 0, 0},{0x86a0, 0xf8, 0, 0}, + {0x86a1, 0xf5, 0, 0},{0x86a2, 0x46, 0, 0},{0x86a3, 0xe5, 0, 0}, + {0x86a4, 0x3d, 0, 0},{0x86a5, 0x34, 0, 0},{0x86a6, 0xff, 0, 0}, + {0x86a7, 0xf5, 0, 0},{0x86a8, 0x45, 0, 0},{0x86a9, 0xe5, 0, 0}, + {0x86aa, 0x3c, 0, 0},{0x86ab, 0x34, 0, 0},{0x86ac, 0xff, 0, 0}, + {0x86ad, 0xf5, 0, 0},{0x86ae, 0x44, 0, 0},{0x86af, 0x75, 0, 0}, + {0x86b0, 0x78, 0, 0},{0x86b1, 0x02, 0, 0},{0x86b2, 0x75, 0, 0}, + {0x86b3, 0x0a, 0, 0},{0x86b4, 0x01, 0, 0},{0x86b5, 0x12, 0, 0}, + {0x86b6, 0x12, 0, 0},{0x86b7, 0x92, 0, 0},{0x86b8, 0xd2, 0, 0}, + {0x86b9, 0x18, 0, 0},{0x86ba, 0xd2, 0, 0},{0x86bb, 0x19, 0, 0}, + {0x86bc, 0xc2, 0, 0},{0x86bd, 0x3a, 0, 0},{0x86be, 0xc2, 0, 0}, + {0x86bf, 0x39, 0, 0},{0x86c0, 0xd2, 0, 0},{0x86c1, 0x1a, 0, 0}, + {0x86c2, 0xd2, 0, 0},{0x86c3, 0x36, 0, 0},{0x86c4, 0xd2, 0, 0}, + {0x86c5, 0x30, 0, 0},{0x86c6, 0x22, 0, 0},{0x86c7, 0x12, 0, 0}, + {0x86c8, 0x10, 0, 0},{0x86c9, 0xb5, 0, 0},{0x86ca, 0x75, 0, 0}, + {0x86cb, 0x78, 0, 0},{0x86cc, 0x02, 0, 0},{0x86cd, 0xe4, 0, 0}, + {0x86ce, 0xf5, 0, 0},{0x86cf, 0x0a, 0, 0},{0x86d0, 0x12, 0, 0}, + {0x86d1, 0x12, 0, 0},{0x86d2, 0x92, 0, 0},{0x86d3, 0xd2, 0, 0}, + {0x86d4, 0x18, 0, 0},{0x86d5, 0xc2, 0, 0},{0x86d6, 0x19, 0, 0}, + {0x86d7, 0x22, 0, 0},{0x86d8, 0x75, 0, 0},{0x86d9, 0x0a, 0, 0}, + {0x86da, 0x01, 0, 0},{0x86db, 0x12, 0, 0},{0x86dc, 0x12, 0, 0}, + {0x86dd, 0x92, 0, 0},{0x86de, 0x22, 0, 0},{0x86df, 0x90, 0, 0}, + {0x86e0, 0x38, 0, 0},{0x86e1, 0x04, 0, 0},{0x86e2, 0xe0, 0, 0}, + {0x86e3, 0xfe, 0, 0},{0x86e4, 0xa3, 0, 0},{0x86e5, 0xe0, 0, 0}, + {0x86e6, 0xfd, 0, 0},{0x86e7, 0xed, 0, 0},{0x86e8, 0xff, 0, 0}, + {0x86e9, 0xee, 0, 0},{0x86ea, 0x54, 0, 0},{0x86eb, 0x0f, 0, 0}, + {0x86ec, 0xf5, 0, 0},{0x86ed, 0x0e, 0, 0},{0x86ee, 0x8f, 0, 0}, + {0x86ef, 0x0f, 0, 0},{0x86f0, 0xa3, 0, 0},{0x86f1, 0xe0, 0, 0}, + {0x86f2, 0xfe, 0, 0},{0x86f3, 0xa3, 0, 0},{0x86f4, 0xe0, 0, 0}, + {0x86f5, 0xfd, 0, 0},{0x86f6, 0xed, 0, 0},{0x86f7, 0xff, 0, 0}, + {0x86f8, 0xee, 0, 0},{0x86f9, 0x54, 0, 0},{0x86fa, 0x07, 0, 0}, + {0x86fb, 0xf5, 0, 0},{0x86fc, 0x10, 0, 0},{0x86fd, 0x8f, 0, 0}, + {0x86fe, 0x11, 0, 0},{0x86ff, 0xe5, 0, 0},{0x8700, 0x0e, 0, 0}, + {0x8701, 0xc4, 0, 0},{0x8702, 0xf8, 0, 0},{0x8703, 0x54, 0, 0}, + {0x8704, 0xf0, 0, 0},{0x8705, 0xc8, 0, 0},{0x8706, 0x68, 0, 0}, + {0x8707, 0xf5, 0, 0},{0x8708, 0x0e, 0, 0},{0x8709, 0xe5, 0, 0}, + {0x870a, 0x0f, 0, 0},{0x870b, 0xc4, 0, 0},{0x870c, 0x54, 0, 0}, + {0x870d, 0x0f, 0, 0},{0x870e, 0x48, 0, 0},{0x870f, 0xf5, 0, 0}, + {0x8710, 0x0f, 0, 0},{0x8711, 0xe5, 0, 0},{0x8712, 0x10, 0, 0}, + {0x8713, 0xc4, 0, 0},{0x8714, 0xf8, 0, 0},{0x8715, 0x54, 0, 0}, + {0x8716, 0xf0, 0, 0},{0x8717, 0xc8, 0, 0},{0x8718, 0x68, 0, 0}, + {0x8719, 0xf5, 0, 0},{0x871a, 0x10, 0, 0},{0x871b, 0xe5, 0, 0}, + {0x871c, 0x11, 0, 0},{0x871d, 0xc4, 0, 0},{0x871e, 0x54, 0, 0}, + {0x871f, 0x0f, 0, 0},{0x8720, 0x48, 0, 0},{0x8721, 0xf5, 0, 0}, + {0x8722, 0x11, 0, 0},{0x8723, 0xe4, 0, 0},{0x8724, 0xf5, 0, 0}, + {0x8725, 0x17, 0, 0},{0x8726, 0x75, 0, 0},{0x8727, 0x16, 0, 0}, + {0x8728, 0x04, 0, 0},{0x8729, 0x12, 0, 0},{0x872a, 0x02, 0, 0}, + {0x872b, 0x66, 0, 0},{0x872c, 0x24, 0, 0},{0x872d, 0x34, 0, 0}, + {0x872e, 0xf8, 0, 0},{0x872f, 0xe6, 0, 0},{0x8730, 0xf5, 0, 0}, + {0x8731, 0x12, 0, 0},{0x8732, 0x12, 0, 0},{0x8733, 0x02, 0, 0}, + {0x8734, 0x66, 0, 0},{0x8735, 0x24, 0, 0},{0x8736, 0x35, 0, 0}, + {0x8737, 0xf8, 0, 0},{0x8738, 0xe6, 0, 0},{0x8739, 0xf5, 0, 0}, + {0x873a, 0x14, 0, 0},{0x873b, 0x12, 0, 0},{0x873c, 0x02, 0, 0}, + {0x873d, 0x66, 0, 0},{0x873e, 0x24, 0, 0},{0x873f, 0x36, 0, 0}, + {0x8740, 0xf8, 0, 0},{0x8741, 0xe6, 0, 0},{0x8742, 0xf5, 0, 0}, + {0x8743, 0x13, 0, 0},{0x8744, 0x12, 0, 0},{0x8745, 0x02, 0, 0}, + {0x8746, 0x66, 0, 0},{0x8747, 0x24, 0, 0},{0x8748, 0x37, 0, 0}, + {0x8749, 0xf8, 0, 0},{0x874a, 0xe6, 0, 0},{0x874b, 0xf5, 0, 0}, + {0x874c, 0x15, 0, 0},{0x874d, 0x12, 0, 0},{0x874e, 0x02, 0, 0}, + {0x874f, 0x79, 0, 0},{0x8750, 0xaf, 0, 0},{0x8751, 0x12, 0, 0}, + {0x8752, 0x12, 0, 0},{0x8753, 0x01, 0, 0},{0x8754, 0x42, 0, 0}, + {0x8755, 0x8f, 0, 0},{0x8756, 0x12, 0, 0},{0x8757, 0x12, 0, 0}, + {0x8758, 0x02, 0, 0},{0x8759, 0x79, 0, 0},{0x875a, 0xaf, 0, 0}, + {0x875b, 0x13, 0, 0},{0x875c, 0x12, 0, 0},{0x875d, 0x01, 0, 0}, + {0x875e, 0x42, 0, 0},{0x875f, 0x8f, 0, 0},{0x8760, 0x13, 0, 0}, + {0x8761, 0x12, 0, 0},{0x8762, 0x02, 0, 0},{0x8763, 0x11, 0, 0}, + {0x8764, 0xaf, 0, 0},{0x8765, 0x14, 0, 0},{0x8766, 0xfc, 0, 0}, + {0x8767, 0xfd, 0, 0},{0x8768, 0xfe, 0, 0},{0x8769, 0x12, 0, 0}, + {0x876a, 0x08, 0, 0},{0x876b, 0x7f, 0, 0},{0x876c, 0x12, 0, 0}, + {0x876d, 0x01, 0, 0},{0x876e, 0x61, 0, 0},{0x876f, 0x7b, 0, 0}, + {0x8770, 0x30, 0, 0},{0x8771, 0x12, 0, 0},{0x8772, 0x01, 0, 0}, + {0x8773, 0x5a, 0, 0},{0x8774, 0x8f, 0, 0},{0x8775, 0x14, 0, 0}, + {0x8776, 0x12, 0, 0},{0x8777, 0x02, 0, 0},{0x8778, 0x11, 0, 0}, + {0x8779, 0xaf, 0, 0},{0x877a, 0x15, 0, 0},{0x877b, 0xfc, 0, 0}, + {0x877c, 0xfd, 0, 0},{0x877d, 0xfe, 0, 0},{0x877e, 0x12, 0, 0}, + {0x877f, 0x08, 0, 0},{0x8780, 0x7f, 0, 0},{0x8781, 0x12, 0, 0}, + {0x8782, 0x01, 0, 0},{0x8783, 0x61, 0, 0},{0x8784, 0xe4, 0, 0}, + {0x8785, 0x7b, 0, 0},{0x8786, 0x30, 0, 0},{0x8787, 0x12, 0, 0}, + {0x8788, 0x01, 0, 0},{0x8789, 0x5b, 0, 0},{0x878a, 0x8f, 0, 0}, + {0x878b, 0x15, 0, 0},{0x878c, 0xc3, 0, 0},{0x878d, 0xe5, 0, 0}, + {0x878e, 0x13, 0, 0},{0x878f, 0x95, 0, 0},{0x8790, 0x12, 0, 0}, + {0x8791, 0xff, 0, 0},{0x8792, 0x0f, 0, 0},{0x8793, 0xef, 0, 0}, + {0x8794, 0xc3, 0, 0},{0x8795, 0x13, 0, 0},{0x8796, 0xff, 0, 0}, + {0x8797, 0xc3, 0, 0},{0x8798, 0x94, 0, 0},{0x8799, 0x04, 0, 0}, + {0x879a, 0x50, 0, 0},{0x879b, 0x27, 0, 0},{0x879c, 0xe5, 0, 0}, + {0x879d, 0x12, 0, 0},{0x879e, 0x9f, 0, 0},{0x879f, 0x40, 0, 0}, + {0x87a0, 0x06, 0, 0},{0x87a1, 0xe5, 0, 0},{0x87a2, 0x12, 0, 0}, + {0x87a3, 0x9f, 0, 0},{0x87a4, 0xfe, 0, 0},{0x87a5, 0x80, 0, 0}, + {0x87a6, 0x02, 0, 0},{0x87a7, 0x7e, 0, 0},{0x87a8, 0x00, 0, 0}, + {0x87a9, 0x8e, 0, 0},{0x87aa, 0x12, 0, 0},{0x87ab, 0xef, 0, 0}, + {0x87ac, 0xfd, 0, 0},{0x87ad, 0xe5, 0, 0},{0x87ae, 0x13, 0, 0}, + {0x87af, 0x2d, 0, 0},{0x87b0, 0xfd, 0, 0},{0x87b1, 0xe4, 0, 0}, + {0x87b2, 0x33, 0, 0},{0x87b3, 0xfc, 0, 0},{0x87b4, 0xc3, 0, 0}, + {0x87b5, 0xed, 0, 0},{0x87b6, 0x95, 0, 0},{0x87b7, 0x0f, 0, 0}, + {0x87b8, 0xec, 0, 0},{0x87b9, 0x95, 0, 0},{0x87ba, 0x0e, 0, 0}, + {0x87bb, 0x50, 0, 0},{0x87bc, 0x02, 0, 0},{0x87bd, 0x80, 0, 0}, + {0x87be, 0x02, 0, 0},{0x87bf, 0xad, 0, 0},{0x87c0, 0x0f, 0, 0}, + {0x87c1, 0x8d, 0, 0},{0x87c2, 0x13, 0, 0},{0x87c3, 0xc3, 0, 0}, + {0x87c4, 0xe5, 0, 0},{0x87c5, 0x15, 0, 0},{0x87c6, 0x95, 0, 0}, + {0x87c7, 0x14, 0, 0},{0x87c8, 0xff, 0, 0},{0x87c9, 0xc3, 0, 0}, + {0x87ca, 0x94, 0, 0},{0x87cb, 0x04, 0, 0},{0x87cc, 0x50, 0, 0}, + {0x87cd, 0x29, 0, 0},{0x87ce, 0xe5, 0, 0},{0x87cf, 0x14, 0, 0}, + {0x87d0, 0x9f, 0, 0},{0x87d1, 0x40, 0, 0},{0x87d2, 0x06, 0, 0}, + {0x87d3, 0xe5, 0, 0},{0x87d4, 0x14, 0, 0},{0x87d5, 0x9f, 0, 0}, + {0x87d6, 0xfe, 0, 0},{0x87d7, 0x80, 0, 0},{0x87d8, 0x02, 0, 0}, + {0x87d9, 0x7e, 0, 0},{0x87da, 0x00, 0, 0},{0x87db, 0x8e, 0, 0}, + {0x87dc, 0x14, 0, 0},{0x87dd, 0xef, 0, 0},{0x87de, 0xfd, 0, 0}, + {0x87df, 0xe5, 0, 0},{0x87e0, 0x15, 0, 0},{0x87e1, 0x2d, 0, 0}, + {0x87e2, 0xfd, 0, 0},{0x87e3, 0xe4, 0, 0},{0x87e4, 0x33, 0, 0}, + {0x87e5, 0xfc, 0, 0},{0x87e6, 0xc3, 0, 0},{0x87e7, 0xed, 0, 0}, + {0x87e8, 0x95, 0, 0},{0x87e9, 0x11, 0, 0},{0x87ea, 0xec, 0, 0}, + {0x87eb, 0x95, 0, 0},{0x87ec, 0x10, 0, 0},{0x87ed, 0x50, 0, 0}, + {0x87ee, 0x04, 0, 0},{0x87ef, 0xaf, 0, 0},{0x87f0, 0x05, 0, 0}, + {0x87f1, 0x80, 0, 0},{0x87f2, 0x02, 0, 0},{0x87f3, 0xaf, 0, 0}, + {0x87f4, 0x11, 0, 0},{0x87f5, 0x8f, 0, 0},{0x87f6, 0x15, 0, 0}, + {0x87f7, 0xe5, 0, 0},{0x87f8, 0x15, 0, 0},{0x87f9, 0xd3, 0, 0}, + {0x87fa, 0x95, 0, 0},{0x87fb, 0x17, 0, 0},{0x87fc, 0x40, 0, 0}, + {0x87fd, 0x04, 0, 0},{0x87fe, 0xaf, 0, 0},{0x87ff, 0x15, 0, 0}, + {0x8800, 0x80, 0, 0},{0x8801, 0x02, 0, 0},{0x8802, 0xaf, 0, 0}, + {0x8803, 0x17, 0, 0},{0x8804, 0x8f, 0, 0},{0x8805, 0x17, 0, 0}, + {0x8806, 0xd3, 0, 0},{0x8807, 0xe5, 0, 0},{0x8808, 0x16, 0, 0}, + {0x8809, 0x64, 0, 0},{0x880a, 0x80, 0, 0},{0x880b, 0x94, 0, 0}, + {0x880c, 0x80, 0, 0},{0x880d, 0x40, 0, 0},{0x880e, 0x04, 0, 0}, + {0x880f, 0xaf, 0, 0},{0x8810, 0x15, 0, 0},{0x8811, 0x80, 0, 0}, + {0x8812, 0x02, 0, 0},{0x8813, 0xaf, 0, 0},{0x8814, 0x17, 0, 0}, + {0x8815, 0x8f, 0, 0},{0x8816, 0x15, 0, 0},{0x8817, 0xe5, 0, 0}, + {0x8818, 0x16, 0, 0},{0x8819, 0xfd, 0, 0},{0x881a, 0x33, 0, 0}, + {0x881b, 0x95, 0, 0},{0x881c, 0xe0, 0, 0},{0x881d, 0xfc, 0, 0}, + {0x881e, 0xed, 0, 0},{0x881f, 0xae, 0, 0},{0x8820, 0x04, 0, 0}, + {0x8821, 0x78, 0, 0},{0x8822, 0x02, 0, 0},{0x8823, 0xc3, 0, 0}, + {0x8824, 0x33, 0, 0},{0x8825, 0xce, 0, 0},{0x8826, 0x33, 0, 0}, + {0x8827, 0xce, 0, 0},{0x8828, 0xd8, 0, 0},{0x8829, 0xf9, 0, 0}, + {0x882a, 0xff, 0, 0},{0x882b, 0x24, 0, 0},{0x882c, 0x01, 0, 0}, + {0x882d, 0xfb, 0, 0},{0x882e, 0xee, 0, 0},{0x882f, 0x34, 0, 0}, + {0x8830, 0x60, 0, 0},{0x8831, 0x8b, 0, 0},{0x8832, 0x82, 0, 0}, + {0x8833, 0xf5, 0, 0},{0x8834, 0x83, 0, 0},{0x8835, 0xe5, 0, 0}, + {0x8836, 0x12, 0, 0},{0x8837, 0xf0, 0, 0},{0x8838, 0xef, 0, 0}, + {0x8839, 0x24, 0, 0},{0x883a, 0x02, 0, 0},{0x883b, 0xff, 0, 0}, + {0x883c, 0xee, 0, 0},{0x883d, 0x34, 0, 0},{0x883e, 0x60, 0, 0}, + {0x883f, 0x8f, 0, 0},{0x8840, 0x82, 0, 0},{0x8841, 0xf5, 0, 0}, + {0x8842, 0x83, 0, 0},{0x8843, 0xe5, 0, 0},{0x8844, 0x14, 0, 0}, + {0x8845, 0xf0, 0, 0},{0x8846, 0xed, 0, 0},{0x8847, 0xae, 0, 0}, + {0x8848, 0x04, 0, 0},{0x8849, 0x78, 0, 0},{0x884a, 0x02, 0, 0}, + {0x884b, 0xc3, 0, 0},{0x884c, 0x33, 0, 0},{0x884d, 0xce, 0, 0}, + {0x884e, 0x33, 0, 0},{0x884f, 0xce, 0, 0},{0x8850, 0xd8, 0, 0}, + {0x8851, 0xf9, 0, 0},{0x8852, 0xff, 0, 0},{0x8853, 0x24, 0, 0}, + {0x8854, 0x03, 0, 0},{0x8855, 0xfd, 0, 0},{0x8856, 0xee, 0, 0}, + {0x8857, 0x34, 0, 0},{0x8858, 0x60, 0, 0},{0x8859, 0x8d, 0, 0}, + {0x885a, 0x82, 0, 0},{0x885b, 0xf5, 0, 0},{0x885c, 0x83, 0, 0}, + {0x885d, 0xe5, 0, 0},{0x885e, 0x13, 0, 0},{0x885f, 0xf0, 0, 0}, + {0x8860, 0xef, 0, 0},{0x8861, 0x24, 0, 0},{0x8862, 0x04, 0, 0}, + {0x8863, 0xff, 0, 0},{0x8864, 0xee, 0, 0},{0x8865, 0x34, 0, 0}, + {0x8866, 0x60, 0, 0},{0x8867, 0x8f, 0, 0},{0x8868, 0x82, 0, 0}, + {0x8869, 0xf5, 0, 0},{0x886a, 0x83, 0, 0},{0x886b, 0xe5, 0, 0}, + {0x886c, 0x15, 0, 0},{0x886d, 0xf0, 0, 0},{0x886e, 0x15, 0, 0}, + {0x886f, 0x16, 0, 0},{0x8870, 0xc3, 0, 0},{0x8871, 0xe5, 0, 0}, + {0x8872, 0x16, 0, 0},{0x8873, 0x64, 0, 0},{0x8874, 0x80, 0, 0}, + {0x8875, 0x94, 0, 0},{0x8876, 0x80, 0, 0},{0x8877, 0x40, 0, 0}, + {0x8878, 0x03, 0, 0},{0x8879, 0x02, 0, 0},{0x887a, 0x07, 0, 0}, + {0x887b, 0x29, 0, 0},{0x887c, 0xc2, 0, 0},{0x887d, 0x30, 0, 0}, + {0x887e, 0x22, 0, 0},{0x887f, 0xe8, 0, 0},{0x8880, 0x8f, 0, 0}, + {0x8881, 0xf0, 0, 0},{0x8882, 0xa4, 0, 0},{0x8883, 0xcc, 0, 0}, + {0x8884, 0x8b, 0, 0},{0x8885, 0xf0, 0, 0},{0x8886, 0xa4, 0, 0}, + {0x8887, 0x2c, 0, 0},{0x8888, 0xfc, 0, 0},{0x8889, 0xe9, 0, 0}, + {0x888a, 0x8e, 0, 0},{0x888b, 0xf0, 0, 0},{0x888c, 0xa4, 0, 0}, + {0x888d, 0x2c, 0, 0},{0x888e, 0xfc, 0, 0},{0x888f, 0x8a, 0, 0}, + {0x8890, 0xf0, 0, 0},{0x8891, 0xed, 0, 0},{0x8892, 0xa4, 0, 0}, + {0x8893, 0x2c, 0, 0},{0x8894, 0xfc, 0, 0},{0x8895, 0xea, 0, 0}, + {0x8896, 0x8e, 0, 0},{0x8897, 0xf0, 0, 0},{0x8898, 0xa4, 0, 0}, + {0x8899, 0xcd, 0, 0},{0x889a, 0xa8, 0, 0},{0x889b, 0xf0, 0, 0}, + {0x889c, 0x8b, 0, 0},{0x889d, 0xf0, 0, 0},{0x889e, 0xa4, 0, 0}, + {0x889f, 0x2d, 0, 0},{0x88a0, 0xcc, 0, 0},{0x88a1, 0x38, 0, 0}, + {0x88a2, 0x25, 0, 0},{0x88a3, 0xf0, 0, 0},{0x88a4, 0xfd, 0, 0}, + {0x88a5, 0xe9, 0, 0},{0x88a6, 0x8f, 0, 0},{0x88a7, 0xf0, 0, 0}, + {0x88a8, 0xa4, 0, 0},{0x88a9, 0x2c, 0, 0},{0x88aa, 0xcd, 0, 0}, + {0x88ab, 0x35, 0, 0},{0x88ac, 0xf0, 0, 0},{0x88ad, 0xfc, 0, 0}, + {0x88ae, 0xeb, 0, 0},{0x88af, 0x8e, 0, 0},{0x88b0, 0xf0, 0, 0}, + {0x88b1, 0xa4, 0, 0},{0x88b2, 0xfe, 0, 0},{0x88b3, 0xa9, 0, 0}, + {0x88b4, 0xf0, 0, 0},{0x88b5, 0xeb, 0, 0},{0x88b6, 0x8f, 0, 0}, + {0x88b7, 0xf0, 0, 0},{0x88b8, 0xa4, 0, 0},{0x88b9, 0xcf, 0, 0}, + {0x88ba, 0xc5, 0, 0},{0x88bb, 0xf0, 0, 0},{0x88bc, 0x2e, 0, 0}, + {0x88bd, 0xcd, 0, 0},{0x88be, 0x39, 0, 0},{0x88bf, 0xfe, 0, 0}, + {0x88c0, 0xe4, 0, 0},{0x88c1, 0x3c, 0, 0},{0x88c2, 0xfc, 0, 0}, + {0x88c3, 0xea, 0, 0},{0x88c4, 0xa4, 0, 0},{0x88c5, 0x2d, 0, 0}, + {0x88c6, 0xce, 0, 0},{0x88c7, 0x35, 0, 0},{0x88c8, 0xf0, 0, 0}, + {0x88c9, 0xfd, 0, 0},{0x88ca, 0xe4, 0, 0},{0x88cb, 0x3c, 0, 0}, + {0x88cc, 0xfc, 0, 0},{0x88cd, 0x22, 0, 0},{0x88ce, 0x75, 0, 0}, + {0x88cf, 0xf0, 0, 0},{0x88d0, 0x08, 0, 0},{0x88d1, 0x75, 0, 0}, + {0x88d2, 0x82, 0, 0},{0x88d3, 0x00, 0, 0},{0x88d4, 0xef, 0, 0}, + {0x88d5, 0x2f, 0, 0},{0x88d6, 0xff, 0, 0},{0x88d7, 0xee, 0, 0}, + {0x88d8, 0x33, 0, 0},{0x88d9, 0xfe, 0, 0},{0x88da, 0xcd, 0, 0}, + {0x88db, 0x33, 0, 0},{0x88dc, 0xcd, 0, 0},{0x88dd, 0xcc, 0, 0}, + {0x88de, 0x33, 0, 0},{0x88df, 0xcc, 0, 0},{0x88e0, 0xc5, 0, 0}, + {0x88e1, 0x82, 0, 0},{0x88e2, 0x33, 0, 0},{0x88e3, 0xc5, 0, 0}, + {0x88e4, 0x82, 0, 0},{0x88e5, 0x9b, 0, 0},{0x88e6, 0xed, 0, 0}, + {0x88e7, 0x9a, 0, 0},{0x88e8, 0xec, 0, 0},{0x88e9, 0x99, 0, 0}, + {0x88ea, 0xe5, 0, 0},{0x88eb, 0x82, 0, 0},{0x88ec, 0x98, 0, 0}, + {0x88ed, 0x40, 0, 0},{0x88ee, 0x0c, 0, 0},{0x88ef, 0xf5, 0, 0}, + {0x88f0, 0x82, 0, 0},{0x88f1, 0xee, 0, 0},{0x88f2, 0x9b, 0, 0}, + {0x88f3, 0xfe, 0, 0},{0x88f4, 0xed, 0, 0},{0x88f5, 0x9a, 0, 0}, + {0x88f6, 0xfd, 0, 0},{0x88f7, 0xec, 0, 0},{0x88f8, 0x99, 0, 0}, + {0x88f9, 0xfc, 0, 0},{0x88fa, 0x0f, 0, 0},{0x88fb, 0xd5, 0, 0}, + {0x88fc, 0xf0, 0, 0},{0x88fd, 0xd6, 0, 0},{0x88fe, 0xe4, 0, 0}, + {0x88ff, 0xce, 0, 0},{0x8900, 0xfb, 0, 0},{0x8901, 0xe4, 0, 0}, + {0x8902, 0xcd, 0, 0},{0x8903, 0xfa, 0, 0},{0x8904, 0xe4, 0, 0}, + {0x8905, 0xcc, 0, 0},{0x8906, 0xf9, 0, 0},{0x8907, 0xa8, 0, 0}, + {0x8908, 0x82, 0, 0},{0x8909, 0x22, 0, 0},{0x890a, 0xb8, 0, 0}, + {0x890b, 0x00, 0, 0},{0x890c, 0xc1, 0, 0},{0x890d, 0xb9, 0, 0}, + {0x890e, 0x00, 0, 0},{0x890f, 0x59, 0, 0},{0x8910, 0xba, 0, 0}, + {0x8911, 0x00, 0, 0},{0x8912, 0x2d, 0, 0},{0x8913, 0xec, 0, 0}, + {0x8914, 0x8b, 0, 0},{0x8915, 0xf0, 0, 0},{0x8916, 0x84, 0, 0}, + {0x8917, 0xcf, 0, 0},{0x8918, 0xce, 0, 0},{0x8919, 0xcd, 0, 0}, + {0x891a, 0xfc, 0, 0},{0x891b, 0xe5, 0, 0},{0x891c, 0xf0, 0, 0}, + {0x891d, 0xcb, 0, 0},{0x891e, 0xf9, 0, 0},{0x891f, 0x78, 0, 0}, + {0x8920, 0x18, 0, 0},{0x8921, 0xef, 0, 0},{0x8922, 0x2f, 0, 0}, + {0x8923, 0xff, 0, 0},{0x8924, 0xee, 0, 0},{0x8925, 0x33, 0, 0}, + {0x8926, 0xfe, 0, 0},{0x8927, 0xed, 0, 0},{0x8928, 0x33, 0, 0}, + {0x8929, 0xfd, 0, 0},{0x892a, 0xec, 0, 0},{0x892b, 0x33, 0, 0}, + {0x892c, 0xfc, 0, 0},{0x892d, 0xeb, 0, 0},{0x892e, 0x33, 0, 0}, + {0x892f, 0xfb, 0, 0},{0x8930, 0x10, 0, 0},{0x8931, 0xd7, 0, 0}, + {0x8932, 0x03, 0, 0},{0x8933, 0x99, 0, 0},{0x8934, 0x40, 0, 0}, + {0x8935, 0x04, 0, 0},{0x8936, 0xeb, 0, 0},{0x8937, 0x99, 0, 0}, + {0x8938, 0xfb, 0, 0},{0x8939, 0x0f, 0, 0},{0x893a, 0xd8, 0, 0}, + {0x893b, 0xe5, 0, 0},{0x893c, 0xe4, 0, 0},{0x893d, 0xf9, 0, 0}, + {0x893e, 0xfa, 0, 0},{0x893f, 0x22, 0, 0},{0x8940, 0x78, 0, 0}, + {0x8941, 0x18, 0, 0},{0x8942, 0xef, 0, 0},{0x8943, 0x2f, 0, 0}, + {0x8944, 0xff, 0, 0},{0x8945, 0xee, 0, 0},{0x8946, 0x33, 0, 0}, + {0x8947, 0xfe, 0, 0},{0x8948, 0xed, 0, 0},{0x8949, 0x33, 0, 0}, + {0x894a, 0xfd, 0, 0},{0x894b, 0xec, 0, 0},{0x894c, 0x33, 0, 0}, + {0x894d, 0xfc, 0, 0},{0x894e, 0xc9, 0, 0},{0x894f, 0x33, 0, 0}, + {0x8950, 0xc9, 0, 0},{0x8951, 0x10, 0, 0},{0x8952, 0xd7, 0, 0}, + {0x8953, 0x05, 0, 0},{0x8954, 0x9b, 0, 0},{0x8955, 0xe9, 0, 0}, + {0x8956, 0x9a, 0, 0},{0x8957, 0x40, 0, 0},{0x8958, 0x07, 0, 0}, + {0x8959, 0xec, 0, 0},{0x895a, 0x9b, 0, 0},{0x895b, 0xfc, 0, 0}, + {0x895c, 0xe9, 0, 0},{0x895d, 0x9a, 0, 0},{0x895e, 0xf9, 0, 0}, + {0x895f, 0x0f, 0, 0},{0x8960, 0xd8, 0, 0},{0x8961, 0xe0, 0, 0}, + {0x8962, 0xe4, 0, 0},{0x8963, 0xc9, 0, 0},{0x8964, 0xfa, 0, 0}, + {0x8965, 0xe4, 0, 0},{0x8966, 0xcc, 0, 0},{0x8967, 0xfb, 0, 0}, + {0x8968, 0x22, 0, 0},{0x8969, 0x75, 0, 0},{0x896a, 0xf0, 0, 0}, + {0x896b, 0x10, 0, 0},{0x896c, 0xef, 0, 0},{0x896d, 0x2f, 0, 0}, + {0x896e, 0xff, 0, 0},{0x896f, 0xee, 0, 0},{0x8970, 0x33, 0, 0}, + {0x8971, 0xfe, 0, 0},{0x8972, 0xed, 0, 0},{0x8973, 0x33, 0, 0}, + {0x8974, 0xfd, 0, 0},{0x8975, 0xcc, 0, 0},{0x8976, 0x33, 0, 0}, + {0x8977, 0xcc, 0, 0},{0x8978, 0xc8, 0, 0},{0x8979, 0x33, 0, 0}, + {0x897a, 0xc8, 0, 0},{0x897b, 0x10, 0, 0},{0x897c, 0xd7, 0, 0}, + {0x897d, 0x07, 0, 0},{0x897e, 0x9b, 0, 0},{0x897f, 0xec, 0, 0}, + {0x8980, 0x9a, 0, 0},{0x8981, 0xe8, 0, 0},{0x8982, 0x99, 0, 0}, + {0x8983, 0x40, 0, 0},{0x8984, 0x0a, 0, 0},{0x8985, 0xed, 0, 0}, + {0x8986, 0x9b, 0, 0},{0x8987, 0xfd, 0, 0},{0x8988, 0xec, 0, 0}, + {0x8989, 0x9a, 0, 0},{0x898a, 0xfc, 0, 0},{0x898b, 0xe8, 0, 0}, + {0x898c, 0x99, 0, 0},{0x898d, 0xf8, 0, 0},{0x898e, 0x0f, 0, 0}, + {0x898f, 0xd5, 0, 0},{0x8990, 0xf0, 0, 0},{0x8991, 0xda, 0, 0}, + {0x8992, 0xe4, 0, 0},{0x8993, 0xcd, 0, 0},{0x8994, 0xfb, 0, 0}, + {0x8995, 0xe4, 0, 0},{0x8996, 0xcc, 0, 0},{0x8997, 0xfa, 0, 0}, + {0x8998, 0xe4, 0, 0},{0x8999, 0xc8, 0, 0},{0x899a, 0xf9, 0, 0}, + {0x899b, 0x22, 0, 0},{0x899c, 0xe8, 0, 0},{0x899d, 0x60, 0, 0}, + {0x899e, 0x0f, 0, 0},{0x899f, 0xec, 0, 0},{0x89a0, 0xc3, 0, 0}, + {0x89a1, 0x13, 0, 0},{0x89a2, 0xfc, 0, 0},{0x89a3, 0xed, 0, 0}, + {0x89a4, 0x13, 0, 0},{0x89a5, 0xfd, 0, 0},{0x89a6, 0xee, 0, 0}, + {0x89a7, 0x13, 0, 0},{0x89a8, 0xfe, 0, 0},{0x89a9, 0xef, 0, 0}, + {0x89aa, 0x13, 0, 0},{0x89ab, 0xff, 0, 0},{0x89ac, 0xd8, 0, 0}, + {0x89ad, 0xf1, 0, 0},{0x89ae, 0x22, 0, 0},{0x89af, 0xe8, 0, 0}, + {0x89b0, 0x60, 0, 0},{0x89b1, 0x0f, 0, 0},{0x89b2, 0xef, 0, 0}, + {0x89b3, 0xc3, 0, 0},{0x89b4, 0x33, 0, 0},{0x89b5, 0xff, 0, 0}, + {0x89b6, 0xee, 0, 0},{0x89b7, 0x33, 0, 0},{0x89b8, 0xfe, 0, 0}, + {0x89b9, 0xed, 0, 0},{0x89ba, 0x33, 0, 0},{0x89bb, 0xfd, 0, 0}, + {0x89bc, 0xec, 0, 0},{0x89bd, 0x33, 0, 0},{0x89be, 0xfc, 0, 0}, + {0x89bf, 0xd8, 0, 0},{0x89c0, 0xf1, 0, 0},{0x89c1, 0x22, 0, 0}, + {0x89c2, 0xe4, 0, 0},{0x89c3, 0x93, 0, 0},{0x89c4, 0xfc, 0, 0}, + {0x89c5, 0x74, 0, 0},{0x89c6, 0x01, 0, 0},{0x89c7, 0x93, 0, 0}, + {0x89c8, 0xfd, 0, 0},{0x89c9, 0x74, 0, 0},{0x89ca, 0x02, 0, 0}, + {0x89cb, 0x93, 0, 0},{0x89cc, 0xfe, 0, 0},{0x89cd, 0x74, 0, 0}, + {0x89ce, 0x03, 0, 0},{0x89cf, 0x93, 0, 0},{0x89d0, 0xff, 0, 0}, + {0x89d1, 0x22, 0, 0},{0x89d2, 0xd0, 0, 0},{0x89d3, 0x83, 0, 0}, + {0x89d4, 0xd0, 0, 0},{0x89d5, 0x82, 0, 0},{0x89d6, 0xf8, 0, 0}, + {0x89d7, 0xe4, 0, 0},{0x89d8, 0x93, 0, 0},{0x89d9, 0x70, 0, 0}, + {0x89da, 0x12, 0, 0},{0x89db, 0x74, 0, 0},{0x89dc, 0x01, 0, 0}, + {0x89dd, 0x93, 0, 0},{0x89de, 0x70, 0, 0},{0x89df, 0x0d, 0, 0}, + {0x89e0, 0xa3, 0, 0},{0x89e1, 0xa3, 0, 0},{0x89e2, 0x93, 0, 0}, + {0x89e3, 0xf8, 0, 0},{0x89e4, 0x74, 0, 0},{0x89e5, 0x01, 0, 0}, + {0x89e6, 0x93, 0, 0},{0x89e7, 0xf5, 0, 0},{0x89e8, 0x82, 0, 0}, + {0x89e9, 0x88, 0, 0},{0x89ea, 0x83, 0, 0},{0x89eb, 0xe4, 0, 0}, + {0x89ec, 0x73, 0, 0},{0x89ed, 0x74, 0, 0},{0x89ee, 0x02, 0, 0}, + {0x89ef, 0x93, 0, 0},{0x89f0, 0x68, 0, 0},{0x89f1, 0x60, 0, 0}, + {0x89f2, 0xef, 0, 0},{0x89f3, 0xa3, 0, 0},{0x89f4, 0xa3, 0, 0}, + {0x89f5, 0xa3, 0, 0},{0x89f6, 0x80, 0, 0},{0x89f7, 0xdf, 0, 0}, + {0x89f8, 0x75, 0, 0},{0x89f9, 0x12, 0, 0},{0x89fa, 0x0a, 0, 0}, + {0x89fb, 0xa2, 0, 0},{0x89fc, 0xaf, 0, 0},{0x89fd, 0x92, 0, 0}, + {0x89fe, 0x32, 0, 0},{0x89ff, 0xc2, 0, 0},{0x8a00, 0xaf, 0, 0}, + {0x8a01, 0xc2, 0, 0},{0x8a02, 0x33, 0, 0},{0x8a03, 0x12, 0, 0}, + {0x8a04, 0x01, 0, 0},{0x8a05, 0x6a, 0, 0},{0x8a06, 0x12, 0, 0}, + {0x8a07, 0x02, 0, 0},{0x8a08, 0x08, 0, 0},{0x8a09, 0x12, 0, 0}, + {0x8a0a, 0x01, 0, 0},{0x8a0b, 0x6a, 0, 0},{0x8a0c, 0x75, 0, 0}, + {0x8a0d, 0x51, 0, 0},{0x8a0e, 0x05, 0, 0},{0x8a0f, 0xaf, 0, 0}, + {0x8a10, 0x51, 0, 0},{0x8a11, 0x15, 0, 0},{0x8a12, 0x51, 0, 0}, + {0x8a13, 0xef, 0, 0},{0x8a14, 0x70, 0, 0},{0x8a15, 0xf9, 0, 0}, + {0x8a16, 0xd2, 0, 0},{0x8a17, 0x28, 0, 0},{0x8a18, 0x12, 0, 0}, + {0x8a19, 0x01, 0, 0},{0x8a1a, 0x6c, 0, 0},{0x8a1b, 0x75, 0, 0}, + {0x8a1c, 0x51, 0, 0},{0x8a1d, 0x0a, 0, 0},{0x8a1e, 0xaf, 0, 0}, + {0x8a1f, 0x51, 0, 0},{0x8a20, 0x15, 0, 0},{0x8a21, 0x51, 0, 0}, + {0x8a22, 0xef, 0, 0},{0x8a23, 0x70, 0, 0},{0x8a24, 0xf9, 0, 0}, + {0x8a25, 0xc2, 0, 0},{0x8a26, 0x29, 0, 0},{0x8a27, 0x12, 0, 0}, + {0x8a28, 0x01, 0, 0},{0x8a29, 0x6c, 0, 0},{0x8a2a, 0x75, 0, 0}, + {0x8a2b, 0x51, 0, 0},{0x8a2c, 0x05, 0, 0},{0x8a2d, 0xaf, 0, 0}, + {0x8a2e, 0x51, 0, 0},{0x8a2f, 0x15, 0, 0},{0x8a30, 0x51, 0, 0}, + {0x8a31, 0xef, 0, 0},{0x8a32, 0x70, 0, 0},{0x8a33, 0xf9, 0, 0}, + {0x8a34, 0xc2, 0, 0},{0x8a35, 0x28, 0, 0},{0x8a36, 0x12, 0, 0}, + {0x8a37, 0x01, 0, 0},{0x8a38, 0x6c, 0, 0},{0x8a39, 0x75, 0, 0}, + {0x8a3a, 0x51, 0, 0},{0x8a3b, 0x05, 0, 0},{0x8a3c, 0xaf, 0, 0}, + {0x8a3d, 0x51, 0, 0},{0x8a3e, 0x15, 0, 0},{0x8a3f, 0x51, 0, 0}, + {0x8a40, 0xef, 0, 0},{0x8a41, 0x70, 0, 0},{0x8a42, 0xf9, 0, 0}, + {0x8a43, 0x75, 0, 0},{0x8a44, 0x13, 0, 0},{0x8a45, 0x18, 0, 0}, + {0x8a46, 0x12, 0, 0},{0x8a47, 0x0b, 0, 0},{0x8a48, 0x38, 0, 0}, + {0x8a49, 0x75, 0, 0},{0x8a4a, 0x51, 0, 0},{0x8a4b, 0x0a, 0, 0}, + {0x8a4c, 0xaf, 0, 0},{0x8a4d, 0x51, 0, 0},{0x8a4e, 0x15, 0, 0}, + {0x8a4f, 0x51, 0, 0},{0x8a50, 0xef, 0, 0},{0x8a51, 0x70, 0, 0}, + {0x8a52, 0xf9, 0, 0},{0x8a53, 0xd2, 0, 0},{0x8a54, 0x28, 0, 0}, + {0x8a55, 0x12, 0, 0},{0x8a56, 0x01, 0, 0},{0x8a57, 0x6c, 0, 0}, + {0x8a58, 0x12, 0, 0},{0x8a59, 0x02, 0, 0},{0x8a5a, 0x25, 0, 0}, + {0x8a5b, 0xaf, 0, 0},{0x8a5c, 0x51, 0, 0},{0x8a5d, 0x15, 0, 0}, + {0x8a5e, 0x51, 0, 0},{0x8a5f, 0xef, 0, 0},{0x8a60, 0x70, 0, 0}, + {0x8a61, 0xf9, 0, 0},{0x8a62, 0xc2, 0, 0},{0x8a63, 0x28, 0, 0}, + {0x8a64, 0x12, 0, 0},{0x8a65, 0x01, 0, 0},{0x8a66, 0x6c, 0, 0}, + {0x8a67, 0x75, 0, 0},{0x8a68, 0x51, 0, 0},{0x8a69, 0x0a, 0, 0}, + {0x8a6a, 0xaf, 0, 0},{0x8a6b, 0x51, 0, 0},{0x8a6c, 0x15, 0, 0}, + {0x8a6d, 0x51, 0, 0},{0x8a6e, 0xef, 0, 0},{0x8a6f, 0x70, 0, 0}, + {0x8a70, 0xf9, 0, 0},{0x8a71, 0x30, 0, 0},{0x8a72, 0x11, 0, 0}, + {0x8a73, 0x03, 0, 0},{0x8a74, 0x02, 0, 0},{0x8a75, 0x0a, 0, 0}, + {0x8a76, 0xef, 0, 0},{0x8a77, 0x12, 0, 0},{0x8a78, 0x01, 0, 0}, + {0x8a79, 0x6a, 0, 0},{0x8a7a, 0x12, 0, 0},{0x8a7b, 0x02, 0, 0}, + {0x8a7c, 0x08, 0, 0},{0x8a7d, 0xe5, 0, 0},{0x8a7e, 0x10, 0, 0}, + {0x8a7f, 0xf5, 0, 0},{0x8a80, 0x13, 0, 0},{0x8a81, 0x12, 0, 0}, + {0x8a82, 0x0b, 0, 0},{0x8a83, 0x38, 0, 0},{0x8a84, 0x75, 0, 0}, + {0x8a85, 0x51, 0, 0},{0x8a86, 0x0a, 0, 0},{0x8a87, 0xaf, 0, 0}, + {0x8a88, 0x51, 0, 0},{0x8a89, 0x15, 0, 0},{0x8a8a, 0x51, 0, 0}, + {0x8a8b, 0xef, 0, 0},{0x8a8c, 0x70, 0, 0},{0x8a8d, 0xf9, 0, 0}, + {0x8a8e, 0xd2, 0, 0},{0x8a8f, 0x28, 0, 0},{0x8a90, 0x12, 0, 0}, + {0x8a91, 0x01, 0, 0},{0x8a92, 0x6c, 0, 0},{0x8a93, 0x12, 0, 0}, + {0x8a94, 0x02, 0, 0},{0x8a95, 0x25, 0, 0},{0x8a96, 0xaf, 0, 0}, + {0x8a97, 0x51, 0, 0},{0x8a98, 0x15, 0, 0},{0x8a99, 0x51, 0, 0}, + {0x8a9a, 0xef, 0, 0},{0x8a9b, 0x70, 0, 0},{0x8a9c, 0xf9, 0, 0}, + {0x8a9d, 0xc2, 0, 0},{0x8a9e, 0x28, 0, 0},{0x8a9f, 0x12, 0, 0}, + {0x8aa0, 0x01, 0, 0},{0x8aa1, 0x6c, 0, 0},{0x8aa2, 0x75, 0, 0}, + {0x8aa3, 0x51, 0, 0},{0x8aa4, 0x0a, 0, 0},{0x8aa5, 0xaf, 0, 0}, + {0x8aa6, 0x51, 0, 0},{0x8aa7, 0x15, 0, 0},{0x8aa8, 0x51, 0, 0}, + {0x8aa9, 0xef, 0, 0},{0x8aaa, 0x70, 0, 0},{0x8aab, 0xf9, 0, 0}, + {0x8aac, 0x30, 0, 0},{0x8aad, 0x11, 0, 0},{0x8aae, 0x04, 0, 0}, + {0x8aaf, 0x15, 0, 0},{0x8ab0, 0x12, 0, 0},{0x8ab1, 0x80, 0, 0}, + {0x8ab2, 0x45, 0, 0},{0x8ab3, 0x12, 0, 0},{0x8ab4, 0x01, 0, 0}, + {0x8ab5, 0x6a, 0, 0},{0x8ab6, 0x12, 0, 0},{0x8ab7, 0x02, 0, 0}, + {0x8ab8, 0x08, 0, 0},{0x8ab9, 0x85, 0, 0},{0x8aba, 0x11, 0, 0}, + {0x8abb, 0x13, 0, 0},{0x8abc, 0x12, 0, 0},{0x8abd, 0x13, 0, 0}, + {0x8abe, 0x72, 0, 0},{0x8abf, 0xc2, 0, 0},{0x8ac0, 0x09, 0, 0}, + {0x8ac1, 0x12, 0, 0},{0x8ac2, 0x02, 0, 0},{0x8ac3, 0x0a, 0, 0}, + {0x8ac4, 0x75, 0, 0},{0x8ac5, 0x51, 0, 0},{0x8ac6, 0x0a, 0, 0}, + {0x8ac7, 0xaf, 0, 0},{0x8ac8, 0x51, 0, 0},{0x8ac9, 0x15, 0, 0}, + {0x8aca, 0x51, 0, 0},{0x8acb, 0xef, 0, 0},{0x8acc, 0x70, 0, 0}, + {0x8acd, 0xf9, 0, 0},{0x8ace, 0xd2, 0, 0},{0x8acf, 0x28, 0, 0}, + {0x8ad0, 0x12, 0, 0},{0x8ad1, 0x01, 0, 0},{0x8ad2, 0x6c, 0, 0}, + {0x8ad3, 0x12, 0, 0},{0x8ad4, 0x02, 0, 0},{0x8ad5, 0x25, 0, 0}, + {0x8ad6, 0xaf, 0, 0},{0x8ad7, 0x51, 0, 0},{0x8ad8, 0x15, 0, 0}, + {0x8ad9, 0x51, 0, 0},{0x8ada, 0xef, 0, 0},{0x8adb, 0x70, 0, 0}, + {0x8adc, 0xf9, 0, 0},{0x8add, 0xc2, 0, 0},{0x8ade, 0x28, 0, 0}, + {0x8adf, 0x12, 0, 0},{0x8ae0, 0x01, 0, 0},{0x8ae1, 0x6c, 0, 0}, + {0x8ae2, 0x75, 0, 0},{0x8ae3, 0x51, 0, 0},{0x8ae4, 0x0a, 0, 0}, + {0x8ae5, 0xaf, 0, 0},{0x8ae6, 0x51, 0, 0},{0x8ae7, 0x15, 0, 0}, + {0x8ae8, 0x51, 0, 0},{0x8ae9, 0xef, 0, 0},{0x8aea, 0x70, 0, 0}, + {0x8aeb, 0xf9, 0, 0},{0x8aec, 0x30, 0, 0},{0x8aed, 0x11, 0, 0}, + {0x8aee, 0x06, 0, 0},{0x8aef, 0x15, 0, 0},{0x8af0, 0x12, 0, 0}, + {0x8af1, 0xd2, 0, 0},{0x8af2, 0x33, 0, 0},{0x8af3, 0x80, 0, 0}, + {0x8af4, 0x03, 0, 0},{0x8af5, 0xe4, 0, 0},{0x8af6, 0xf5, 0, 0}, + {0x8af7, 0x12, 0, 0},{0x8af8, 0x12, 0, 0},{0x8af9, 0x01, 0, 0}, + {0x8afa, 0x6a, 0, 0},{0x8afb, 0x12, 0, 0},{0x8afc, 0x02, 0, 0}, + {0x8afd, 0x08, 0, 0},{0x8afe, 0xc2, 0, 0},{0x8aff, 0x29, 0, 0}, + {0x8b00, 0x12, 0, 0},{0x8b01, 0x01, 0, 0},{0x8b02, 0x6c, 0, 0}, + {0x8b03, 0x75, 0, 0},{0x8b04, 0x51, 0, 0},{0x8b05, 0x05, 0, 0}, + {0x8b06, 0xaf, 0, 0},{0x8b07, 0x51, 0, 0},{0x8b08, 0x15, 0, 0}, + {0x8b09, 0x51, 0, 0},{0x8b0a, 0xef, 0, 0},{0x8b0b, 0x70, 0, 0}, + {0x8b0c, 0xf9, 0, 0},{0x8b0d, 0xd2, 0, 0},{0x8b0e, 0x28, 0, 0}, + {0x8b0f, 0x12, 0, 0},{0x8b10, 0x01, 0, 0},{0x8b11, 0x6c, 0, 0}, + {0x8b12, 0x75, 0, 0},{0x8b13, 0x51, 0, 0},{0x8b14, 0x05, 0, 0}, + {0x8b15, 0xaf, 0, 0},{0x8b16, 0x51, 0, 0},{0x8b17, 0x15, 0, 0}, + {0x8b18, 0x51, 0, 0},{0x8b19, 0xef, 0, 0},{0x8b1a, 0x70, 0, 0}, + {0x8b1b, 0xf9, 0, 0},{0x8b1c, 0x12, 0, 0},{0x8b1d, 0x01, 0, 0}, + {0x8b1e, 0x6a, 0, 0},{0x8b1f, 0x75, 0, 0},{0x8b20, 0x51, 0, 0}, + {0x8b21, 0x05, 0, 0},{0x8b22, 0xaf, 0, 0},{0x8b23, 0x51, 0, 0}, + {0x8b24, 0x15, 0, 0},{0x8b25, 0x51, 0, 0},{0x8b26, 0xef, 0, 0}, + {0x8b27, 0x70, 0, 0},{0x8b28, 0xf9, 0, 0},{0x8b29, 0xa2, 0, 0}, + {0x8b2a, 0x32, 0, 0},{0x8b2b, 0x92, 0, 0},{0x8b2c, 0xaf, 0, 0}, + {0x8b2d, 0xe5, 0, 0},{0x8b2e, 0x12, 0, 0},{0x8b2f, 0xd3, 0, 0}, + {0x8b30, 0x94, 0, 0},{0x8b31, 0x00, 0, 0},{0x8b32, 0x40, 0, 0}, + {0x8b33, 0x03, 0, 0},{0x8b34, 0x02, 0, 0},{0x8b35, 0x09, 0, 0}, + {0x8b36, 0xff, 0, 0},{0x8b37, 0x22, 0, 0},{0x8b38, 0x12, 0, 0}, + {0x8b39, 0x13, 0, 0},{0x8b3a, 0x72, 0, 0},{0x8b3b, 0xc2, 0, 0}, + {0x8b3c, 0x09, 0, 0},{0x8b3d, 0x90, 0, 0},{0x8b3e, 0x30, 0, 0}, + {0x8b3f, 0x18, 0, 0},{0x8b40, 0xe5, 0, 0},{0x8b41, 0x21, 0, 0}, + {0x8b42, 0xf0, 0, 0},{0x8b43, 0x22, 0, 0},{0x8b44, 0xc0, 0, 0}, + {0x8b45, 0xe0, 0, 0},{0x8b46, 0xc0, 0, 0},{0x8b47, 0xf0, 0, 0}, + {0x8b48, 0xc0, 0, 0},{0x8b49, 0x83, 0, 0},{0x8b4a, 0xc0, 0, 0}, + {0x8b4b, 0x82, 0, 0},{0x8b4c, 0xc0, 0, 0},{0x8b4d, 0xd0, 0, 0}, + {0x8b4e, 0x75, 0, 0},{0x8b4f, 0xd0, 0, 0},{0x8b50, 0x00, 0, 0}, + {0x8b51, 0xc0, 0, 0},{0x8b52, 0x00, 0, 0},{0x8b53, 0xc0, 0, 0}, + {0x8b54, 0x01, 0, 0},{0x8b55, 0xc0, 0, 0},{0x8b56, 0x02, 0, 0}, + {0x8b57, 0xc0, 0, 0},{0x8b58, 0x03, 0, 0},{0x8b59, 0xc0, 0, 0}, + {0x8b5a, 0x04, 0, 0},{0x8b5b, 0xc0, 0, 0},{0x8b5c, 0x05, 0, 0}, + {0x8b5d, 0xc0, 0, 0},{0x8b5e, 0x06, 0, 0},{0x8b5f, 0xc0, 0, 0}, + {0x8b60, 0x07, 0, 0},{0x8b61, 0x90, 0, 0},{0x8b62, 0x3f, 0, 0}, + {0x8b63, 0x0c, 0, 0},{0x8b64, 0xe0, 0, 0},{0x8b65, 0xf5, 0, 0}, + {0x8b66, 0x08, 0, 0},{0x8b67, 0xe5, 0, 0},{0x8b68, 0x08, 0, 0}, + {0x8b69, 0x20, 0, 0},{0x8b6a, 0xe3, 0, 0},{0x8b6b, 0x03, 0, 0}, + {0x8b6c, 0x02, 0, 0},{0x8b6d, 0x0b, 0, 0},{0x8b6e, 0xf5, 0, 0}, + {0x8b6f, 0x30, 0, 0},{0x8b70, 0x35, 0, 0},{0x8b71, 0x03, 0, 0}, + {0x8b72, 0x02, 0, 0},{0x8b73, 0x0b, 0, 0},{0x8b74, 0xf5, 0, 0}, + {0x8b75, 0x90, 0, 0},{0x8b76, 0x60, 0, 0},{0x8b77, 0x16, 0, 0}, + {0x8b78, 0xe0, 0, 0},{0x8b79, 0xf5, 0, 0},{0x8b7a, 0x69, 0, 0}, + {0x8b7b, 0xa3, 0, 0},{0x8b7c, 0xe0, 0, 0},{0x8b7d, 0xf5, 0, 0}, + {0x8b7e, 0x6a, 0, 0},{0x8b7f, 0x90, 0, 0},{0x8b80, 0x60, 0, 0}, + {0x8b81, 0x1e, 0, 0},{0x8b82, 0xe0, 0, 0},{0x8b83, 0xf5, 0, 0}, + {0x8b84, 0x6b, 0, 0},{0x8b85, 0xa3, 0, 0},{0x8b86, 0xe0, 0, 0}, + {0x8b87, 0xf5, 0, 0},{0x8b88, 0x6c, 0, 0},{0x8b89, 0x90, 0, 0}, + {0x8b8a, 0x60, 0, 0},{0x8b8b, 0x26, 0, 0},{0x8b8c, 0xe0, 0, 0}, + {0x8b8d, 0xf5, 0, 0},{0x8b8e, 0x6d, 0, 0},{0x8b8f, 0xa3, 0, 0}, + {0x8b90, 0xe0, 0, 0},{0x8b91, 0xf5, 0, 0},{0x8b92, 0x6e, 0, 0}, + {0x8b93, 0x90, 0, 0},{0x8b94, 0x60, 0, 0},{0x8b95, 0x2e, 0, 0}, + {0x8b96, 0xe0, 0, 0},{0x8b97, 0xf5, 0, 0},{0x8b98, 0x6f, 0, 0}, + {0x8b99, 0xa3, 0, 0},{0x8b9a, 0xe0, 0, 0},{0x8b9b, 0xf5, 0, 0}, + {0x8b9c, 0x70, 0, 0},{0x8b9d, 0x90, 0, 0},{0x8b9e, 0x60, 0, 0}, + {0x8b9f, 0x36, 0, 0},{0x8ba0, 0x12, 0, 0},{0x8ba1, 0x00, 0, 0}, + {0x8ba2, 0x16, 0, 0},{0x8ba3, 0x12, 0, 0},{0x8ba4, 0x01, 0, 0}, + {0x8ba5, 0xc8, 0, 0},{0x8ba6, 0x40, 0, 0},{0x8ba7, 0x06, 0, 0}, + {0x8ba8, 0x75, 0, 0},{0x8ba9, 0x2a, 0, 0},{0x8baa, 0xff, 0, 0}, + {0x8bab, 0x75, 0, 0},{0x8bac, 0x2b, 0, 0},{0x8bad, 0xff, 0, 0}, + {0x8bae, 0x85, 0, 0},{0x8baf, 0x2a, 0, 0},{0x8bb0, 0x73, 0, 0}, + {0x8bb1, 0x85, 0, 0},{0x8bb2, 0x2b, 0, 0},{0x8bb3, 0x74, 0, 0}, + {0x8bb4, 0x90, 0, 0},{0x8bb5, 0x60, 0, 0},{0x8bb6, 0x1a, 0, 0}, + {0x8bb7, 0xe0, 0, 0},{0x8bb8, 0xf5, 0, 0},{0x8bb9, 0x69, 0, 0}, + {0x8bba, 0xa3, 0, 0},{0x8bbb, 0xe0, 0, 0},{0x8bbc, 0xf5, 0, 0}, + {0x8bbd, 0x6a, 0, 0},{0x8bbe, 0x90, 0, 0},{0x8bbf, 0x60, 0, 0}, + {0x8bc0, 0x22, 0, 0},{0x8bc1, 0xe0, 0, 0},{0x8bc2, 0xf5, 0, 0}, + {0x8bc3, 0x6b, 0, 0},{0x8bc4, 0xa3, 0, 0},{0x8bc5, 0xe0, 0, 0}, + {0x8bc6, 0xf5, 0, 0},{0x8bc7, 0x6c, 0, 0},{0x8bc8, 0x90, 0, 0}, + {0x8bc9, 0x60, 0, 0},{0x8bca, 0x2a, 0, 0},{0x8bcb, 0xe0, 0, 0}, + {0x8bcc, 0xf5, 0, 0},{0x8bcd, 0x6d, 0, 0},{0x8bce, 0xa3, 0, 0}, + {0x8bcf, 0xe0, 0, 0},{0x8bd0, 0xf5, 0, 0},{0x8bd1, 0x6e, 0, 0}, + {0x8bd2, 0x90, 0, 0},{0x8bd3, 0x60, 0, 0},{0x8bd4, 0x32, 0, 0}, + {0x8bd5, 0xe0, 0, 0},{0x8bd6, 0xf5, 0, 0},{0x8bd7, 0x6f, 0, 0}, + {0x8bd8, 0xa3, 0, 0},{0x8bd9, 0xe0, 0, 0},{0x8bda, 0xf5, 0, 0}, + {0x8bdb, 0x70, 0, 0},{0x8bdc, 0x90, 0, 0},{0x8bdd, 0x60, 0, 0}, + {0x8bde, 0x3a, 0, 0},{0x8bdf, 0x12, 0, 0},{0x8be0, 0x00, 0, 0}, + {0x8be1, 0x16, 0, 0},{0x8be2, 0x12, 0, 0},{0x8be3, 0x01, 0, 0}, + {0x8be4, 0xc8, 0, 0},{0x8be5, 0x40, 0, 0},{0x8be6, 0x06, 0, 0}, + {0x8be7, 0x75, 0, 0},{0x8be8, 0x2a, 0, 0},{0x8be9, 0xff, 0, 0}, + {0x8bea, 0x75, 0, 0},{0x8beb, 0x2b, 0, 0},{0x8bec, 0xff, 0, 0}, + {0x8bed, 0x85, 0, 0},{0x8bee, 0x2a, 0, 0},{0x8bef, 0x75, 0, 0}, + {0x8bf0, 0x85, 0, 0},{0x8bf1, 0x2b, 0, 0},{0x8bf2, 0x76, 0, 0}, + {0x8bf3, 0xd2, 0, 0},{0x8bf4, 0x3b, 0, 0},{0x8bf5, 0xe5, 0, 0}, + {0x8bf6, 0x08, 0, 0},{0x8bf7, 0x30, 0, 0},{0x8bf8, 0xe5, 0, 0}, + {0x8bf9, 0x41, 0, 0},{0x8bfa, 0x90, 0, 0},{0x8bfb, 0x56, 0, 0}, + {0x8bfc, 0x90, 0, 0},{0x8bfd, 0xe0, 0, 0},{0x8bfe, 0xf5, 0, 0}, + {0x8bff, 0x55, 0, 0},{0x8c00, 0xe5, 0, 0},{0x8c01, 0x7a, 0, 0}, + {0x8c02, 0x12, 0, 0},{0x8c03, 0x01, 0, 0},{0x8c04, 0xc1, 0, 0}, + {0x8c05, 0xad, 0, 0},{0x8c06, 0x55, 0, 0},{0x8c07, 0xc3, 0, 0}, + {0x8c08, 0xef, 0, 0},{0x8c09, 0x9d, 0, 0},{0x8c0a, 0x74, 0, 0}, + {0x8c0b, 0x80, 0, 0},{0x8c0c, 0xf8, 0, 0},{0x8c0d, 0x6e, 0, 0}, + {0x8c0e, 0x98, 0, 0},{0x8c0f, 0x50, 0, 0},{0x8c10, 0x02, 0, 0}, + {0x8c11, 0x80, 0, 0},{0x8c12, 0x01, 0, 0},{0x8c13, 0xc3, 0, 0}, + {0x8c14, 0x92, 0, 0},{0x8c15, 0x27, 0, 0},{0x8c16, 0xaf, 0, 0}, + {0x8c17, 0x55, 0, 0},{0x8c18, 0xef, 0, 0},{0x8c19, 0x24, 0, 0}, + {0x8c1a, 0x01, 0, 0},{0x8c1b, 0xff, 0, 0},{0x8c1c, 0xe4, 0, 0}, + {0x8c1d, 0x33, 0, 0},{0x8c1e, 0xfe, 0, 0},{0x8c1f, 0xc3, 0, 0}, + {0x8c20, 0xef, 0, 0},{0x8c21, 0x95, 0, 0},{0x8c22, 0x7a, 0, 0}, + {0x8c23, 0x74, 0, 0},{0x8c24, 0x80, 0, 0},{0x8c25, 0xf8, 0, 0}, + {0x8c26, 0x6e, 0, 0},{0x8c27, 0x98, 0, 0},{0x8c28, 0x50, 0, 0}, + {0x8c29, 0x02, 0, 0},{0x8c2a, 0x80, 0, 0},{0x8c2b, 0x02, 0, 0}, + {0x8c2c, 0xa2, 0, 0},{0x8c2d, 0x27, 0, 0},{0x8c2e, 0x92, 0, 0}, + {0x8c2f, 0x27, 0, 0},{0x8c30, 0x30, 0, 0},{0x8c31, 0x27, 0, 0}, + {0x8c32, 0x04, 0, 0},{0x8c33, 0xaf, 0, 0},{0x8c34, 0x55, 0, 0}, + {0x8c35, 0x80, 0, 0},{0x8c36, 0x02, 0, 0},{0x8c37, 0xaf, 0, 0}, + {0x8c38, 0x7a, 0, 0},{0x8c39, 0x8f, 0, 0},{0x8c3a, 0x7a, 0, 0}, + {0x8c3b, 0xe5, 0, 0},{0x8c3c, 0x08, 0, 0},{0x8c3d, 0x30, 0, 0}, + {0x8c3e, 0xe1, 0, 0},{0x8c3f, 0x08, 0, 0},{0x8c40, 0x90, 0, 0}, + {0x8c41, 0x30, 0, 0},{0x8c42, 0x24, 0, 0},{0x8c43, 0xe0, 0, 0}, + {0x8c44, 0xf5, 0, 0},{0x8c45, 0x33, 0, 0},{0x8c46, 0xe4, 0, 0}, + {0x8c47, 0xf0, 0, 0},{0x8c48, 0x90, 0, 0},{0x8c49, 0x3f, 0, 0}, + {0x8c4a, 0x0c, 0, 0},{0x8c4b, 0xe5, 0, 0},{0x8c4c, 0x08, 0, 0}, + {0x8c4d, 0xf0, 0, 0},{0x8c4e, 0xd0, 0, 0},{0x8c4f, 0x07, 0, 0}, + {0x8c50, 0xd0, 0, 0},{0x8c51, 0x06, 0, 0},{0x8c52, 0xd0, 0, 0}, + {0x8c53, 0x05, 0, 0},{0x8c54, 0xd0, 0, 0},{0x8c55, 0x04, 0, 0}, + {0x8c56, 0xd0, 0, 0},{0x8c57, 0x03, 0, 0},{0x8c58, 0xd0, 0, 0}, + {0x8c59, 0x02, 0, 0},{0x8c5a, 0xd0, 0, 0},{0x8c5b, 0x01, 0, 0}, + {0x8c5c, 0xd0, 0, 0},{0x8c5d, 0x00, 0, 0},{0x8c5e, 0xd0, 0, 0}, + {0x8c5f, 0xd0, 0, 0},{0x8c60, 0xd0, 0, 0},{0x8c61, 0x82, 0, 0}, + {0x8c62, 0xd0, 0, 0},{0x8c63, 0x83, 0, 0},{0x8c64, 0xd0, 0, 0}, + {0x8c65, 0xf0, 0, 0},{0x8c66, 0xd0, 0, 0},{0x8c67, 0xe0, 0, 0}, + {0x8c68, 0x32, 0, 0},{0x8c69, 0xe5, 0, 0},{0x8c6a, 0x33, 0, 0}, + {0x8c6b, 0x70, 0, 0},{0x8c6c, 0x03, 0, 0},{0x8c6d, 0x02, 0, 0}, + {0x8c6e, 0x0d, 0, 0},{0x8c6f, 0x76, 0, 0},{0x8c70, 0xc2, 0, 0}, + {0x8c71, 0xaf, 0, 0},{0x8c72, 0xaf, 0, 0},{0x8c73, 0x33, 0, 0}, + {0x8c74, 0xe4, 0, 0},{0x8c75, 0xf5, 0, 0},{0x8c76, 0x33, 0, 0}, + {0x8c77, 0xd2, 0, 0},{0x8c78, 0xaf, 0, 0},{0x8c79, 0x90, 0, 0}, + {0x8c7a, 0x30, 0, 0},{0x8c7b, 0x25, 0, 0},{0x8c7c, 0xe0, 0, 0}, + {0x8c7d, 0xf5, 0, 0},{0x8c7e, 0x7d, 0, 0},{0x8c7f, 0x90, 0, 0}, + {0x8c80, 0x50, 0, 0},{0x8c81, 0x82, 0, 0},{0x8c82, 0xe0, 0, 0}, + {0x8c83, 0xf5, 0, 0},{0x8c84, 0x65, 0, 0},{0x8c85, 0xa3, 0, 0}, + {0x8c86, 0xe0, 0, 0},{0x8c87, 0xf5, 0, 0},{0x8c88, 0x66, 0, 0}, + {0x8c89, 0xa3, 0, 0},{0x8c8a, 0xe0, 0, 0},{0x8c8b, 0xf5, 0, 0}, + {0x8c8c, 0x67, 0, 0},{0x8c8d, 0xa3, 0, 0},{0x8c8e, 0xe0, 0, 0}, + {0x8c8f, 0xf5, 0, 0},{0x8c90, 0x68, 0, 0},{0x8c91, 0xef, 0, 0}, + {0x8c92, 0x12, 0, 0},{0x8c93, 0x09, 0, 0},{0x8c94, 0xd2, 0, 0}, + {0x8c95, 0x0c, 0, 0},{0x8c96, 0xba, 0, 0},{0x8c97, 0x03, 0, 0}, + {0x8c98, 0x0c, 0, 0},{0x8c99, 0xcf, 0, 0},{0x8c9a, 0x05, 0, 0}, + {0x8c9b, 0x0c, 0, 0},{0x8c9c, 0xf9, 0, 0},{0x8c9d, 0x06, 0, 0}, + {0x8c9e, 0x0c, 0, 0},{0x8c9f, 0xe7, 0, 0},{0x8ca0, 0x08, 0, 0}, + {0x8ca1, 0x0d, 0, 0},{0x8ca2, 0x06, 0, 0},{0x8ca3, 0x10, 0, 0}, + {0x8ca4, 0x0d, 0, 0},{0x8ca5, 0x1a, 0, 0},{0x8ca6, 0x12, 0, 0}, + {0x8ca7, 0x0d, 0, 0},{0x8ca8, 0x1f, 0, 0},{0x8ca9, 0x20, 0, 0}, + {0x8caa, 0x0d, 0, 0},{0x8cab, 0x2d, 0, 0},{0x8cac, 0x21, 0, 0}, + {0x8cad, 0x0d, 0, 0},{0x8cae, 0x32, 0, 0},{0x8caf, 0x30, 0, 0}, + {0x8cb0, 0x0d, 0, 0},{0x8cb1, 0x5b, 0, 0},{0x8cb2, 0x50, 0, 0}, + {0x8cb3, 0x0d, 0, 0},{0x8cb4, 0x3d, 0, 0},{0x8cb5, 0xd8, 0, 0}, + {0x8cb6, 0x00, 0, 0},{0x8cb7, 0x00, 0, 0},{0x8cb8, 0x0d, 0, 0}, + {0x8cb9, 0x68, 0, 0},{0x8cba, 0x20, 0, 0},{0x8cbb, 0x05, 0, 0}, + {0x8cbc, 0x03, 0, 0},{0x8cbd, 0x02, 0, 0},{0x8cbe, 0x0d, 0, 0}, + {0x8cbf, 0x68, 0, 0},{0x8cc0, 0x30, 0, 0},{0x8cc1, 0x00, 0, 0}, + {0x8cc2, 0x03, 0, 0},{0x8cc3, 0x02, 0, 0},{0x8cc4, 0x0d, 0, 0}, + {0x8cc5, 0x68, 0, 0},{0x8cc6, 0xd2, 0, 0},{0x8cc7, 0x07, 0, 0}, + {0x8cc8, 0xc2, 0, 0},{0x8cc9, 0x06, 0, 0},{0x8cca, 0x12, 0, 0}, + {0x8ccb, 0x02, 0, 0},{0x8ccc, 0x90, 0, 0},{0x8ccd, 0x80, 0, 0}, + {0x8cce, 0x24, 0, 0},{0x8ccf, 0x20, 0, 0},{0x8cd0, 0x05, 0, 0}, + {0x8cd1, 0x03, 0, 0},{0x8cd2, 0x02, 0, 0},{0x8cd3, 0x0d, 0, 0}, + {0x8cd4, 0x68, 0, 0},{0x8cd5, 0x30, 0, 0},{0x8cd6, 0x00, 0, 0}, + {0x8cd7, 0x03, 0, 0},{0x8cd8, 0x02, 0, 0},{0x8cd9, 0x0d, 0, 0}, + {0x8cda, 0x68, 0, 0},{0x8cdb, 0xc2, 0, 0},{0x8cdc, 0x07, 0, 0}, + {0x8cdd, 0xd2, 0, 0},{0x8cde, 0x06, 0, 0},{0x8cdf, 0x12, 0, 0}, + {0x8ce0, 0x02, 0, 0},{0x8ce1, 0xa5, 0, 0},{0x8ce2, 0xc2, 0, 0}, + {0x8ce3, 0x04, 0, 0},{0x8ce4, 0x02, 0, 0},{0x8ce5, 0x0d, 0, 0}, + {0x8ce6, 0x68, 0, 0},{0x8ce7, 0x12, 0, 0},{0x8ce8, 0x02, 0, 0}, + {0x8ce9, 0x40, 0, 0},{0x8cea, 0x30, 0, 0},{0x8ceb, 0x05, 0, 0}, + {0x8cec, 0x06, 0, 0},{0x8ced, 0xe4, 0, 0},{0x8cee, 0xf5, 0, 0}, + {0x8cef, 0x0c, 0, 0},{0x8cf0, 0x12, 0, 0},{0x8cf1, 0x0f, 0, 0}, + {0x8cf2, 0x11, 0, 0},{0x8cf3, 0xc2, 0, 0},{0x8cf4, 0x31, 0, 0}, + {0x8cf5, 0xd2, 0, 0},{0x8cf6, 0x34, 0, 0},{0x8cf7, 0x80, 0, 0}, + {0x8cf8, 0x6f, 0, 0},{0x8cf9, 0x30, 0, 0},{0x8cfa, 0x07, 0, 0}, + {0x8cfb, 0x6c, 0, 0},{0x8cfc, 0x30, 0, 0},{0x8cfd, 0x06, 0, 0}, + {0x8cfe, 0x69, 0, 0},{0x8cff, 0x12, 0, 0},{0x8d00, 0x02, 0, 0}, + {0x8d01, 0x90, 0, 0},{0x8d02, 0xd2, 0, 0},{0x8d03, 0x31, 0, 0}, + {0x8d04, 0x80, 0, 0},{0x8d05, 0x62, 0, 0},{0x8d06, 0x20, 0, 0}, + {0x8d07, 0x07, 0, 0},{0x8d08, 0x03, 0, 0},{0x8d09, 0x30, 0, 0}, + {0x8d0a, 0x06, 0, 0},{0x8d0b, 0x09, 0, 0},{0x8d0c, 0xe5, 0, 0}, + {0x8d0d, 0x7d, 0, 0},{0x8d0e, 0x64, 0, 0},{0x8d0f, 0x0e, 0, 0}, + {0x8d10, 0x70, 0, 0},{0x8d11, 0x56, 0, 0},{0x8d12, 0x20, 0, 0}, + {0x8d13, 0x00, 0, 0},{0x8d14, 0x53, 0, 0},{0x8d15, 0x12, 0, 0}, + {0x8d16, 0x05, 0, 0},{0x8d17, 0x16, 0, 0},{0x8d18, 0x80, 0, 0}, + {0x8d19, 0x4e, 0, 0},{0x8d1a, 0x12, 0, 0},{0x8d1b, 0x06, 0, 0}, + {0x8d1c, 0xdf, 0, 0},{0x8d1d, 0x80, 0, 0},{0x8d1e, 0x49, 0, 0}, + {0x8d1f, 0x30, 0, 0},{0x8d20, 0x05, 0, 0},{0x8d21, 0x46, 0, 0}, + {0x8d22, 0x20, 0, 0},{0x8d23, 0x07, 0, 0},{0x8d24, 0x43, 0, 0}, + {0x8d25, 0x20, 0, 0},{0x8d26, 0x06, 0, 0},{0x8d27, 0x40, 0, 0}, + {0x8d28, 0x12, 0, 0},{0x8d29, 0x15, 0, 0},{0x8d2a, 0x4c, 0, 0}, + {0x8d2b, 0x80, 0, 0},{0x8d2c, 0x3b, 0, 0},{0x8d2d, 0x12, 0, 0}, + {0x8d2e, 0x11, 0, 0},{0x8d2f, 0x7d, 0, 0},{0x8d30, 0x80, 0, 0}, + {0x8d31, 0x36, 0, 0},{0x8d32, 0x20, 0, 0},{0x8d33, 0x07, 0, 0}, + {0x8d34, 0x33, 0, 0},{0x8d35, 0x20, 0, 0},{0x8d36, 0x06, 0, 0}, + {0x8d37, 0x30, 0, 0},{0x8d38, 0x12, 0, 0},{0x8d39, 0x15, 0, 0}, + {0x8d3a, 0x5b, 0, 0},{0x8d3b, 0x80, 0, 0},{0x8d3c, 0x2b, 0, 0}, + {0x8d3d, 0xe5, 0, 0},{0x8d3e, 0x7d, 0, 0},{0x8d3f, 0x64, 0, 0}, + {0x8d40, 0x01, 0, 0},{0x8d41, 0x70, 0, 0},{0x8d42, 0x25, 0, 0}, + {0x8d43, 0xd2, 0, 0},{0x8d44, 0x35, 0, 0},{0x8d45, 0x90, 0, 0}, + {0x8d46, 0x50, 0, 0},{0x8d47, 0x82, 0, 0},{0x8d48, 0xe5, 0, 0}, + {0x8d49, 0x73, 0, 0},{0x8d4a, 0xf0, 0, 0},{0x8d4b, 0xa3, 0, 0}, + {0x8d4c, 0xe5, 0, 0},{0x8d4d, 0x74, 0, 0},{0x8d4e, 0xf0, 0, 0}, + {0x8d4f, 0xa3, 0, 0},{0x8d50, 0xe5, 0, 0},{0x8d51, 0x75, 0, 0}, + {0x8d52, 0xf0, 0, 0},{0x8d53, 0xa3, 0, 0},{0x8d54, 0xe5, 0, 0}, + {0x8d55, 0x76, 0, 0},{0x8d56, 0xf0, 0, 0},{0x8d57, 0xc2, 0, 0}, + {0x8d58, 0x35, 0, 0},{0x8d59, 0x80, 0, 0},{0x8d5a, 0x0d, 0, 0}, + {0x8d5b, 0x90, 0, 0},{0x8d5c, 0x50, 0, 0},{0x8d5d, 0x82, 0, 0}, + {0x8d5e, 0x30, 0, 0},{0x8d5f, 0x33, 0, 0},{0x8d60, 0x05, 0, 0}, + {0x8d61, 0x74, 0, 0},{0x8d62, 0x55, 0, 0},{0x8d63, 0xf0, 0, 0}, + {0x8d64, 0x80, 0, 0},{0x8d65, 0x02, 0, 0},{0x8d66, 0xe4, 0, 0}, + {0x8d67, 0xf0, 0, 0},{0x8d68, 0x20, 0, 0},{0x8d69, 0x07, 0, 0}, + {0x8d6a, 0x06, 0, 0},{0x8d6b, 0x30, 0, 0},{0x8d6c, 0x06, 0, 0}, + {0x8d6d, 0x03, 0, 0},{0x8d6e, 0x30, 0, 0},{0x8d6f, 0x04, 0, 0}, + {0x8d70, 0x05, 0, 0},{0x8d71, 0x90, 0, 0},{0x8d72, 0x30, 0, 0}, + {0x8d73, 0x25, 0, 0},{0x8d74, 0xe4, 0, 0},{0x8d75, 0xf0, 0, 0}, + {0x8d76, 0x22, 0, 0},{0x8d77, 0x30, 0, 0},{0x8d78, 0x04, 0, 0}, + {0x8d79, 0x03, 0, 0},{0x8d7a, 0x02, 0, 0},{0x8d7b, 0x0e, 0, 0}, + {0x8d7c, 0x62, 0, 0},{0x8d7d, 0xd2, 0, 0},{0x8d7e, 0x04, 0, 0}, + {0x8d7f, 0xe5, 0, 0},{0x8d80, 0x7d, 0, 0},{0x8d81, 0xb4, 0, 0}, + {0x8d82, 0x01, 0, 0},{0x8d83, 0x06, 0, 0},{0x8d84, 0x12, 0, 0}, + {0x8d85, 0x15, 0, 0},{0x8d86, 0x2c, 0, 0},{0x8d87, 0x02, 0, 0}, + {0x8d88, 0x0e, 0, 0},{0x8d89, 0x5b, 0, 0},{0x8d8a, 0xe5, 0, 0}, + {0x8d8b, 0x7d, 0, 0},{0x8d8c, 0xb4, 0, 0},{0x8d8d, 0x02, 0, 0}, + {0x8d8e, 0x06, 0, 0},{0x8d8f, 0x12, 0, 0},{0x8d90, 0x15, 0, 0}, + {0x8d91, 0x3d, 0, 0},{0x8d92, 0x02, 0, 0},{0x8d93, 0x0e, 0, 0}, + {0x8d94, 0x5b, 0, 0},{0x8d95, 0xe5, 0, 0},{0x8d96, 0x7d, 0, 0}, + {0x8d97, 0xb4, 0, 0},{0x8d98, 0x03, 0, 0},{0x8d99, 0x05, 0, 0}, + {0x8d9a, 0xe4, 0, 0},{0x8d9b, 0xf5, 0, 0},{0x8d9c, 0x0c, 0, 0}, + {0x8d9d, 0x80, 0, 0},{0x8d9e, 0x08, 0, 0},{0x8d9f, 0xe5, 0, 0}, + {0x8da0, 0x7d, 0, 0},{0x8da1, 0xb4, 0, 0},{0x8da2, 0x04, 0, 0}, + {0x8da3, 0x09, 0, 0},{0x8da4, 0x85, 0, 0},{0x8da5, 0x7b, 0, 0}, + {0x8da6, 0x0c, 0, 0},{0x8da7, 0x12, 0, 0},{0x8da8, 0x0f, 0, 0}, + {0x8da9, 0x11, 0, 0},{0x8daa, 0x02, 0, 0},{0x8dab, 0x0e, 0, 0}, + {0x8dac, 0x5b, 0, 0},{0x8dad, 0xe5, 0, 0},{0x8dae, 0x7d, 0, 0}, + {0x8daf, 0x64, 0, 0},{0x8db0, 0x0f, 0, 0},{0x8db1, 0x70, 0, 0}, + {0x8db2, 0x1f, 0, 0},{0x8db3, 0x12, 0, 0},{0x8db4, 0x02, 0, 0}, + {0x8db5, 0xac, 0, 0},{0x8db6, 0x40, 0, 0},{0x8db7, 0x06, 0, 0}, + {0x8db8, 0x7e, 0, 0},{0x8db9, 0x00, 0, 0},{0x8dba, 0x7f, 0, 0}, + {0x8dbb, 0xff, 0, 0},{0x8dbc, 0x80, 0, 0},{0x8dbd, 0x04, 0, 0}, + {0x8dbe, 0xae, 0, 0},{0x8dbf, 0x67, 0, 0},{0x8dc0, 0xaf, 0, 0}, + {0x8dc1, 0x68, 0, 0},{0x8dc2, 0x12, 0, 0},{0x8dc3, 0x02, 0, 0}, + {0x8dc4, 0x58, 0, 0},{0x8dc5, 0xc3, 0, 0},{0x8dc6, 0x33, 0, 0}, + {0x8dc7, 0xce, 0, 0},{0x8dc8, 0x33, 0, 0},{0x8dc9, 0xce, 0, 0}, + {0x8dca, 0xd8, 0, 0},{0x8dcb, 0xf9, 0, 0},{0x8dcc, 0x12, 0, 0}, + {0x8dcd, 0x0e, 0, 0},{0x8dce, 0x63, 0, 0},{0x8dcf, 0x02, 0, 0}, + {0x8dd0, 0x0e, 0, 0},{0x8dd1, 0x5b, 0, 0},{0x8dd2, 0xe5, 0, 0}, + {0x8dd3, 0x7d, 0, 0},{0x8dd4, 0x64, 0, 0},{0x8dd5, 0x10, 0, 0}, + {0x8dd6, 0x60, 0, 0},{0x8dd7, 0x03, 0, 0},{0x8dd8, 0x02, 0, 0}, + {0x8dd9, 0x0e, 0, 0},{0x8dda, 0x5b, 0, 0},{0x8ddb, 0xf5, 0, 0}, + {0x8ddc, 0x65, 0, 0},{0x8ddd, 0xf5, 0, 0},{0x8dde, 0x66, 0, 0}, + {0x8ddf, 0xf5, 0, 0},{0x8de0, 0x67, 0, 0},{0x8de1, 0xab, 0, 0}, + {0x8de2, 0x68, 0, 0},{0x8de3, 0xaa, 0, 0},{0x8de4, 0x67, 0, 0}, + {0x8de5, 0xa9, 0, 0},{0x8de6, 0x66, 0, 0},{0x8de7, 0xa8, 0, 0}, + {0x8de8, 0x65, 0, 0},{0x8de9, 0x12, 0, 0},{0x8dea, 0x01, 0, 0}, + {0x8deb, 0xa0, 0, 0},{0x8dec, 0xfe, 0, 0},{0x8ded, 0xe4, 0, 0}, + {0x8dee, 0xfc, 0, 0},{0x8def, 0xfd, 0, 0},{0x8df0, 0x12, 0, 0}, + {0x8df1, 0x01, 0, 0},{0x8df2, 0xe7, 0, 0},{0x8df3, 0xe4, 0, 0}, + {0x8df4, 0x7b, 0, 0},{0x8df5, 0xff, 0, 0},{0x8df6, 0xfa, 0, 0}, + {0x8df7, 0xf9, 0, 0},{0x8df8, 0xf8, 0, 0},{0x8df9, 0x12, 0, 0}, + {0x8dfa, 0x02, 0, 0},{0x8dfb, 0x6d, 0, 0},{0x8dfc, 0x85, 0, 0}, + {0x8dfd, 0x49, 0, 0},{0x8dfe, 0x82, 0, 0},{0x8dff, 0x85, 0, 0}, + {0x8e00, 0x48, 0, 0},{0x8e01, 0x83, 0, 0},{0x8e02, 0xe4, 0, 0}, + {0x8e03, 0x93, 0, 0},{0x8e04, 0xff, 0, 0},{0x8e05, 0xe4, 0, 0}, + {0x8e06, 0xfc, 0, 0},{0x8e07, 0xfd, 0, 0},{0x8e08, 0xfe, 0, 0}, + {0x8e09, 0xe5, 0, 0},{0x8e0a, 0x68, 0, 0},{0x8e0b, 0x2f, 0, 0}, + {0x8e0c, 0xf5, 0, 0},{0x8e0d, 0x68, 0, 0},{0x8e0e, 0xee, 0, 0}, + {0x8e0f, 0x35, 0, 0},{0x8e10, 0x67, 0, 0},{0x8e11, 0xf5, 0, 0}, + {0x8e12, 0x67, 0, 0},{0x8e13, 0xed, 0, 0},{0x8e14, 0x35, 0, 0}, + {0x8e15, 0x66, 0, 0},{0x8e16, 0xf5, 0, 0},{0x8e17, 0x66, 0, 0}, + {0x8e18, 0xec, 0, 0},{0x8e19, 0x35, 0, 0},{0x8e1a, 0x65, 0, 0}, + {0x8e1b, 0xf5, 0, 0},{0x8e1c, 0x65, 0, 0},{0x8e1d, 0x12, 0, 0}, + {0x8e1e, 0x02, 0, 0},{0x8e1f, 0xac, 0, 0},{0x8e20, 0x40, 0, 0}, + {0x8e21, 0x04, 0, 0},{0x8e22, 0x7f, 0, 0},{0x8e23, 0xff, 0, 0}, + {0x8e24, 0x80, 0, 0},{0x8e25, 0x04, 0, 0},{0x8e26, 0xae, 0, 0}, + {0x8e27, 0x67, 0, 0},{0x8e28, 0xaf, 0, 0},{0x8e29, 0x68, 0, 0}, + {0x8e2a, 0x12, 0, 0},{0x8e2b, 0x02, 0, 0},{0x8e2c, 0x58, 0, 0}, + {0x8e2d, 0xc3, 0, 0},{0x8e2e, 0x33, 0, 0},{0x8e2f, 0xce, 0, 0}, + {0x8e30, 0x33, 0, 0},{0x8e31, 0xce, 0, 0},{0x8e32, 0xd8, 0, 0}, + {0x8e33, 0xf9, 0, 0},{0x8e34, 0x12, 0, 0},{0x8e35, 0x0e, 0, 0}, + {0x8e36, 0x63, 0, 0},{0x8e37, 0xe4, 0, 0},{0x8e38, 0xf5, 0, 0}, + {0x8e39, 0x66, 0, 0},{0x8e3a, 0xf5, 0, 0},{0x8e3b, 0x66, 0, 0}, + {0x8e3c, 0xe5, 0, 0},{0x8e3d, 0x66, 0, 0},{0x8e3e, 0xd3, 0, 0}, + {0x8e3f, 0x95, 0, 0},{0x8e40, 0x7b, 0, 0},{0x8e41, 0x50, 0, 0}, + {0x8e42, 0x15, 0, 0},{0x8e43, 0xaf, 0, 0},{0x8e44, 0x66, 0, 0}, + {0x8e45, 0xe5, 0, 0},{0x8e46, 0x49, 0, 0},{0x8e47, 0x2f, 0, 0}, + {0x8e48, 0x12, 0, 0},{0x8e49, 0x02, 0, 0},{0x8e4a, 0x4f, 0, 0}, + {0x8e4b, 0x93, 0, 0},{0x8e4c, 0xc3, 0, 0},{0x8e4d, 0x95, 0, 0}, + {0x8e4e, 0x68, 0, 0},{0x8e4f, 0xe4, 0, 0},{0x8e50, 0x95, 0, 0}, + {0x8e51, 0x67, 0, 0},{0x8e52, 0x50, 0, 0},{0x8e53, 0x04, 0, 0}, + {0x8e54, 0x05, 0, 0},{0x8e55, 0x66, 0, 0},{0x8e56, 0x80, 0, 0}, + {0x8e57, 0xe4, 0, 0},{0x8e58, 0x85, 0, 0},{0x8e59, 0x66, 0, 0}, + {0x8e5a, 0x7c, 0, 0},{0x8e5b, 0x90, 0, 0},{0x8e5c, 0x30, 0, 0}, + {0x8e5d, 0x25, 0, 0},{0x8e5e, 0xe4, 0, 0},{0x8e5f, 0xf0, 0, 0}, + {0x8e60, 0xd2, 0, 0},{0x8e61, 0x34, 0, 0},{0x8e62, 0x22, 0, 0}, + {0x8e63, 0xf5, 0, 0},{0x8e64, 0x68, 0, 0},{0x8e65, 0x8e, 0, 0}, + {0x8e66, 0x67, 0, 0},{0x8e67, 0x85, 0, 0},{0x8e68, 0x67, 0, 0}, + {0x8e69, 0x10, 0, 0},{0x8e6a, 0x85, 0, 0},{0x8e6b, 0x68, 0, 0}, + {0x8e6c, 0x11, 0, 0},{0x8e6d, 0x12, 0, 0},{0x8e6e, 0x09, 0, 0}, + {0x8e6f, 0xf8, 0, 0},{0x8e70, 0x22, 0, 0},{0x8e71, 0x12, 0, 0}, + {0x8e72, 0x02, 0, 0},{0x8e73, 0x85, 0, 0},{0x8e74, 0xb5, 0, 0}, + {0x8e75, 0x07, 0, 0},{0x8e76, 0x03, 0, 0},{0x8e77, 0xd3, 0, 0}, + {0x8e78, 0x80, 0, 0},{0x8e79, 0x01, 0, 0},{0x8e7a, 0xc3, 0, 0}, + {0x8e7b, 0x40, 0, 0},{0x8e7c, 0x03, 0, 0},{0x8e7d, 0x02, 0, 0}, + {0x8e7e, 0x0f, 0, 0},{0x8e7f, 0x10, 0, 0},{0x8e80, 0x90, 0, 0}, + {0x8e81, 0x30, 0, 0},{0x8e82, 0x04, 0, 0},{0x8e83, 0xe0, 0, 0}, + {0x8e84, 0x44, 0, 0},{0x8e85, 0x20, 0, 0},{0x8e86, 0xf0, 0, 0}, + {0x8e87, 0xa3, 0, 0},{0x8e88, 0xe0, 0, 0},{0x8e89, 0x44, 0, 0}, + {0x8e8a, 0x40, 0, 0},{0x8e8b, 0xf0, 0, 0},{0x8e8c, 0x90, 0, 0}, + {0x8e8d, 0x50, 0, 0},{0x8e8e, 0x25, 0, 0},{0x8e8f, 0xe0, 0, 0}, + {0x8e90, 0x44, 0, 0},{0x8e91, 0x04, 0, 0},{0x8e92, 0xf0, 0, 0}, + {0x8e93, 0x90, 0, 0},{0x8e94, 0x50, 0, 0},{0x8e95, 0x03, 0, 0}, + {0x8e96, 0xe0, 0, 0},{0x8e97, 0x54, 0, 0},{0x8e98, 0xfd, 0, 0}, + {0x8e99, 0xf0, 0, 0},{0x8e9a, 0x90, 0, 0},{0x8e9b, 0x50, 0, 0}, + {0x8e9c, 0x27, 0, 0},{0x8e9d, 0xe0, 0, 0},{0x8e9e, 0x44, 0, 0}, + {0x8e9f, 0x01, 0, 0},{0x8ea0, 0xf0, 0, 0},{0x8ea1, 0x90, 0, 0}, + {0x8ea2, 0x50, 0, 0},{0x8ea3, 0x31, 0, 0},{0x8ea4, 0xe4, 0, 0}, + {0x8ea5, 0xf0, 0, 0},{0x8ea6, 0x90, 0, 0},{0x8ea7, 0x50, 0, 0}, + {0x8ea8, 0x33, 0, 0},{0x8ea9, 0xf0, 0, 0},{0x8eaa, 0x90, 0, 0}, + {0x8eab, 0x30, 0, 0},{0x8eac, 0x1e, 0, 0},{0x8ead, 0x12, 0, 0}, + {0x8eae, 0x01, 0, 0},{0x8eaf, 0xfb, 0, 0},{0x8eb0, 0x90, 0, 0}, + {0x8eb1, 0x30, 0, 0},{0x8eb2, 0x18, 0, 0},{0x8eb3, 0x12, 0, 0}, + {0x8eb4, 0x01, 0, 0},{0x8eb5, 0xfb, 0, 0},{0x8eb6, 0x90, 0, 0}, + {0x8eb7, 0x30, 0, 0},{0x8eb8, 0x1b, 0, 0},{0x8eb9, 0x12, 0, 0}, + {0x8eba, 0x01, 0, 0},{0x8ebb, 0xfb, 0, 0},{0x8ebc, 0xe0, 0, 0}, + {0x8ebd, 0xf5, 0, 0},{0x8ebe, 0x25, 0, 0},{0x8ebf, 0x90, 0, 0}, + {0x8ec0, 0x30, 0, 0},{0x8ec1, 0x18, 0, 0},{0x8ec2, 0xe0, 0, 0}, + {0x8ec3, 0xf5, 0, 0},{0x8ec4, 0x21, 0, 0},{0x8ec5, 0x90, 0, 0}, + {0x8ec6, 0x60, 0, 0},{0x8ec7, 0x00, 0, 0},{0x8ec8, 0x74, 0, 0}, + {0x8ec9, 0xf5, 0, 0},{0x8eca, 0xf0, 0, 0},{0x8ecb, 0x90, 0, 0}, + {0x8ecc, 0x3f, 0, 0},{0x8ecd, 0x01, 0, 0},{0x8ece, 0xe4, 0, 0}, + {0x8ecf, 0xf0, 0, 0},{0x8ed0, 0xa3, 0, 0},{0x8ed1, 0xf0, 0, 0}, + {0x8ed2, 0x90, 0, 0},{0x8ed3, 0x3f, 0, 0},{0x8ed4, 0x01, 0, 0}, + {0x8ed5, 0xe0, 0, 0},{0x8ed6, 0x44, 0, 0},{0x8ed7, 0x08, 0, 0}, + {0x8ed8, 0xf0, 0, 0},{0x8ed9, 0xe0, 0, 0},{0x8eda, 0x44, 0, 0}, + {0x8edb, 0x20, 0, 0},{0x8edc, 0xf0, 0, 0},{0x8edd, 0x90, 0, 0}, + {0x8ede, 0x3f, 0, 0},{0x8edf, 0x05, 0, 0},{0x8ee0, 0x74, 0, 0}, + {0x8ee1, 0x30, 0, 0},{0x8ee2, 0xf0, 0, 0},{0x8ee3, 0xa3, 0, 0}, + {0x8ee4, 0x74, 0, 0},{0x8ee5, 0x24, 0, 0},{0x8ee6, 0xf0, 0, 0}, + {0x8ee7, 0x90, 0, 0},{0x8ee8, 0x3f, 0, 0},{0x8ee9, 0x0b, 0, 0}, + {0x8eea, 0xe0, 0, 0},{0x8eeb, 0x44, 0, 0},{0x8eec, 0x0f, 0, 0}, + {0x8eed, 0xf0, 0, 0},{0x8eee, 0x90, 0, 0},{0x8eef, 0x3f, 0, 0}, + {0x8ef0, 0x01, 0, 0},{0x8ef1, 0xe0, 0, 0},{0x8ef2, 0x44, 0, 0}, + {0x8ef3, 0x02, 0, 0},{0x8ef4, 0xf0, 0, 0},{0x8ef5, 0xc2, 0, 0}, + {0x8ef6, 0x8c, 0, 0},{0x8ef7, 0x75, 0, 0},{0x8ef8, 0x89, 0, 0}, + {0x8ef9, 0x03, 0, 0},{0x8efa, 0x75, 0, 0},{0x8efb, 0xa8, 0, 0}, + {0x8efc, 0x07, 0, 0},{0x8efd, 0x75, 0, 0},{0x8efe, 0xb8, 0, 0}, + {0x8eff, 0x04, 0, 0},{0x8f00, 0xe4, 0, 0},{0x8f01, 0xf5, 0, 0}, + {0x8f02, 0xd8, 0, 0},{0x8f03, 0xf5, 0, 0},{0x8f04, 0xe8, 0, 0}, + {0x8f05, 0x90, 0, 0},{0x8f06, 0x30, 0, 0},{0x8f07, 0x01, 0, 0}, + {0x8f08, 0xe0, 0, 0},{0x8f09, 0x44, 0, 0},{0x8f0a, 0x40, 0, 0}, + {0x8f0b, 0xf0, 0, 0},{0x8f0c, 0xe0, 0, 0},{0x8f0d, 0x54, 0, 0}, + {0x8f0e, 0xbf, 0, 0},{0x8f0f, 0xf0, 0, 0},{0x8f10, 0x22, 0, 0}, + {0x8f11, 0xe5, 0, 0},{0x8f12, 0x0c, 0, 0},{0x8f13, 0xd3, 0, 0}, + {0x8f14, 0x95, 0, 0},{0x8f15, 0x7b, 0, 0},{0x8f16, 0x40, 0, 0}, + {0x8f17, 0x01, 0, 0},{0x8f18, 0x22, 0, 0},{0x8f19, 0xe5, 0, 0}, + {0x8f1a, 0x49, 0, 0},{0x8f1b, 0x25, 0, 0},{0x8f1c, 0x0c, 0, 0}, + {0x8f1d, 0x12, 0, 0},{0x8f1e, 0x02, 0, 0},{0x8f1f, 0x4f, 0, 0}, + {0x8f20, 0x93, 0, 0},{0x8f21, 0x75, 0, 0},{0x8f22, 0x0d, 0, 0}, + {0x8f23, 0x00, 0, 0},{0x8f24, 0xf5, 0, 0},{0x8f25, 0x0e, 0, 0}, + {0x8f26, 0x45, 0, 0},{0x8f27, 0x0d, 0, 0},{0x8f28, 0x70, 0, 0}, + {0x8f29, 0x05, 0, 0},{0x8f2a, 0x85, 0, 0},{0x8f2b, 0x64, 0, 0}, + {0x8f2c, 0x0f, 0, 0},{0x8f2d, 0x80, 0, 0},{0x8f2e, 0x1b, 0, 0}, + {0x8f2f, 0x12, 0, 0},{0x8f30, 0x02, 0, 0},{0x8f31, 0x9b, 0, 0}, + {0x8f32, 0xc3, 0, 0},{0x8f33, 0x33, 0, 0},{0x8f34, 0xce, 0, 0}, + {0x8f35, 0x33, 0, 0},{0x8f36, 0xce, 0, 0},{0x8f37, 0xd8, 0, 0}, + {0x8f38, 0xf9, 0, 0},{0x8f39, 0xf5, 0, 0},{0x8f3a, 0x0e, 0, 0}, + {0x8f3b, 0x8e, 0, 0},{0x8f3c, 0x0d, 0, 0},{0x8f3d, 0x85, 0, 0}, + {0x8f3e, 0x0d, 0, 0},{0x8f3f, 0x10, 0, 0},{0x8f40, 0xf5, 0, 0}, + {0x8f41, 0x11, 0, 0},{0x8f42, 0x12, 0, 0},{0x8f43, 0x09, 0, 0}, + {0x8f44, 0xf8, 0, 0},{0x8f45, 0x30, 0, 0},{0x8f46, 0x33, 0, 0}, + {0x8f47, 0x63, 0, 0},{0x8f48, 0xc3, 0, 0},{0x8f49, 0x22, 0, 0}, + {0x8f4a, 0xe5, 0, 0},{0x8f4b, 0x0f, 0, 0},{0x8f4c, 0xd3, 0, 0}, + {0x8f4d, 0x94, 0, 0},{0x8f4e, 0x10, 0, 0},{0x8f4f, 0x40, 0, 0}, + {0x8f50, 0x33, 0, 0},{0x8f51, 0xe5, 0, 0},{0x8f52, 0x0f, 0, 0}, + {0x8f53, 0xd3, 0, 0},{0x8f54, 0x94, 0, 0},{0x8f55, 0x60, 0, 0}, + {0x8f56, 0x40, 0, 0},{0x8f57, 0x05, 0, 0},{0x8f58, 0x75, 0, 0}, + {0x8f59, 0x0f, 0, 0},{0x8f5a, 0x58, 0, 0},{0x8f5b, 0x80, 0, 0}, + {0x8f5c, 0x11, 0, 0},{0x8f5d, 0xe5, 0, 0},{0x8f5e, 0x0f, 0, 0}, + {0x8f5f, 0xd3, 0, 0},{0x8f60, 0x94, 0, 0},{0x8f61, 0x40, 0, 0}, + {0x8f62, 0x40, 0, 0},{0x8f63, 0x04, 0, 0},{0x8f64, 0x74, 0, 0}, + {0x8f65, 0xf0, 0, 0},{0x8f66, 0x80, 0, 0},{0x8f67, 0x02, 0, 0}, + {0x8f68, 0x74, 0, 0},{0x8f69, 0xf8, 0, 0},{0x8f6a, 0x25, 0, 0}, + {0x8f6b, 0x0f, 0, 0},{0x8f6c, 0xf5, 0, 0},{0x8f6d, 0x0f, 0, 0}, + {0x8f6e, 0x75, 0, 0},{0x8f6f, 0x0d, 0, 0},{0x8f70, 0x00, 0, 0}, + {0x8f71, 0x85, 0, 0},{0x8f72, 0x0f, 0, 0},{0x8f73, 0x0e, 0, 0}, + {0x8f74, 0x12, 0, 0},{0x8f75, 0x02, 0, 0},{0x8f76, 0x9b, 0, 0}, + {0x8f77, 0xc3, 0, 0},{0x8f78, 0x33, 0, 0},{0x8f79, 0xce, 0, 0}, + {0x8f7a, 0x33, 0, 0},{0x8f7b, 0xce, 0, 0},{0x8f7c, 0xd8, 0, 0}, + {0x8f7d, 0xf9, 0, 0},{0x8f7e, 0xf5, 0, 0},{0x8f7f, 0x0e, 0, 0}, + {0x8f80, 0x8e, 0, 0},{0x8f81, 0x0d, 0, 0},{0x8f82, 0x80, 0, 0}, + {0x8f83, 0x0a, 0, 0},{0x8f84, 0xe4, 0, 0},{0x8f85, 0xf5, 0, 0}, + {0x8f86, 0x0f, 0, 0},{0x8f87, 0x75, 0, 0},{0x8f88, 0x0d, 0, 0}, + {0x8f89, 0x80, 0, 0},{0x8f8a, 0xf5, 0, 0},{0x8f8b, 0x0e, 0, 0}, + {0x8f8c, 0xf5, 0, 0},{0x8f8d, 0x64, 0, 0},{0x8f8e, 0x85, 0, 0}, + {0x8f8f, 0x0d, 0, 0},{0x8f90, 0x10, 0, 0},{0x8f91, 0x85, 0, 0}, + {0x8f92, 0x0e, 0, 0},{0x8f93, 0x11, 0, 0},{0x8f94, 0x12, 0, 0}, + {0x8f95, 0x09, 0, 0},{0x8f96, 0xf8, 0, 0},{0x8f97, 0x30, 0, 0}, + {0x8f98, 0x33, 0, 0},{0x8f99, 0x02, 0, 0},{0x8f9a, 0xc3, 0, 0}, + {0x8f9b, 0x22, 0, 0},{0x8f9c, 0xe5, 0, 0},{0x8f9d, 0x0f, 0, 0}, + {0x8f9e, 0x60, 0, 0},{0x8f9f, 0x0b, 0, 0},{0x8fa0, 0x75, 0, 0}, + {0x8fa1, 0x10, 0, 0},{0x8fa2, 0x00, 0, 0},{0x8fa3, 0x75, 0, 0}, + {0x8fa4, 0x11, 0, 0},{0x8fa5, 0x32, 0, 0},{0x8fa6, 0x12, 0, 0}, + {0x8fa7, 0x14, 0, 0},{0x8fa8, 0xcd, 0, 0},{0x8fa9, 0x80, 0, 0}, + {0x8faa, 0x9f, 0, 0},{0x8fab, 0x85, 0, 0},{0x8fac, 0x0c, 0, 0}, + {0x8fad, 0x7c, 0, 0},{0x8fae, 0xd3, 0, 0},{0x8faf, 0x22, 0, 0}, + {0x8fb0, 0x30, 0, 0},{0x8fb1, 0x3c, 0, 0},{0x8fb2, 0x09, 0, 0}, + {0x8fb3, 0x30, 0, 0},{0x8fb4, 0x20, 0, 0},{0x8fb5, 0x06, 0, 0}, + {0x8fb6, 0xae, 0, 0},{0x8fb7, 0x56, 0, 0},{0x8fb8, 0xaf, 0, 0}, + {0x8fb9, 0x57, 0, 0},{0x8fba, 0x80, 0, 0},{0x8fbb, 0x04, 0, 0}, + {0x8fbc, 0xae, 0, 0},{0x8fbd, 0x69, 0, 0},{0x8fbe, 0xaf, 0, 0}, + {0x8fbf, 0x6a, 0, 0},{0x8fc0, 0x8e, 0, 0},{0x8fc1, 0x56, 0, 0}, + {0x8fc2, 0x8f, 0, 0},{0x8fc3, 0x57, 0, 0},{0x8fc4, 0x30, 0, 0}, + {0x8fc5, 0x3c, 0, 0},{0x8fc6, 0x09, 0, 0},{0x8fc7, 0x30, 0, 0}, + {0x8fc8, 0x21, 0, 0},{0x8fc9, 0x06, 0, 0},{0x8fca, 0xae, 0, 0}, + {0x8fcb, 0x58, 0, 0},{0x8fcc, 0xaf, 0, 0},{0x8fcd, 0x59, 0, 0}, + {0x8fce, 0x80, 0, 0},{0x8fcf, 0x04, 0, 0},{0x8fd0, 0xae, 0, 0}, + {0x8fd1, 0x6b, 0, 0},{0x8fd2, 0xaf, 0, 0},{0x8fd3, 0x6c, 0, 0}, + {0x8fd4, 0x8e, 0, 0},{0x8fd5, 0x58, 0, 0},{0x8fd6, 0x8f, 0, 0}, + {0x8fd7, 0x59, 0, 0},{0x8fd8, 0x30, 0, 0},{0x8fd9, 0x3c, 0, 0}, + {0x8fda, 0x09, 0, 0},{0x8fdb, 0x30, 0, 0},{0x8fdc, 0x22, 0, 0}, + {0x8fdd, 0x06, 0, 0},{0x8fde, 0xae, 0, 0},{0x8fdf, 0x5a, 0, 0}, + {0x8fe0, 0xaf, 0, 0},{0x8fe1, 0x5b, 0, 0},{0x8fe2, 0x80, 0, 0}, + {0x8fe3, 0x04, 0, 0},{0x8fe4, 0xae, 0, 0},{0x8fe5, 0x6d, 0, 0}, + {0x8fe6, 0xaf, 0, 0},{0x8fe7, 0x6e, 0, 0},{0x8fe8, 0x8e, 0, 0}, + {0x8fe9, 0x5a, 0, 0},{0x8fea, 0x8f, 0, 0},{0x8feb, 0x5b, 0, 0}, + {0x8fec, 0x30, 0, 0},{0x8fed, 0x3c, 0, 0},{0x8fee, 0x09, 0, 0}, + {0x8fef, 0x30, 0, 0},{0x8ff0, 0x23, 0, 0},{0x8ff1, 0x06, 0, 0}, + {0x8ff2, 0xae, 0, 0},{0x8ff3, 0x5c, 0, 0},{0x8ff4, 0xaf, 0, 0}, + {0x8ff5, 0x5d, 0, 0},{0x8ff6, 0x80, 0, 0},{0x8ff7, 0x04, 0, 0}, + {0x8ff8, 0xae, 0, 0},{0x8ff9, 0x6f, 0, 0},{0x8ffa, 0xaf, 0, 0}, + {0x8ffb, 0x70, 0, 0},{0x8ffc, 0x8e, 0, 0},{0x8ffd, 0x5c, 0, 0}, + {0x8ffe, 0x8f, 0, 0},{0x8fff, 0x5d, 0, 0},{0x9000, 0x30, 0, 0}, + {0x9001, 0x3c, 0, 0},{0x9002, 0x09, 0, 0},{0x9003, 0x30, 0, 0}, + {0x9004, 0x24, 0, 0},{0x9005, 0x06, 0, 0},{0x9006, 0xae, 0, 0}, + {0x9007, 0x5e, 0, 0},{0x9008, 0xaf, 0, 0},{0x9009, 0x5f, 0, 0}, + {0x900a, 0x80, 0, 0},{0x900b, 0x04, 0, 0},{0x900c, 0xae, 0, 0}, + {0x900d, 0x71, 0, 0},{0x900e, 0xaf, 0, 0},{0x900f, 0x72, 0, 0}, + {0x9010, 0x8e, 0, 0},{0x9011, 0x5e, 0, 0},{0x9012, 0x8f, 0, 0}, + {0x9013, 0x5f, 0, 0},{0x9014, 0x30, 0, 0},{0x9015, 0x3c, 0, 0}, + {0x9016, 0x09, 0, 0},{0x9017, 0x30, 0, 0},{0x9018, 0x25, 0, 0}, + {0x9019, 0x06, 0, 0},{0x901a, 0xae, 0, 0},{0x901b, 0x60, 0, 0}, + {0x901c, 0xaf, 0, 0},{0x901d, 0x61, 0, 0},{0x901e, 0x80, 0, 0}, + {0x901f, 0x04, 0, 0},{0x9020, 0xae, 0, 0},{0x9021, 0x73, 0, 0}, + {0x9022, 0xaf, 0, 0},{0x9023, 0x74, 0, 0},{0x9024, 0x8e, 0, 0}, + {0x9025, 0x60, 0, 0},{0x9026, 0x8f, 0, 0},{0x9027, 0x61, 0, 0}, + {0x9028, 0x30, 0, 0},{0x9029, 0x3c, 0, 0},{0x902a, 0x09, 0, 0}, + {0x902b, 0x30, 0, 0},{0x902c, 0x26, 0, 0},{0x902d, 0x06, 0, 0}, + {0x902e, 0xae, 0, 0},{0x902f, 0x62, 0, 0},{0x9030, 0xaf, 0, 0}, + {0x9031, 0x63, 0, 0},{0x9032, 0x80, 0, 0},{0x9033, 0x04, 0, 0}, + {0x9034, 0xae, 0, 0},{0x9035, 0x75, 0, 0},{0x9036, 0xaf, 0, 0}, + {0x9037, 0x76, 0, 0},{0x9038, 0x8e, 0, 0},{0x9039, 0x62, 0, 0}, + {0x903a, 0x8f, 0, 0},{0x903b, 0x63, 0, 0},{0x903c, 0x22, 0, 0}, + {0x903d, 0xd3, 0, 0},{0x903e, 0xe5, 0, 0},{0x903f, 0x57, 0, 0}, + {0x9040, 0x95, 0, 0},{0x9041, 0x6a, 0, 0},{0x9042, 0xe5, 0, 0}, + {0x9043, 0x56, 0, 0},{0x9044, 0x95, 0, 0},{0x9045, 0x69, 0, 0}, + {0x9046, 0x40, 0, 0},{0x9047, 0x03, 0, 0},{0x9048, 0xd3, 0, 0}, + {0x9049, 0x80, 0, 0},{0x904a, 0x01, 0, 0},{0x904b, 0xc3, 0, 0}, + {0x904c, 0x92, 0, 0},{0x904d, 0x20, 0, 0},{0x904e, 0xd3, 0, 0}, + {0x904f, 0xe5, 0, 0},{0x9050, 0x59, 0, 0},{0x9051, 0x95, 0, 0}, + {0x9052, 0x6c, 0, 0},{0x9053, 0xe5, 0, 0},{0x9054, 0x58, 0, 0}, + {0x9055, 0x95, 0, 0},{0x9056, 0x6b, 0, 0},{0x9057, 0x40, 0, 0}, + {0x9058, 0x03, 0, 0},{0x9059, 0xd3, 0, 0},{0x905a, 0x80, 0, 0}, + {0x905b, 0x01, 0, 0},{0x905c, 0xc3, 0, 0},{0x905d, 0x92, 0, 0}, + {0x905e, 0x21, 0, 0},{0x905f, 0xd3, 0, 0},{0x9060, 0xe5, 0, 0}, + {0x9061, 0x5b, 0, 0},{0x9062, 0x95, 0, 0},{0x9063, 0x6e, 0, 0}, + {0x9064, 0xe5, 0, 0},{0x9065, 0x5a, 0, 0},{0x9066, 0x95, 0, 0}, + {0x9067, 0x6d, 0, 0},{0x9068, 0x40, 0, 0},{0x9069, 0x03, 0, 0}, + {0x906a, 0xd3, 0, 0},{0x906b, 0x80, 0, 0},{0x906c, 0x01, 0, 0}, + {0x906d, 0xc3, 0, 0},{0x906e, 0x92, 0, 0},{0x906f, 0x22, 0, 0}, + {0x9070, 0xd3, 0, 0},{0x9071, 0xe5, 0, 0},{0x9072, 0x5d, 0, 0}, + {0x9073, 0x95, 0, 0},{0x9074, 0x70, 0, 0},{0x9075, 0xe5, 0, 0}, + {0x9076, 0x5c, 0, 0},{0x9077, 0x95, 0, 0},{0x9078, 0x6f, 0, 0}, + {0x9079, 0x40, 0, 0},{0x907a, 0x03, 0, 0},{0x907b, 0xd3, 0, 0}, + {0x907c, 0x80, 0, 0},{0x907d, 0x01, 0, 0},{0x907e, 0xc3, 0, 0}, + {0x907f, 0x92, 0, 0},{0x9080, 0x23, 0, 0},{0x9081, 0xd3, 0, 0}, + {0x9082, 0xe5, 0, 0},{0x9083, 0x5f, 0, 0},{0x9084, 0x95, 0, 0}, + {0x9085, 0x72, 0, 0},{0x9086, 0xe5, 0, 0},{0x9087, 0x5e, 0, 0}, + {0x9088, 0x95, 0, 0},{0x9089, 0x71, 0, 0},{0x908a, 0x40, 0, 0}, + {0x908b, 0x03, 0, 0},{0x908c, 0xd3, 0, 0},{0x908d, 0x80, 0, 0}, + {0x908e, 0x01, 0, 0},{0x908f, 0xc3, 0, 0},{0x9090, 0x92, 0, 0}, + {0x9091, 0x24, 0, 0},{0x9092, 0xd3, 0, 0},{0x9093, 0xe5, 0, 0}, + {0x9094, 0x61, 0, 0},{0x9095, 0x95, 0, 0},{0x9096, 0x74, 0, 0}, + {0x9097, 0xe5, 0, 0},{0x9098, 0x60, 0, 0},{0x9099, 0x95, 0, 0}, + {0x909a, 0x73, 0, 0},{0x909b, 0x40, 0, 0},{0x909c, 0x03, 0, 0}, + {0x909d, 0xd3, 0, 0},{0x909e, 0x80, 0, 0},{0x909f, 0x01, 0, 0}, + {0x90a0, 0xc3, 0, 0},{0x90a1, 0x92, 0, 0},{0x90a2, 0x25, 0, 0}, + {0x90a3, 0xd3, 0, 0},{0x90a4, 0xe5, 0, 0},{0x90a5, 0x63, 0, 0}, + {0x90a6, 0x95, 0, 0},{0x90a7, 0x76, 0, 0},{0x90a8, 0xe5, 0, 0}, + {0x90a9, 0x62, 0, 0},{0x90aa, 0x95, 0, 0},{0x90ab, 0x75, 0, 0}, + {0x90ac, 0x40, 0, 0},{0x90ad, 0x03, 0, 0},{0x90ae, 0xd3, 0, 0}, + {0x90af, 0x80, 0, 0},{0x90b0, 0x01, 0, 0},{0x90b1, 0xc3, 0, 0}, + {0x90b2, 0x92, 0, 0},{0x90b3, 0x26, 0, 0},{0x90b4, 0x22, 0, 0}, + {0x90b5, 0xe5, 0, 0},{0x90b6, 0x0a, 0, 0},{0x90b7, 0x70, 0, 0}, + {0x90b8, 0x04, 0, 0},{0x90b9, 0x7a, 0, 0},{0x90ba, 0x11, 0, 0}, + {0x90bb, 0x7b, 0, 0},{0x90bc, 0xee, 0, 0},{0x90bd, 0xe5, 0, 0}, + {0x90be, 0x0a, 0, 0},{0x90bf, 0xb4, 0, 0},{0x90c0, 0x01, 0, 0}, + {0x90c1, 0x04, 0, 0},{0x90c2, 0x7a, 0, 0},{0x90c3, 0x12, 0, 0}, + {0x90c4, 0x7b, 0, 0},{0x90c5, 0x02, 0, 0},{0x90c6, 0xe5, 0, 0}, + {0x90c7, 0x0a, 0, 0},{0x90c8, 0xb4, 0, 0},{0x90c9, 0x02, 0, 0}, + {0x90ca, 0x04, 0, 0},{0x90cb, 0x7a, 0, 0},{0x90cc, 0x12, 0, 0}, + {0x90cd, 0x7b, 0, 0},{0x90ce, 0x16, 0, 0},{0x90cf, 0x8b, 0, 0}, + {0x90d0, 0x82, 0, 0},{0x90d1, 0x8a, 0, 0},{0x90d2, 0x83, 0, 0}, + {0x90d3, 0x12, 0, 0},{0x90d4, 0x09, 0, 0},{0x90d5, 0xc2, 0, 0}, + {0x90d6, 0x8f, 0, 0},{0x90d7, 0x37, 0, 0},{0x90d8, 0x8e, 0, 0}, + {0x90d9, 0x36, 0, 0},{0x90da, 0x8d, 0, 0},{0x90db, 0x35, 0, 0}, + {0x90dc, 0x8c, 0, 0},{0x90dd, 0x34, 0, 0},{0x90de, 0xe5, 0, 0}, + {0x90df, 0x82, 0, 0},{0x90e0, 0x24, 0, 0},{0x90e1, 0x04, 0, 0}, + {0x90e2, 0xf5, 0, 0},{0x90e3, 0x82, 0, 0},{0x90e4, 0xe4, 0, 0}, + {0x90e5, 0x35, 0, 0},{0x90e6, 0x83, 0, 0},{0x90e7, 0xf5, 0, 0}, + {0x90e8, 0x83, 0, 0},{0x90e9, 0x12, 0, 0},{0x90ea, 0x09, 0, 0}, + {0x90eb, 0xc2, 0, 0},{0x90ec, 0x8f, 0, 0},{0x90ed, 0x3b, 0, 0}, + {0x90ee, 0x8e, 0, 0},{0x90ef, 0x3a, 0, 0},{0x90f0, 0x8d, 0, 0}, + {0x90f1, 0x39, 0, 0},{0x90f2, 0x8c, 0, 0},{0x90f3, 0x38, 0, 0}, + {0x90f4, 0xeb, 0, 0},{0x90f5, 0x24, 0, 0},{0x90f6, 0x08, 0, 0}, + {0x90f7, 0x12, 0, 0},{0x90f8, 0x02, 0, 0},{0x90f9, 0x2f, 0, 0}, + {0x90fa, 0x12, 0, 0},{0x90fb, 0x01, 0, 0},{0x90fc, 0x97, 0, 0}, + {0x90fd, 0xeb, 0, 0},{0x90fe, 0x24, 0, 0},{0x90ff, 0x0c, 0, 0}, + {0x9100, 0x12, 0, 0},{0x9101, 0x02, 0, 0},{0x9102, 0x2f, 0, 0}, + {0x9103, 0x8f, 0, 0},{0x9104, 0x43, 0, 0},{0x9105, 0x8e, 0, 0}, + {0x9106, 0x42, 0, 0},{0x9107, 0x8d, 0, 0},{0x9108, 0x41, 0, 0}, + {0x9109, 0x8c, 0, 0},{0x910a, 0x40, 0, 0},{0x910b, 0xeb, 0, 0}, + {0x910c, 0x24, 0, 0},{0x910d, 0x10, 0, 0},{0x910e, 0x12, 0, 0}, + {0x910f, 0x02, 0, 0},{0x9110, 0x2f, 0, 0},{0x9111, 0x8f, 0, 0}, + {0x9112, 0x47, 0, 0},{0x9113, 0x8e, 0, 0},{0x9114, 0x46, 0, 0}, + {0x9115, 0x8d, 0, 0},{0x9116, 0x45, 0, 0},{0x9117, 0x8c, 0, 0}, + {0x9118, 0x44, 0, 0},{0x9119, 0x22, 0, 0},{0x911a, 0x30, 0, 0}, + {0x911b, 0x3c, 0, 0},{0x911c, 0x07, 0, 0},{0x911d, 0x30, 0, 0}, + {0x911e, 0x20, 0, 0},{0x911f, 0x04, 0, 0},{0x9120, 0xaf, 0, 0}, + {0x9121, 0x4a, 0, 0},{0x9122, 0x80, 0, 0},{0x9123, 0x02, 0, 0}, + {0x9124, 0xaf, 0, 0},{0x9125, 0x7c, 0, 0},{0x9126, 0x8f, 0, 0}, + {0x9127, 0x4a, 0, 0},{0x9128, 0x30, 0, 0},{0x9129, 0x3c, 0, 0}, + {0x912a, 0x07, 0, 0},{0x912b, 0x30, 0, 0},{0x912c, 0x21, 0, 0}, + {0x912d, 0x04, 0, 0},{0x912e, 0xaf, 0, 0},{0x912f, 0x4b, 0, 0}, + {0x9130, 0x80, 0, 0},{0x9131, 0x02, 0, 0},{0x9132, 0xaf, 0, 0}, + {0x9133, 0x7c, 0, 0},{0x9134, 0x8f, 0, 0},{0x9135, 0x4b, 0, 0}, + {0x9136, 0x30, 0, 0},{0x9137, 0x3c, 0, 0},{0x9138, 0x07, 0, 0}, + {0x9139, 0x30, 0, 0},{0x913a, 0x22, 0, 0},{0x913b, 0x04, 0, 0}, + {0x913c, 0xaf, 0, 0},{0x913d, 0x4c, 0, 0},{0x913e, 0x80, 0, 0}, + {0x913f, 0x02, 0, 0},{0x9140, 0xaf, 0, 0},{0x9141, 0x7c, 0, 0}, + {0x9142, 0x8f, 0, 0},{0x9143, 0x4c, 0, 0},{0x9144, 0x30, 0, 0}, + {0x9145, 0x3c, 0, 0},{0x9146, 0x07, 0, 0},{0x9147, 0x30, 0, 0}, + {0x9148, 0x23, 0, 0},{0x9149, 0x04, 0, 0},{0x914a, 0xaf, 0, 0}, + {0x914b, 0x4d, 0, 0},{0x914c, 0x80, 0, 0},{0x914d, 0x02, 0, 0}, + {0x914e, 0xaf, 0, 0},{0x914f, 0x7c, 0, 0},{0x9150, 0x8f, 0, 0}, + {0x9151, 0x4d, 0, 0},{0x9152, 0x30, 0, 0},{0x9153, 0x3c, 0, 0}, + {0x9154, 0x07, 0, 0},{0x9155, 0x30, 0, 0},{0x9156, 0x24, 0, 0}, + {0x9157, 0x04, 0, 0},{0x9158, 0xaf, 0, 0},{0x9159, 0x4e, 0, 0}, + {0x915a, 0x80, 0, 0},{0x915b, 0x02, 0, 0},{0x915c, 0xaf, 0, 0}, + {0x915d, 0x7c, 0, 0},{0x915e, 0x8f, 0, 0},{0x915f, 0x4e, 0, 0}, + {0x9160, 0x30, 0, 0},{0x9161, 0x3c, 0, 0},{0x9162, 0x07, 0, 0}, + {0x9163, 0x30, 0, 0},{0x9164, 0x25, 0, 0},{0x9165, 0x04, 0, 0}, + {0x9166, 0xaf, 0, 0},{0x9167, 0x4f, 0, 0},{0x9168, 0x80, 0, 0}, + {0x9169, 0x02, 0, 0},{0x916a, 0xaf, 0, 0},{0x916b, 0x7c, 0, 0}, + {0x916c, 0x8f, 0, 0},{0x916d, 0x4f, 0, 0},{0x916e, 0x30, 0, 0}, + {0x916f, 0x3c, 0, 0},{0x9170, 0x07, 0, 0},{0x9171, 0x30, 0, 0}, + {0x9172, 0x26, 0, 0},{0x9173, 0x04, 0, 0},{0x9174, 0xaf, 0, 0}, + {0x9175, 0x50, 0, 0},{0x9176, 0x80, 0, 0},{0x9177, 0x02, 0, 0}, + {0x9178, 0xaf, 0, 0},{0x9179, 0x7c, 0, 0},{0x917a, 0x8f, 0, 0}, + {0x917b, 0x50, 0, 0},{0x917c, 0x22, 0, 0},{0x917d, 0xe5, 0, 0}, + {0x917e, 0x7d, 0, 0},{0x917f, 0x64, 0, 0},{0x9180, 0x01, 0, 0}, + {0x9181, 0x70, 0, 0},{0x9182, 0x47, 0, 0},{0x9183, 0xe5, 0, 0}, + {0x9184, 0x49, 0, 0},{0x9185, 0x25, 0, 0},{0x9186, 0x7c, 0, 0}, + {0x9187, 0x12, 0, 0},{0x9188, 0x01, 0, 0},{0x9189, 0xa4, 0, 0}, + {0x918a, 0xfe, 0, 0},{0x918b, 0xe4, 0, 0},{0x918c, 0x8f, 0, 0}, + {0x918d, 0x68, 0, 0},{0x918e, 0x8e, 0, 0},{0x918f, 0x67, 0, 0}, + {0x9190, 0xf5, 0, 0},{0x9191, 0x66, 0, 0},{0x9192, 0xf5, 0, 0}, + {0x9193, 0x65, 0, 0},{0x9194, 0x12, 0, 0},{0x9195, 0x01, 0, 0}, + {0x9196, 0xf2, 0, 0},{0x9197, 0x7b, 0, 0},{0x9198, 0xff, 0, 0}, + {0x9199, 0xfa, 0, 0},{0x919a, 0xf9, 0, 0},{0x919b, 0xf8, 0, 0}, + {0x919c, 0x12, 0, 0},{0x919d, 0x01, 0, 0},{0x919e, 0xe7, 0, 0}, + {0x919f, 0xc0, 0, 0},{0x91a0, 0x05, 0, 0},{0x91a1, 0xc0, 0, 0}, + {0x91a2, 0x06, 0, 0},{0x91a3, 0xc0, 0, 0},{0x91a4, 0x07, 0, 0}, + {0x91a5, 0x12, 0, 0},{0x91a6, 0x01, 0, 0},{0x91a7, 0xa0, 0, 0}, + {0x91a8, 0xab, 0, 0},{0x91a9, 0x07, 0, 0},{0x91aa, 0xfa, 0, 0}, + {0x91ab, 0xe4, 0, 0},{0x91ac, 0xf9, 0, 0},{0x91ad, 0xf8, 0, 0}, + {0x91ae, 0xd0, 0, 0},{0x91af, 0x07, 0, 0},{0x91b0, 0xd0, 0, 0}, + {0x91b1, 0x06, 0, 0},{0x91b2, 0xd0, 0, 0},{0x91b3, 0x05, 0, 0}, + {0x91b4, 0x12, 0, 0},{0x91b5, 0x02, 0, 0},{0x91b6, 0x6d, 0, 0}, + {0x91b7, 0x85, 0, 0},{0x91b8, 0x68, 0, 0},{0x91b9, 0x65, 0, 0}, + {0x91ba, 0x85, 0, 0},{0x91bb, 0x7c, 0, 0},{0x91bc, 0x66, 0, 0}, + {0x91bd, 0xe5, 0, 0},{0x91be, 0x49, 0, 0},{0x91bf, 0x25, 0, 0}, + {0x91c0, 0x7c, 0, 0},{0x91c1, 0x12, 0, 0},{0x91c2, 0x02, 0, 0}, + {0x91c3, 0x4f, 0, 0},{0x91c4, 0x93, 0, 0},{0x91c5, 0x75, 0, 0}, + {0x91c6, 0x67, 0, 0},{0x91c7, 0x00, 0, 0},{0x91c8, 0xf5, 0, 0}, + {0x91c9, 0x68, 0, 0},{0x91ca, 0x90, 0, 0},{0x91cb, 0x50, 0, 0}, + {0x91cc, 0x82, 0, 0},{0x91cd, 0xe5, 0, 0},{0x91ce, 0x65, 0, 0}, + {0x91cf, 0xf0, 0, 0},{0x91d0, 0xa3, 0, 0},{0x91d1, 0xe5, 0, 0}, + {0x91d2, 0x66, 0, 0},{0x91d3, 0xf0, 0, 0},{0x91d4, 0xa3, 0, 0}, + {0x91d5, 0xe5, 0, 0},{0x91d6, 0x67, 0, 0},{0x91d7, 0xf0, 0, 0}, + {0x91d8, 0xa3, 0, 0},{0x91d9, 0xe5, 0, 0},{0x91da, 0x68, 0, 0}, + {0x91db, 0xf0, 0, 0},{0x91dc, 0x22, 0, 0},{0x91dd, 0x56, 0, 0}, + {0x91de, 0x0c, 0, 0},{0x91df, 0x04, 0, 0},{0x91e0, 0x00, 0, 0}, + {0x91e1, 0x2a, 0, 0},{0x91e2, 0x35, 0, 0},{0x91e3, 0x40, 0, 0}, + {0x91e4, 0x49, 0, 0},{0x91e5, 0x52, 0, 0},{0x91e6, 0x5b, 0, 0}, + {0x91e7, 0x64, 0, 0},{0x91e8, 0x6d, 0, 0},{0x91e9, 0x76, 0, 0}, + {0x91ea, 0x7f, 0, 0},{0x91eb, 0x88, 0, 0},{0x91ec, 0x91, 0, 0}, + {0x91ed, 0x07, 0, 0},{0x91ee, 0x20, 0, 0},{0x91ef, 0x12, 0, 0}, + {0x91f0, 0x28, 0, 0},{0x91f1, 0x1e, 0, 0},{0x91f2, 0x18, 0, 0}, + {0x91f3, 0x18, 0, 0},{0x91f4, 0x28, 0, 0},{0x91f5, 0x1e, 0, 0}, + {0x91f6, 0x18, 0, 0},{0x91f7, 0x12, 0, 0},{0x91f8, 0x28, 0, 0}, + {0x91f9, 0x1e, 0, 0},{0x91fa, 0x18, 0, 0},{0x91fb, 0x12, 0, 0}, + {0x91fc, 0x28, 0, 0},{0x91fd, 0x18, 0, 0},{0x91fe, 0x18, 0, 0}, + {0x91ff, 0x12, 0, 0},{0x9200, 0x20, 0, 0},{0x9201, 0x18, 0, 0}, + {0x9202, 0x28, 0, 0},{0x9203, 0x1c, 0, 0},{0x9204, 0x30, 0, 0}, + {0x9205, 0x24, 0, 0},{0x9206, 0x10, 0, 0},{0x9207, 0x1c, 0, 0}, + {0x9208, 0x18, 0, 0},{0x9209, 0x24, 0, 0},{0x920a, 0x1c, 0, 0}, + {0x920b, 0x14, 0, 0},{0x920c, 0x24, 0, 0},{0x920d, 0x1c, 0, 0}, + {0x920e, 0x28, 0, 0},{0x920f, 0x0c, 0, 0},{0x9210, 0x30, 0, 0}, + {0x9211, 0x14, 0, 0},{0x9212, 0x10, 0, 0},{0x9213, 0x0c, 0, 0}, + {0x9214, 0x18, 0, 0},{0x9215, 0x14, 0, 0},{0x9216, 0x1c, 0, 0}, + {0x9217, 0x20, 0, 0},{0x9218, 0x24, 0, 0},{0x9219, 0x28, 0, 0}, + {0x921a, 0x0c, 0, 0},{0x921b, 0x14, 0, 0},{0x921c, 0x14, 0, 0}, + {0x921d, 0x1c, 0, 0},{0x921e, 0x1c, 0, 0},{0x921f, 0x14, 0, 0}, + {0x9220, 0x24, 0, 0},{0x9221, 0x1c, 0, 0},{0x9222, 0x2c, 0, 0}, + {0x9223, 0x14, 0, 0},{0x9224, 0x34, 0, 0},{0x9225, 0x1c, 0, 0}, + {0x9226, 0x1c, 0, 0},{0x9227, 0x08, 0, 0},{0x9228, 0x24, 0, 0}, + {0x9229, 0x10, 0, 0},{0x922a, 0x19, 0, 0},{0x922b, 0x19, 0, 0}, + {0x922c, 0x1c, 0, 0},{0x922d, 0x19, 0, 0},{0x922e, 0x19, 0, 0}, + {0x922f, 0x10, 0, 0},{0x9230, 0x10, 0, 0},{0x9231, 0x10, 0, 0}, + {0x9232, 0x10, 0, 0},{0x9233, 0x10, 0, 0},{0x9234, 0x00, 0, 0}, + {0x9235, 0x00, 0, 0},{0x9236, 0x00, 0, 0},{0x9237, 0x00, 0, 0}, + {0x9238, 0x00, 0, 0},{0x9239, 0xc2, 0, 0},{0x923a, 0x34, 0, 0}, + {0x923b, 0x20, 0, 0},{0x923c, 0x05, 0, 0},{0x923d, 0x05, 0, 0}, + {0x923e, 0x75, 0, 0},{0x923f, 0x0a, 0, 0},{0x9240, 0xee, 0, 0}, + {0x9241, 0x80, 0, 0},{0x9242, 0x36, 0, 0},{0x9243, 0x20, 0, 0}, + {0x9244, 0x07, 0, 0},{0x9245, 0x08, 0, 0},{0x9246, 0x20, 0, 0}, + {0x9247, 0x06, 0, 0},{0x9248, 0x05, 0, 0},{0x9249, 0xe4, 0, 0}, + {0x924a, 0xf5, 0, 0},{0x924b, 0x0a, 0, 0},{0x924c, 0x80, 0, 0}, + {0x924d, 0x2b, 0, 0},{0x924e, 0x20, 0, 0},{0x924f, 0x07, 0, 0}, + {0x9250, 0x08, 0, 0},{0x9251, 0x30, 0, 0},{0x9252, 0x06, 0, 0}, + {0x9253, 0x05, 0, 0},{0x9254, 0x75, 0, 0},{0x9255, 0x0a, 0, 0}, + {0x9256, 0x20, 0, 0},{0x9257, 0x80, 0, 0},{0x9258, 0x20, 0, 0}, + {0x9259, 0x30, 0, 0},{0x925a, 0x00, 0, 0},{0x925b, 0x05, 0, 0}, + {0x925c, 0x75, 0, 0},{0x925d, 0x0a, 0, 0},{0x925e, 0x01, 0, 0}, + {0x925f, 0x80, 0, 0},{0x9260, 0x18, 0, 0},{0x9261, 0xe5, 0, 0}, + {0x9262, 0x20, 0, 0},{0x9263, 0x54, 0, 0},{0x9264, 0x07, 0, 0}, + {0x9265, 0xff, 0, 0},{0x9266, 0xbf, 0, 0},{0x9267, 0x06, 0, 0}, + {0x9268, 0x0d, 0, 0},{0x9269, 0x30, 0, 0},{0x926a, 0x31, 0, 0}, + {0x926b, 0x04, 0, 0},{0x926c, 0x7f, 0, 0},{0x926d, 0x12, 0, 0}, + {0x926e, 0x80, 0, 0},{0x926f, 0x02, 0, 0},{0x9270, 0x7f, 0, 0}, + {0x9271, 0x02, 0, 0},{0x9272, 0x8f, 0, 0},{0x9273, 0x0a, 0, 0}, + {0x9274, 0x80, 0, 0},{0x9275, 0x03, 0, 0},{0x9276, 0x75, 0, 0}, + {0x9277, 0x0a, 0, 0},{0x9278, 0xfe, 0, 0},{0x9279, 0x90, 0, 0}, + {0x927a, 0x30, 0, 0},{0x927b, 0x27, 0, 0},{0x927c, 0xe5, 0, 0}, + {0x927d, 0x0a, 0, 0},{0x927e, 0xf0, 0, 0},{0x927f, 0xe5, 0, 0}, + {0x9280, 0x23, 0, 0},{0x9281, 0x54, 0, 0},{0x9282, 0xf8, 0, 0}, + {0x9283, 0xf5, 0, 0},{0x9284, 0x0a, 0, 0},{0x9285, 0xe5, 0, 0}, + {0x9286, 0x78, 0, 0},{0x9287, 0x25, 0, 0},{0x9288, 0x0a, 0, 0}, + {0x9289, 0xf5, 0, 0},{0x928a, 0x0a, 0, 0},{0x928b, 0x90, 0, 0}, + {0x928c, 0x30, 0, 0},{0x928d, 0x26, 0, 0},{0x928e, 0xe5, 0, 0}, + {0x928f, 0x0a, 0, 0},{0x9290, 0xf0, 0, 0},{0x9291, 0x22, 0, 0}, + {0x9292, 0xe5, 0, 0},{0x9293, 0x0a, 0, 0},{0x9294, 0x70, 0, 0}, + {0x9295, 0x04, 0, 0},{0x9296, 0x7e, 0, 0},{0x9297, 0x12, 0, 0}, + {0x9298, 0x7f, 0, 0},{0x9299, 0x2a, 0, 0},{0x929a, 0xe5, 0, 0}, + {0x929b, 0x0a, 0, 0},{0x929c, 0xb4, 0, 0},{0x929d, 0x01, 0, 0}, + {0x929e, 0x04, 0, 0},{0x929f, 0x7e, 0, 0},{0x92a0, 0x12, 0, 0}, + {0x92a1, 0x7f, 0, 0},{0x92a2, 0x2f, 0, 0},{0x92a3, 0xe5, 0, 0}, + {0x92a4, 0x0a, 0, 0},{0x92a5, 0xb4, 0, 0},{0x92a6, 0x02, 0, 0}, + {0x92a7, 0x04, 0, 0},{0x92a8, 0x7e, 0, 0},{0x92a9, 0x12, 0, 0}, + {0x92aa, 0x7f, 0, 0},{0x92ab, 0x34, 0, 0},{0x92ac, 0x8f, 0, 0}, + {0x92ad, 0x82, 0, 0},{0x92ae, 0x8e, 0, 0},{0x92af, 0x83, 0, 0}, + {0x92b0, 0xe4, 0, 0},{0x92b1, 0x93, 0, 0},{0x92b2, 0xf5, 0, 0}, + {0x92b3, 0x2c, 0, 0},{0x92b4, 0x74, 0, 0},{0x92b5, 0x01, 0, 0}, + {0x92b6, 0x93, 0, 0},{0x92b7, 0xf5, 0, 0},{0x92b8, 0x2d, 0, 0}, + {0x92b9, 0x74, 0, 0},{0x92ba, 0x02, 0, 0},{0x92bb, 0x93, 0, 0}, + {0x92bc, 0xf5, 0, 0},{0x92bd, 0x2e, 0, 0},{0x92be, 0x74, 0, 0}, + {0x92bf, 0x03, 0, 0},{0x92c0, 0x93, 0, 0},{0x92c1, 0xf5, 0, 0}, + {0x92c2, 0x2f, 0, 0},{0x92c3, 0x74, 0, 0},{0x92c4, 0x04, 0, 0}, + {0x92c5, 0x93, 0, 0},{0x92c6, 0xf5, 0, 0},{0x92c7, 0x30, 0, 0}, + {0x92c8, 0xe5, 0, 0},{0x92c9, 0x0a, 0, 0},{0x92ca, 0xb4, 0, 0}, + {0x92cb, 0x01, 0, 0},{0x92cc, 0x07, 0, 0},{0x92cd, 0x74, 0, 0}, + {0x92ce, 0x2c, 0, 0},{0x92cf, 0x25, 0, 0},{0x92d0, 0x78, 0, 0}, + {0x92d1, 0xf8, 0, 0},{0x92d2, 0x76, 0, 0},{0x92d3, 0x40, 0, 0}, + {0x92d4, 0xe5, 0, 0},{0x92d5, 0x0a, 0, 0},{0x92d6, 0xb4, 0, 0}, + {0x92d7, 0x02, 0, 0},{0x92d8, 0x07, 0, 0},{0x92d9, 0x74, 0, 0}, + {0x92da, 0x2c, 0, 0},{0x92db, 0x25, 0, 0},{0x92dc, 0x78, 0, 0}, + {0x92dd, 0xf8, 0, 0},{0x92de, 0x76, 0, 0},{0x92df, 0x80, 0, 0}, + {0x92e0, 0x22, 0, 0},{0x92e1, 0xc0, 0, 0},{0x92e2, 0xe0, 0, 0}, + {0x92e3, 0xc0, 0, 0},{0x92e4, 0x83, 0, 0},{0x92e5, 0xc0, 0, 0}, + {0x92e6, 0x82, 0, 0},{0x92e7, 0xc0, 0, 0},{0x92e8, 0xd0, 0, 0}, + {0x92e9, 0x90, 0, 0},{0x92ea, 0x3f, 0, 0},{0x92eb, 0x0d, 0, 0}, + {0x92ec, 0xe0, 0, 0},{0x92ed, 0xf5, 0, 0},{0x92ee, 0x09, 0, 0}, + {0x92ef, 0xe5, 0, 0},{0x92f0, 0x09, 0, 0},{0x92f1, 0x30, 0, 0}, + {0x92f2, 0xe0, 0, 0},{0x92f3, 0x2e, 0, 0},{0x92f4, 0xe5, 0, 0}, + {0x92f5, 0x79, 0, 0},{0x92f6, 0xb4, 0, 0},{0x92f7, 0x01, 0, 0}, + {0x92f8, 0x09, 0, 0},{0x92f9, 0x90, 0, 0},{0x92fa, 0x3a, 0, 0}, + {0x92fb, 0x00, 0, 0},{0x92fc, 0xe0, 0, 0},{0x92fd, 0xf5, 0, 0}, + {0x92fe, 0x77, 0, 0},{0x92ff, 0x44, 0, 0},{0x9300, 0x01, 0, 0}, + {0x9301, 0xf0, 0, 0},{0x9302, 0xe5, 0, 0},{0x9303, 0x79, 0, 0}, + {0x9304, 0xb4, 0, 0},{0x9305, 0x03, 0, 0},{0x9306, 0x09, 0, 0}, + {0x9307, 0x90, 0, 0},{0x9308, 0x3a, 0, 0},{0x9309, 0x00, 0, 0}, + {0x930a, 0xe0, 0, 0},{0x930b, 0xf5, 0, 0},{0x930c, 0x77, 0, 0}, + {0x930d, 0x54, 0, 0},{0x930e, 0xfe, 0, 0},{0x930f, 0xf0, 0, 0}, + {0x9310, 0xe5, 0, 0},{0x9311, 0x79, 0, 0},{0x9312, 0xb4, 0, 0}, + {0x9313, 0x03, 0, 0},{0x9314, 0x05, 0, 0},{0x9315, 0x75, 0, 0}, + {0x9316, 0x79, 0, 0},{0x9317, 0x00, 0, 0},{0x9318, 0x80, 0, 0}, + {0x9319, 0x02, 0, 0},{0x931a, 0x05, 0, 0},{0x931b, 0x79, 0, 0}, + {0x931c, 0x90, 0, 0},{0x931d, 0x3f, 0, 0},{0x931e, 0x0d, 0, 0}, + {0x931f, 0x74, 0, 0},{0x9320, 0x01, 0, 0},{0x9321, 0xf0, 0, 0}, + {0x9322, 0xd0, 0, 0},{0x9323, 0xd0, 0, 0},{0x9324, 0xd0, 0, 0}, + {0x9325, 0x82, 0, 0},{0x9326, 0xd0, 0, 0},{0x9327, 0x83, 0, 0}, + {0x9328, 0xd0, 0, 0},{0x9329, 0xe0, 0, 0},{0x932a, 0x32, 0, 0}, + {0x932b, 0x90, 0, 0},{0x932c, 0x50, 0, 0},{0x932d, 0x27, 0, 0}, + {0x932e, 0xe0, 0, 0},{0x932f, 0x44, 0, 0},{0x9330, 0x01, 0, 0}, + {0x9331, 0xf0, 0, 0},{0x9332, 0x90, 0, 0},{0x9333, 0x50, 0, 0}, + {0x9334, 0x34, 0, 0},{0x9335, 0x74, 0, 0},{0x9336, 0x80, 0, 0}, + {0x9337, 0xf0, 0, 0},{0x9338, 0xa3, 0, 0},{0x9339, 0x74, 0, 0}, + {0x933a, 0x2a, 0, 0},{0x933b, 0xf0, 0, 0},{0x933c, 0xa3, 0, 0}, + {0x933d, 0x74, 0, 0},{0x933e, 0x14, 0, 0},{0x933f, 0xf0, 0, 0}, + {0x9340, 0x90, 0, 0},{0x9341, 0x50, 0, 0},{0x9342, 0x30, 0, 0}, + {0x9343, 0xe4, 0, 0},{0x9344, 0xf0, 0, 0},{0x9345, 0xa3, 0, 0}, + {0x9346, 0x74, 0, 0},{0x9347, 0x02, 0, 0},{0x9348, 0xf0, 0, 0}, + {0x9349, 0xa3, 0, 0},{0x934a, 0xe4, 0, 0},{0x934b, 0xf0, 0, 0}, + {0x934c, 0xa3, 0, 0},{0x934d, 0x74, 0, 0},{0x934e, 0x80, 0, 0}, + {0x934f, 0xf0, 0, 0},{0x9350, 0xe4, 0, 0},{0x9351, 0xf5, 0, 0}, + {0x9352, 0x0a, 0, 0},{0x9353, 0x12, 0, 0},{0x9354, 0x10, 0, 0}, + {0x9355, 0xb5, 0, 0},{0x9356, 0x75, 0, 0},{0x9357, 0x78, 0, 0}, + {0x9358, 0x02, 0, 0},{0x9359, 0x75, 0, 0},{0x935a, 0x0a, 0, 0}, + {0x935b, 0x01, 0, 0},{0x935c, 0x12, 0, 0},{0x935d, 0x12, 0, 0}, + {0x935e, 0x92, 0, 0},{0x935f, 0xd2, 0, 0},{0x9360, 0x18, 0, 0}, + {0x9361, 0xd2, 0, 0},{0x9362, 0x19, 0, 0},{0x9363, 0xc2, 0, 0}, + {0x9364, 0x3a, 0, 0},{0x9365, 0xc2, 0, 0},{0x9366, 0x39, 0, 0}, + {0x9367, 0xd2, 0, 0},{0x9368, 0x1a, 0, 0},{0x9369, 0xd2, 0, 0}, + {0x936a, 0x36, 0, 0},{0x936b, 0xd2, 0, 0},{0x936c, 0x30, 0, 0}, + {0x936d, 0xc2, 0, 0},{0x936e, 0x35, 0, 0},{0x936f, 0xc2, 0, 0}, + {0x9370, 0x3b, 0, 0},{0x9371, 0x22, 0, 0},{0x9372, 0x85, 0, 0}, + {0x9373, 0x13, 0, 0},{0x9374, 0x14, 0, 0},{0x9375, 0x7f, 0, 0}, + {0x9376, 0x08, 0, 0},{0x9377, 0xe5, 0, 0},{0x9378, 0x14, 0, 0}, + {0x9379, 0x30, 0, 0},{0x937a, 0xe7, 0, 0},{0x937b, 0x04, 0, 0}, + {0x937c, 0xd2, 0, 0},{0x937d, 0x29, 0, 0},{0x937e, 0x80, 0, 0}, + {0x937f, 0x02, 0, 0},{0x9380, 0xc2, 0, 0},{0x9381, 0x29, 0, 0}, + {0x9382, 0x12, 0, 0},{0x9383, 0x01, 0, 0},{0x9384, 0x6c, 0, 0}, + {0x9385, 0x75, 0, 0},{0x9386, 0x51, 0, 0},{0x9387, 0x0a, 0, 0}, + {0x9388, 0xae, 0, 0},{0x9389, 0x51, 0, 0},{0x938a, 0x15, 0, 0}, + {0x938b, 0x51, 0, 0},{0x938c, 0xee, 0, 0},{0x938d, 0x70, 0, 0}, + {0x938e, 0xf9, 0, 0},{0x938f, 0xe5, 0, 0},{0x9390, 0x14, 0, 0}, + {0x9391, 0x25, 0, 0},{0x9392, 0xe0, 0, 0},{0x9393, 0xf5, 0, 0}, + {0x9394, 0x14, 0, 0},{0x9395, 0xd2, 0, 0},{0x9396, 0x28, 0, 0}, + {0x9397, 0x12, 0, 0},{0x9398, 0x01, 0, 0},{0x9399, 0x6c, 0, 0}, + {0x939a, 0x75, 0, 0},{0x939b, 0x51, 0, 0},{0x939c, 0x0a, 0, 0}, + {0x939d, 0xae, 0, 0},{0x939e, 0x51, 0, 0},{0x939f, 0x15, 0, 0}, + {0x93a0, 0x51, 0, 0},{0x93a1, 0xee, 0, 0},{0x93a2, 0x70, 0, 0}, + {0x93a3, 0xf9, 0, 0},{0x93a4, 0xc2, 0, 0},{0x93a5, 0x28, 0, 0}, + {0x93a6, 0x12, 0, 0},{0x93a7, 0x01, 0, 0},{0x93a8, 0x6c, 0, 0}, + {0x93a9, 0x75, 0, 0},{0x93aa, 0x51, 0, 0},{0x93ab, 0x05, 0, 0}, + {0x93ac, 0xae, 0, 0},{0x93ad, 0x51, 0, 0},{0x93ae, 0x15, 0, 0}, + {0x93af, 0x51, 0, 0},{0x93b0, 0xee, 0, 0},{0x93b1, 0x70, 0, 0}, + {0x93b2, 0xf9, 0, 0},{0x93b3, 0xdf, 0, 0},{0x93b4, 0xc2, 0, 0}, + {0x93b5, 0x22, 0, 0},{0x93b6, 0xc2, 0, 0},{0x93b7, 0xaf, 0, 0}, + {0x93b8, 0x90, 0, 0},{0x93b9, 0x30, 0, 0},{0x93ba, 0x27, 0, 0}, + {0x93bb, 0x74, 0, 0},{0x93bc, 0xfa, 0, 0},{0x93bd, 0xf0, 0, 0}, + {0x93be, 0x12, 0, 0},{0x93bf, 0x0e, 0, 0},{0x93c0, 0x71, 0, 0}, + {0x93c1, 0x12, 0, 0},{0x93c2, 0x14, 0, 0},{0x93c3, 0x84, 0, 0}, + {0x93c4, 0xe4, 0, 0},{0x93c5, 0xf5, 0, 0},{0x93c6, 0x33, 0, 0}, + {0x93c7, 0xd2, 0, 0},{0x93c8, 0xaf, 0, 0},{0x93c9, 0x12, 0, 0}, + {0x93ca, 0x0c, 0, 0},{0x93cb, 0x69, 0, 0},{0x93cc, 0x30, 0, 0}, + {0x93cd, 0x30, 0, 0},{0x93ce, 0x03, 0, 0},{0x93cf, 0x12, 0, 0}, + {0x93d0, 0x06, 0, 0},{0x93d1, 0xdf, 0, 0},{0x93d2, 0x30, 0, 0}, + {0x93d3, 0x34, 0, 0},{0x93d4, 0x03, 0, 0},{0x93d5, 0x12, 0, 0}, + {0x93d6, 0x12, 0, 0},{0x93d7, 0x39, 0, 0},{0x93d8, 0x30, 0, 0}, + {0x93d9, 0x3b, 0, 0},{0x93da, 0xee, 0, 0},{0x93db, 0xc2, 0, 0}, + {0x93dc, 0x3b, 0, 0},{0x93dd, 0xd2, 0, 0},{0x93de, 0x35, 0, 0}, + {0x93df, 0x30, 0, 0},{0x93e0, 0x00, 0, 0},{0x93e1, 0x05, 0, 0}, + {0x93e2, 0x12, 0, 0},{0x93e3, 0x14, 0, 0},{0x93e4, 0x57, 0, 0}, + {0x93e5, 0x80, 0, 0},{0x93e6, 0x09, 0, 0},{0x93e7, 0x20, 0, 0}, + {0x93e8, 0x07, 0, 0},{0x93e9, 0x06, 0, 0},{0x93ea, 0x30, 0, 0}, + {0x93eb, 0x06, 0, 0},{0x93ec, 0x03, 0, 0},{0x93ed, 0x12, 0, 0}, + {0x93ee, 0x0d, 0, 0},{0x93ef, 0x77, 0, 0},{0x93f0, 0xc2, 0, 0}, + {0x93f1, 0x35, 0, 0},{0x93f2, 0x80, 0, 0},{0x93f3, 0xd5, 0, 0}, + {0x93f4, 0x12, 0, 0},{0x93f5, 0x10, 0, 0},{0x93f6, 0x3d, 0, 0}, + {0x93f7, 0xd2, 0, 0},{0x93f8, 0x3c, 0, 0},{0x93f9, 0x12, 0, 0}, + {0x93fa, 0x0f, 0, 0},{0x93fb, 0xb0, 0, 0},{0x93fc, 0xd2, 0, 0}, + {0x93fd, 0x3c, 0, 0},{0x93fe, 0x12, 0, 0},{0x93ff, 0x11, 0, 0}, + {0x9400, 0x1a, 0, 0},{0x9401, 0xe5, 0, 0},{0x9402, 0x7b, 0, 0}, + {0x9403, 0xd3, 0, 0},{0x9404, 0x95, 0, 0},{0x9405, 0x7c, 0, 0}, + {0x9406, 0x40, 0, 0},{0x9407, 0x03, 0, 0},{0x9408, 0xd3, 0, 0}, + {0x9409, 0x80, 0, 0},{0x940a, 0x01, 0, 0},{0x940b, 0xc3, 0, 0}, + {0x940c, 0x50, 0, 0},{0x940d, 0x14, 0, 0},{0x940e, 0x20, 0, 0}, + {0x940f, 0x37, 0, 0},{0x9410, 0x0e, 0, 0},{0x9411, 0xe5, 0, 0}, + {0x9412, 0x24, 0, 0},{0x9413, 0x54, 0, 0},{0x9414, 0x1f, 0, 0}, + {0x9415, 0xff, 0, 0},{0x9416, 0xbf, 0, 0},{0x9417, 0x1f, 0, 0}, + {0x9418, 0x06, 0, 0},{0x9419, 0x30, 0, 0},{0x941a, 0x25, 0, 0}, + {0x941b, 0x03, 0, 0},{0x941c, 0x20, 0, 0},{0x941d, 0x26, 0, 0}, + {0x941e, 0x03, 0, 0},{0x941f, 0x02, 0, 0},{0x9420, 0x15, 0, 0}, + {0x9421, 0x2c, 0, 0},{0x9422, 0x12, 0, 0},{0x9423, 0x02, 0, 0}, + {0x9424, 0xb6, 0, 0},{0x9425, 0x22, 0, 0},{0x9426, 0xe5, 0, 0}, + {0x9427, 0x7c, 0, 0},{0x9428, 0x70, 0, 0},{0x9429, 0x19, 0, 0}, + {0x942a, 0x12, 0, 0},{0x942b, 0x15, 0, 0},{0x942c, 0x04, 0, 0}, + {0x942d, 0xc2, 0, 0},{0x942e, 0x3c, 0, 0},{0x942f, 0x12, 0, 0}, + {0x9430, 0x0f, 0, 0},{0x9431, 0xb0, 0, 0},{0x9432, 0xc2, 0, 0}, + {0x9433, 0x3c, 0, 0},{0x9434, 0x12, 0, 0},{0x9435, 0x11, 0, 0}, + {0x9436, 0x1a, 0, 0},{0x9437, 0xc2, 0, 0},{0x9438, 0x03, 0, 0}, + {0x9439, 0x12, 0, 0},{0x943a, 0x15, 0, 0},{0x943b, 0x2c, 0, 0}, + {0x943c, 0xd2, 0, 0},{0x943d, 0x02, 0, 0},{0x943e, 0xd2, 0, 0}, + {0x943f, 0x01, 0, 0},{0x9440, 0xd2, 0, 0},{0x9441, 0x00, 0, 0}, + {0x9442, 0x22, 0, 0},{0x9443, 0x30, 0, 0},{0x9444, 0x03, 0, 0}, + {0x9445, 0x08, 0, 0},{0x9446, 0xc2, 0, 0},{0x9447, 0x03, 0, 0}, + {0x9448, 0xc2, 0, 0},{0x9449, 0x04, 0, 0},{0x944a, 0x12, 0, 0}, + {0x944b, 0x02, 0, 0},{0x944c, 0xa5, 0, 0},{0x944d, 0x22, 0, 0}, + {0x944e, 0xe4, 0, 0},{0x944f, 0xf5, 0, 0},{0x9450, 0x0c, 0, 0}, + {0x9451, 0x12, 0, 0},{0x9452, 0x0f, 0, 0},{0x9453, 0x11, 0, 0}, + {0x9454, 0xd2, 0, 0},{0x9455, 0x03, 0, 0},{0x9456, 0x22, 0, 0}, + {0x9457, 0xe5, 0, 0},{0x9458, 0x20, 0, 0},{0x9459, 0x54, 0, 0}, + {0x945a, 0x07, 0, 0},{0x945b, 0xff, 0, 0},{0x945c, 0xbf, 0, 0}, + {0x945d, 0x01, 0, 0},{0x945e, 0x03, 0, 0},{0x945f, 0x02, 0, 0}, + {0x9460, 0x14, 0, 0},{0x9461, 0x26, 0, 0},{0x9462, 0xe5, 0, 0}, + {0x9463, 0x20, 0, 0},{0x9464, 0x54, 0, 0},{0x9465, 0x07, 0, 0}, + {0x9466, 0xff, 0, 0},{0x9467, 0xbf, 0, 0},{0x9468, 0x07, 0, 0}, + {0x9469, 0x03, 0, 0},{0x946a, 0x02, 0, 0},{0x946b, 0x14, 0, 0}, + {0x946c, 0xaa, 0, 0},{0x946d, 0xe5, 0, 0},{0x946e, 0x20, 0, 0}, + {0x946f, 0x54, 0, 0},{0x9470, 0x07, 0, 0},{0x9471, 0xff, 0, 0}, + {0x9472, 0xbf, 0, 0},{0x9473, 0x03, 0, 0},{0x9474, 0x03, 0, 0}, + {0x9475, 0x02, 0, 0},{0x9476, 0x13, 0, 0},{0x9477, 0xf4, 0, 0}, + {0x9478, 0xe5, 0, 0},{0x9479, 0x20, 0, 0},{0x947a, 0x54, 0, 0}, + {0x947b, 0x07, 0, 0},{0x947c, 0xff, 0, 0},{0x947d, 0xbf, 0, 0}, + {0x947e, 0x05, 0, 0},{0x947f, 0x03, 0, 0},{0x9480, 0x12, 0, 0}, + {0x9481, 0x15, 0, 0},{0x9482, 0x6a, 0, 0},{0x9483, 0x22, 0, 0}, + {0x9484, 0x12, 0, 0},{0x9485, 0x13, 0, 0},{0x9486, 0x2b, 0, 0}, + {0x9487, 0x12, 0, 0},{0x9488, 0x15, 0, 0},{0x9489, 0x78, 0, 0}, + {0x948a, 0x50, 0, 0},{0x948b, 0x04, 0, 0},{0x948c, 0xd2, 0, 0}, + {0x948d, 0x05, 0, 0},{0x948e, 0x80, 0, 0},{0x948f, 0x02, 0, 0}, + {0x9490, 0xc2, 0, 0},{0x9491, 0x05, 0, 0},{0x9492, 0x12, 0, 0}, + {0x9493, 0x02, 0, 0},{0x9494, 0x40, 0, 0},{0x9495, 0xc2, 0, 0}, + {0x9496, 0x37, 0, 0},{0x9497, 0xc2, 0, 0},{0x9498, 0x31, 0, 0}, + {0x9499, 0xd2, 0, 0},{0x949a, 0x34, 0, 0},{0x949b, 0x12, 0, 0}, + {0x949c, 0x02, 0, 0},{0x949d, 0x85, 0, 0},{0x949e, 0xb5, 0, 0}, + {0x949f, 0x07, 0, 0},{0x94a0, 0x03, 0, 0},{0x94a1, 0xd3, 0, 0}, + {0x94a2, 0x80, 0, 0},{0x94a3, 0x01, 0, 0},{0x94a4, 0xc3, 0, 0}, + {0x94a5, 0x40, 0, 0},{0x94a6, 0x02, 0, 0},{0x94a7, 0xc2, 0, 0}, + {0x94a8, 0x05, 0, 0},{0x94a9, 0x22, 0, 0},{0x94aa, 0x12, 0, 0}, + {0x94ab, 0x10, 0, 0},{0x94ac, 0x3d, 0, 0},{0x94ad, 0xd2, 0, 0}, + {0x94ae, 0x3c, 0, 0},{0x94af, 0x12, 0, 0},{0x94b0, 0x0f, 0, 0}, + {0x94b1, 0xb0, 0, 0},{0x94b2, 0xd2, 0, 0},{0x94b3, 0x3c, 0, 0}, + {0x94b4, 0x12, 0, 0},{0x94b5, 0x11, 0, 0},{0x94b6, 0x1a, 0, 0}, + {0x94b7, 0x12, 0, 0},{0x94b8, 0x15, 0, 0},{0x94b9, 0x2c, 0, 0}, + {0x94ba, 0xe5, 0, 0},{0x94bb, 0x32, 0, 0},{0x94bc, 0xd3, 0, 0}, + {0x94bd, 0x95, 0, 0},{0x94be, 0x7c, 0, 0},{0x94bf, 0x40, 0, 0}, + {0x94c0, 0x05, 0, 0},{0x94c1, 0xe4, 0, 0},{0x94c2, 0x95, 0, 0}, + {0x94c3, 0x7c, 0, 0},{0x94c4, 0x40, 0, 0},{0x94c5, 0x06, 0, 0}, + {0x94c6, 0xc2, 0, 0},{0x94c7, 0x02, 0, 0},{0x94c8, 0xd2, 0, 0}, + {0x94c9, 0x01, 0, 0},{0x94ca, 0xd2, 0, 0},{0x94cb, 0x00, 0, 0}, + {0x94cc, 0x22, 0, 0},{0x94cd, 0xe4, 0, 0},{0x94ce, 0xff, 0, 0}, + {0x94cf, 0xfe, 0, 0},{0x94d0, 0xc3, 0, 0},{0x94d1, 0xef, 0, 0}, + {0x94d2, 0x95, 0, 0},{0x94d3, 0x11, 0, 0},{0x94d4, 0xee, 0, 0}, + {0x94d5, 0x95, 0, 0},{0x94d6, 0x10, 0, 0},{0x94d7, 0x50, 0, 0}, + {0x94d8, 0x15, 0, 0},{0x94d9, 0x7d, 0, 0},{0x94da, 0x8a, 0, 0}, + {0x94db, 0x7c, 0, 0},{0x94dc, 0x02, 0, 0},{0x94dd, 0xed, 0, 0}, + {0x94de, 0x1d, 0, 0},{0x94df, 0xaa, 0, 0},{0x94e0, 0x04, 0, 0}, + {0x94e1, 0x70, 0, 0},{0x94e2, 0x01, 0, 0},{0x94e3, 0x1c, 0, 0}, + {0x94e4, 0x4a, 0, 0},{0x94e5, 0x70, 0, 0},{0x94e6, 0xf6, 0, 0}, + {0x94e7, 0x0f, 0, 0},{0x94e8, 0xbf, 0, 0},{0x94e9, 0x00, 0, 0}, + {0x94ea, 0x01, 0, 0},{0x94eb, 0x0e, 0, 0},{0x94ec, 0x80, 0, 0}, + {0x94ed, 0xe2, 0, 0},{0x94ee, 0x22, 0, 0},{0x94ef, 0x75, 0, 0}, + {0x94f0, 0x48, 0, 0},{0x94f1, 0x11, 0, 0},{0x94f2, 0x75, 0, 0}, + {0x94f3, 0x49, 0, 0},{0x94f4, 0xe0, 0, 0},{0x94f5, 0x90, 0, 0}, + {0x94f6, 0x11, 0, 0},{0x94f7, 0xde, 0, 0},{0x94f8, 0xe4, 0, 0}, + {0x94f9, 0x93, 0, 0},{0x94fa, 0xf5, 0, 0},{0x94fb, 0x7b, 0, 0}, + {0x94fc, 0xa3, 0, 0},{0x94fd, 0xe4, 0, 0},{0x94fe, 0x93, 0, 0}, + {0x94ff, 0xf5, 0, 0},{0x9500, 0x32, 0, 0},{0x9501, 0xc2, 0, 0}, + {0x9502, 0x38, 0, 0},{0x9503, 0x22, 0, 0},{0x9504, 0xe4, 0, 0}, + {0x9505, 0xff, 0, 0},{0x9506, 0xef, 0, 0},{0x9507, 0x25, 0, 0}, + {0x9508, 0xe0, 0, 0},{0x9509, 0x24, 0, 0},{0x950a, 0x56, 0, 0}, + {0x950b, 0xf8, 0, 0},{0x950c, 0xe4, 0, 0},{0x950d, 0xf6, 0, 0}, + {0x950e, 0x08, 0, 0},{0x950f, 0xf6, 0, 0},{0x9510, 0x0f, 0, 0}, + {0x9511, 0xbf, 0, 0},{0x9512, 0x07, 0, 0},{0x9513, 0xf2, 0, 0}, + {0x9514, 0x53, 0, 0},{0x9515, 0x24, 0, 0},{0x9516, 0x80, 0, 0}, + {0x9517, 0x22, 0, 0},{0x9518, 0xc2, 0, 0},{0x9519, 0x03, 0, 0}, + {0x951a, 0xd2, 0, 0},{0x951b, 0x04, 0, 0},{0x951c, 0x12, 0, 0}, + {0x951d, 0x02, 0, 0},{0x951e, 0xa5, 0, 0},{0x951f, 0xc2, 0, 0}, + {0x9520, 0x3c, 0, 0},{0x9521, 0x12, 0, 0},{0x9522, 0x0f, 0, 0}, + {0x9523, 0xb0, 0, 0},{0x9524, 0xc2, 0, 0},{0x9525, 0x3c, 0, 0}, + {0x9526, 0x12, 0, 0},{0x9527, 0x11, 0, 0},{0x9528, 0x1a, 0, 0}, + {0x9529, 0xd2, 0, 0},{0x952a, 0x34, 0, 0},{0x952b, 0x22, 0, 0}, + {0x952c, 0xe5, 0, 0},{0x952d, 0x7c, 0, 0},{0x952e, 0xc3, 0, 0}, + {0x952f, 0x95, 0, 0},{0x9530, 0x7b, 0, 0},{0x9531, 0x40, 0, 0}, + {0x9532, 0x01, 0, 0},{0x9533, 0x22, 0, 0},{0x9534, 0xe5, 0, 0}, + {0x9535, 0x7c, 0, 0},{0x9536, 0x04, 0, 0},{0x9537, 0xf5, 0, 0}, + {0x9538, 0x0c, 0, 0},{0x9539, 0x12, 0, 0},{0x953a, 0x0f, 0, 0}, + {0x953b, 0x11, 0, 0},{0x953c, 0x22, 0, 0},{0x953d, 0xe5, 0, 0}, + {0x953e, 0x7c, 0, 0},{0x953f, 0x70, 0, 0},{0x9540, 0x02, 0, 0}, + {0x9541, 0xc3, 0, 0},{0x9542, 0x22, 0, 0},{0x9543, 0xe5, 0, 0}, + {0x9544, 0x7c, 0, 0},{0x9545, 0x14, 0, 0},{0x9546, 0xf5, 0, 0}, + {0x9547, 0x0c, 0, 0},{0x9548, 0x12, 0, 0},{0x9549, 0x0f, 0, 0}, + {0x954a, 0x11, 0, 0},{0x954b, 0x22, 0, 0},{0x954c, 0xe5, 0, 0}, + {0x954d, 0x7d, 0, 0},{0x954e, 0xb4, 0, 0},{0x954f, 0x01, 0, 0}, + {0x9550, 0x09, 0, 0},{0x9551, 0x12, 0, 0},{0x9552, 0x14, 0, 0}, + {0x9553, 0xef, 0, 0},{0x9554, 0xe4, 0, 0},{0x9555, 0xf5, 0, 0}, + {0x9556, 0x0c, 0, 0},{0x9557, 0x12, 0, 0},{0x9558, 0x0f, 0, 0}, + {0x9559, 0x11, 0, 0},{0x955a, 0x22, 0, 0},{0x955b, 0xe5, 0, 0}, + {0x955c, 0x7d, 0, 0},{0x955d, 0x24, 0, 0},{0x955e, 0xfe, 0, 0}, + {0x955f, 0x60, 0, 0},{0x9560, 0x06, 0, 0},{0x9561, 0x04, 0, 0}, + {0x9562, 0x70, 0, 0},{0x9563, 0x05, 0, 0},{0x9564, 0xd2, 0, 0}, + {0x9565, 0x37, 0, 0},{0x9566, 0x22, 0, 0},{0x9567, 0xc2, 0, 0}, + {0x9568, 0x37, 0, 0},{0x9569, 0x22, 0, 0},{0x956a, 0xe5, 0, 0}, + {0x956b, 0x31, 0, 0},{0x956c, 0xd3, 0, 0},{0x956d, 0x94, 0, 0}, + {0x956e, 0x00, 0, 0},{0x956f, 0x40, 0, 0},{0x9570, 0x03, 0, 0}, + {0x9571, 0x15, 0, 0},{0x9572, 0x31, 0, 0},{0x9573, 0x22, 0, 0}, + {0x9574, 0x12, 0, 0},{0x9575, 0x15, 0, 0},{0x9576, 0x18, 0, 0}, + {0x9577, 0x22, 0, 0},{0x9578, 0x12, 0, 0},{0x9579, 0x14, 0, 0}, + {0x957a, 0xef, 0, 0},{0x957b, 0xe4, 0, 0},{0x957c, 0xf5, 0, 0}, + {0x957d, 0x0c, 0, 0},{0x957e, 0x12, 0, 0},{0x957f, 0x0f, 0, 0}, + {0x9580, 0x11, 0, 0},{0x9581, 0x22, 0, 0},{0x3024, 0x00, 0, 0}, + {0x3025, 0x00, 0, 0},{0x5082, 0x00, 0, 0},{0x5083, 0x00, 0, 0}, + {0x5084, 0x00, 0, 0},{0x5085, 0x00, 0, 0},{0x3026, 0x00, 0, 0}, + {0x3027, 0xFF, 0, 0},{0x3000, 0x00, 0, 0}, +}; + static struct reg_value ov5642_setting_15fps_QCIF_176_144[] = { {0x3103, 0x93, 0, 0}, {0x3008, 0x82, 0, 0}, {0x3017, 0x7f, 0, 0}, {0x3018, 0xfc, 0, 0}, {0x3810, 0xc2, 0, 0}, {0x3615, 0xf0, 0, 0}, @@ -3153,6 +5039,51 @@ static s32 ov5642_read_reg(u16 reg, u8 *val) return u8RdVal; } +static int ov5642_set_idle_mode(void) { + register u16 RegAddr = 0; + u8 ReadVal = 0; + u8 WriteVal = 0; + int retval = 0; + int lc = 0; + + ReadVal = -1; + RegAddr = REG_STA_FOCUS; + retval = ov5642_read_reg(RegAddr, &ReadVal); + if (retval < 0) { + pr_err("%s, read reg 0x%x failed\n", __FUNCTION__, RegAddr); + } + + for (lc = 0; (lc < 100) && (ReadVal != S_IDLE); ++lc) { + WriteVal = CMD_IDLE_MODE; + RegAddr = REG_CMD_MAIN; + retval = ov5642_write_reg(RegAddr, WriteVal); + if (retval < 0) { + pr_err("%s, write reg 0x%x failed\n", __FUNCTION__, RegAddr); + } + + mdelay(1); + + ReadVal = -1; + RegAddr = REG_STA_FOCUS; + retval = ov5642_read_reg(RegAddr, &ReadVal); + if (retval < 0) { + pr_err("%s, read reg 0x%x failed\n", __FUNCTION__, RegAddr); + } + } + + if (ReadVal != S_IDLE) + retval = -1; + else + retval = 0; + return retval; +} + +static int ov5642_config_auto_focus(void){ + ov5642_write_reg(REG_CMD_TAG, 0x01); + ov5642_write_reg(REG_CMD_MAIN, 0x10); + return 0; +} + static int ov5642_set_rot_mode(struct reg_value *rot_mode) { s32 i = 0; @@ -3194,6 +5125,29 @@ static int ov5642_set_rot_mode(struct reg_value *rot_mode) err: return retval; } + +static int ov5642_auto_focus_start(void) { + register u16 RegAddr = 0; + u8 RegVal = 0; + int retval = 0; + + retval = ov5642_set_idle_mode(); + ov5642_config_auto_focus(); + + if (retval > -1) { + RegVal = CMD_SINGLE_FOCUS_MODE; + RegAddr = REG_CMD_MAIN; + retval = ov5642_write_reg(RegAddr, RegVal); + if (retval < 0) { + pr_err("%s, write reg 0x%x failed\n", __FUNCTION__, RegAddr); + } + } else { + pr_err("Could not get camera into idle mode. Abandoning focus attempt"); + } + + return retval; +} + static int ov5642_init_mode(enum ov5642_frame_rate frame_rate, enum ov5642_mode mode); static int ov5642_write_snapshot_para(enum ov5642_frame_rate frame_rate, @@ -3737,6 +5691,12 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) switch (vc->id) { case V4L2_CID_BRIGHTNESS: break; + case V4L2_CID_AUTO_FOCUS_START: + retval = ov5642_auto_focus_start(); + break; + case V4L2_CID_AUTO_FOCUS_STOP: + retval = ov5642_set_idle_mode(); + break; case V4L2_CID_CONTRAST: break; case V4L2_CID_SATURATION: @@ -3815,6 +5775,66 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) return retval; } +static int ioctl_send_command(struct v4l2_int_device *s, struct v4l2_send_command_control *vc) { + int ret = -1; + int retval1,retval2; + u8 loca_val=0; + + ret = ov5642_set_idle_mode(); + if (0 != ret) + pr_err("error %d setting idle mode\n", ret); + ov5642_config_auto_focus(); + switch (vc->id) { + case 101: //step to near + pr_debug("Stepping to near object\n"); + retval1=ov5642_write_reg(REG_CMD_TAG, 0x01); + retval2=ov5642_write_reg(REG_CMD_MAIN, 0x05); + if(retval1 == 0 && retval2 == 0) + ret = 0; + break; + case 102: //step to far + pr_debug("Stepping to far object\n"); + retval1=ov5642_write_reg(REG_CMD_TAG, 0x02); + retval2=ov5642_write_reg(REG_CMD_MAIN, 0x05); + if(retval1 == 0 && retval2 == 0) + ret = 0; + break; + + case 103: //step to furthest + pr_debug("Stepping to furthest object\n"); + retval1=ov5642_write_reg(REG_CMD_TAG, 0x03); + retval2=ov5642_write_reg(REG_CMD_MAIN, 0x05); + if(retval1 == 0 && retval2 == 0) + ret = 0; + break; + + case 104: //step to nearest + pr_debug("Stepping to nearest object\n"); + retval1=ov5642_write_reg(REG_CMD_TAG, 0x04); + retval2=ov5642_write_reg(REG_CMD_MAIN, 0x05); + if(retval1 == 0 && retval2 == 0) + ret = 0; + break; + + + case 105: //step to specified position + pr_debug("Stepping to position: %d\n", vc->value0); + if(vc->value0 < 0 || vc->value0 > 255) + return ret; + loca_val = vc->value0; + retval1=ov5642_write_reg(REG_CMD_TAG, 0x10); + retval2=ov5642_write_reg(REG_CMD_PARA0, loca_val); + ret=ov5642_write_reg(REG_CMD_MAIN, 0x05); + if(retval1 != 0 && retval2 != 0 && ret != 0) + ret = -1; + break; + default: + break; + } + + return ret; +} + /*! * ioctl_enum_framesizes - V4L2 sensor interface handler for * VIDIOC_ENUM_FRAMESIZES ioctl @@ -4055,6 +6075,8 @@ static struct v4l2_int_ioctl_desc ov5642_ioctl_desc[] = { (v4l2_int_ioctl_func *)ioctl_enum_frameintervals }, { vidioc_int_g_chip_ident_num, (v4l2_int_ioctl_func *)ioctl_g_chip_ident }, + {vidioc_int_send_command_num, + (v4l2_int_ioctl_func *) ioctl_send_command}, }; static struct v4l2_int_slave ov5642_slave = { @@ -4190,6 +6212,16 @@ static int ov5642_probe(struct i2c_client *client, ov5642_standby(1); ov5642_int_device.priv = &ov5642_data; + + pr_info("Upload Auto-focus firmware"); + firmware_regs = ov5642_af_firmware; + for (i = 0; i < ARRAY_SIZE(ov5642_af_firmware); ++i, ++firmware_regs) { + retval = ov5642_write_reg(firmware_regs->u16RegAddr, + firmware_regs->u8Val); + if (retval < 0) + break; + } + retval = v4l2_int_device_register(&ov5642_int_device); clk_disable_unprepare(ov5642_data.sensor_clk); diff --git a/drivers/media/platform/mxc/capture/v4l2-int-device.h b/drivers/media/platform/mxc/capture/v4l2-int-device.h index 810d87f28794a3..0c284752623435 100644 --- a/drivers/media/platform/mxc/capture/v4l2-int-device.h +++ b/drivers/media/platform/mxc/capture/v4l2-int-device.h @@ -188,6 +188,7 @@ enum v4l2_int_ioctl_num { vidioc_int_querystd_num, vidioc_int_s_std_num, vidioc_int_s_video_routing_num, + vidioc_int_send_command_num, /* * @@ -292,6 +293,7 @@ V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *); V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *); V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *); +V4L2_INT_WRAPPER_1(send_command, struct v4l2_send_command_control, *); V4L2_INT_WRAPPER_0(dev_init); V4L2_INT_WRAPPER_0(dev_exit); diff --git a/drivers/staging/media/omap24xx/v4l2-int-device.h b/drivers/staging/media/omap24xx/v4l2-int-device.h index 0286c95814ffa4..de5b9a62144155 100644 --- a/drivers/staging/media/omap24xx/v4l2-int-device.h +++ b/drivers/staging/media/omap24xx/v4l2-int-device.h @@ -187,6 +187,7 @@ enum v4l2_int_ioctl_num { vidioc_int_querystd_num, vidioc_int_s_std_num, vidioc_int_s_video_routing_num, + vidioc_int_send_command_num, /* * @@ -289,6 +290,7 @@ V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *); V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *); V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *); V4L2_INT_WRAPPER_1(s_video_routing, struct v4l2_routing, *); +V4L2_INT_WRAPPER_1(send_command, struct v4l2_send_command_control, *); V4L2_INT_WRAPPER_0(dev_init); V4L2_INT_WRAPPER_0(dev_exit); diff --git a/include/uapi/linux/mxc_v4l2.h b/include/uapi/linux/mxc_v4l2.h index 49345fea15ec91..248d6764304261 100644 --- a/include/uapi/linux/mxc_v4l2.h +++ b/include/uapi/linux/mxc_v4l2.h @@ -48,6 +48,11 @@ #define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6 #define V4L2_MXC_ROTATE_90_LEFT 7 +#define V4L2_MXC_CAM_ROTATE_NONE 8 +#define V4L2_MXC_CAM_ROTATE_VERT_FLIP 9 +#define V4L2_MXC_CAM_ROTATE_HORIZ_FLIP 10 +#define V4L2_MXC_CAM_ROTATE_180 11 + struct v4l2_mxc_offset { uint32_t u_offset; uint32_t v_offset; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 2cbe605bbe04f6..d2b1e71760b22a 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -672,6 +672,14 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_AUTO_EXPOSURE_BIAS (V4L2_CID_CAMERA_CLASS_BASE+19) #define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20) + +/* Backported from Samsung Kernel */ +#define V4L2_CID_AUTO_FOCUS_START (V4L2_CID_CAMERA_CLASS_BASE+28) +#define V4L2_CID_AUTO_FOCUS_STOP (V4L2_CID_CAMERA_CLASS_BASE+29) +#define V4L2_CID_AUTO_FOCUS_STATUS (V4L2_CID_CAMERA_CLASS_BASE+30) +#define V4L2_CID_SEND_COMMAND (V4L2_CID_CAMERA_CLASS_BASE+34) + + enum v4l2_auto_n_preset_white_balance { V4L2_WHITE_BALANCE_MANUAL = 0, V4L2_WHITE_BALANCE_AUTO = 1, diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 5e1ed792c57148..e8619de25e42fa 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1247,6 +1247,13 @@ struct v4l2_ext_controls { struct v4l2_ext_control *controls; }; +struct v4l2_send_command_control { + __u32 id; + __u32 value0; + __u32 value1; + char debug[256]; +}; + #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) @@ -1978,6 +1985,8 @@ struct v4l2_create_buffers { Never use these in applications! */ #define VIDIOC_DBG_G_CHIP_INFO _IOWR('V', 102, struct v4l2_dbg_chip_info) +#define VIDIOC_SEND_COMMAND _IOWR('V', 103, struct v4l2_send_command_control) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ From fda9cf792262138179f0cdd31ac18b6ce6823e55 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 26 Jun 2014 12:54:21 -0700 Subject: [PATCH 0358/1983] v4l2-int-device: add v4l2_if_type enums for BT1120 --- drivers/media/platform/mxc/capture/v4l2-int-device.h | 6 ++++++ drivers/staging/media/omap24xx/v4l2-int-device.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/media/platform/mxc/capture/v4l2-int-device.h b/drivers/media/platform/mxc/capture/v4l2-int-device.h index 0c284752623435..6bbd24b3525aa3 100644 --- a/drivers/media/platform/mxc/capture/v4l2-int-device.h +++ b/drivers/media/platform/mxc/capture/v4l2-int-device.h @@ -113,6 +113,12 @@ enum v4l2_if_type { * on certain image sensors. */ V4L2_IF_TYPE_BT656, + V4L2_IF_TYPE_BT1120_PROGRESSIVE_DDR, + V4L2_IF_TYPE_BT1120_PROGRESSIVE_SDR, + V4L2_IF_TYPE_BT1120_INTERLACED_DDR, + V4L2_IF_TYPE_BT1120_INTERLACED_SDR, + V4L2_IF_TYPE_BT656_PROGRESSIVE, + V4L2_IF_TYPE_BT656_INTERLACED }; enum v4l2_if_type_bt656_mode { diff --git a/drivers/staging/media/omap24xx/v4l2-int-device.h b/drivers/staging/media/omap24xx/v4l2-int-device.h index de5b9a62144155..e63807d45fbaad 100644 --- a/drivers/staging/media/omap24xx/v4l2-int-device.h +++ b/drivers/staging/media/omap24xx/v4l2-int-device.h @@ -112,6 +112,12 @@ enum v4l2_if_type { * on certain image sensors. */ V4L2_IF_TYPE_BT656, + V4L2_IF_TYPE_BT1120_PROGRESSIVE_DDR, + V4L2_IF_TYPE_BT1120_PROGRESSIVE_SDR, + V4L2_IF_TYPE_BT1120_INTERLACED_DDR, + V4L2_IF_TYPE_BT1120_INTERLACED_SDR, + V4L2_IF_TYPE_BT656_PROGRESSIVE, + V4L2_IF_TYPE_BT656_INTERLACED, }; enum v4l2_if_type_bt656_mode { From ec20966a4b9c56f1e704efc689c4a4a12924ef38 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 18 Jan 2015 01:50:59 +0900 Subject: [PATCH 0359/1983] Revert "[media] v4l2-int-device: remove unused chip_ident reference" This reverts commit 12869145718571ffa4f6e650a6f759934eeca0d9. --- drivers/staging/media/omap24xx/v4l2-int-device.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/media/omap24xx/v4l2-int-device.h b/drivers/staging/media/omap24xx/v4l2-int-device.h index e63807d45fbaad..2c5cfaf5f30627 100644 --- a/drivers/staging/media/omap24xx/v4l2-int-device.h +++ b/drivers/staging/media/omap24xx/v4l2-int-device.h @@ -227,6 +227,8 @@ enum v4l2_int_ioctl_num { vidioc_int_reset_num, /* VIDIOC_INT_INIT */ vidioc_int_init_num, + /* VIDIOC_DBG_G_CHIP_IDENT */ + vidioc_int_g_chip_ident_num, /* * @@ -309,5 +311,6 @@ V4L2_INT_WRAPPER_1(enum_frameintervals, struct v4l2_frmivalenum, *); V4L2_INT_WRAPPER_0(reset); V4L2_INT_WRAPPER_0(init); +V4L2_INT_WRAPPER_1(g_chip_ident, int, *); #endif From 59e58c2ee08a7d691ceed9cb2fdd81a90e0ed3ea Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 18 Jan 2015 02:18:45 +0900 Subject: [PATCH 0360/1983] mxc: mipi: support MIPI DPHY clock setting https://community.freescale.com/message/328301 --- drivers/mxc/mipi/mxc_mipi_csi2.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mxc/mipi/mxc_mipi_csi2.c b/drivers/mxc/mipi/mxc_mipi_csi2.c index 45d9ec0572546c..fc4f8ff0f0350b 100644 --- a/drivers/mxc/mipi/mxc_mipi_csi2.c +++ b/drivers/mxc/mipi/mxc_mipi_csi2.c @@ -38,6 +38,7 @@ #include "mxc_mipi_csi2.h" static struct mipi_csi2_info *gmipi_csi2; +static u8 dphy_clk; void _mipi_csi2_lock(struct mipi_csi2_info *info) { @@ -278,6 +279,8 @@ EXPORT_SYMBOL(mipi_csi2_pixelclk_disable); */ int mipi_csi2_reset(struct mipi_csi2_info *info) { + u32 tst_ctrl1 = (u32)0x0 | (u32)dphy_clk << 0; + _mipi_csi2_lock(info); mipi_csi2_write(info, 0x0, MIPI_CSI2_PHY_SHUTDOWNZ); @@ -290,7 +293,7 @@ int mipi_csi2_reset(struct mipi_csi2_info *info) mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0); mipi_csi2_write(info, 0x00010044, MIPI_CSI2_PHY_TST_CTRL1); mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0); - mipi_csi2_write(info, 0x00000014, MIPI_CSI2_PHY_TST_CTRL1); + mipi_csi2_write(info, tst_ctrl1, MIPI_CSI2_PHY_TST_CTRL1); mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0); mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0); @@ -327,9 +330,11 @@ EXPORT_SYMBOL(mipi_csi2_get_info); static int mipi_csi2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct resource *res; u32 mipi_csi2_dphy_ver; int ret; + u8 clk; gmipi_csi2 = kmalloc(sizeof(struct mipi_csi2_info), GFP_KERNEL); if (!gmipi_csi2) { @@ -380,6 +385,11 @@ static int mipi_csi2_probe(struct platform_device *pdev) goto err; } + dphy_clk = 0x14; + ret = of_property_read_u8(np, "mipi_dphy_clk", &clk); + if (!ret) + dphy_clk = clk; + /* mipi dphy clk enable for register access */ clk_prepare_enable(gmipi_csi2->dphy_clk); /* get mipi csi2 dphy version */ From f064d8eb8ad68b7f8b32782c273531c4cacf6df4 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 18 Jan 2015 02:25:21 +0900 Subject: [PATCH 0361/1983] v4l2: mxc: support V4L2_PIX_FMT_SBGGR8 --- drivers/media/platform/mxc/capture/ipu_csi_enc.c | 2 ++ drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index dde7e07c589825..1a0ffe449764fd 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -120,6 +120,8 @@ static int csi_enc_setup(cam_data *cam) pixel_fmt = IPU_PIX_FMT_BGR32; else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) pixel_fmt = IPU_PIX_FMT_RGB32; + else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) + pixel_fmt = IPU_PIX_FMT_GENERIC; else { printk(KERN_ERR "format not supported\n"); return -EINVAL; diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 54a0aa4adec1e8..1ba384675e2f5d 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -393,7 +393,8 @@ static inline int valid_mode(u32 palette) (palette == V4L2_PIX_FMT_YUYV) || (palette == V4L2_PIX_FMT_YUV420) || (palette == V4L2_PIX_FMT_YVU420) || - (palette == V4L2_PIX_FMT_NV12)); + (palette == V4L2_PIX_FMT_NV12 || + palette == V4L2_PIX_FMT_SBGGR8)); } /*! @@ -918,6 +919,10 @@ static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) size = f->fmt.pix.width * f->fmt.pix.height * 3 / 2; bytesperline = f->fmt.pix.width; break; + case V4L2_PIX_FMT_SBGGR8: + size = f->fmt.pix.width * f->fmt.pix.height; + bytesperline = f->fmt.pix.width; + break; default: break; } From 8a146f931e91977cc4862ce018466878463c823f Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 18 Jan 2015 14:21:56 +0900 Subject: [PATCH 0362/1983] v4l2: mxc: fix V4L2_PIX_FMT_* to IPU_PIX_FMT_* conversion issue --- .../platform/mxc/capture/mxc_v4l2_capture.c | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 1ba384675e2f5d..c9f849119430e7 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1388,7 +1388,45 @@ void setup_ifparm(cam_data *cam, int init_defrect) pr_debug(" g_fmt_cap returns widthxheight of input as %d x %d\n", cam_fmt.fmt.pix.width, cam_fmt.fmt.pix.height); - csi_param.data_fmt = cam_fmt.fmt.pix.pixelformat; + switch (cam_fmt.fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + csi_param.data_fmt = IPU_PIX_FMT_RGB565; + break; + case V4L2_PIX_FMT_BGR24: + csi_param.data_fmt = IPU_PIX_FMT_BGR24; + break; + case V4L2_PIX_FMT_RGB24: + csi_param.data_fmt = IPU_PIX_FMT_RGB24; + break; + case V4L2_PIX_FMT_BGR32: + csi_param.data_fmt = IPU_PIX_FMT_BGR32; + break; + case V4L2_PIX_FMT_RGB32: + csi_param.data_fmt = IPU_PIX_FMT_RGB32; + break; + case V4L2_PIX_FMT_YUV422P: + csi_param.data_fmt = IPU_PIX_FMT_YUV422P; + break; + case V4L2_PIX_FMT_UYVY: + csi_param.data_fmt = IPU_PIX_FMT_UYVY; + break; + case V4L2_PIX_FMT_YUYV: + csi_param.data_fmt = IPU_PIX_FMT_YUYV; + break; + case V4L2_PIX_FMT_YUV420: + csi_param.data_fmt = IPU_PIX_FMT_YUV420P; + break; + case V4L2_PIX_FMT_YVU420: + csi_param.data_fmt = IPU_PIX_FMT_YVU420P;; + break; + case V4L2_PIX_FMT_NV12: + csi_param.data_fmt = IPU_PIX_FMT_NV12; + break; + case V4L2_PIX_FMT_SBGGR8: + default: + csi_param.data_fmt = IPU_PIX_FMT_GENERIC; + break; + } cam->crop_bounds.top = cam->crop_bounds.left = 0; cam->crop_bounds.width = cam_fmt.fmt.pix.width; From bd45b7c0372bf24d177dd4407a315cad5f19c5a8 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 18 Jan 2015 14:44:39 +0900 Subject: [PATCH 0363/1983] v4l2: mxc: select current_input on boot --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index c9f849119430e7..58cc19e33757fd 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -2772,7 +2772,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) const struct of_device_id *of_id = of_match_device(mxc_v4l2_dt_ids, &pdev->dev); struct device_node *np = pdev->dev.of_node; - int ipu_id, csi_id, mclk_source, mipi_camera; + int ipu_id, csi_id, mclk_source, mipi_camera, def_input; int ret = 0; struct v4l2_device *v4l2_dev; static int camera_id; @@ -2801,6 +2801,10 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) if (ret) mipi_camera = 0; + ret = of_property_read_u32(np, "default_input", &def_input); + if (ret || (def_input != 0 && def_input != 1)) + def_input = 0; + /* Default everything to 0 */ memset(cam, 0, sizeof(cam_data)); @@ -2893,6 +2897,7 @@ static int init_camera_struct(cam_data *cam, struct platform_device *pdev) cam->mipi_camera = mipi_camera; cam->mclk_source = mclk_source; cam->mclk_on[cam->mclk_source] = false; + cam->current_input = def_input; cam->enc_callback = camera_callback; init_waitqueue_head(&cam->power_queue); From 36fc61759f2f100af8ae3e5af1244ca07fe9930b Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 18 Jan 2015 22:40:47 +0900 Subject: [PATCH 0364/1983] Revert "[media] v4l2: always require v4l2_dev, rename parent to dev_parent" This reverts commit 1c1d86a1ea07506c070cfb217a009d53990bdeb0. --- drivers/media/v4l2-core/v4l2-dev.c | 34 ++++++++++++++++------------ drivers/media/v4l2-core/v4l2-ioctl.c | 7 +++++- include/media/v4l2-dev.h | 4 ++-- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 07b7d26c6cc602..687df7b9116b27 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -499,8 +499,8 @@ static const struct file_operations v4l2_fops = { }; /** - * get_index - assign stream index number based on v4l2_dev - * @vdev: video_device to assign index number to, vdev->v4l2_dev should be assigned + * get_index - assign stream index number based on parent device + * @vdev: video_device to assign index number to, vdev->parent should be assigned * * Note that when this is called the new device has not yet been registered * in the video_device array, but it was able to obtain a minor number. @@ -518,11 +518,15 @@ static int get_index(struct video_device *vdev) static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES); int i; + /* Some drivers do not set the parent. In that case always return 0. */ + if (vdev->parent == NULL) + return 0; + bitmap_zero(used, VIDEO_NUM_DEVICES); for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_device[i] != NULL && - video_device[i]->v4l2_dev == vdev->v4l2_dev) { + video_device[i]->parent == vdev->parent) { set_bit(video_device[i]->index, used); } } @@ -776,9 +780,6 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* the release callback MUST be present */ if (WARN_ON(!vdev->release)) return -EINVAL; - /* the v4l2_dev pointer MUST be present */ - if (WARN_ON(!vdev->v4l2_dev)) - return -EINVAL; /* v4l2_fh support */ spin_lock_init(&vdev->fh_lock); @@ -806,14 +807,16 @@ int __video_register_device(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; - if (vdev->dev_parent == NULL) - vdev->dev_parent = vdev->v4l2_dev->dev; - if (vdev->ctrl_handler == NULL) - vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; - /* If the prio state pointer is NULL, then use the v4l2_device - prio state. */ - if (vdev->prio == NULL) - vdev->prio = &vdev->v4l2_dev->prio; + if (vdev->v4l2_dev) { + if (vdev->v4l2_dev->dev) + vdev->parent = vdev->v4l2_dev->dev; + if (vdev->ctrl_handler == NULL) + vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; + /* If the prio state pointer is NULL, then use the v4l2_device + prio state. */ + if (vdev->prio == NULL) + vdev->prio = &vdev->v4l2_dev->prio; + } /* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES @@ -899,7 +902,8 @@ int __video_register_device(struct video_device *vdev, int type, int nr, /* Part 4: register the device with sysfs */ vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); - vdev->dev.parent = vdev->dev_parent; + if (vdev->parent) + vdev->dev.parent = vdev->parent; dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev); if (ret < 0) { diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 18b4e51fd7af35..8c1d63d6288c13 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1843,7 +1843,12 @@ static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops, p->flags |= V4L2_CHIP_FL_WRITABLE; if (ops->vidioc_g_register) p->flags |= V4L2_CHIP_FL_READABLE; - strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name)); + if (vfd->v4l2_dev) + strlcpy(p->name, vfd->v4l2_dev->name, sizeof(p->name)); + else if (vfd->parent) + strlcpy(p->name, vfd->parent->driver->name, sizeof(p->name)); + else + strlcpy(p->name, "bridge", sizeof(p->name)); if (ops->vidioc_g_chip_info) return ops->vidioc_g_chip_info(file, fh, arg); if (p->match.addr) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index c768c9f8abc2ca..b2c3776a1cfff2 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -96,9 +96,9 @@ struct video_device struct device dev; /* v4l device */ struct cdev *cdev; /* character device */ + /* Set either parent or v4l2_dev if your driver uses v4l2_device */ + struct device *parent; /* device parent */ struct v4l2_device *v4l2_dev; /* v4l2_device parent */ - /* Only set parent if that can't be deduced from v4l2_dev */ - struct device *dev_parent; /* device parent */ /* Control handler associated with this device node. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; From f5887daf6c13a07ef3af6eed6792d38592a2c2b5 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 20 Jan 2015 01:27:43 +0900 Subject: [PATCH 0365/1983] mxc_v4l2_capture: add OV5647 MIPI driver https://community.freescale.com/thread/310786 --- drivers/media/platform/mxc/capture/Kconfig | 6 + drivers/media/platform/mxc/capture/Makefile | 3 + .../media/platform/mxc/capture/ov5647_mipi.c | 1788 +++++++++++++++++ 3 files changed, 1797 insertions(+) create mode 100644 drivers/media/platform/mxc/capture/ov5647_mipi.c diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig index aa504fdba57ad0..ef0cd82efdf55e 100644 --- a/drivers/media/platform/mxc/capture/Kconfig +++ b/drivers/media/platform/mxc/capture/Kconfig @@ -29,6 +29,12 @@ config MXC_CAMERA_OV5640_MIPI ---help--- If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here. +config MXC_CAMERA_OV5647_MIPI + tristate "OmniVision ov5647 camera support using mipi" + depends on !VIDEO_MXC_EMMA_CAMERA && I2C + ---help--- + If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here. + config MXC_TVIN_ADV7180 tristate "Analog Device adv7180 TV Decoder Input support" depends on !VIDEO_MXC_EMMA_CAMERA && I2C diff --git a/drivers/media/platform/mxc/capture/Makefile b/drivers/media/platform/mxc/capture/Makefile index 86854ba9354a3e..48fef0fc5bd603 100644 --- a/drivers/media/platform/mxc/capture/Makefile +++ b/drivers/media/platform/mxc/capture/Makefile @@ -15,6 +15,9 @@ obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o ov5640_camera_mipi-objs := ov5640_mipi.o obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi.o +ov5647_camera_mipi-objs := ov5647_mipi.o +obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.o + adv7180_tvin-objs := adv7180.o obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c new file mode 100644 index 00000000000000..61ac0716285aa2 --- /dev/null +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -0,0 +1,1788 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "v4l2-int-device.h" +#include "mxc_v4l2_capture.h" + +#define OV5647_VOLTAGE_ANALOG 2800000 +#define OV5647_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5647_VOLTAGE_DIGITAL_IO 1800000 + +#define MIN_FPS 15 +#define MAX_FPS 30 +#define DEFAULT_FPS 30 + +#define OV5647_XCLK_MIN 6000000 +#define OV5647_XCLK_MAX 27000000 + +#define OV5647_CHIP_ID_HIGH_BYTE 0x300A +#define OV5647_CHIP_ID_LOW_BYTE 0x300B + +enum ov5647_mode { + ov5647_mode_MIN = 0, + ov5647_mode_960P_1280_960 = 0, + ov5647_mode_MAX = 0, + ov5647_mode_INIT = 0xff, /*only for sensor init*/ +}; + +enum ov5647_frame_rate { + ov5647_15_fps, + ov5647_30_fps +}; + +/* image size under 1280 * 960 are SUBSAMPLING + * image size upper 1280 * 960 are SCALING + */ +enum ov5647_downsize_mode { + SUBSAMPLING, + SCALING, +}; + +struct reg_value { + u16 u16RegAddr; + u8 u8Val; + u8 u8Mask; + u32 u32Delay_ms; +}; + +struct ov5647_mode_info { + enum ov5647_mode mode; + enum ov5647_downsize_mode dn_mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +/*! + * Maintains the information on the current state of the sesor. + */ +static struct sensor_data ov5647_data; +static int pwn_gpio = -EINVAL; +static int pwn_active; +static int rst_gpio = -EINVAL; +static int rst_active; + +static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x21 , 0 , 0 } , + { 0x3036 , 0x37 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x09 , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x07 , 0 , 0 } , + { 0x380d , 0x68 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x05 , 0 , 0 } , + { 0x3809 , 0x00 , 0 , 0 } , + { 0x380a , 0x03 , 0 , 0 } , + { 0x380b , 0xc0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x07 , 0 , 0 } , + { 0x3807 , 0xa1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3503 , 0x03 , 0 , 0 } , /* manual AE */ + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , /* manual AWB */ + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + + + +static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { + { + {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, + ov5647_setting_30fps_960P_1280_960, + ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960)}, + }, + { + {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, + ov5647_setting_30fps_960P_1280_960, + ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960)}, + }, +}; + +static struct regulator *io_regulator; +static struct regulator *core_regulator; +static struct regulator *analog_regulator; +static struct regulator *gpo_regulator; + +static int ov5647_probe(struct i2c_client *adapter, + const struct i2c_device_id *device_id); +static int ov5647_remove(struct i2c_client *client); + +static s32 ov5647_read_reg(u16 reg, u8 *val); +static s32 ov5647_write_reg(u16 reg, u8 val); + +static const struct i2c_device_id ov5647_id[] = { + {"ov5647_mipi", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov5647_id); + +static struct i2c_driver ov5647_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ov5647_mipi", + }, + .probe = ov5647_probe, + .remove = ov5647_remove, + .id_table = ov5647_id, +}; + +static void ov5647_standby(s32 enable) +{ + if (!gpio_is_valid(pwn_gpio)) + return; + + if (enable) + gpio_set_value(pwn_gpio, !pwn_active); + else + gpio_set_value(pwn_gpio, pwn_active); + pr_debug("ov5647_mipi_camera_powerdown: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio); + msleep(2); +} + +static void ov5647_reset(void) +{ + if (!gpio_is_valid(rst_gpio)) + return; + + /* camera reset */ + gpio_set_value(rst_gpio, !rst_active); + + /* camera power dowmn */ + if (gpio_is_valid(pwn_gpio)) { + gpio_set_value(pwn_gpio, 1); + msleep(5); + + gpio_set_value(pwn_gpio, 0); + msleep(5); + } + + gpio_set_value(rst_gpio, rst_active); + msleep(1); + + gpio_set_value(rst_gpio, !rst_active); + msleep(5); + + if (gpio_is_valid(pwn_gpio)) + gpio_set_value(pwn_gpio, !pwn_active); +} + +static int ov5647_power_on(struct device *dev) +{ + int ret = 0; + + io_regulator = devm_regulator_get(dev, "DOVDD"); + if (!IS_ERR(io_regulator)) { + regulator_set_voltage(io_regulator, + OV5647_VOLTAGE_DIGITAL_IO, + OV5647_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(io_regulator); + if (ret) { + pr_err("%s:io set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:io set voltage ok\n", __func__); + } + } else { + pr_err("%s: cannot get io voltage error\n", __func__); + io_regulator = NULL; + } + + core_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(core_regulator)) { + regulator_set_voltage(core_regulator, + OV5647_VOLTAGE_DIGITAL_CORE, + OV5647_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(core_regulator); + if (ret) { + pr_err("%s:core set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:core set voltage ok\n", __func__); + } + } else { + core_regulator = NULL; + pr_err("%s: cannot get core voltage error\n", __func__); + } + + analog_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(analog_regulator)) { + regulator_set_voltage(analog_regulator, + OV5647_VOLTAGE_ANALOG, + OV5647_VOLTAGE_ANALOG); + ret = regulator_enable(analog_regulator); + if (ret) { + pr_err("%s:analog set voltage error\n", + __func__); + return ret; + } else { + dev_dbg(dev, + "%s:analog set voltage ok\n", __func__); + } + } else { + analog_regulator = NULL; + pr_err("%s: cannot get analog voltage error\n", __func__); + } + + return ret; +} + +static s32 ov5647_write_reg(u16 reg, u8 val) +{ + u8 au8Buf[3] = {0}; + + au8Buf[0] = reg >> 8; + au8Buf[1] = reg & 0xff; + au8Buf[2] = val; + + if (i2c_master_send(ov5647_data.i2c_client, au8Buf, 3) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, val); + return -1; + } + pr_debug("reg=%x,val=%x\n", reg, val); + return 0; +} + +static s32 ov5647_read_reg(u16 reg, u8 *val) +{ + struct sensor_data *sensor = &ov5647_data; + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msgs[2]; + u8 buf[2]; + int ret; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = buf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 1; + msgs[1].buf = buf; + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) { + pr_err("%s(mipi):reg=%x ret=%d\n", __func__, reg, ret); + return ret; + } + *val = buf[0]; + pr_debug("%s(mipi):reg=%x,val=%x\n", __func__, reg, buf[0]); + return buf[0]; +} + +static int prev_sysclk, prev_HTS; +static int AE_low, AE_high, AE_Target = 52; + +void OV5647_stream_on(void) +{ + ov5647_write_reg(0x4202, 0x00); + ov5647_write_reg(0x300D, 0x00); +} + +void OV5647_stream_off(void) +{ + ov5647_write_reg(0x4202, 0x0f); + ov5647_write_reg(0x300D, 0x01); +} + +static const int sclk_rdiv_map[] = {1, 2, 4, 8}; + +int OV5647_get_sysclk(void) +{ + /* calculate sysclk */ + int tmp; + unsigned Multiplier, PreDiv, SysDiv, Pll_rdiv, Bit_div2x = 1; + unsigned div, sclk_rdiv, sysclk; + u8 temp; + + tmp = ov5647_read_reg(0x3034, &temp); + if (tmp < 0) + return tmp; + tmp &= 0x0f; + if (tmp == 8 || tmp == 10) + Bit_div2x = tmp / 2; + + tmp = ov5647_read_reg(0x3035, &temp); + if (tmp < 0) + return tmp; + SysDiv = tmp >> 4; + if (SysDiv == 0) + SysDiv = 16; + + tmp = ov5647_read_reg(0x3036, &temp); + if (tmp < 0) + return tmp; + Multiplier = tmp; + + tmp = ov5647_read_reg(0x3037, &temp); + if (tmp < 0) + return tmp; + PreDiv = tmp & 0x0f; + Pll_rdiv = ((tmp >> 4) & 0x01) + 1; + + tmp = ov5647_read_reg(0x3108, &temp); + if (tmp < 0) + return tmp; + sclk_rdiv = sclk_rdiv_map[tmp & 0x03]; + + sysclk = ov5647_data.mclk / 10000 * Multiplier; + div = PreDiv * SysDiv * Pll_rdiv * Bit_div2x * sclk_rdiv; + if (!div) { + pr_err("%s:Error divide by 0, (%d * %d * %d * %d * %d)\n", + __func__, PreDiv, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv); + return -EINVAL; + } + if (!sysclk) { + pr_err("%s:Error 0 clk, ov5647_data.mclk=%d, Multiplier=%d\n", + __func__, ov5647_data.mclk, Multiplier); + return -EINVAL; + } + sysclk /= div; + pr_debug("%s: sysclk(%d) = %d / 10000 * %d / (%d * %d * %d * %d * %d)\n", + __func__, sysclk, ov5647_data.mclk, Multiplier, + PreDiv, SysDiv, Pll_rdiv, Bit_div2x, sclk_rdiv); + return sysclk; +} + +void OV5647_set_night_mode(void) +{ + /* read HTS from register settings */ + u8 mode; + + ov5647_read_reg(0x3a00, &mode); + mode &= 0xfb; + ov5647_write_reg(0x3a00, mode); +} + +int OV5647_get_HTS(void) +{ + /* read HTS from register settings */ + int HTS; + u8 temp; + + HTS = ov5647_read_reg(0x380c, &temp); + HTS = (HTS<<8) + ov5647_read_reg(0x380d, &temp); + + return HTS; +} + +int OV5647_get_VTS(void) +{ + /* read VTS from register settings */ + int VTS; + u8 temp; + + /* total vertical size[15:8] high byte */ + VTS = ov5647_read_reg(0x380e, &temp); + + VTS = (VTS<<8) + ov5647_read_reg(0x380f, &temp); + + return VTS; +} + +int OV5647_set_VTS(int VTS) +{ + /* write VTS to registers */ + int temp; + + temp = VTS & 0xff; + ov5647_write_reg(0x380f, temp); + + temp = VTS>>8; + ov5647_write_reg(0x380e, temp); + + return 0; +} + +int OV5647_get_shutter(void) +{ + /* read shutter, in number of line period */ + int shutter; + u8 temp; + + shutter = (ov5647_read_reg(0x03500, &temp) & 0x0f); + shutter = (shutter<<8) + ov5647_read_reg(0x3501, &temp); + shutter = (shutter<<4) + (ov5647_read_reg(0x3502, &temp)>>4); + + return shutter; +} + +int OV5647_set_shutter(int shutter) +{ + /* write shutter, in number of line period */ + int temp; + + shutter = shutter & 0xffff; + + temp = shutter & 0x0f; + temp = temp<<4; + ov5647_write_reg(0x3502, temp); + + temp = shutter & 0xfff; + temp = temp>>4; + ov5647_write_reg(0x3501, temp); + + temp = shutter>>12; + ov5647_write_reg(0x3500, temp); + + return 0; +} + +int OV5647_get_gain16(void) +{ + /* read gain, 16 = 1x */ + int gain16; + u8 temp; + + gain16 = ov5647_read_reg(0x350a, &temp) & 0x03; + gain16 = (gain16<<8) + ov5647_read_reg(0x350b, &temp); + + return gain16; +} + +int OV5647_set_gain16(int gain16) +{ + /* write gain, 16 = 1x */ + u8 temp; + gain16 = gain16 & 0x3ff; + + temp = gain16 & 0xff; + ov5647_write_reg(0x350b, temp); + + temp = gain16>>8; + ov5647_write_reg(0x350a, temp); + + return 0; +} + +int OV5647_get_light_freq(void) +{ + /* get banding filter value */ + int temp, temp1, light_freq = 0; + u8 tmp; + + temp = ov5647_read_reg(0x3c01, &tmp); + + if (temp & 0x80) { + /* manual */ + temp1 = ov5647_read_reg(0x3c00, &tmp); + if (temp1 & 0x04) { + /* 50Hz */ + light_freq = 50; + } else { + /* 60Hz */ + light_freq = 60; + } + } else { + /* auto */ + temp1 = ov5647_read_reg(0x3c0c, &tmp); + if (temp1 & 0x01) { + /* 50Hz */ + light_freq = 50; + } else { + /* 60Hz */ + light_freq = 60; + } + } + return light_freq; +} + +void OV5647_set_bandingfilter(void) +{ + int prev_VTS; + int band_step60, max_band60, band_step50, max_band50; + + /* read preview PCLK */ + prev_sysclk = OV5647_get_sysclk(); + /* read preview HTS */ + prev_HTS = OV5647_get_HTS(); + + /* read preview VTS */ + prev_VTS = OV5647_get_VTS(); + + /* calculate banding filter */ + /* 60Hz */ + band_step60 = prev_sysclk * 100/prev_HTS * 100/120; + ov5647_write_reg(0x3a0a, (band_step60 >> 8)); + ov5647_write_reg(0x3a0b, (band_step60 & 0xff)); + + max_band60 = (int)((prev_VTS-4)/band_step60); + ov5647_write_reg(0x3a0d, max_band60); + + /* 50Hz */ + band_step50 = prev_sysclk * 100/prev_HTS; + ov5647_write_reg(0x3a08, (band_step50 >> 8)); + ov5647_write_reg(0x3a09, (band_step50 & 0xff)); + + max_band50 = (int)((prev_VTS-4)/band_step50); + ov5647_write_reg(0x3a0e, max_band50); +} + +int OV5647_set_AE_target(int target) +{ + /* stable in high */ + int fast_high, fast_low; + AE_low = target * 23 / 25; /* 0.92 */ + AE_high = target * 27 / 25; /* 1.08 */ + + fast_high = AE_high<<1; + if (fast_high > 255) + fast_high = 255; + + fast_low = AE_low >> 1; + + ov5647_write_reg(0x3a0f, AE_high); + ov5647_write_reg(0x3a10, AE_low); + ov5647_write_reg(0x3a1b, AE_high); + ov5647_write_reg(0x3a1e, AE_low); + ov5647_write_reg(0x3a11, fast_high); + ov5647_write_reg(0x3a1f, fast_low); + + return 0; +} + +void OV5647_turn_on_AE_AG(int enable) +{ + u8 ae_ag_ctrl; + + ov5647_read_reg(0x3503, &ae_ag_ctrl); + if (enable) { + /* turn on auto AE/AG */ + ae_ag_ctrl = ae_ag_ctrl & ~(0x03); + } else { + /* turn off AE/AG */ + ae_ag_ctrl = ae_ag_ctrl | 0x03; + } + ov5647_write_reg(0x3503, ae_ag_ctrl); +} + +bool binning_on(void) +{ + u8 temp; + ov5647_read_reg(0x3821, &temp); + temp &= 0xfe; + if (temp) + return true; + else + return false; +} + +static void ov5647_set_virtual_channel(int channel) +{ + u8 channel_id; + + ov5647_read_reg(0x4814, &channel_id); + channel_id &= ~(3 << 6); + ov5647_write_reg(0x4814, channel_id | (channel << 6)); +} + +/* download ov5647 settings to sensor through i2c */ +static int ov5647_download_firmware(struct reg_value *pModeSetting, s32 ArySize) +{ + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u8 Mask = 0; + register u8 Val = 0; + u8 RegVal = 0; + int i, retval = 0; + + for (i = 0; i < ArySize; ++i, ++pModeSetting) { + Delay_ms = pModeSetting->u32Delay_ms; + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u8Val; + Mask = pModeSetting->u8Mask; + + if (Mask) { + retval = ov5647_read_reg(RegAddr, &RegVal); + if (retval < 0) + goto err; + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = ov5647_write_reg(RegAddr, Val); + if (retval < 0) + goto err; + + if (Delay_ms) + msleep(Delay_ms); + } +err: + return retval; +} + +/* sensor changes between scaling and subsampling + * go through exposure calcualtion + */ +static int ov5647_change_mode_exposure_calc(enum ov5647_frame_rate frame_rate, + enum ov5647_mode mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + u8 average; + int prev_shutter, prev_gain16; + int cap_shutter, cap_gain16; + int cap_sysclk, cap_HTS, cap_VTS; + int light_freq, cap_bandfilt, cap_maxband; + long cap_gain16_shutter; + int retval = 0; + + /* check if the input mode and frame rate is valid */ + pModeSetting = + ov5647_mode_info_data[frame_rate][mode].init_data_ptr; + ArySize = + ov5647_mode_info_data[frame_rate][mode].init_data_size; + + ov5647_data.pix.width = + ov5647_mode_info_data[frame_rate][mode].width; + ov5647_data.pix.height = + ov5647_mode_info_data[frame_rate][mode].height; + + if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 || + pModeSetting == NULL || ArySize == 0) + return -EINVAL; + + /* turn off AE/AG */ + OV5647_turn_on_AE_AG(0); + + /* read preview shutter */ + prev_shutter = OV5647_get_shutter(); + if ((binning_on()) && (mode != ov5647_mode_960P_1280_960)) + prev_shutter *= 2; + + /* read preview gain */ + prev_gain16 = OV5647_get_gain16(); + + /* get average */ + ov5647_read_reg(0x5693, &average); + + /* turn off night mode for capture */ + OV5647_set_night_mode(); + + /* turn off overlay */ + /* ov5647_write_reg(0x3022, 0x06);//if no af function, just skip it */ + + OV5647_stream_off(); + + /* Write capture setting */ + retval = ov5647_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + /* read capture VTS */ + cap_VTS = OV5647_get_VTS(); + cap_HTS = OV5647_get_HTS(); + cap_sysclk = OV5647_get_sysclk(); + + /* calculate capture banding filter */ + light_freq = OV5647_get_light_freq(); + if (light_freq == 60) { + /* 60Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120; + } else { + /* 50Hz */ + cap_bandfilt = cap_sysclk * 100 / cap_HTS; + } + cap_maxband = (int)((cap_VTS - 4)/cap_bandfilt); + + /* calculate capture shutter/gain16 */ + if (average > AE_low && average < AE_high) { + /* in stable range */ + cap_gain16_shutter = + prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk + * prev_HTS/cap_HTS * AE_Target / average; + } else { + cap_gain16_shutter = + prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk + * prev_HTS/cap_HTS; + } + + /* gain to shutter */ + if (cap_gain16_shutter < (cap_bandfilt * 16)) { + /* shutter < 1/100 */ + cap_shutter = cap_gain16_shutter/16; + if (cap_shutter < 1) + cap_shutter = 1; + + cap_gain16 = cap_gain16_shutter/cap_shutter; + if (cap_gain16 < 16) + cap_gain16 = 16; + } else { + if (cap_gain16_shutter > + (cap_bandfilt * cap_maxband * 16)) { + /* exposure reach max */ + cap_shutter = cap_bandfilt * cap_maxband; + cap_gain16 = cap_gain16_shutter / cap_shutter; + } else { + /* 1/100 < (cap_shutter = n/100) =< max */ + cap_shutter = + ((int) (cap_gain16_shutter/16 / cap_bandfilt)) + *cap_bandfilt; + cap_gain16 = cap_gain16_shutter / cap_shutter; + } + } + + /* write capture gain */ + OV5647_set_gain16(cap_gain16); + + /* write capture shutter */ + if (cap_shutter > (cap_VTS - 4)) { + cap_VTS = cap_shutter + 4; + OV5647_set_VTS(cap_VTS); + } + OV5647_set_shutter(cap_shutter); + + OV5647_stream_on(); + +err: + return retval; +} + +/* if sensor changes inside scaling or subsampling + * change mode directly + * */ +static int ov5647_change_mode_direct(enum ov5647_frame_rate frame_rate, + enum ov5647_mode mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + int retval = 0; + + /* check if the input mode and frame rate is valid */ + pModeSetting = + ov5647_mode_info_data[frame_rate][mode].init_data_ptr; + ArySize = + ov5647_mode_info_data[frame_rate][mode].init_data_size; + + ov5647_data.pix.width = + ov5647_mode_info_data[frame_rate][mode].width; + ov5647_data.pix.height = + ov5647_mode_info_data[frame_rate][mode].height; + + if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 || + pModeSetting == NULL || ArySize == 0) + return -EINVAL; + + /* turn off AE/AG */ + OV5647_turn_on_AE_AG(0); + + OV5647_stream_off(); + + /* Write capture setting */ + retval = ov5647_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + + OV5647_stream_on(); + + OV5647_turn_on_AE_AG(1); + +err: + return retval; +} + +static int ov5647_init_mode(enum ov5647_frame_rate frame_rate, + enum ov5647_mode mode, enum ov5647_mode orig_mode) +{ + struct reg_value *pModeSetting = NULL; + s32 ArySize = 0; + int retval = 0; + void *mipi_csi2_info; + u32 mipi_reg, msec_wait4stable = 0; + enum ov5647_downsize_mode dn_mode, orig_dn_mode; + + if ((mode > ov5647_mode_MAX || mode < ov5647_mode_MIN) + && (mode != ov5647_mode_INIT)) { + pr_err("Wrong ov5647 mode detected!\n"); + return -1; + } + + mipi_csi2_info = mipi_csi2_get_info(); + + /* initial mipi dphy */ + if (!mipi_csi2_info) { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -1; + } + + ov5647_write_reg(0x4800, 0x25); + OV5647_stream_off(); + + if (!mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_enable(mipi_csi2_info); + + if (!mipi_csi2_get_status(mipi_csi2_info)) { + pr_err("Can not enable mipi csi2 driver!\n"); + return -1; + } + + mipi_csi2_set_lanes(mipi_csi2_info, 2); + + /*Only reset MIPI CSI2 HW at sensor initialize*/ + if (mode == ov5647_mode_INIT) + mipi_csi2_reset(mipi_csi2_info); + + /* reg 0x3034 == 0x08 is 8bit mode */ + mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_RAW8); + + if (orig_mode != ov5647_mode_INIT) { + dn_mode = ov5647_mode_info_data[frame_rate][mode].dn_mode; + orig_dn_mode = ov5647_mode_info_data[frame_rate][orig_mode].dn_mode; + } + else { + orig_dn_mode = dn_mode = 0; + } + + if (mode == ov5647_mode_INIT) { + pModeSetting = ov5647_setting_30fps_960P_1280_960; + ArySize = ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960); + + ov5647_data.pix.width = 1280; + ov5647_data.pix.height = 960; + retval = ov5647_download_firmware(pModeSetting, ArySize); + if (retval < 0) + goto err; + } else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || + (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { + /* change between subsampling and scaling + * go through exposure calucation */ + retval = ov5647_change_mode_exposure_calc(frame_rate, mode); + } else { + /* change inside subsampling or scaling + * download firmware directly */ + retval = ov5647_change_mode_direct(frame_rate, mode); + } + + if (retval < 0) + goto err; + + OV5647_set_AE_target(AE_Target); + OV5647_get_light_freq(); + OV5647_set_bandingfilter(); + ov5647_set_virtual_channel(ov5647_data.virtual_channel); + + /* add delay to wait for sensor stable */ + if (frame_rate == ov5647_15_fps) { + /* dump the first nine frames: 1/15*9 */ + msec_wait4stable = 600; + } else if (frame_rate == ov5647_30_fps) { + /* dump the first nine frames: 1/30*9 */ + msec_wait4stable = 300; + } + msleep(msec_wait4stable); + + if (mipi_csi2_info) { + unsigned int i = 0; + u8 resetval; + + /* wait for mipi sensor ready */ + while (1) { + mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); + if (mipi_reg != 0x200) + break; + if (i++ >= 20) { + pr_err("mipi csi2 can not receive sensor clk! %x\n", mipi_reg); + return -1; + } + msleep(10); + } + + i = 0; + /* wait for mipi stable */ + while (1) { + mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); + if (!mipi_reg) + break; + if (i++ >= 20) { + pr_err("mipi csi2 can not receive data correctly!\n"); + return -1; + } + msleep(10); + } + + pr_debug("receiving data"); + mipi_reg = mipi_csi2_get_error2(mipi_csi2_info); + pr_debug("mipi_csi2 error2 = 0x%X\n", mipi_reg); + mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); + pr_debug("mipi_csi2_dphy_status = 0x%X\n", mipi_reg); + + ov5647_read_reg(0x0100, &resetval); + if (!resetval&0x01) { + pr_info("DEVICE WAS IN SOFTWARE STANDBY"); + ov5647_write_reg(0x0100, 0x01); + } + + ov5647_write_reg(0x4800, 0x04); + msleep(266); + OV5647_stream_on(); + msleep(30); + OV5647_turn_on_AE_AG(1); + } +err: + return retval; +} + +/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -1; + } + + memset(p, 0, sizeof(*p)); + p->u.bt656.clock_curr = ov5647_data.mclk; + pr_debug(" clock_curr=mclk=%d\n", ov5647_data.mclk); + p->if_type = V4L2_IF_TYPE_BT656; + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT; + p->u.bt656.clock_min = OV5647_XCLK_MIN; + p->u.bt656.clock_max = OV5647_XCLK_MAX; + p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ + + return 0; +} + +/*! + * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl + * @s: pointer to standard V4L2 device structure + * @on: indicates power mode (on or off) + * + * Turns the power on or off, depending on the value of on and returns the + * appropriate error code. + */ +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor_data *sensor = s->priv; + + if (on && !sensor->on) { + if (io_regulator) + if (regulator_enable(io_regulator) != 0) + return -EIO; + if (core_regulator) + if (regulator_enable(core_regulator) != 0) + return -EIO; + if (gpo_regulator) + if (regulator_enable(gpo_regulator) != 0) + return -EIO; + if (analog_regulator) + if (regulator_enable(analog_regulator) != 0) + return -EIO; + /* Make sure power on */ + ov5647_standby(0); + } else if (!on && sensor->on) { + if (analog_regulator) + regulator_disable(analog_regulator); + if (core_regulator) + regulator_disable(core_regulator); + if (io_regulator) + regulator_disable(io_regulator); + if (gpo_regulator) + regulator_disable(gpo_regulator); + + ov5647_standby(1); + } + + sensor->on = on; + + return 0; +} + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + int ret = 0; + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->streamcap.capability; + cparm->timeperframe = sensor->streamcap.timeperframe; + cparm->capturemode = sensor->streamcap.capturemode; + ret = 0; + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + enum ov5647_frame_rate frame_rate; + enum ov5647_mode orig_mode; + int ret = 0; + + /* Make sure power on */ + ov5647_standby(0); + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* Check that the new frame rate is allowed. */ + if ((timeperframe->numerator == 0) || + (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + /* Actual frame rate we use */ + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps == 15) + frame_rate = ov5647_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5647_30_fps; + else { + pr_err(" The camera frame rate is not supported!\n"); + return -EINVAL; + } + + orig_mode = sensor->streamcap.capturemode; + ret = ov5647_init_mode(frame_rate, + (u32)a->parm.capture.capturemode, orig_mode); + if (ret < 0) + return ret; + + sensor->streamcap.timeperframe = *timeperframe; + sensor->streamcap.capturemode = + (u32)a->parm.capture.capturemode; + + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + pr_debug(" type is not " \ + "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", + a->type); + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + return ret; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct sensor_data *sensor = s->priv; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->fmt.pix = sensor->pix; + pr_debug("%s: %dx%d\n", __func__, sensor->pix.width, sensor->pix.height); + break; + + case V4L2_BUF_TYPE_SENSOR: + pr_debug("%s: left=%d, top=%d, %dx%d\n", __func__, + sensor->spix.left, sensor->spix.top, + sensor->spix.swidth, sensor->spix.sheight); + f->fmt.spix = sensor->spix; + break; + + case V4L2_BUF_TYPE_PRIVATE: + break; + + default: + f->fmt.pix = sensor->pix; + break; + } + + return 0; +} + +/*! + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int ret = 0; + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + vc->value = ov5647_data.brightness; + break; + case V4L2_CID_HUE: + vc->value = ov5647_data.hue; + break; + case V4L2_CID_CONTRAST: + vc->value = ov5647_data.contrast; + break; + case V4L2_CID_SATURATION: + vc->value = ov5647_data.saturation; + break; + case V4L2_CID_RED_BALANCE: + vc->value = ov5647_data.red; + break; + case V4L2_CID_BLUE_BALANCE: + vc->value = ov5647_data.blue; + break; + case V4L2_CID_EXPOSURE: + vc->value = ov5647_data.ae_mode; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/*! + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = 0; + + pr_debug("In ov5647:ioctl_s_ctrl %d\n", + vc->id); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + break; + case V4L2_CID_CONTRAST: + break; + case V4L2_CID_SATURATION: + break; + case V4L2_CID_HUE: + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + break; + case V4L2_CID_DO_WHITE_BALANCE: + break; + case V4L2_CID_RED_BALANCE: + break; + case V4L2_CID_BLUE_BALANCE: + break; + case V4L2_CID_GAMMA: + break; + case V4L2_CID_EXPOSURE: + break; + case V4L2_CID_AUTOGAIN: + break; + case V4L2_CID_GAIN: + break; + case V4L2_CID_HFLIP: + break; + case V4L2_CID_VFLIP: + break; + default: + retval = -EPERM; + break; + } + + return retval; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index > ov5647_mode_MAX) + return -EINVAL; + + fsize->pixel_format = ov5647_data.pix.pixelformat; + fsize->discrete.width = + max(ov5647_mode_info_data[0][fsize->index].width, + ov5647_mode_info_data[1][fsize->index].width); + fsize->discrete.height = + max(ov5647_mode_info_data[0][fsize->index].height, + ov5647_mode_info_data[1][fsize->index].height); + return 0; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, + "ov5647_mipi_camera"); + + return 0; +} + +/*! + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + + return 0; +} + +/*! + * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT + * @s: pointer to standard V4L2 device structure + * @fmt: pointer to standard V4L2 fmt description structure + * + * Return 0. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index > ov5647_mode_MAX) + return -EINVAL; + + fmt->pixelformat = ov5647_data.pix.pixelformat; + + return 0; +} + +/*! + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct sensor_data *sensor = s->priv; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + int ret; + enum ov5647_frame_rate frame_rate; + void *mipi_csi2_info; + + ov5647_data.on = true; + + /* mclk */ + tgt_xclk = ov5647_data.mclk; + tgt_xclk = min(tgt_xclk, (u32)OV5647_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)OV5647_XCLK_MIN); + ov5647_data.mclk = tgt_xclk; + + pr_debug(" Setting mclk to %d MHz\n", tgt_xclk / 1000000); + + /* Default camera frame rate is set in probe */ + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 15) + frame_rate = ov5647_15_fps; + else if (tgt_fps == 30) + frame_rate = ov5647_30_fps; + else + return -EINVAL; /* Only support 15fps or 30fps now. */ + + mipi_csi2_info = mipi_csi2_get_info(); + + /* enable mipi csi2 */ + if (mipi_csi2_info) + mipi_csi2_enable(mipi_csi2_info); + else { + printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!\n", + __func__, __FILE__); + return -EPERM; + } + + ret = ov5647_init_mode(frame_rate, ov5647_mode_INIT, ov5647_mode_INIT); + + return ret; +} + +/*! + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the device when slave detaches to the master. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + void *mipi_csi2_info; + + mipi_csi2_info = mipi_csi2_get_info(); + + /* disable mipi csi2 */ + if (mipi_csi2_info) + if (mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_disable(mipi_csi2_info); + + return 0; +} + +/*! + * This structure defines all the ioctls for this module and links them to the + * enumeration. + */ +static struct v4l2_int_ioctl_desc ov5647_ioctl_desc[] = { + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func *) ioctl_dev_init}, + {vidioc_int_dev_exit_num, ioctl_dev_exit}, + {vidioc_int_s_power_num, (v4l2_int_ioctl_func *) ioctl_s_power}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *) ioctl_g_ifparm}, +/* {vidioc_int_g_needs_reset_num, + (v4l2_int_ioctl_func *)ioctl_g_needs_reset}, */ +/* {vidioc_int_reset_num, (v4l2_int_ioctl_func *)ioctl_reset}, */ + {vidioc_int_init_num, (v4l2_int_ioctl_func *) ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, +/* {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, */ + {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, +/* {vidioc_int_s_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, */ + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, +/* {vidioc_int_queryctrl_num, (v4l2_int_ioctl_func *)ioctl_queryctrl}, */ + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, + {vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *) ioctl_g_chip_ident}, +}; + +static struct v4l2_int_slave ov5647_slave = { + .ioctls = ov5647_ioctl_desc, + .num_ioctls = ARRAY_SIZE(ov5647_ioctl_desc), +}; + +static struct v4l2_int_device ov5647_int_device = { + .module = THIS_MODULE, + .name = "ov5647_mipi", + .type = v4l2_int_type_slave, + .u = { + .slave = &ov5647_slave, + }, +}; + +static ssize_t show_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val; + s32 rval = ov5647_read_reg(ov5647_data.last_reg, &val); + + return sprintf(buf, "ov5647[0x%04x]=0x%02x\n",ov5647_data.last_reg, rval); +} +static ssize_t set_reg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int regnum, value; + int num_parsed = sscanf(buf, "%04x=%02x", ®num, &value); + if (1 <= num_parsed) { + if (0xffff < (unsigned)regnum){ + pr_err("%s:invalid regnum %x\n", __func__, regnum); + return 0; + } + ov5647_data.last_reg = regnum; + } + if (2 == num_parsed) { + if (0xff < (unsigned)value) { + pr_err("%s:invalid value %x\n", __func__, value); + return 0; + } + ov5647_write_reg(ov5647_data.last_reg, value); + } + return count; +} +static DEVICE_ATTR(ov5647_reg, S_IRUGO|S_IWUGO, show_reg, set_reg); + +/*! + * ov5647 I2C probe function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +static int ov5647_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + int retval, init; + u8 chip_id_high, chip_id_low; + struct sensor_data *sensor = &ov5647_data; + enum of_gpio_flags flags; + + /* request power down pin */ + pwn_gpio = of_get_named_gpio_flags(dev->of_node, "pwn-gpios", 0, &flags); + if (gpio_is_valid(pwn_gpio)) { + pwn_active = !(flags & OF_GPIO_ACTIVE_LOW); + init = (flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + + retval = devm_gpio_request_one(dev, pwn_gpio, init, "ov5647_mipi_pwdn"); + if (retval < 0) { + dev_warn(dev, "request of pwn_gpio failed"); + pwn_gpio = -EINVAL; + } + } + + /* request reset pin */ + rst_gpio = of_get_named_gpio_flags(dev->of_node, "rst-gpios", 0, &flags); + if (gpio_is_valid(rst_gpio)) { + rst_active = !(flags & OF_GPIO_ACTIVE_LOW); + init = (flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + + retval = devm_gpio_request_one(dev, rst_gpio, init, "ov5647_mipi_reset"); + if (retval < 0) { + dev_warn(dev, "request of ov5647_mipi_reset failed"); + rst_gpio = -EINVAL; + } + } + + /* Set initial values for the sensor struct. */ + memset(&ov5647_data, 0, sizeof(ov5647_data)); + + sensor->mipi_camera = 1; + ov5647_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); + if (IS_ERR(ov5647_data.sensor_clk)) { + /* assuming clock enabled by default */ + ov5647_data.sensor_clk = NULL; + dev_err(dev, "clock-frequency missing or invalid\n"); + return PTR_ERR(ov5647_data.sensor_clk); + } + + retval = of_property_read_u32(dev->of_node, "mclk", + &(ov5647_data.mclk)); + if (retval) { + dev_err(dev, "mclk missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "mclk_source", + (u32 *) &(ov5647_data.mclk_source)); + if (retval) { + dev_err(dev, "mclk_source missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "ipu_id", + &sensor->ipu_id); + if (retval) { + dev_err(dev, "ipu_id missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "csi_id", + &(ov5647_data.csi)); + if (retval) { + dev_err(dev, "csi id missing or invalid\n"); + return retval; + } + + clk_prepare_enable(ov5647_data.sensor_clk); + + ov5647_data.io_init = ov5647_reset; + ov5647_data.i2c_client = client; + /* real OV5647 pixelformat is V4L2_PIX_FMT_SBGGR10. */ + /* i.MX6 CSI CPD convert 10 bits color data to 8 bits. */ + /* (see drivers/mxc/ipu3/ipu_capture.c - _ipu_csi_init) */ + ov5647_data.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + ov5647_data.pix.width = 1280; + ov5647_data.pix.height = 960; + ov5647_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | + V4L2_CAP_TIMEPERFRAME; + ov5647_data.streamcap.capturemode = 0; + ov5647_data.streamcap.timeperframe.denominator = DEFAULT_FPS; + ov5647_data.streamcap.timeperframe.numerator = 1; + + ov5647_power_on(dev); + + ov5647_reset(); + + ov5647_standby(0); + + retval = ov5647_read_reg(OV5647_CHIP_ID_HIGH_BYTE, &chip_id_high); + if (retval < 0 || chip_id_high != 0x56) { + pr_warning("camera ov5647_mipi is not found\n"); + clk_disable_unprepare(ov5647_data.sensor_clk); + return -ENODEV; + } + retval = ov5647_read_reg(OV5647_CHIP_ID_LOW_BYTE, &chip_id_low); + if (retval < 0 || chip_id_low != 0x47) { + pr_warning("camera ov5647_mipi is not found\n"); + clk_disable_unprepare(ov5647_data.sensor_clk); + return -ENODEV; + } + + sensor->virtual_channel = sensor->csi | (sensor->ipu_id << 1); + ov5647_standby(1); + + ov5647_int_device.priv = &ov5647_data; + retval = v4l2_int_device_register(&ov5647_int_device); + +// clk_disable_unprepare(ov5647_data.sensor_clk); + + if (device_create_file(dev, &dev_attr_ov5647_reg)) + dev_err(dev, "%s: error creating ov5647_reg entry\n", __func__); + pr_info("camera ov5647_mipi is found\n"); + return retval; +} + +/*! + * ov5647 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int ov5647_remove(struct i2c_client *client) +{ + v4l2_int_device_unregister(&ov5647_int_device); + + if (gpo_regulator) + regulator_disable(gpo_regulator); + + if (analog_regulator) + regulator_disable(analog_regulator); + + if (core_regulator) + regulator_disable(core_regulator); + + if (io_regulator) + regulator_disable(io_regulator); + + return 0; +} + +/*! + * ov5647 init function + * Called by insmod ov5647_camera.ko. + * + * @return Error code indicating success or failure + */ +static __init int ov5647_init(void) +{ + u8 err; + + err = i2c_add_driver(&ov5647_i2c_driver); + if (err != 0) + pr_err("%s:driver registration failed, error=%d\n", + __func__, err); + + return err; +} + +/*! + * OV5647 cleanup function + * Called on rmmod ov5647_camera.ko + * + * @return Error code indicating success or failure + */ +static void __exit ov5647_clean(void) +{ + i2c_del_driver(&ov5647_i2c_driver); +} + +module_init(ov5647_init); +module_exit(ov5647_clean); + +MODULE_AUTHOR("Viion Systems Inc."); +MODULE_DESCRIPTION("OV5647 MIPI Camera Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("CSI"); From 7400033bbea621472c09ff3b07cde8edcd43e8b2 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 27 Jan 2015 00:16:06 +0900 Subject: [PATCH 0366/1983] mxc_v4l2_capture: ov5647: fix type uninitialized issue --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 61ac0716285aa2..1d7e61da5c5baf 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -1391,6 +1391,7 @@ static int ioctl_enum_framesizes(struct v4l2_int_device *s, return -EINVAL; fsize->pixel_format = ov5647_data.pix.pixelformat; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = max(ov5647_mode_info_data[0][fsize->index].width, ov5647_mode_info_data[1][fsize->index].width); From 787b48d857886938a338f05d0fb9784d3ddeb23e Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 27 Jan 2015 00:18:03 +0900 Subject: [PATCH 0367/1983] mxc_v4l2_capture: add workaround for gstreamer --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 58cc19e33757fd..e1b9d1e4802f5e 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -2086,6 +2086,8 @@ static long mxc_v4l_do_ioctl(struct file *file, /*! * V4l2 VIDIOC_S_FMT ioctl */ + /* XXX: workaround for gstreamer */ + case VIDIOC_TRY_FMT: case VIDIOC_S_FMT: { struct v4l2_format *sf = arg; pr_debug(" case VIDIOC_S_FMT\n"); @@ -2551,7 +2553,8 @@ static long mxc_v4l_do_ioctl(struct file *file, break; } - case VIDIOC_TRY_FMT: + /* XXX: workaround for gstreamer */ +/* case VIDIOC_TRY_FMT: */ case VIDIOC_QUERYCTRL: case VIDIOC_G_TUNER: case VIDIOC_S_TUNER: From a043788f2c2394ea20b5719dae64c699f2a11d73 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sat, 31 Jan 2015 03:50:38 +0900 Subject: [PATCH 0368/1983] mxc_v4l2_capture: ov5647: rewrite gpio code --- .../media/platform/mxc/capture/ov5647_mipi.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 1d7e61da5c5baf..a0ad2ec08c3b2c 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -91,7 +91,7 @@ struct ov5647_mode_info { */ static struct sensor_data ov5647_data; static int pwn_gpio = -EINVAL; -static int pwn_active; +static int powon_active; static int rst_gpio = -EINVAL; static int rst_active; @@ -249,9 +249,9 @@ static void ov5647_standby(s32 enable) return; if (enable) - gpio_set_value(pwn_gpio, !pwn_active); + gpio_set_value(pwn_gpio, !powon_active); else - gpio_set_value(pwn_gpio, pwn_active); + gpio_set_value(pwn_gpio, powon_active); pr_debug("ov5647_mipi_camera_powerdown: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio); msleep(2); } @@ -266,10 +266,10 @@ static void ov5647_reset(void) /* camera power dowmn */ if (gpio_is_valid(pwn_gpio)) { - gpio_set_value(pwn_gpio, 1); + gpio_set_value(pwn_gpio, !powon_active); msleep(5); - gpio_set_value(pwn_gpio, 0); + gpio_set_value(pwn_gpio, powon_active); msleep(5); } @@ -280,7 +280,7 @@ static void ov5647_reset(void) msleep(5); if (gpio_is_valid(pwn_gpio)) - gpio_set_value(pwn_gpio, !pwn_active); + gpio_set_value(pwn_gpio, !powon_active); } static int ov5647_power_on(struct device *dev) @@ -1613,7 +1613,9 @@ static int ov5647_probe(struct i2c_client *client, /* request power down pin */ pwn_gpio = of_get_named_gpio_flags(dev->of_node, "pwn-gpios", 0, &flags); if (gpio_is_valid(pwn_gpio)) { - pwn_active = !(flags & OF_GPIO_ACTIVE_LOW); + /* powon_active - camera power on */ + /* !powon_active - camera power down */ + powon_active = !(flags & OF_GPIO_ACTIVE_LOW); init = (flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; retval = devm_gpio_request_one(dev, pwn_gpio, init, "ov5647_mipi_pwdn"); @@ -1626,6 +1628,8 @@ static int ov5647_probe(struct i2c_client *client, /* request reset pin */ rst_gpio = of_get_named_gpio_flags(dev->of_node, "rst-gpios", 0, &flags); if (gpio_is_valid(rst_gpio)) { + /* rst_active - camera reset */ + /* !rst_active - clear camera reset */ rst_active = !(flags & OF_GPIO_ACTIVE_LOW); init = (flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; From a059130fffc38b5be56ac878c2ba58d92d8f4ad0 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sat, 31 Jan 2015 22:41:50 +0900 Subject: [PATCH 0369/1983] mxc_v4l2_capture: ov5647: add support for 1280x720(720p) --- .../media/platform/mxc/capture/ov5647_mipi.c | 113 +++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index a0ad2ec08c3b2c..b5a3abb6822dc9 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -53,7 +53,8 @@ enum ov5647_mode { ov5647_mode_MIN = 0, ov5647_mode_960P_1280_960 = 0, - ov5647_mode_MAX = 0, + ov5647_mode_720P_1280_720 = 1, + ov5647_mode_MAX = 1, ov5647_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -199,18 +200,126 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; - +static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x21 , 0 , 0 } , + { 0x3036 , 0x50 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x09 , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x06 , 0 , 0 } , + { 0x380d , 0xd6 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x05 , 0 , 0 } , + { 0x3809 , 0x00 , 0 , 0 } , + { 0x380a , 0x02 , 0 , 0 } , + { 0x380b , 0xd0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x06 , 0 , 0 } , + { 0x3807 , 0xb2 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3503 , 0x03 , 0 , 0 } , /* manual AE */ + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , /* manual AWB */ + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, ov5647_setting_30fps_960P_1280_960, ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960)}, + {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5647_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)}, }, { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, ov5647_setting_30fps_960P_1280_960, ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960)}, + {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5647_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)}, }, }; From cd3248216bbbb21d85dc5d2c2061b0d9f7c813f6 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 1 Feb 2015 16:18:06 +0900 Subject: [PATCH 0370/1983] mxc_v4l2_capture: add workaround for gstreamer fix 720p capturing issue. --- .../platform/mxc/capture/mxc_v4l2_capture.c | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index e1b9d1e4802f5e..66bf4aa05bc5d1 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -812,7 +812,7 @@ static int mxc_v4l2_g_fmt(cam_data *cam, struct v4l2_format *f) * * @return status 0 success, EINVAL failed */ -static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) +static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f, bool try_fmt) { int retval = 0; int size = 0; @@ -932,10 +932,22 @@ static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) else bytesperline = f->fmt.pix.bytesperline; - if (f->fmt.pix.sizeimage < size) - f->fmt.pix.sizeimage = size; - else - size = f->fmt.pix.sizeimage; + if (try_fmt) { + /* XXX: workaround for gstreamer */ + if (f->fmt.pix.sizeimage < size || + f->fmt.pix.sizeimage % size) + f->fmt.pix.sizeimage = size; + else + size = f->fmt.pix.sizeimage; + + break; + } + else { + if (f->fmt.pix.sizeimage < size) + f->fmt.pix.sizeimage = size; + else + size = f->fmt.pix.sizeimage; + } cam->v2f.fmt.pix = f->fmt.pix; @@ -951,7 +963,8 @@ static int mxc_v4l2_s_fmt(cam_data *cam, struct v4l2_format *f) case V4L2_BUF_TYPE_VIDEO_OVERLAY: pr_debug(" type=V4L2_BUF_TYPE_VIDEO_OVERLAY\n"); retval = verify_preview(cam, &f->fmt.win); - cam->win = f->fmt.win; + if (!try_fmt) + cam->win = f->fmt.win; break; default: retval = -EINVAL; @@ -2087,11 +2100,16 @@ static long mxc_v4l_do_ioctl(struct file *file, * V4l2 VIDIOC_S_FMT ioctl */ /* XXX: workaround for gstreamer */ - case VIDIOC_TRY_FMT: + case VIDIOC_TRY_FMT: { + struct v4l2_format *sf = arg; + pr_debug(" case VIDIOC_TRY_FMT\n"); + retval = mxc_v4l2_s_fmt(cam, sf, true); + break; + } case VIDIOC_S_FMT: { struct v4l2_format *sf = arg; pr_debug(" case VIDIOC_S_FMT\n"); - retval = mxc_v4l2_s_fmt(cam, sf); + retval = mxc_v4l2_s_fmt(cam, sf, false); break; } From 33ac6b0df17e1897d43d20326144eafc5aae306a Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 1 Feb 2015 16:29:20 +0900 Subject: [PATCH 0371/1983] mxc_v4l2_capture: ov5647: add support for capturing mode setting usage: - 960p mode(default) echo 0 > /sys/bus/i2c/devices/0-0036/ov5647_mode - 720p mode echo 1 > /sys/bus/i2c/devices/0-0036/ov5647_mode --- .../media/platform/mxc/capture/ov5647_mipi.c | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index b5a3abb6822dc9..d5bfd99cbcb3ac 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -1068,11 +1068,10 @@ static int ov5647_init_mode(enum ov5647_frame_rate frame_rate, } if (mode == ov5647_mode_INIT) { - pModeSetting = ov5647_setting_30fps_960P_1280_960; - ArySize = ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960); + int index = (int)ov5647_data.streamcap.capturemode; - ov5647_data.pix.width = 1280; - ov5647_data.pix.height = 960; + pModeSetting = ov5647_mode_info_data[frame_rate][index].init_data_ptr; + ArySize = ov5647_mode_info_data[frame_rate][index].init_data_size; retval = ov5647_download_firmware(pModeSetting, ArySize); if (retval < 0) goto err; @@ -1702,7 +1701,38 @@ static ssize_t set_reg(struct device *dev, } return count; } + +/* XXX: workaround for v4l2 client except for gstreamer-imx */ +static ssize_t show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ov5647 mode = 0x%02x\n", (int)ov5647_data.streamcap.capturemode); +} + +static ssize_t set_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long mode; + + mode = simple_strtoul(buf, NULL, 10); + if ((enum ov5647_mode)mode >= ov5647_mode_MIN && + (enum ov5647_mode)mode <= ov5647_mode_MAX) { + + ov5647_data.streamcap.capturemode = mode; + ov5647_data.pix.width = + max(ov5647_mode_info_data[0][mode].width, + ov5647_mode_info_data[1][mode].width); + ov5647_data.pix.height = + max(ov5647_mode_info_data[0][mode].height, + ov5647_mode_info_data[1][mode].height); + } + + return count; +} + static DEVICE_ATTR(ov5647_reg, S_IRUGO|S_IWUGO, show_reg, set_reg); +static DEVICE_ATTR(ov5647_mode, S_IRUGO|S_IWUGO, show_mode, set_mode); /*! * ov5647 I2C probe function @@ -1801,7 +1831,7 @@ static int ov5647_probe(struct i2c_client *client, ov5647_data.pix.height = 960; ov5647_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | V4L2_CAP_TIMEPERFRAME; - ov5647_data.streamcap.capturemode = 0; + ov5647_data.streamcap.capturemode = ov5647_mode_960P_1280_960; ov5647_data.streamcap.timeperframe.denominator = DEFAULT_FPS; ov5647_data.streamcap.timeperframe.numerator = 1; @@ -1834,6 +1864,9 @@ static int ov5647_probe(struct i2c_client *client, if (device_create_file(dev, &dev_attr_ov5647_reg)) dev_err(dev, "%s: error creating ov5647_reg entry\n", __func__); + if (device_create_file(dev, &dev_attr_ov5647_mode)) + dev_err(dev, "%s: error creating ov5647_mode entry\n", __func__); + pr_info("camera ov5647_mipi is found\n"); return retval; } From 7b83ee4f1183326f1e3b16ae742d72b63db891ca Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Mon, 2 Feb 2015 00:12:56 +0900 Subject: [PATCH 0372/1983] mxc_v4l2_capture: ov5647: add support for 1920x1080(1080p) usage: echo 2 > /sys/bus/i2c/devices/0-0036/ov5647_mode --- .../media/platform/mxc/capture/ov5647_mipi.c | 114 +++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index d5bfd99cbcb3ac..f82b38ce6e0cd2 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -54,7 +54,8 @@ enum ov5647_mode { ov5647_mode_MIN = 0, ov5647_mode_960P_1280_960 = 0, ov5647_mode_720P_1280_720 = 1, - ov5647_mode_MAX = 1, + ov5647_mode_1080P_1920_1080 = 2, + ov5647_mode_MAX = 2, ov5647_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -304,6 +305,108 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x21 , 0 , 0 } , + { 0x3036 , 0x41 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x06 , 0 , 0 } , + { 0x3820 , 0x00 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x04 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x36 , 0 , 0 } , + { 0x380d , 0x4e , 0 , 0 } , + { 0x380e , 0x04 , 0 , 0 } , + { 0x380f , 0x60 , 0 , 0 } , + { 0x3814 , 0x11 , 0 , 0 } , + { 0x3815 , 0x11 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x07 , 0 , 0 } , + { 0x3809 , 0x80 , 0 , 0 } , + { 0x380a , 0x04 , 0 , 0 } , + { 0x380b , 0x39 , 0 , 0 } , + { 0x3800 , 0x01 , 0 , 0 } , + { 0x3801 , 0x5c , 0 , 0 } , + { 0x3802 , 0x01 , 0 , 0 } , + { 0x3803 , 0xb2 , 0 , 0 } , + { 0x3804 , 0x08 , 0 , 0 } , + { 0x3805 , 0xe7 , 0 , 0 } , + { 0x3806 , 0x05 , 0 , 0 } , + { 0x3807 , 0xf1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -312,6 +415,9 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, ov5647_setting_30fps_720P_1280_720, ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)}, + {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, + ov5647_setting_30fps_1080P_1920_1080, + ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, }, { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -320,6 +426,9 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, ov5647_setting_30fps_720P_1280_720, ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)}, + {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, + ov5647_setting_30fps_1080P_1920_1080, + ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, }, }; @@ -880,7 +989,8 @@ static int ov5647_change_mode_exposure_calc(enum ov5647_frame_rate frame_rate, /* read preview shutter */ prev_shutter = OV5647_get_shutter(); - if ((binning_on()) && (mode != ov5647_mode_960P_1280_960)) + if ((binning_on()) && (mode != ov5647_mode_960P_1280_960) && + (mode != ov5647_mode_720P_1280_720) && (mode != ov5647_mode_1080P_1920_1080)) prev_shutter *= 2; /* read preview gain */ From 86f4af455173d6fd1bd2bc1a24c9698b17bb9ce0 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Mon, 2 Feb 2015 00:33:46 +0900 Subject: [PATCH 0373/1983] mxc_v4l2_capture: ov5647: improve register settings --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index f82b38ce6e0cd2..e626a8b5df616d 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -109,7 +109,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x3820 , 0x41 , 0 , 0 } , { 0x3827 , 0xec , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , - { 0x3612 , 0x09 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , @@ -191,12 +191,10 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif - { 0x3503 , 0x03 , 0 , 0 } , /* manual AE */ { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , { 0x350b , 0x7f , 0 , 0 } , - { 0x5001 , 0x01 , 0 , 0 } , /* manual AWB */ { 0x3c00 , 0x04 , 0 , 300 } , { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; @@ -213,7 +211,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x3820 , 0x41 , 0 , 0 } , { 0x3827 , 0xec , 0 , 0 } , { 0x370c , 0x03 , 0 , 0 } , - { 0x3612 , 0x09 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , @@ -295,12 +293,10 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif - { 0x3503 , 0x03 , 0 , 0 } , /* manual AE */ { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , { 0x350b , 0x7f , 0 , 0 } , - { 0x5001 , 0x01 , 0 , 0 } , /* manual AWB */ { 0x3c00 , 0x04 , 0 , 300 } , { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; From 88a05acc4583b80dc146a57e84ef6092a9503ad4 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Mon, 2 Feb 2015 01:22:34 +0900 Subject: [PATCH 0374/1983] mxc_v4l2_capture: ov5647: add support for 640x480(VGA) usage: echo 3 > /sys/bus/i2c/devices/0-0036/ov5647_mode --- .../media/platform/mxc/capture/ov5647_mipi.c | 111 +++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index e626a8b5df616d..588ccf514730fe 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -55,7 +55,8 @@ enum ov5647_mode { ov5647_mode_960P_1280_960 = 0, ov5647_mode_720P_1280_720 = 1, ov5647_mode_1080P_1920_1080 = 2, - ov5647_mode_MAX = 2, + ov5647_mode_VGA_640_480 = 3, + ov5647_mode_MAX = 3, ov5647_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -403,6 +404,108 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x21 , 0 , 0 } , + { 0x3036 , 0x37 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x07 , 0 , 0 } , + { 0x380d , 0x68 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x02 , 0 , 0 } , + { 0x3809 , 0x80 , 0 , 0 } , + { 0x380a , 0x01 , 0 , 0 } , + { 0x380b , 0xe0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x07 , 0 , 0 } , + { 0x3807 , 0xa1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -414,6 +517,9 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, ov5647_setting_30fps_1080P_1920_1080, ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, + {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, + ov5647_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480)}, }, { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -425,6 +531,9 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, ov5647_setting_30fps_1080P_1920_1080, ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, + {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, + ov5647_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480)}, }, }; From 3851e6ac499ff6d2308635fee9c97d22109d1c9e Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Mon, 2 Feb 2015 22:16:29 +0900 Subject: [PATCH 0375/1983] mxc_v4l2_capture: ov5647: clock tuning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 1080p set clock same as Raspberry Pi camera(calibrate 10/8bit difference) -- Raspberry Pi camera 1080p clock == 0x3035:0x21/0x3036:0x62(RPi community's information) - 960p - 720p - VGA calculated based on 1080p clock and ov5640_mipi.c --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 588ccf514730fe..2d43c5b6e2056e 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -103,7 +103,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x37 , 0 , 0 } , + { 0x3036 , 0x49 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -204,8 +204,8 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ - { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x50 , 0 , 0 } , + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x7b , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -307,7 +307,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x41 , 0 , 0 } , + { 0x3036 , 0x7b , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x06 , 0 , 0 } , @@ -409,7 +409,7 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x37 , 0 , 0 } , + { 0x3036 , 0x52 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , From bb24e9b9f40a9d3bef6c1bbd0d364dad98f1bc6f Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Mon, 2 Feb 2015 22:27:08 +0900 Subject: [PATCH 0376/1983] mxc_v4l2_capture: ov5647: restore default register value(4-7bit) fix misunderstanding of register 0x3034[7:4] --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 2d43c5b6e2056e..2f6b2beb3d2d2d 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -101,7 +101,7 @@ static int rst_active; static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , - { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x49 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , @@ -203,7 +203,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , - { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , { 0x3036 , 0x7b , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , @@ -305,7 +305,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , - { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x7b , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , @@ -407,7 +407,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , - { 0x3034 , 0x08 , 0 , 0 } , /* was 0x1A */ + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , { 0x3036 , 0x52 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , @@ -1271,7 +1271,7 @@ static int ov5647_init_mode(enum ov5647_frame_rate frame_rate, if (mode == ov5647_mode_INIT) mipi_csi2_reset(mipi_csi2_info); - /* reg 0x3034 == 0x08 is 8bit mode */ + /* reg 0x3034[3:0] == 0x8 is 8bit mode */ mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_RAW8); if (orig_mode != ov5647_mode_INIT) { From 435d8c3ab85747c71275a647f06034403dd3467f Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Mon, 2 Feb 2015 23:13:31 +0900 Subject: [PATCH 0377/1983] mxc_v4l2_capture: ov5647: fix VGA subsample setting --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 2f6b2beb3d2d2d..1d720a398861ab 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -439,8 +439,8 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x380d , 0x68 , 0 , 0 } , { 0x380e , 0x03 , 0 , 0 } , { 0x380f , 0xd8 , 0 , 0 } , - { 0x3814 , 0x31 , 0 , 0 } , - { 0x3815 , 0x31 , 0 , 0 } , + { 0x3814 , 0x71 , 0 , 0 } , + { 0x3815 , 0x71 , 0 , 0 } , { 0x3708 , 0x64 , 0 , 0 } , { 0x3709 , 0x52 , 0 , 0 } , { 0x3808 , 0x02 , 0 , 0 } , From 34e1e9eb47651071afe07128c890192e244b3fca Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 3 Feb 2015 00:19:06 +0900 Subject: [PATCH 0378/1983] mxc_v4l2_capture: ov5647: add support for 15fps --- .../media/platform/mxc/capture/ov5647_mipi.c | 424 +++++++++++++++++- 1 file changed, 416 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 1d720a398861ab..f7e8df0897a451 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -200,6 +200,108 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x49 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x07 , 0 , 0 } , + { 0x380d , 0x68 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x05 , 0 , 0 } , + { 0x3809 , 0x00 , 0 , 0 } , + { 0x380a , 0x03 , 0 , 0 } , + { 0x380b , 0xc0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x07 , 0 , 0 } , + { 0x3807 , 0xa1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , @@ -302,6 +404,108 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x3d , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x06 , 0 , 0 } , + { 0x380d , 0xd6 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x05 , 0 , 0 } , + { 0x3809 , 0x00 , 0 , 0 } , + { 0x380a , 0x02 , 0 , 0 } , + { 0x380b , 0xd0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x06 , 0 , 0 } , + { 0x3807 , 0xb2 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , @@ -404,6 +608,108 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x7b , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x06 , 0 , 0 } , + { 0x3820 , 0x00 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x04 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x36 , 0 , 0 } , + { 0x380d , 0x4e , 0 , 0 } , + { 0x380e , 0x04 , 0 , 0 } , + { 0x380f , 0x60 , 0 , 0 } , + { 0x3814 , 0x11 , 0 , 0 } , + { 0x3815 , 0x11 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x07 , 0 , 0 } , + { 0x3809 , 0x80 , 0 , 0 } , + { 0x380a , 0x04 , 0 , 0 } , + { 0x380b , 0x39 , 0 , 0 } , + { 0x3800 , 0x01 , 0 , 0 } , + { 0x3801 , 0x5c , 0 , 0 } , + { 0x3802 , 0x01 , 0 , 0 } , + { 0x3803 , 0xb2 , 0 , 0 } , + { 0x3804 , 0x08 , 0 , 0 } , + { 0x3805 , 0xe7 , 0 , 0 } , + { 0x3806 , 0x05 , 0 , 0 } , + { 0x3807 , 0xf1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x0100 , 0x00 , 0 , 0 } , { 0x0103 , 0x01 , 0 , 0 } , @@ -506,20 +812,122 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x52 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5000 , 0x06 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x07 , 0 , 0 } , + { 0x380d , 0x68 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x71 , 0 , 0 } , + { 0x3815 , 0x71 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x02 , 0 , 0 } , + { 0x3809 , 0x80 , 0 , 0 } , + { 0x380a , 0x01 , 0 , 0 } , + { 0x380b , 0xe0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x07 , 0 , 0 } , + { 0x3807 , 0xa1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 0 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, - ov5647_setting_30fps_960P_1280_960, - ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960)}, + ov5647_setting_15fps_960P_1280_960, + ARRAY_SIZE(ov5647_setting_15fps_960P_1280_960)}, {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, - ov5647_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)}, + ov5647_setting_15fps_720P_1280_720, + ARRAY_SIZE(ov5647_setting_15fps_720P_1280_720)}, {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, - ov5647_setting_30fps_1080P_1920_1080, - ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, + ov5647_setting_15fps_1080P_1920_1080, + ARRAY_SIZE(ov5647_setting_15fps_1080P_1920_1080)}, {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, - ov5647_setting_30fps_VGA_640_480, - ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480)}, + ov5647_setting_15fps_VGA_640_480, + ARRAY_SIZE(ov5647_setting_15fps_VGA_640_480)}, }, { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, From a4e739bf6390a732556cab315bae01ddd9f0d53f Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 3 Feb 2015 22:37:52 +0900 Subject: [PATCH 0379/1983] mxc_v4l2_capture: ov5647: add LED turn on/off gpio --- .../media/platform/mxc/capture/ov5647_mipi.c | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index f7e8df0897a451..6a96f09734af08 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -97,6 +97,8 @@ static int pwn_gpio = -EINVAL; static int powon_active; static int rst_gpio = -EINVAL; static int rst_active; +static int led_gpio = -EINVAL; +static int led_active; static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x0100 , 0x00 , 0 , 0 } , @@ -979,10 +981,17 @@ static void ov5647_standby(s32 enable) if (!gpio_is_valid(pwn_gpio)) return; - if (enable) + if (enable) { gpio_set_value(pwn_gpio, !powon_active); - else + if (gpio_is_valid(led_gpio)) + gpio_set_value(led_gpio, !led_active); + } + else { gpio_set_value(pwn_gpio, powon_active); + if (gpio_is_valid(led_gpio)) + gpio_set_value(led_gpio, led_active); + } + pr_debug("ov5647_mipi_camera_powerdown: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio); msleep(2); } @@ -2367,7 +2376,7 @@ static int ov5647_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - int retval, init; + int retval, init, gpio; u8 chip_id_high, chip_id_low; struct sensor_data *sensor = &ov5647_data; enum of_gpio_flags flags; @@ -2402,6 +2411,21 @@ static int ov5647_probe(struct i2c_client *client, } } + /* request LED(for sanity) pin */ + gpio = of_get_named_gpio_flags(dev->of_node, "led-gpios", 0, &flags); + if (gpio_is_valid(gpio)) { + /* led_active - LED turn on */ + /* !led_active - LED trun off */ + led_active = !(flags & OF_GPIO_ACTIVE_LOW); + init = (flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + + retval = devm_gpio_request_one(dev, gpio, init, "ov5647_mipi_led"); + if (retval < 0) { + dev_warn(dev, "request of led_gpio failed"); + gpio = -EINVAL; + } + } + /* Set initial values for the sensor struct. */ memset(&ov5647_data, 0, sizeof(ov5647_data)); @@ -2480,6 +2504,7 @@ static int ov5647_probe(struct i2c_client *client, sensor->virtual_channel = sensor->csi | (sensor->ipu_id << 1); ov5647_standby(1); + led_gpio = gpio; ov5647_int_device.priv = &ov5647_data; retval = v4l2_int_device_register(&ov5647_int_device); From 713059854e84b3f6b10490b83a5184415b734231 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Wed, 4 Feb 2015 23:18:04 +0900 Subject: [PATCH 0380/1983] mxc_v4l2_capture: ov5647: add workaround for i2c write error --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 6a96f09734af08..50fa52d3a963b0 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -182,7 +182,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -284,7 +284,7 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -386,7 +386,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -488,7 +488,7 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -590,7 +590,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -692,7 +692,7 @@ static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -794,7 +794,7 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , @@ -896,7 +896,7 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x3a1b , 0x58 , 0 , 0 } , { 0x3a1e , 0x50 , 0 , 0 } , { 0x3a11 , 0x60 , 0 , 0 } , - { 0x3a1f , 0x28 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , { 0x4001 , 0x02 , 0 , 0 } , { 0x4004 , 0x02 , 0 , 0 } , { 0x4000 , 0x09 , 0 , 0 } , From 82e1a85cf2f75e6e943c174b250ff9efee1a67c7 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 8 Feb 2015 01:44:22 +0900 Subject: [PATCH 0381/1983] mxc_v4l2_capture: ov5647: color balance tuning --- .../media/platform/mxc/capture/ov5647_mipi.c | 512 +++++++++++++++++- 1 file changed, 504 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 50fa52d3a963b0..2e4d87c7ceecf7 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -114,7 +114,6 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -194,6 +193,69 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -216,7 +278,6 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -296,6 +357,69 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -318,7 +442,6 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -398,6 +521,69 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -420,7 +606,6 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -500,6 +685,69 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -522,7 +770,6 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x04 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -602,6 +849,69 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -624,7 +934,6 @@ static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x04 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -704,6 +1013,69 @@ static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -726,7 +1098,6 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -806,6 +1177,69 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , @@ -828,7 +1262,6 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5000 , 0x06 , 0 , 0 } , { 0x5001 , 0x00 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , @@ -908,6 +1341,69 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ #endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , { 0x350a , 0x00 , 0 , 0 } , From 8b9ce57764f68822e61b17acbc7e5768bfc27c75 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Fri, 13 Feb 2015 01:44:38 +0900 Subject: [PATCH 0382/1983] mxc_v4l2_capture: ov5647: clock tuning to improbe fps --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 2e4d87c7ceecf7..122f4d30794fc4 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -105,7 +105,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x49 , 0 , 0 } , + { 0x3036 , 0x6a , 0 , 0 } , /* XXX: maybe not 30fps(workaround) */ { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -433,7 +433,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , - { 0x3036 , 0x7b , 0 , 0 } , + { 0x3036 , 0xac , 0 , 0 } , /* XXX: maybe not 30fps(workaround) */ { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -761,7 +761,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x7b , 0 , 0 } , + { 0x3036 , 0xb8 , 0 , 0 } , /* XXX: maybe not 30fps(workaround) */ { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x06 , 0 , 0 } , From 42e4a8e112fd8aea6f986a6b8fa3c4bdc20a2105 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sat, 14 Feb 2015 22:21:07 +0900 Subject: [PATCH 0383/1983] Revert "mxc_v4l2_capture: ov5647: clock tuning to improbe fps" This reverts commit 1136eceb57d44ef4e9dd4ca256817f5d0f72c73d. (fix low brightness) --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 122f4d30794fc4..2e4d87c7ceecf7 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -105,7 +105,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x6a , 0 , 0 } , /* XXX: maybe not 30fps(workaround) */ + { 0x3036 , 0x49 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -433,7 +433,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , - { 0x3036 , 0xac , 0 , 0 } , /* XXX: maybe not 30fps(workaround) */ + { 0x3036 , 0x7b , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -761,7 +761,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0xb8 , 0 , 0 } , /* XXX: maybe not 30fps(workaround) */ + { 0x3036 , 0x7b , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x06 , 0 , 0 } , From 12dd70e49279e757dd2eebf9143ea11f71202340 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sat, 14 Feb 2015 22:22:58 +0900 Subject: [PATCH 0384/1983] mxc_v4l2_capture: ov5647: add support for 1024x768(XGA) usage: echo 4 > /sys/bus/i2c/devices/0-0036/ov5647_mode --- .../media/platform/mxc/capture/ov5647_mipi.c | 353 +++++++++++++++++- 1 file changed, 344 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 2e4d87c7ceecf7..30309e51b4064e 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -56,7 +56,8 @@ enum ov5647_mode { ov5647_mode_720P_1280_720 = 1, ov5647_mode_1080P_1920_1080 = 2, ov5647_mode_VGA_640_480 = 3, - ov5647_mode_MAX = 3, + ov5647_mode_XGA_1024_768 = 4, + ov5647_mode_MAX = 4, ov5647_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -1412,34 +1413,368 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_30fps_XGA_1024_768[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x21 , 0 , 0 } , + { 0x3036 , 0x52 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x07 , 0 , 0 } , + { 0x380d , 0x68 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x04 , 0 , 0 } , + { 0x3809 , 0x00 , 0 , 0 } , + { 0x380a , 0x03 , 0 , 0 } , + { 0x380b , 0x00 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x07 , 0 , 0 } , + { 0x3807 , 0xa1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + +static struct reg_value ov5647_setting_15fps_XGA_1024_768[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x52 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5001 , 0x00 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x07 , 0 , 0 } , + { 0x380d , 0x68 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x04 , 0 , 0 } , + { 0x3809 , 0x00 , 0 , 0 } , + { 0x380a , 0x03 , 0 , 0 } , + { 0x380b , 0x00 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x07 , 0 , 0 } , + { 0x3807 , 0xa1 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xcc , 0 , 0 } , + { 0x5825 , 0xcc , 0 , 0 } , + { 0x5826 , 0xcc , 0 , 0 } , + { 0x5827 , 0xcc , 0 , 0 } , + { 0x5828 , 0xcc , 0 , 0 } , + { 0x5829 , 0xcc , 0 , 0 } , + { 0x582a , 0xbb , 0 , 0 } , + { 0x582b , 0xbb , 0 , 0 } , + { 0x582c , 0xbb , 0 , 0 } , + { 0x582d , 0xcc , 0 , 0 } , + { 0x582e , 0xcc , 0 , 0 } , + { 0x582f , 0xbb , 0 , 0 } , + { 0x5830 , 0xaa , 0 , 0 } , + { 0x5831 , 0xbb , 0 , 0 } , + { 0x5832 , 0xcc , 0 , 0 } , + { 0x5833 , 0xcc , 0 , 0 } , + { 0x5834 , 0xbb , 0 , 0 } , + { 0x5835 , 0xbb , 0 , 0 } , + { 0x5836 , 0xbb , 0 , 0 } , + { 0x5837 , 0xcc , 0 , 0 } , + { 0x5838 , 0xcc , 0 , 0 } , + { 0x5839 , 0xcc , 0 , 0 } , + { 0x583a , 0xcc , 0 , 0 } , + { 0x583b , 0xcc , 0 , 0 } , + { 0x583c , 0xcc , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { { - {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, + {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, ov5647_setting_15fps_960P_1280_960, ARRAY_SIZE(ov5647_setting_15fps_960P_1280_960)}, - {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, ov5647_setting_15fps_720P_1280_720, ARRAY_SIZE(ov5647_setting_15fps_720P_1280_720)}, - {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, + {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, ov5647_setting_15fps_1080P_1920_1080, ARRAY_SIZE(ov5647_setting_15fps_1080P_1920_1080)}, - {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, + {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, ov5647_setting_15fps_VGA_640_480, ARRAY_SIZE(ov5647_setting_15fps_VGA_640_480)}, + {ov5647_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, + ov5647_setting_15fps_XGA_1024_768, + ARRAY_SIZE(ov5647_setting_15fps_XGA_1024_768)}, }, { - {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, + {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, ov5647_setting_30fps_960P_1280_960, ARRAY_SIZE(ov5647_setting_30fps_960P_1280_960)}, - {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + {ov5647_mode_720P_1280_720, SUBSAMPLING, 1280, 720, ov5647_setting_30fps_720P_1280_720, ARRAY_SIZE(ov5647_setting_30fps_720P_1280_720)}, - {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, + {ov5647_mode_1080P_1920_1080, SCALING, 1920, 1080, ov5647_setting_30fps_1080P_1920_1080, ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, - {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, + {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, ov5647_setting_30fps_VGA_640_480, ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480)}, + {ov5647_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, + ov5647_setting_30fps_XGA_1024_768, + ARRAY_SIZE(ov5647_setting_30fps_XGA_1024_768)}, }, }; From 41fecd400b2a25394b8202292f4cf3b8056bc1f8 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 15 Feb 2015 00:13:06 +0900 Subject: [PATCH 0385/1983] mxc_v4l2_capture: ov5647: clock tuning --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 30309e51b4064e..cdcc409c909501 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -106,7 +106,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x21 , 0 , 0 } , - { 0x3036 , 0x49 , 0 , 0 } , + { 0x3036 , 0x52 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -270,7 +270,7 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , - { 0x3036 , 0x49 , 0 , 0 } , + { 0x3036 , 0x52 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -434,7 +434,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , - { 0x3036 , 0x7b , 0 , 0 } , + { 0x3036 , 0x92 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -598,7 +598,7 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , - { 0x3036 , 0x3d , 0 , 0 } , + { 0x3036 , 0x49 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , From df206a8227e256e1e66ef5ffbf4d2d25a92536cc Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sun, 15 Feb 2015 00:57:58 +0900 Subject: [PATCH 0386/1983] mxc_v4l2_capture: ov5647: change default resolution to 1024x768 --- drivers/media/platform/mxc/capture/ov5647_mipi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index cdcc409c909501..441f3d38d5cb4f 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -3305,11 +3305,11 @@ static int ov5647_probe(struct i2c_client *client, /* i.MX6 CSI CPD convert 10 bits color data to 8 bits. */ /* (see drivers/mxc/ipu3/ipu_capture.c - _ipu_csi_init) */ ov5647_data.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; - ov5647_data.pix.width = 1280; - ov5647_data.pix.height = 960; + ov5647_data.pix.width = 1024; + ov5647_data.pix.height = 768; ov5647_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | V4L2_CAP_TIMEPERFRAME; - ov5647_data.streamcap.capturemode = ov5647_mode_960P_1280_960; + ov5647_data.streamcap.capturemode = ov5647_mode_XGA_1024_768; ov5647_data.streamcap.timeperframe.denominator = DEFAULT_FPS; ov5647_data.streamcap.timeperframe.numerator = 1; From d6c5a84cd504be91e84c52a0b5ab774ae75d6241 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sat, 28 Mar 2015 13:36:01 +0900 Subject: [PATCH 0387/1983] merge Freescale Virtual frame buffer driver https://github.com/Freescale/linux-module-virtfb --- drivers/video/Kconfig | 5 + drivers/video/Makefile | 1 + drivers/video/vfb_phymem.c | 498 +++++++++++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 drivers/video/vfb_phymem.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 329f44aa53661f..1ac2ce6c5c6b24 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2276,6 +2276,11 @@ config FB_VIRTUAL If unsure, say N. +config FB_VIRTUAL_PHYMEM + tristate "Virtual Frame Buffer with contiguous physical memory regions support" + depends on FB + default n + config XEN_FBDEV_FRONTEND tristate "Xen virtual frame buffer support" depends on FB && XEN diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 2dcc9cf1f4f4a4..2753d33db68043 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -172,6 +172,7 @@ obj-$(CONFIG_FB_SIMPLE) += simplefb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o +obj-$(CONFIG_FB_VIRTUAL_PHYMEM) += vfb_phymem.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o diff --git a/drivers/video/vfb_phymem.c b/drivers/video/vfb_phymem.c new file mode 100644 index 00000000000000..0ec9a312906cf2 --- /dev/null +++ b/drivers/video/vfb_phymem.c @@ -0,0 +1,498 @@ +/* + * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/*! + * @defgroup Framebuffer Framebuffer Driver for surface sharing. + */ + +/*! + * @file virtual_fb.c + * + * @brief Virtual Frame buffer driver for surface sharing + * + * @ingroup Framebuffer + */ + +/*! + * Include files + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Driver name + */ +#define VIRT_FB_NAME "virtual_fb" + + +static int vfbcount = 1; +module_param(vfbcount, int, 0); +static struct fb_info ** g_fb_list; + +static int virtfb_map_video_memory(struct fb_info *fbi); +static int virtfb_unmap_video_memory(struct fb_info *fbi); + +/* + * Set fixed framebuffer parameters based on variable settings. + * + * @param info framebuffer information pointer + */ +static int virtfb_set_fix(struct fb_info *info) +{ + struct fb_fix_screeninfo *fix = &info->fix; + struct fb_var_screeninfo *var = &info->var; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ywrapstep = 1; + fix->ypanstep = 1; + + return 0; +} + + +/* + * Set framebuffer parameters and change the operating mode. + * + * @param info framebuffer information pointer + */ +static int virtfb_set_par(struct fb_info *fbi) +{ + int retval = 0; + u32 mem_len; + + dev_dbg(fbi->device, "Reconfiguring framebuffer\n"); + + virtfb_set_fix(fbi); + + mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { + if (fbi->fix.smem_start) + virtfb_unmap_video_memory(fbi); + + if (virtfb_map_video_memory(fbi) < 0) + return -ENOMEM; + } + + + return retval; +} + + +/* + * Check framebuffer variable parameters and adjust to valid values. + * + * @param var framebuffer variable parameters + * + * @param info framebuffer information pointer + */ +static int virtfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + + /* fg should not bigger than bg */ + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && + (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) && + (var->bits_per_pixel != 8)) + var->bits_per_pixel = 16; + + switch (var->bits_per_pixel) { + case 8: + var->red.length = 3; + var->red.offset = 5; + var->red.msb_right = 0; + + var->green.length = 3; + var->green.offset = 2; + var->green.msb_right = 0; + + var->blue.length = 2; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 16: + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 24: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 32: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 8; + var->transp.offset = 24; + var->transp.msb_right = 0; + break; + } + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + return 0; +} + +/* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + * + * @param var Variable screen buffer information + * @param info Framebuffer information pointer + */ +static int +virtfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + + if (info->var.yoffset == var->yoffset) + return 0; /* No change, do nothing */ + + if ((var->yoffset + info->var.yres) > info->var.yres_virtual) + return -EINVAL; + + info->var.yoffset = var->yoffset; + + return 0; +} + +/* + * Function to handle custom mmap for virtual framebuffer. + * + * @param fbi framebuffer information pointer + * + * @param vma Pointer to vm_area_struct + */ +static int virtfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +{ + u32 len; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset < fbi->fix.smem_len) { + /* mapping framebuffer memory */ + len = fbi->fix.smem_len - offset; + vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT; + } else { + return -EINVAL; + } + + len = PAGE_ALIGN(len); + if (vma->vm_end - vma->vm_start > len) + return -EINVAL; + + /* make buffers bufferable */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + vma->vm_flags |= VM_IO; + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + dev_dbg(fbi->device, "mmap remap_pfn_range failed\n"); + return -ENOBUFS; + } + + return 0; +} + +/*! + * This structure contains the pointers to the control functions that are + * invoked by the core framebuffer driver to perform operations like + * blitting, rectangle filling, copy regions and cursor definition. + */ +static struct fb_ops virtfb_ops = { + .owner = THIS_MODULE, + .fb_set_par = virtfb_set_par, + .fb_check_var = virtfb_check_var, + .fb_pan_display = virtfb_pan_display, + .fb_mmap = virtfb_mmap, +}; + + +/* + * Main framebuffer functions + */ + +/*! + * Allocates the DRAM memory for the frame buffer. This buffer is remapped + * into a non-cached, non-buffered, memory region to allow palette and pixel + * writes to occur without flushing the cache. Once this area is remapped, + * all virtual memory access to the video memory should occur at the new region. + * + * @param fbi framebuffer information pointer + * + * @return Error code indicating success or failure + */ +static int virtfb_map_video_memory(struct fb_info *fbi) +{ + if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) + fbi->fix.smem_len = fbi->var.yres_virtual * + fbi->fix.line_length; + + fbi->screen_base = dma_alloc_coherent(fbi->device, + fbi->fix.smem_len, + (dma_addr_t *)&fbi->fix.smem_start, + GFP_DMA | GFP_KERNEL); + if (fbi->screen_base == 0) { + dev_err(fbi->device, "Unable to allocate framebuffer memory\n"); + fbi->fix.smem_len = 0; + fbi->fix.smem_start = 0; + return -EBUSY; + } + + dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n", + (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len); + + fbi->screen_size = fbi->fix.smem_len; + + /* Clear the screen */ + memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); + + return 0; +} + +/*! + * De-allocates the DRAM memory for the frame buffer. + * + * @param fbi framebuffer information pointer + * + * @return Error code indicating success or failure + */ +static int virtfb_unmap_video_memory(struct fb_info *fbi) +{ + dma_free_coherent(fbi->device, fbi->fix.smem_len, + fbi->screen_base, fbi->fix.smem_start); + fbi->screen_base = 0; + fbi->fix.smem_start = 0; + fbi->fix.smem_len = 0; + return 0; +} + +/*! + * Initializes the framebuffer information pointer. After allocating + * sufficient memory for the framebuffer structure, the fields are + * filled with custom information passed in from the configurable + * structures. This includes information such as bits per pixel, + * color maps, screen width/height and RGBA offsets. + * + * @return Framebuffer structure initialized with our information + */ +static struct fb_info *virtfb_init_fbinfo(struct fb_ops *ops) +{ + struct fb_info *fbi; + + /* + * Allocate sufficient memory for the fb structure + */ + fbi = framebuffer_alloc(sizeof(unsigned int), NULL); + if (!fbi) + return NULL; + + + fbi->var.activate = FB_ACTIVATE_NOW; + + fbi->fbops = ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + + + return fbi; +} + + +static int virtfb_register(struct fb_info *fbi, unsigned int id) +{ + struct fb_videomode m; + int ret = 0; + + //TODO: Set framebuffer ID + sprintf(fbi->fix.id, "virt_fb%d", id); + + //Setup small default resolution + fbi->var.xres_virtual = fbi->var.xres = fbi->var.yres_virtual = fbi->var.yres = 128; + fbi->var.bits_per_pixel = 16; + + virtfb_check_var(&fbi->var, fbi); + + virtfb_set_fix(fbi); + + /*added first mode to fbi modelist*/ + if (!fbi->modelist.next || !fbi->modelist.prev) + INIT_LIST_HEAD(&fbi->modelist); + fb_var_to_videomode(&m, &fbi->var); + fb_add_videomode(&m, &fbi->modelist); + + fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_set_var(fbi, &fbi->var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + + ret = register_framebuffer(fbi); + if (ret < 0) + goto err0; + + return ret; +err0: + return ret; +} + +static void virtfb_unregister(struct fb_info *fbi) +{ + + unregister_framebuffer(fbi); +} + +/*! + * Main entry function for the framebuffer. The function registers the power + * management callback functions with the kernel and also registers the MXCFB + * callback functions with the core Linux framebuffer driver \b fbmem.c + * + * @return Error code indicating success or failure + */ +int __init virtfb_init(void) +{ + + u32 * fbNum; + int i, ret = 0; + + /* + * Initialize FB structures + */ + + g_fb_list = kzalloc(sizeof(struct fb_info*) * vfbcount, GFP_KERNEL); + for(i=0;ipar; + *fbNum = i; + + ret = virtfb_register(g_fb_list[i], i); + if (ret < 0) + goto virtfb_register_failed; + } + + + return 0; +virtfb_register_failed: +init_fbinfo_failed: + for(i=0;i Date: Sat, 28 Mar 2015 14:08:56 +0900 Subject: [PATCH 0388/1983] mxc_v4l2_capture: modify timeout value for stability --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 66bf4aa05bc5d1..124980acaf54f9 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1671,7 +1671,7 @@ static int mxc_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf) if (!wait_event_interruptible_timeout(cam->enc_queue, cam->enc_counter != 0, - 10 * HZ)) { + 50 * HZ)) { pr_err("ERROR: v4l2 capture: mxc_v4l_dqueue timeout " "enc_counter %x\n", cam->enc_counter); From 9e7f1fd4f1758a5fd5e3f509512182dbac64cbd1 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 31 Mar 2015 22:31:17 +0900 Subject: [PATCH 0389/1983] mxc_v4l2_capture: ov5647: modify color settings --- .../media/platform/mxc/capture/ov5647_mipi.c | 522 +++++++++--------- 1 file changed, 261 insertions(+), 261 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 441f3d38d5cb4f..a9ead7e72a6931 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -115,7 +115,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -231,31 +231,31 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -279,7 +279,7 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -395,31 +395,31 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -434,7 +434,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x0103 , 0x01 , 0 , 0 } , { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ { 0x3035 , 0x41 , 0 , 0 } , - { 0x3036 , 0x92 , 0 , 0 } , + { 0x3036 , 0xa0 , 0 , 0 } , { 0x303c , 0x11 , 0 , 0 } , { 0x3106 , 0xf5 , 0 , 0 } , { 0x3821 , 0x07 , 0 , 0 } , @@ -443,7 +443,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -559,31 +559,31 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -607,7 +607,7 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -723,31 +723,31 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -771,7 +771,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x04 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -887,31 +887,31 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -935,7 +935,7 @@ static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x04 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1051,31 +1051,31 @@ static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -1099,7 +1099,7 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1215,31 +1215,31 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -1263,7 +1263,7 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1379,31 +1379,31 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -1427,7 +1427,7 @@ static struct reg_value ov5647_setting_30fps_XGA_1024_768[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1543,31 +1543,31 @@ static struct reg_value ov5647_setting_30fps_XGA_1024_768[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , @@ -1591,7 +1591,7 @@ static struct reg_value ov5647_setting_15fps_XGA_1024_768[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1707,31 +1707,31 @@ static struct reg_value ov5647_setting_15fps_XGA_1024_768[] = { { 0x5821 , 0x00 , 0 , 0 } , { 0x5822 , 0x00 , 0 , 0 } , { 0x5823 , 0x00 , 0 , 0 } , - { 0x5824 , 0xcc , 0 , 0 } , - { 0x5825 , 0xcc , 0 , 0 } , - { 0x5826 , 0xcc , 0 , 0 } , - { 0x5827 , 0xcc , 0 , 0 } , - { 0x5828 , 0xcc , 0 , 0 } , - { 0x5829 , 0xcc , 0 , 0 } , - { 0x582a , 0xbb , 0 , 0 } , - { 0x582b , 0xbb , 0 , 0 } , - { 0x582c , 0xbb , 0 , 0 } , - { 0x582d , 0xcc , 0 , 0 } , - { 0x582e , 0xcc , 0 , 0 } , - { 0x582f , 0xbb , 0 , 0 } , - { 0x5830 , 0xaa , 0 , 0 } , - { 0x5831 , 0xbb , 0 , 0 } , - { 0x5832 , 0xcc , 0 , 0 } , - { 0x5833 , 0xcc , 0 , 0 } , - { 0x5834 , 0xbb , 0 , 0 } , - { 0x5835 , 0xbb , 0 , 0 } , - { 0x5836 , 0xbb , 0 , 0 } , - { 0x5837 , 0xcc , 0 , 0 } , - { 0x5838 , 0xcc , 0 , 0 } , - { 0x5839 , 0xcc , 0 , 0 } , - { 0x583a , 0xcc , 0 , 0 } , - { 0x583b , 0xcc , 0 , 0 } , - { 0x583c , 0xcc , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , { 0x583d , 0xce , 0 , 0 } , { 0x3501 , 0x10 , 0 , 0 } , { 0x3502 , 0x80 , 0 , 0 } , From 000773f108411a217d150acba92d5b563658158f Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Wed, 1 Apr 2015 00:45:37 +0900 Subject: [PATCH 0390/1983] mxc_v4l2_capture: ov5647: add support for 960x720 --- .../media/platform/mxc/capture/ov5647_mipi.c | 337 +++++++++++++++++- 1 file changed, 336 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index a9ead7e72a6931..113f4aaee07633 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -57,7 +57,8 @@ enum ov5647_mode { ov5647_mode_1080P_1920_1080 = 2, ov5647_mode_VGA_640_480 = 3, ov5647_mode_XGA_1024_768 = 4, - ov5647_mode_MAX = 4, + ov5647_mode_960_720 = 5, + ov5647_mode_MAX = 5, ov5647_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -1741,6 +1742,334 @@ static struct reg_value ov5647_setting_15fps_XGA_1024_768[] = { { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ }; +static struct reg_value ov5647_setting_30fps_960_720[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0xa0 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x06 , 0 , 0 } , + { 0x380d , 0xd6 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x03 , 0 , 0 } , + { 0x3809 , 0xc0 , 0 , 0 } , + { 0x380a , 0x02 , 0 , 0 } , + { 0x380b , 0xd0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x06 , 0 , 0 } , + { 0x3807 , 0xb2 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + +static struct reg_value ov5647_setting_15fps_960_720[] = { + { 0x0100 , 0x00 , 0 , 0 } , + { 0x0103 , 0x01 , 0 , 0 } , + { 0x3034 , 0x18 , 0 , 0 } , /* was 0x1A */ + { 0x3035 , 0x41 , 0 , 0 } , + { 0x3036 , 0x49 , 0 , 0 } , + { 0x303c , 0x11 , 0 , 0 } , + { 0x3106 , 0xf5 , 0 , 0 } , + { 0x3821 , 0x07 , 0 , 0 } , + { 0x3820 , 0x41 , 0 , 0 } , + { 0x3827 , 0xec , 0 , 0 } , + { 0x370c , 0x03 , 0 , 0 } , + { 0x3612 , 0x4b , 0 , 0 } , + { 0x3618 , 0x00 , 0 , 0 } , + { 0x5001 , 0x03 , 0 , 0 } , + { 0x5002 , 0x40 , 0 , 0 } , + { 0x5003 , 0x08 , 0 , 0 } , + { 0x5a00 , 0x08 , 0 , 0 } , + { 0x3000 , 0x00 , 0 , 0 } , + { 0x3001 , 0x00 , 0 , 0 } , + { 0x3002 , 0x00 , 0 , 0 } , + { 0x3016 , 0x08 , 0 , 0 } , + { 0x3017 , 0xe0 , 0 , 0 } , + { 0x3018 , 0x44 , 0 , 0 } , + { 0x301c , 0xf8 , 0 , 0 } , + { 0x301d , 0xf0 , 0 , 0 } , + { 0x3a18 , 0x00 , 0 , 0 } , + { 0x3a19 , 0xf8 , 0 , 0 } , + { 0x3c01 , 0x80 , 0 , 0 } , + { 0x3b07 , 0x0c , 0 , 0 } , + { 0x380c , 0x06 , 0 , 0 } , + { 0x380d , 0xd6 , 0 , 0 } , + { 0x380e , 0x03 , 0 , 0 } , + { 0x380f , 0xd8 , 0 , 0 } , + { 0x3814 , 0x31 , 0 , 0 } , + { 0x3815 , 0x31 , 0 , 0 } , + { 0x3708 , 0x64 , 0 , 0 } , + { 0x3709 , 0x52 , 0 , 0 } , + { 0x3808 , 0x03 , 0 , 0 } , + { 0x3809 , 0xc0 , 0 , 0 } , + { 0x380a , 0x02 , 0 , 0 } , + { 0x380b , 0xd0 , 0 , 0 } , + { 0x3800 , 0x00 , 0 , 0 } , + { 0x3801 , 0x00 , 0 , 0 } , + { 0x3802 , 0x00 , 0 , 0 } , + { 0x3803 , 0x00 , 0 , 0 } , + { 0x3804 , 0x0a , 0 , 0 } , + { 0x3805 , 0x3f , 0 , 0 } , + { 0x3806 , 0x06 , 0 , 0 } , + { 0x3807 , 0xb2 , 0 , 0 } , + { 0x3811 , 0x08 , 0 , 0 } , + { 0x3813 , 0x02 , 0 , 0 } , + { 0x3630 , 0x2e , 0 , 0 } , + { 0x3632 , 0xe2 , 0 , 0 } , + { 0x3633 , 0x23 , 0 , 0 } , + { 0x3634 , 0x44 , 0 , 0 } , + { 0x3636 , 0x06 , 0 , 0 } , + { 0x3620 , 0x64 , 0 , 0 } , + { 0x3621 , 0xe0 , 0 , 0 } , + { 0x3600 , 0x37 , 0 , 0 } , + { 0x3704 , 0xa0 , 0 , 0 } , + { 0x3703 , 0x5a , 0 , 0 } , + { 0x3715 , 0x78 , 0 , 0 } , + { 0x3717 , 0x01 , 0 , 0 } , + { 0x3731 , 0x02 , 0 , 0 } , + { 0x370b , 0x60 , 0 , 0 } , + { 0x3705 , 0x1a , 0 , 0 } , + { 0x3f05 , 0x02 , 0 , 0 } , + { 0x3f06 , 0x10 , 0 , 0 } , + { 0x3f01 , 0x0a , 0 , 0 } , + { 0x3a08 , 0x01 , 0 , 0 } , + { 0x3a09 , 0x27 , 0 , 0 } , + { 0x3a0a , 0x00 , 0 , 0 } , + { 0x3a0b , 0xf6 , 0 , 0 } , + { 0x3a0d , 0x04 , 0 , 0 } , + { 0x3a0e , 0x03 , 0 , 0 } , + { 0x3a0f , 0x58 , 0 , 0 } , + { 0x3a10 , 0x50 , 0 , 0 } , + { 0x3a1b , 0x58 , 0 , 0 } , + { 0x3a1e , 0x50 , 0 , 0 } , + { 0x3a11 , 0x60 , 0 , 0 } , + { 0x3a1f , 0x28 , 0 , 300 } , + { 0x4001 , 0x02 , 0 , 0 } , + { 0x4004 , 0x02 , 0 , 0 } , + { 0x4000 , 0x09 , 0 , 0 } , + { 0x4837 , 0x24 , 0 , 0 } , + { 0x4050 , 0x6e , 0 , 0 } , + { 0x4051 , 0x8f , 0 , 0 } , + { 0x0100 , 0x01 , 0 , 0 } , +#if 0 + { 0x503d , 0xc0 , 0 , 0 } , /* test pattern */ + { 0x503e , 0x11 , 0 , 0 } , /* test pattern */ +#endif + { 0x5000 , 0x86 , 0 , 0 } , /* enable LENC registers */ + { 0x5800 , 0x00 , 0 , 0 } , + { 0x5801 , 0x00 , 0 , 0 } , + { 0x5802 , 0x00 , 0 , 0 } , + { 0x5803 , 0x00 , 0 , 0 } , + { 0x5804 , 0x00 , 0 , 0 } , + { 0x5805 , 0x00 , 0 , 0 } , + { 0x5806 , 0x00 , 0 , 0 } , + { 0x5807 , 0x00 , 0 , 0 } , + { 0x5808 , 0x00 , 0 , 0 } , + { 0x5809 , 0x00 , 0 , 0 } , + { 0x580a , 0x00 , 0 , 0 } , + { 0x580b , 0x00 , 0 , 0 } , + { 0x580c , 0x00 , 0 , 0 } , + { 0x580d , 0x00 , 0 , 0 } , + { 0x580e , 0x00 , 0 , 0 } , + { 0x580f , 0x00 , 0 , 0 } , + { 0x5810 , 0x00 , 0 , 0 } , + { 0x5811 , 0x00 , 0 , 0 } , + { 0x5812 , 0x00 , 0 , 0 } , + { 0x5813 , 0x00 , 0 , 0 } , + { 0x5814 , 0x00 , 0 , 0 } , + { 0x5815 , 0x00 , 0 , 0 } , + { 0x5816 , 0x00 , 0 , 0 } , + { 0x5817 , 0x00 , 0 , 0 } , + { 0x5818 , 0x00 , 0 , 0 } , + { 0x5819 , 0x00 , 0 , 0 } , + { 0x581a , 0x00 , 0 , 0 } , + { 0x581b , 0x00 , 0 , 0 } , + { 0x581c , 0x00 , 0 , 0 } , + { 0x581d , 0x00 , 0 , 0 } , + { 0x581e , 0x00 , 0 , 0 } , + { 0x581f , 0x00 , 0 , 0 } , + { 0x5820 , 0x00 , 0 , 0 } , + { 0x5821 , 0x00 , 0 , 0 } , + { 0x5822 , 0x00 , 0 , 0 } , + { 0x5823 , 0x00 , 0 , 0 } , + { 0x5824 , 0xfe , 0 , 0 } , + { 0x5825 , 0xfe , 0 , 0 } , + { 0x5826 , 0xfe , 0 , 0 } , + { 0x5827 , 0xfe , 0 , 0 } , + { 0x5828 , 0xfe , 0 , 0 } , + { 0x5829 , 0xfe , 0 , 0 } , + { 0x582a , 0xed , 0 , 0 } , + { 0x582b , 0xed , 0 , 0 } , + { 0x582c , 0xed , 0 , 0 } , + { 0x582d , 0xfe , 0 , 0 } , + { 0x582e , 0xfe , 0 , 0 } , + { 0x582f , 0xed , 0 , 0 } , + { 0x5830 , 0xdc , 0 , 0 } , + { 0x5831 , 0xed , 0 , 0 } , + { 0x5832 , 0xfe , 0 , 0 } , + { 0x5833 , 0xfe , 0 , 0 } , + { 0x5834 , 0xed , 0 , 0 } , + { 0x5835 , 0xed , 0 , 0 } , + { 0x5836 , 0xed , 0 , 0 } , + { 0x5837 , 0xfe , 0 , 0 } , + { 0x5838 , 0xfe , 0 , 0 } , + { 0x5839 , 0xfe , 0 , 0 } , + { 0x583a , 0xfe , 0 , 0 } , + { 0x583b , 0xfe , 0 , 0 } , + { 0x583c , 0xfe , 0 , 0 } , + { 0x583d , 0xce , 0 , 0 } , + { 0x3501 , 0x10 , 0 , 0 } , + { 0x3502 , 0x80 , 0 , 0 } , + { 0x350a , 0x00 , 0 , 0 } , + { 0x350b , 0x7f , 0 , 0 } , + { 0x3c00 , 0x04 , 0 , 300 } , + { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ +}; + static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -1758,6 +2087,9 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { {ov5647_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, ov5647_setting_15fps_XGA_1024_768, ARRAY_SIZE(ov5647_setting_15fps_XGA_1024_768)}, + {ov5647_mode_960_720, SUBSAMPLING, 960, 720, + ov5647_setting_15fps_960_720, + ARRAY_SIZE(ov5647_setting_15fps_960_720)}, }, { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -1775,6 +2107,9 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { {ov5647_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, ov5647_setting_30fps_XGA_1024_768, ARRAY_SIZE(ov5647_setting_30fps_XGA_1024_768)}, + {ov5647_mode_960_720, SUBSAMPLING, 960, 720, + ov5647_setting_30fps_960_720, + ARRAY_SIZE(ov5647_setting_15fps_960_720)}, }, }; From 421c48c8bfa3c314a9618557b787f10fd2e98fd7 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Thu, 2 Apr 2015 21:49:48 +0900 Subject: [PATCH 0391/1983] mxc_v4l2_capture: ov5647: modify color settings --- .../media/platform/mxc/capture/ov5647_mipi.c | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 113f4aaee07633..3c0e29ecc21f69 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -116,7 +116,7 @@ static struct reg_value ov5647_setting_30fps_960P_1280_960[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -280,7 +280,7 @@ static struct reg_value ov5647_setting_15fps_960P_1280_960[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -444,7 +444,7 @@ static struct reg_value ov5647_setting_30fps_720P_1280_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -608,7 +608,7 @@ static struct reg_value ov5647_setting_15fps_720P_1280_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -772,7 +772,7 @@ static struct reg_value ov5647_setting_30fps_1080P_1920_1080[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x04 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -936,7 +936,7 @@ static struct reg_value ov5647_setting_15fps_1080P_1920_1080[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x04 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1100,7 +1100,7 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1264,7 +1264,7 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1428,7 +1428,7 @@ static struct reg_value ov5647_setting_30fps_XGA_1024_768[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1592,7 +1592,7 @@ static struct reg_value ov5647_setting_15fps_XGA_1024_768[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1756,7 +1756,7 @@ static struct reg_value ov5647_setting_30fps_960_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -1920,7 +1920,7 @@ static struct reg_value ov5647_setting_15fps_960_720[] = { { 0x370c , 0x03 , 0 , 0 } , { 0x3612 , 0x4b , 0 , 0 } , { 0x3618 , 0x00 , 0 , 0 } , - { 0x5001 , 0x03 , 0 , 0 } , + { 0x5001 , 0x01 , 0 , 0 } , { 0x5002 , 0x40 , 0 , 0 } , { 0x5003 , 0x08 , 0 , 0 } , { 0x5a00 , 0x08 , 0 , 0 } , @@ -2299,7 +2299,7 @@ static s32 ov5647_read_reg(u16 reg, u8 *val) } static int prev_sysclk, prev_HTS; -static int AE_low, AE_high, AE_Target = 52; +static int AE_low, AE_high, AE_Target = 44; void OV5647_stream_on(void) { From 718dfce2a2a5c3faed9e06790f8081fde6c0fa48 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Sat, 4 Apr 2015 17:44:00 +0900 Subject: [PATCH 0392/1983] mxc_v4l2_capture: ov5647: more large memory allocate for VIDIOC_REQBUFS ioctl OV5647 data is bayer format. It is nessesary to allocate YUV420 size image buffer to debayer this data with GPU shader. Because Vivante direct texture mapping API does not support bayer format(GL_LUMINANCE). --- .../platform/mxc/capture/mxc_v4l2_capture.c | 17 +++++++++++---- .../platform/mxc/capture/mxc_v4l2_capture.h | 6 +++++- .../media/platform/mxc/capture/ov5647_mipi.c | 21 +++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 124980acaf54f9..772d661250030f 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -269,13 +269,23 @@ static int mxc_free_frame_buf(cam_data *cam) static int mxc_allocate_frame_buf(cam_data *cam, int count) { int i; + u32 map_sizeimage; + struct sensor_data *sensor = cam->sensor->priv; - pr_debug("%s: size=%d\n", __func__, cam->v2f.fmt.pix.sizeimage); + if (sensor && sensor->adata) { + const struct additional_data *adata = sensor->adata; + map_sizeimage = adata->map_sizeimage; + } + else { + map_sizeimage = cam->v2f.fmt.pix.sizeimage; + } + + pr_debug("%s: size=%d\n", __func__, map_sizeimage); for (i = 0; i < count; i++) { cam->frame[i].vaddress = dma_alloc_coherent(0, - PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage), + PAGE_ALIGN(map_sizeimage), &cam->frame[i].paddress, GFP_DMA | GFP_KERNEL); if (cam->frame[i].vaddress == 0) { @@ -286,8 +296,7 @@ static int mxc_allocate_frame_buf(cam_data *cam, int count) cam->frame[i].buffer.index = i; cam->frame[i].buffer.flags = V4L2_BUF_FLAG_MAPPED; cam->frame[i].buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buffer.length = - PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage); + cam->frame[i].buffer.length = PAGE_ALIGN(map_sizeimage); cam->frame[i].buffer.memory = V4L2_MEMORY_MMAP; cam->frame[i].buffer.m.offset = cam->frame[i].paddress; cam->frame[i].index = i; diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h index 28d9b6e932eabe..75f93b19b5ca02 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.h @@ -242,8 +242,12 @@ typedef struct _cam_data { struct scatterlist sg[2]; } cam_data; +struct additional_data { + u32 map_sizeimage; +}; + struct sensor_data { - const struct ov5642_platform_data *platform_data; + const struct additional_data *adata; struct v4l2_int_device *v4l2_int_device; struct i2c_client *i2c_client; struct v4l2_pix_format pix; diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 3c0e29ecc21f69..3df259902356e9 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -95,6 +95,7 @@ struct ov5647_mode_info { * Maintains the information on the current state of the sesor. */ static struct sensor_data ov5647_data; +static struct additional_data ov5647_data_add; static int pwn_gpio = -EINVAL; static int powon_active; static int rst_gpio = -EINVAL; @@ -2667,6 +2668,8 @@ static int ov5647_change_mode_exposure_calc(enum ov5647_frame_rate frame_rate, ov5647_mode_info_data[frame_rate][mode].width; ov5647_data.pix.height = ov5647_mode_info_data[frame_rate][mode].height; + ov5647_data_add.map_sizeimage = + ov5647_data.pix.width * ov5647_data.pix.height * 3 / 2; if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 || pModeSetting == NULL || ArySize == 0) @@ -2789,6 +2792,8 @@ static int ov5647_change_mode_direct(enum ov5647_frame_rate frame_rate, ov5647_mode_info_data[frame_rate][mode].width; ov5647_data.pix.height = ov5647_mode_info_data[frame_rate][mode].height; + ov5647_data_add.map_sizeimage = + ov5647_data.pix.width * ov5647_data.pix.height * 3 / 2; if (ov5647_data.pix.width == 0 || ov5647_data.pix.height == 0 || pModeSetting == NULL || ArySize == 0) @@ -3524,6 +3529,8 @@ static ssize_t set_mode(struct device *dev, ov5647_data.pix.height = max(ov5647_mode_info_data[0][mode].height, ov5647_mode_info_data[1][mode].height); + ov5647_data_add.map_sizeimage = + ov5647_data.pix.width * ov5647_data.pix.height * 3 / 2; } return count; @@ -3546,6 +3553,7 @@ static int ov5647_probe(struct i2c_client *client, u8 chip_id_high, chip_id_low; struct sensor_data *sensor = &ov5647_data; enum of_gpio_flags flags; + bool extbuf; /* request power down pin */ pwn_gpio = of_get_named_gpio_flags(dev->of_node, "pwn-gpios", 0, &flags); @@ -3592,8 +3600,12 @@ static int ov5647_probe(struct i2c_client *client, } } + /* allocate extended video frame buffer */ + extbuf = of_find_property(dev->of_node, "extended-buffer", NULL); + /* Set initial values for the sensor struct. */ memset(&ov5647_data, 0, sizeof(ov5647_data)); + memset(&ov5647_data_add, 0, sizeof(ov5647_data_add)); sensor->mipi_camera = 1; ov5647_data.sensor_clk = devm_clk_get(dev, "csi_mclk"); @@ -3648,6 +3660,15 @@ static int ov5647_probe(struct i2c_client *client, ov5647_data.streamcap.timeperframe.denominator = DEFAULT_FPS; ov5647_data.streamcap.timeperframe.numerator = 1; + /* lager memory allocate for Vivante direct texture mapping API */ + /* (VIDIOC_REQBUFS ioctl) */ + ov5647_data_add.map_sizeimage = + ov5647_data.pix.width * ov5647_data.pix.height * 3 / 2; /* I420 */ + + if (extbuf) { + ov5647_data.adata = &ov5647_data_add; + } + ov5647_power_on(dev); ov5647_reset(); From b39bf3374f6ebd2effe71301ed2cfa398c281724 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Wed, 15 Apr 2015 23:04:16 +0900 Subject: [PATCH 0393/1983] mxc_v4l2_capture: ov5647: add 640x480 mode which angle of view is narrow --- .../media/platform/mxc/capture/ov5647_mipi.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 3df259902356e9..4b5d6f2ebe10a2 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -58,7 +58,8 @@ enum ov5647_mode { ov5647_mode_VGA_640_480 = 3, ov5647_mode_XGA_1024_768 = 4, ov5647_mode_960_720 = 5, - ov5647_mode_MAX = 5, + ov5647_mode_VGA_640_480_narrow = 6, + ov5647_mode_MAX = 6, ov5647_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -1249,6 +1250,8 @@ static struct reg_value ov5647_setting_30fps_VGA_640_480[] = { { 0x350b , 0x7f , 0 , 0 } , { 0x3c00 , 0x04 , 0 , 300 } , { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ + { 0x3814 , 0x31 , 0 , 0 } , /* overwrite */ + { 0x3815 , 0x31 , 0 , 0 } , /* overwrite */ }; static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { @@ -1413,6 +1416,8 @@ static struct reg_value ov5647_setting_15fps_VGA_640_480[] = { { 0x350b , 0x7f , 0 , 0 } , { 0x3c00 , 0x04 , 0 , 300 } , { 0x3002 , 0xe4 , 0x01, 0 } , /* enable all outputs */ + { 0x3814 , 0x31 , 0 , 0 } , /* overwrite */ + { 0x3815 , 0x31 , 0 , 0 } , /* overwrite */ }; static struct reg_value ov5647_setting_30fps_XGA_1024_768[] = { @@ -2084,13 +2089,16 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { ARRAY_SIZE(ov5647_setting_15fps_1080P_1920_1080)}, {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, ov5647_setting_15fps_VGA_640_480, - ARRAY_SIZE(ov5647_setting_15fps_VGA_640_480)}, + ARRAY_SIZE(ov5647_setting_15fps_VGA_640_480) - 2}, {ov5647_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, ov5647_setting_15fps_XGA_1024_768, ARRAY_SIZE(ov5647_setting_15fps_XGA_1024_768)}, {ov5647_mode_960_720, SUBSAMPLING, 960, 720, ov5647_setting_15fps_960_720, ARRAY_SIZE(ov5647_setting_15fps_960_720)}, + {ov5647_mode_VGA_640_480_narrow, SUBSAMPLING, 640, 480, + ov5647_setting_15fps_VGA_640_480, + ARRAY_SIZE(ov5647_setting_15fps_VGA_640_480)}, }, { {ov5647_mode_960P_1280_960, SUBSAMPLING, 1280, 960, @@ -2104,13 +2112,16 @@ static struct ov5647_mode_info ov5647_mode_info_data[2][ov5647_mode_MAX + 1] = { ARRAY_SIZE(ov5647_setting_30fps_1080P_1920_1080)}, {ov5647_mode_VGA_640_480, SUBSAMPLING, 640, 480, ov5647_setting_30fps_VGA_640_480, - ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480)}, + ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480) - 2}, {ov5647_mode_XGA_1024_768, SUBSAMPLING, 1024, 768, ov5647_setting_30fps_XGA_1024_768, ARRAY_SIZE(ov5647_setting_30fps_XGA_1024_768)}, {ov5647_mode_960_720, SUBSAMPLING, 960, 720, ov5647_setting_30fps_960_720, ARRAY_SIZE(ov5647_setting_15fps_960_720)}, + {ov5647_mode_VGA_640_480_narrow, SUBSAMPLING, 640, 480, + ov5647_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5647_setting_30fps_VGA_640_480)}, }, }; From c4710c1fff65f43b28659d77e149ac32d44bf637 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 18:38:01 +0200 Subject: [PATCH 0394/1983] mach: imx: Only attempt to disable frequencies that are enabled. When speed grading check the cpufreq ops and only attempt to disable the offending op if it is enabled. --- arch/arm/mach-imx/mach-imx6q.c | 40 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index eebbd4c0e67704..59b282fca1b003 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -397,6 +397,7 @@ static void __init imx6q_init_machine(void) static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev) { struct device_node *np; + struct dev_pm_opp *opp; void __iomem *base; u32 val; @@ -424,22 +425,37 @@ static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev) val >>= OCOTP_CFG3_SPEED_SHIFT; val &= 0x3; - if (val != OCOTP_CFG3_SPEED_1P2GHZ) - if (dev_pm_opp_disable(cpu_dev, 1200000000)) - pr_warn("failed to disable 1.2 GHz OPP\n"); - if (val < OCOTP_CFG3_SPEED_996MHZ) - if (dev_pm_opp_disable(cpu_dev, 996000000)) - pr_warn("failed to disable 996 MHz OPP\n"); + if (val != OCOTP_CFG3_SPEED_1P2GHZ) { + opp = dev_pm_opp_find_freq_exact(cpu_dev, 1200000000, true); + if (!IS_ERR(opp)) { + if (dev_pm_opp_disable(cpu_dev, 1200000000)) + pr_warn("failed to disable 1.2 GHz OPP\n"); + } + } + if (val < OCOTP_CFG3_SPEED_996MHZ) { + opp = dev_pm_opp_find_freq_exact(cpu_dev, 996000000, true); + if (!IS_ERR(opp)) { + if (dev_pm_opp_disable(cpu_dev, 996000000)) + pr_warn("failed to disable 996 MHz OPP\n"); + } + } if (cpu_is_imx6q()) { - if (val != OCOTP_CFG3_SPEED_852MHZ) - if (dev_pm_opp_disable(cpu_dev, 852000000)) - pr_warn("failed to disable 852 MHz OPP\n"); + if (val != OCOTP_CFG3_SPEED_852MHZ) { + opp = dev_pm_opp_find_freq_exact(cpu_dev, 852000000, true); + if (!IS_ERR(opp)) { + if (dev_pm_opp_disable(cpu_dev, 852000000)) + pr_warn("failed to disable 852 MHz OPP\n"); + } + } } if (IS_ENABLED(CONFIG_MX6_VPU_352M)) { - if (dev_pm_opp_disable(cpu_dev, 396000000)) - pr_warn("failed to disable 396MHz OPP\n"); - pr_info("remove 396MHz OPP for VPU running at 352MHz!\n"); + opp = dev_pm_opp_find_freq_exact(cpu_dev, 352000000, true); + if (!IS_ERR(opp)) { + if (dev_pm_opp_disable(cpu_dev, 396000000)) + pr_warn("failed to disable 396MHz OPP\n"); + pr_info("remove 396MHz OPP for VPU running at 352MHz!\n"); + } } put_node: From 3865ecdd1b21ff3989a43398ca75c6da547c56f8 Mon Sep 17 00:00:00 2001 From: Naoki Aizu Date: Tue, 20 Jan 2015 01:31:01 +0900 Subject: [PATCH 0395/1983] dts: hummingboard: add Raspberry Pi camera support This adds support to use a Raspberry Pi camera on the Hummingboard platform. --- arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 55 +++++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index 1b9f62f4a81d6e..7dcae4297b200e 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -145,10 +145,20 @@ hdmi-controller = <&hdmi_audio>; }; - v4l2_out { - compatible = "fsl,mxc_v4l2_output"; - status = "okay"; - }; + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + mipi_camera = <1>; + default_input = <1>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; }; &audmux { @@ -188,6 +198,27 @@ pinctrl-0 = <&pinctrl_hummingboard_i2c1>; status = "okay"; + /* Raspberry Pi camera rev 1.3 */ + ov5647_mipi: ov5647_mipi@36 { + compatible = "ovti,ov5647_mipi"; + reg = <0x36>; + /* Pi camera has its own 25MHz clock. */ + clocks = <&clks 0>; + clock-names = "csi_mclk"; + DOVDD-supply = <®_3p3v>; + AVDD-supply = <®_3p3v>; + DVDD-supply = <®_3p3v>; + pwn-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>; + led-gpios = <&gpio6 15 GPIO_ACTIVE_HIGH>; + ipu_id = <0>; + csi_id = <1>; + mclk = <25000000>; + mclk_source = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_mipi>; + extended-buffer; + }; + /* Pro baseboard model */ rtc: pcf8523@68 { compatible = "nxp,pcf8523"; @@ -286,6 +317,13 @@ >; }; + pinctrl_hummingboard_mipi: hummingboard_mipi { + fsl,pins = < + MX6QDL_PAD_SD4_DAT2__GPIO2_IO10 0x17059 + MX6QDL_PAD_NANDF_CS2__GPIO6_IO15 0x13059 + >; + }; + pinctrl_hummingboard_pcie_reset: hummingboard-pcie-reset { fsl,pins = < MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0x1b0b1 @@ -372,6 +410,15 @@ }; }; +&mipi_csi { + ipu_id = <0>; + csi_id = <1>; + v_channel = <0>; + lanes = <2>; + mipi_dphy_clk = /bits/ 8 <0x28>; + status = "okay"; +}; + &pcie { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_pcie_reset>; From f0577b08c7f6d5aa78a9bd14c10c38d45db1c1b9 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 18:42:28 +0200 Subject: [PATCH 0396/1983] media: mxc: Fix some Kconfig depends It was very easy to select one option and then end up with unresolved symbols. Lets try and properly select sub-modules where possible. --- drivers/media/platform/mxc/capture/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig index ef0cd82efdf55e..a3e47d16b47222 100644 --- a/drivers/media/platform/mxc/capture/Kconfig +++ b/drivers/media/platform/mxc/capture/Kconfig @@ -6,6 +6,8 @@ menu "MXC Camera/V4L2 PRP Features support" config VIDEO_MXC_IPU_CAMERA bool select VIDEO_V4L2_MXC_INT_DEVICE + select MXC_IPU_CSI_ENC + select MXC_IPU_PRP_ENC depends on VIDEO_MXC_CAPTURE && MXC_IPU default y @@ -31,6 +33,7 @@ config MXC_CAMERA_OV5640_MIPI config MXC_CAMERA_OV5647_MIPI tristate "OmniVision ov5647 camera support using mipi" + depends on MXC_MIPI_CSI2 depends on !VIDEO_MXC_EMMA_CAMERA && I2C ---help--- If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here. From 100630baf659350a7fec42444e4745bc57afad44 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 19:01:36 +0200 Subject: [PATCH 0397/1983] mach: imx: Make imx6dl gpu_2d clocking sane I am not sure who tested clocking the 2D core of the GPU up to 250+Mhz, but I found it would regularly hang the SOC on boot. I am giving it a bit of a boost by clocking it form the 396MHZ pfd rather than the 352MHZ one. I may modify this later if things seem unstable. --- arch/arm/mach-imx/clk-imx6q.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 2558dd0b0720e7..4430e42eb483ed 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -531,9 +531,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) /* for mx6dl, change gpu3d_core parent to 594_PFD*/ imx_clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], clk[IMX6QDL_CLK_PLL2_PFD1_594M]); imx_clk_set_rate(clk[IMX6QDL_CLK_GPU3D_CORE], 528000000); - /* for mx6dl, change gpu2d_core parent to 594_PFD*/ - imx_clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], clk[IMX6QDL_CLK_PLL2_PFD1_594M]); - imx_clk_set_rate(clk[IMX6QDL_CLK_GPU2D_CORE], 528000000); + imx_clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]); } else if (cpu_is_imx6q()) { imx_clk_set_rate(clk[IMX6QDL_CLK_GPU3D_SHADER], 594000000); imx_clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], clk[IMX6QDL_CLK_MMDC_CH0_AXI]); From 4384883f32777f58bf5befbf40e70b873465eab2 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 8 Jul 2015 19:31:27 +0200 Subject: [PATCH 0398/1983] dts: imx6dl: disable epdc device by default This should be enabled per platform that has it implemented. --- arch/arm/boot/dts/imx6dl.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi index 66bf5b0efa12ec..b29d02b9d86127 100644 --- a/arch/arm/boot/dts/imx6dl.dtsi +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -143,6 +143,7 @@ interrupts = <0 97 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6QDL_CLK_IPU2>, <&clks IMX6QDL_CLK_IPU2_DI1>; clock-names = "epdc_axi", "epdc_pix"; + status = "disabled"; }; lcdif: lcdif@020f8000 { From 11ca06e2f3d61d5018c35f5e3c780257fb86306d Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 10 Jul 2015 07:27:11 +0200 Subject: [PATCH 0399/1983] PCI: imx6: de-assert core reset on error Just as on shutdown we de-assert core reset if we fail to probe a device connected. This puts the hardware in the best state so it doesn't hang on a subsequent reboot. This should probably be something done in the bootloader. --- drivers/pci/host/pci-imx6.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 1c781f94c538e7..4dcf001b1908fe 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -1254,7 +1254,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) } else { ret = imx6_add_pcie_port(pp, pdev); if (ret < 0) - return ret; + goto err; platform_set_drvdata(pdev, imx6_pcie); @@ -1264,6 +1264,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) return 0; err: + /* bring down link, so bootloader gets clean state in case of reboot */ + imx6_pcie_assert_core_reset(&imx6_pcie->pp); return ret; } From 70dd4fa27faf2372b6170875b5bb596b4fe5cd73 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 10 Jul 2015 08:08:30 +0200 Subject: [PATCH 0400/1983] PCI: imx: partially revert 0a057c8b1a7e0 On iMX6dl/q I was seeing hangs occasionally when no pcie card was connected to the board. I traced this down to disabling the pcie clock introduced in this commit. commit 0a057c8b1a7e0bce50d51ed37a1603f23dc2efd9 Author: Richard Zhu Date: Thu Dec 25 14:36:44 2014 +0800 MLK-10058-4 pci: imx6: refine imx6sx pcie pm Since this seems to only be tested with the iMX6SX and I see no benefit on the other iMX6 chipsets since the PCI bus is clocked directly from the AXI clock, I have wrapped this in the XTEME power savings config option as well. I also tested on our platform and saw no power differences if the clocks were enabled or disabled with no card present. --- drivers/pci/host/pci-imx6.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 4dcf001b1908fe..22a0cce2457d42 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -520,24 +520,25 @@ static int imx6_pcie_start_link(struct pcie_port *pp) out: if (ret) { dev_err(pp->dev, "Failed to bring link up!\n"); - clk_disable_unprepare(imx6_pcie->pcie); - if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS) - && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)) - clk_disable_unprepare(imx6_pcie->pcie_bus); - clk_disable_unprepare(imx6_pcie->pcie_phy); - if (is_imx6sx_pcie(imx6_pcie)) { - /* Disable clks and power down PCIe PHY */ - clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); - release_bus_freq(BUS_FREQ_HIGH); - - /* - * Power down PCIe PHY. - */ - regulator_disable(imx6_pcie->pcie_phy_regulator); - } else { - clk_disable_unprepare(imx6_pcie->ref_100m); - release_bus_freq(BUS_FREQ_HIGH); + if (IS_ENABLED(CONFIG_PCI_IMX6SX_EXTREMELY_PWR_SAVE)) { + clk_disable_unprepare(imx6_pcie->pcie); + if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS) + && !IS_ENABLED(CONFIG_RC_MODE_IN_EP_RC_SYS)) + clk_disable_unprepare(imx6_pcie->pcie_bus); + clk_disable_unprepare(imx6_pcie->pcie_phy); + if (is_imx6sx_pcie(imx6_pcie)) { + /* Disable clks and power down PCIe PHY */ + clk_disable_unprepare(imx6_pcie->pcie_inbound_axi); + + /* + * Power down PCIe PHY. + */ + regulator_disable(imx6_pcie->pcie_phy_regulator); + } else { + clk_disable_unprepare(imx6_pcie->ref_100m); + } } + release_bus_freq(BUS_FREQ_HIGH); } else { tmp = readl(pp->dbi_base + 0x80); dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf); From 6061e7aaa5ae49d348f14bbf135e4dfcaf63273d Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 10 Jul 2015 08:21:02 +0200 Subject: [PATCH 0401/1983] PCI: imx: demote the level of certain messages. Certain messages should not be considered errors as they are more informational about how the driver and hardware are working. We drop these down to the info and warn level so they don't spam the console for normal operation. --- drivers/pci/host/pci-imx6.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 22a0cce2457d42..8e7960b743cefd 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -446,7 +446,7 @@ static int imx6_pcie_wait_for_link(struct pcie_port *pp) if (--count) continue; - dev_err(pp->dev, "phy link never came up\n"); + dev_info(pp->dev, "phy link never came up\n"); dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); @@ -519,7 +519,7 @@ static int imx6_pcie_start_link(struct pcie_port *pp) out: if (ret) { - dev_err(pp->dev, "Failed to bring link up!\n"); + dev_info(pp->dev, "Failed to bring link up!\n"); if (IS_ENABLED(CONFIG_PCI_IMX6SX_EXTREMELY_PWR_SAVE)) { clk_disable_unprepare(imx6_pcie->pcie); if (!IS_ENABLED(CONFIG_EP_MODE_IN_EP_RC_SYS) @@ -645,7 +645,7 @@ static int imx6_pcie_link_up(struct pcie_port *pp) if ((debug_r0 & 0x3f) != 0x0d) return 0; - dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); + dev_warn(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc); imx6_pcie_reset_phy(pp); @@ -684,7 +684,7 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_warn(&pdev->dev, "failed to initialize host\n"); return ret; } From ecb84f3405c313ed8222a61415161f15b1056116 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Fri, 10 Jul 2015 15:26:09 +0200 Subject: [PATCH 0402/1983] i2c: imx: handle bus contention better If the I2C bus is busy, then the driver returns -EAGAIN. However, it is not set to allow any retries in this case. Add a 'retry' count of 500, and let the system try for up to two full seconds before considering an error. The bus-busy bit has a 25% chance of incorrectly assuming the bus is idle. Therefor, we should check to see if the bus is idle, then issue a START. If this fails, try again. --- drivers/i2c/busses/i2c-imx.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 6c2ac05bc0d576..9df2d1e6f27f5e 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -261,15 +261,34 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) { unsigned long orig_jiffies = jiffies; unsigned int temp; + int successes = 0; dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); while (1) { temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); - if (for_busy && (temp & I2SR_IBB)) - break; - if (!for_busy && !(temp & I2SR_IBB)) - break; + + /* check for arbitration lost */ + if (temp & I2SR_IAL) { + temp &= ~I2SR_IAL; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); + return -EAGAIN; + } + + if (for_busy) { + if (temp & I2SR_IBB) { + if (successes++ > 2) + break; + } else + successes = 0 ; + } + if (!for_busy) { + if (!(temp & I2SR_IBB)) { + if (successes++ > 2) + break; + } else + successes = 0 ; + } if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { dev_dbg(&i2c_imx->adapter.dev, "<%s> I2C bus is busy\n", __func__); @@ -653,6 +672,8 @@ static int i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.algo = &i2c_imx_algo; i2c_imx->adapter.dev.parent = &pdev->dev; i2c_imx->adapter.nr = pdev->id; + i2c_imx->adapter.retries = 500; + i2c_imx->adapter.timeout = msecs_to_jiffies(2000); i2c_imx->adapter.dev.of_node = pdev->dev.of_node; i2c_imx->base = base; From 617dd7a27cd4350a07768a6d7a32c7a7c63f2f15 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 22 Aug 2014 12:49:37 +0200 Subject: [PATCH 0403/1983] video: mxc_hdmi: Implement Hotplug Jitter timer Wait a second to make sure to only handle a single state change when we are triggered by an interrupt. Sometimes jiggly connections can send a group of spurious signals. This also has the side effect of fixing the detection flapping when detecting hotplug in DVI mode. --- drivers/video/mxc/mxc_hdmi.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 80ed39c00c204d..809143b5cd8604 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -157,7 +157,8 @@ struct mxc_hdmi { struct clk *hdmi_isfr_clk; struct clk *hdmi_iahb_clk; struct clk *mipi_core_clk; - struct delayed_work hotplug_work; + struct timer_list jitter_timer; + struct work_struct hotplug_work; struct delayed_work hdcp_hdp_work; struct notifier_block nb; @@ -2085,9 +2086,8 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) static void hotplug_worker(struct work_struct *work) { - struct delayed_work *delay_work = to_delayed_work(work); struct mxc_hdmi *hdmi = - container_of(delay_work, struct mxc_hdmi, hotplug_work); + container_of(work, struct mxc_hdmi, hotplug_work); u32 hdmi_phy_stat0, hdmi_phy_pol0, hdmi_phy_mask0; unsigned long flags; char event_string[32]; @@ -2146,6 +2146,13 @@ static void hotplug_worker(struct work_struct *work) spin_unlock_irqrestore(&hdmi->irq_lock, flags); } +static void hotplug_work_launch(unsigned long data) +{ + struct mxc_hdmi *hdmi = (struct mxc_hdmi *)data; + pr_debug("%s\n", __func__); + schedule_work(&hdmi->hotplug_work); +} + static void hdcp_hdp_worker(struct work_struct *work) { struct delayed_work *delay_work = to_delayed_work(work); @@ -2190,6 +2197,7 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) if (intr_stat & hdmi->plug_event) { dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); + dev_dbg(&hdmi->pdev->dev, "intr_stat %u plug_event %u\n", intr_stat, hdmi->plug_event); hdmi->latest_intr_stat = intr_stat; /* Mute interrupts until handled */ @@ -2205,7 +2213,9 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) /* Clear Hotplug interrupts */ hdmi_writeb(hdmi->plug_event, HDMI_IH_PHY_STAT0); - schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20)); + if(hdmi_inited) { + mod_timer(&hdmi->jitter_timer, jiffies + HZ); + } } /* Check HDCP interrupt state */ @@ -2714,7 +2724,8 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, hdmi->plug_event = HDMI_DVI_IH_STAT; hdmi->plug_mask = HDMI_DVI_STAT; - INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker); + setup_timer(&hdmi->jitter_timer, hotplug_work_launch, (unsigned long)hdmi); + INIT_WORK(&hdmi->hotplug_work, hotplug_worker); INIT_DELAYED_WORK(&hdmi->hdcp_hdp_work, hdcp_hdp_worker); /* Configure registers related to HDMI interrupt From bb2d025f85ae0be3e74c9b5fa598c67b01dc0e9f Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 26 Jun 2015 13:31:48 +0200 Subject: [PATCH 0404/1983] gpu: galcore: Add an initial flattened code drop of galcore This is a flattened version of galcore 5.0.11.p4.25762 from Freescale. it has basic changes just to test the build but will not work the upstream device-tree. I will work from here. --- drivers/gpu/Makefile | 1 + drivers/gpu/galcore/Kbuild | 255 + drivers/gpu/galcore/Kconfig | 8 + .../default/gc_hal_kernel_allocator_array.h | 34 + .../freescale/gc_hal_kernel_allocator_array.h | 45 + .../freescale/gc_hal_kernel_allocator_cma.c | 386 + .../gpu/galcore/arch/gc_hal_kernel_context.c | 2317 +++++ .../gpu/galcore/arch/gc_hal_kernel_context.h | 183 + .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 8036 +++++++++++++++ .../gpu/galcore/arch/gc_hal_kernel_hardware.h | 160 + .../gpu/galcore/arch/gc_hal_kernel_recorder.c | 679 ++ .../gc_hal_kernel_hardware_command_vg.c | 932 ++ .../gc_hal_kernel_hardware_command_vg.h | 319 + .../archvg/gc_hal_kernel_hardware_vg.c | 2119 ++++ .../archvg/gc_hal_kernel_hardware_vg.h | 74 + drivers/gpu/galcore/config | 37 + drivers/gpu/galcore/gc_hal_kernel.c | 5041 ++++++++++ drivers/gpu/galcore/gc_hal_kernel.h | 1489 +++ drivers/gpu/galcore/gc_hal_kernel_allocator.c | 897 ++ drivers/gpu/galcore/gc_hal_kernel_allocator.h | 400 + drivers/gpu/galcore/gc_hal_kernel_command.c | 3423 +++++++ .../gpu/galcore/gc_hal_kernel_command_vg.c | 3787 ++++++++ drivers/gpu/galcore/gc_hal_kernel_db.c | 1861 ++++ drivers/gpu/galcore/gc_hal_kernel_debug.c | 2785 ++++++ drivers/gpu/galcore/gc_hal_kernel_debug.h | 103 + drivers/gpu/galcore/gc_hal_kernel_debugfs.c | 1166 +++ drivers/gpu/galcore/gc_hal_kernel_debugfs.h | 135 + drivers/gpu/galcore/gc_hal_kernel_device.c | 2706 ++++++ drivers/gpu/galcore/gc_hal_kernel_device.h | 215 + drivers/gpu/galcore/gc_hal_kernel_event.c | 3459 +++++++ drivers/gpu/galcore/gc_hal_kernel_heap.c | 858 ++ .../gpu/galcore/gc_hal_kernel_interrupt_vg.c | 877 ++ drivers/gpu/galcore/gc_hal_kernel_iommu.c | 202 + drivers/gpu/galcore/gc_hal_kernel_linux.c | 497 + drivers/gpu/galcore/gc_hal_kernel_linux.h | 364 + drivers/gpu/galcore/gc_hal_kernel_math.c | 32 + drivers/gpu/galcore/gc_hal_kernel_mmu.c | 2260 +++++ drivers/gpu/galcore/gc_hal_kernel_mmu_vg.c | 522 + drivers/gpu/galcore/gc_hal_kernel_os.c | 8587 +++++++++++++++++ drivers/gpu/galcore/gc_hal_kernel_os.h | 88 + drivers/gpu/galcore/gc_hal_kernel_platform.h | 279 + drivers/gpu/galcore/gc_hal_kernel_power.c | 347 + drivers/gpu/galcore/gc_hal_kernel_precomp.h | 29 + drivers/gpu/galcore/gc_hal_kernel_probe.c | 1324 +++ drivers/gpu/galcore/gc_hal_kernel_security.c | 239 + .../galcore/gc_hal_kernel_security_channel.c | 385 + drivers/gpu/galcore/gc_hal_kernel_sync.c | 177 + drivers/gpu/galcore/gc_hal_kernel_sync.h | 72 + drivers/gpu/galcore/gc_hal_kernel_vg.c | 833 ++ drivers/gpu/galcore/gc_hal_kernel_vg.h | 85 + .../gpu/galcore/gc_hal_kernel_video_memory.c | 2807 ++++++ drivers/gpu/galcore/inc/gc_hal.h | 2859 ++++++ drivers/gpu/galcore/inc/gc_hal_base.h | 5520 +++++++++++ drivers/gpu/galcore/inc/gc_hal_driver.h | 1136 +++ drivers/gpu/galcore/inc/gc_hal_driver_vg.h | 270 + drivers/gpu/galcore/inc/gc_hal_dump.h | 89 + drivers/gpu/galcore/inc/gc_hal_eglplatform.h | 672 ++ .../gpu/galcore/inc/gc_hal_eglplatform_type.h | 286 + drivers/gpu/galcore/inc/gc_hal_engine.h | 2587 +++++ drivers/gpu/galcore/inc/gc_hal_engine_vg.h | 1215 +++ drivers/gpu/galcore/inc/gc_hal_enum.h | 1608 +++ .../gpu/galcore/inc/gc_hal_kernel_buffer.h | 225 + drivers/gpu/galcore/inc/gc_hal_mem.h | 530 + drivers/gpu/galcore/inc/gc_hal_options.h | 1271 +++ drivers/gpu/galcore/inc/gc_hal_profiler.h | 585 ++ drivers/gpu/galcore/inc/gc_hal_raster.h | 1038 ++ drivers/gpu/galcore/inc/gc_hal_rename.h | 243 + .../galcore/inc/gc_hal_security_interface.h | 137 + drivers/gpu/galcore/inc/gc_hal_statistics.h | 99 + drivers/gpu/galcore/inc/gc_hal_types.h | 929 ++ drivers/gpu/galcore/inc/gc_hal_version.h | 39 + drivers/gpu/galcore/inc/gc_hal_vg.h | 896 ++ .../gc_hal_kernel_platform_imx6q14.c | 683 ++ .../gc_hal_kernel_platform_imx6q14.config | 15 + drivers/video/Kconfig | 2 + 75 files changed, 86850 insertions(+) create mode 100644 drivers/gpu/galcore/Kbuild create mode 100644 drivers/gpu/galcore/Kconfig create mode 100644 drivers/gpu/galcore/allocator/default/gc_hal_kernel_allocator_array.h create mode 100644 drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_array.h create mode 100644 drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_cma.c create mode 100644 drivers/gpu/galcore/arch/gc_hal_kernel_context.c create mode 100644 drivers/gpu/galcore/arch/gc_hal_kernel_context.h create mode 100644 drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c create mode 100644 drivers/gpu/galcore/arch/gc_hal_kernel_hardware.h create mode 100644 drivers/gpu/galcore/arch/gc_hal_kernel_recorder.c create mode 100644 drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.c create mode 100644 drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.h create mode 100644 drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c create mode 100644 drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.h create mode 100644 drivers/gpu/galcore/config create mode 100644 drivers/gpu/galcore/gc_hal_kernel.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_allocator.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_allocator.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_command.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_command_vg.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_db.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_debug.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_debug.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_debugfs.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_debugfs.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_device.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_device.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_event.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_heap.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_iommu.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_linux.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_linux.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_math.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_mmu.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_mmu_vg.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_os.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_os.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_platform.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_power.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_precomp.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_probe.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_security.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_security_channel.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_sync.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_sync.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_vg.c create mode 100644 drivers/gpu/galcore/gc_hal_kernel_vg.h create mode 100644 drivers/gpu/galcore/gc_hal_kernel_video_memory.c create mode 100644 drivers/gpu/galcore/inc/gc_hal.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_base.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_driver.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_driver_vg.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_dump.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_eglplatform.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_engine.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_engine_vg.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_enum.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_mem.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_options.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_profiler.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_raster.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_rename.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_security_interface.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_statistics.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_types.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_version.h create mode 100644 drivers/gpu/galcore/inc/gc_hal_vg.h create mode 100644 drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c create mode 100644 drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.config diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index d8a22c2a579d45..0bc3f3abe4b826 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1,2 +1,3 @@ obj-y += drm/ vga/ obj-$(CONFIG_TEGRA_HOST1X) += host1x/ +obj-$(CONFIG_VIVANTE_GALCORE) += galcore/ diff --git a/drivers/gpu/galcore/Kbuild b/drivers/gpu/galcore/Kbuild new file mode 100644 index 00000000000000..24fe8c1c688f62 --- /dev/null +++ b/drivers/gpu/galcore/Kbuild @@ -0,0 +1,255 @@ +############################################################################## +# +# Copyright (C) 2005 - 2014 by Vivante Corp. +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +############################################################################## + + +# +# Linux build file for kernel HAL driver. +# + +AQROOT := $(srctree)/drivers/gpu/galcore + +include $(AQROOT)/config + +KERNEL_DIR ?= $(TOOL_DIR)/kernel + +# Check and include platform config. +ifneq ($(PLATFORM),) + +# Get platform config path. +PLATFORM_CONFIG ?= $(AQROOT)/platform/$(PLATFORM).config + +# Check whether it exists. +PLATFORM_CONFIG := $(wildcard $(PLATFORM_CONFIG)) + +# Include it if exists. +ifneq ($(PLATFORM_CONFIG),) +include $(PLATFORM_CONFIG) +endif + +endif + +MODULE_NAME ?= galcore +CUSTOMER_ALLOCATOR_OBJS ?= +ALLOCATOR_ARRAY_H_LOCATION ?= allocator/default/ + +EXTRA_CFLAGS += -Werror + +OBJS := gc_hal_kernel_device.o \ + gc_hal_kernel_linux.o \ + gc_hal_kernel_math.o \ + gc_hal_kernel_os.o \ + gc_hal_kernel_debugfs.o \ + gc_hal_kernel_allocator.o \ + +ifneq ($(CONFIG_IOMMU_SUPPORT),) +OBJS += gc_hal_kernel_iommu.o +endif + +OBJS += gc_hal_kernel_probe.o +ifneq ($(PLATFORM),) +OBJS += platform/$(PLATFORM).o +endif + +OBJS += gc_hal_kernel.o \ + gc_hal_kernel_command.o \ + gc_hal_kernel_db.o \ + gc_hal_kernel_debug.o \ + gc_hal_kernel_event.o \ + gc_hal_kernel_heap.o \ + gc_hal_kernel_mmu.o \ + gc_hal_kernel_video_memory.o \ + gc_hal_kernel_power.o \ + +OBJS += arch/gc_hal_kernel_context.o \ + arch/gc_hal_kernel_hardware.o + +ifeq ($(VIVANTE_ENABLE_3D), 1) +OBJS += arch/gc_hal_kernel_recorder.o +endif + +ifeq ($(VIVANTE_ENABLE_VG), 1) +OBJS +=\ + gc_hal_kernel_vg.o \ + gc_hal_kernel_command_vg.o \ + gc_hal_kernel_interrupt_vg.o \ + gc_hal_kernel_mmu_vg.o \ + archvg/gc_hal_kernel_hardware_command_vg.o \ + archvg/gc_hal_kernel_hardware_vg.o +endif + +ifneq ($(CONFIG_SYNC),) +EXTRA_CFLAGS += -Idrivers/staging/android + +OBJS += gc_hal_kernel_sync.o +endif + +ifeq ($(SECURITY), 1) +OBJS += gc_hal_kernel_security_channel.o \ + gc_hal_kernel_security.o +endif + +ifneq ($(CUSTOMER_ALLOCATOR_OBJS),) +OBJS += $(CUSTOMER_ALLOCATOR_OBJS) +endif + +ifeq ($(KERNELRELEASE), ) + +.PHONY: all clean install + +# Define targets. +all: + @make V=$(V) ARCH=$(ARCH_TYPE) -C $(KERNEL_DIR) SUBDIRS=`pwd` modules + +clean: + @rm -rf $(OBJS) + @rm -rf modules.order Module.symvers + @find $(AQROOT) -name ".gc_*.cmd" | xargs rm -f + +install: all + @mkdir -p $(SDK_DIR)/drivers + +else + + +EXTRA_CFLAGS += -DLINUX -DDRIVER + +ifeq ($(DEBUG), 1) +EXTRA_CFLAGS += -DDBG=1 -DDEBUG -D_DEBUG +else +EXTRA_CFLAGS += -DDBG=0 +endif + +ifeq ($(NO_DMA_COHERENT), 1) +EXTRA_CFLAGS += -DNO_DMA_COHERENT +endif + +ifneq ($(USE_PLATFORM_DRIVER), 0) +EXTRA_CFLAGS += -DUSE_PLATFORM_DRIVER=1 +else +EXTRA_CFLAGS += -DUSE_PLATFORM_DRIVER=0 +endif + +EXTRA_CFLAGS += -DVIVANTE_PROFILER=1 +EXTRA_CFLAGS += -DVIVANTE_PROFILER_CONTEXT=1 + +ifeq ($(ENABLE_GPU_CLOCK_BY_DRIVER), 1) +EXTRA_CFLAGS += -DENABLE_GPU_CLOCK_BY_DRIVER=1 +else +EXTRA_CFLAGS += -DENABLE_GPU_CLOCK_BY_DRIVER=0 +endif + +ifeq ($(USE_NEW_LINUX_SIGNAL), 1) +EXTRA_CFLAGS += -DUSE_NEW_LINUX_SIGNAL=1 +else +EXTRA_CFLAGS += -DUSE_NEW_LINUX_SIGNAL=0 +endif + +ifeq ($(FORCE_ALL_VIDEO_MEMORY_CACHED), 1) +EXTRA_CFLAGS += -DgcdPAGED_MEMORY_CACHEABLE=1 +else +EXTRA_CFLAGS += -DgcdPAGED_MEMORY_CACHEABLE=0 +endif + +ifeq ($(NONPAGED_MEMORY_CACHEABLE), 1) +EXTRA_CFLAGS += -DgcdNONPAGED_MEMORY_CACHEABLE=1 +else +EXTRA_CFLAGS += -DgcdNONPAGED_MEMORY_CACHEABLE=0 +endif + +ifeq ($(NONPAGED_MEMORY_BUFFERABLE), 1) +EXTRA_CFLAGS += -DgcdNONPAGED_MEMORY_BUFFERABLE=1 +else +EXTRA_CFLAGS += -DgcdNONPAGED_MEMORY_BUFFERABLE=0 +endif + +ifeq ($(CACHE_FUNCTION_UNIMPLEMENTED), 1) +EXTRA_CFLAGS += -DgcdCACHE_FUNCTION_UNIMPLEMENTED=1 +else +EXTRA_CFLAGS += -DgcdCACHE_FUNCTION_UNIMPLEMENTED=0 +endif + +ifeq ($(CONFIG_SMP), y) +EXTRA_CFLAGS += -DgcdSMP=1 +else +EXTRA_CFLAGS += -DgcdSMP=0 +endif + +ifeq ($(VIVANTE_ENABLE_3D),0) +EXTRA_CFLAGS += -DgcdENABLE_3D=0 +else +EXTRA_CFLAGS += -DgcdENABLE_3D=1 +endif + +ifeq ($(VIVANTE_ENABLE_2D),0) +EXTRA_CFLAGS += -DgcdENABLE_2D=0 +else +EXTRA_CFLAGS += -DgcdENABLE_2D=1 +endif + +ifeq ($(VIVANTE_ENABLE_VG),0) +EXTRA_CFLAGS += -DgcdENABLE_VG=0 +else +EXTRA_CFLAGS += -DgcdENABLE_VG=1 +endif + +ifeq ($(ENABLE_OUTER_CACHE_PATCH), 1) +EXTRA_CFLAGS += -DgcdENABLE_OUTER_CACHE_PATCH=1 +else +EXTRA_CFLAGS += -DgcdENABLE_OUTER_CACHE_PATCH=0 +endif + +ifeq ($(USE_BANK_ALIGNMENT), 1) + EXTRA_CFLAGS += -DgcdENABLE_BANK_ALIGNMENT=1 + ifneq ($(BANK_BIT_START), 0) + ifneq ($(BANK_BIT_END), 0) + EXTRA_CFLAGS += -DgcdBANK_BIT_START=$(BANK_BIT_START) + EXTRA_CFLAGS += -DgcdBANK_BIT_END=$(BANK_BIT_END) + endif + endif + + ifneq ($(BANK_CHANNEL_BIT), 0) + EXTRA_CFLAGS += -DgcdBANK_CHANNEL_BIT=$(BANK_CHANNEL_BIT) + endif +endif + +ifeq ($(gcdFPGA_BUILD), 1) +EXTRA_CFLAGS += -DgcdFPGA_BUILD=1 +else +EXTRA_CFLAGS += -DgcdFPGA_BUILD=0 +endif + +ifeq ($(SECURITY), 1) +EXTRA_CFLAGS += -DgcdSECURITY=1 +endif + +EXTRA_CFLAGS += -I$(AQROOT) +EXTRA_CFLAGS += -I$(AQROOT)/arch +EXTRA_CFLAGS += -I$(AQROOT)/inc +EXTRA_CFLAGS += -I$(AQROOT)/$(ALLOCATOR_ARRAY_H_LOCATION) + +ifeq ($(VIVANTE_ENABLE_VG), 1) +EXTRA_CFLAGS += -I$(AQROOT)/archvg +endif + +obj-$(CONFIG_VIVANTE_GALCORE) += galcore.o + +galcore-objs := $(OBJS) + +endif diff --git a/drivers/gpu/galcore/Kconfig b/drivers/gpu/galcore/Kconfig new file mode 100644 index 00000000000000..7b55726a691640 --- /dev/null +++ b/drivers/gpu/galcore/Kconfig @@ -0,0 +1,8 @@ +menu "Vivante GPU support" + +config VIVANTE_GALCORE + tristate "Vivante GPU support" + ---help--- + Say Y to get the GPU driver support. + +endmenu diff --git a/drivers/gpu/galcore/allocator/default/gc_hal_kernel_allocator_array.h b/drivers/gpu/galcore/allocator/default/gc_hal_kernel_allocator_array.h new file mode 100644 index 00000000000000..fa2190f73a0a99 --- /dev/null +++ b/drivers/gpu/galcore/allocator/default/gc_hal_kernel_allocator_array.h @@ -0,0 +1,34 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +extern gceSTATUS +_DefaultAlloctorInit( + IN gckOS Os, + OUT gckALLOCATOR * Allocator + ); + +gcsALLOCATOR_DESC allocatorArray[] = +{ + /* Default allocator. */ + gcmkDEFINE_ALLOCATOR_DESC("default", _DefaultAlloctorInit), +}; + + diff --git a/drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_array.h b/drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_array.h new file mode 100644 index 00000000000000..acdd929a0f44cf --- /dev/null +++ b/drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_array.h @@ -0,0 +1,45 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +extern gceSTATUS +_DefaultAlloctorInit( + IN gckOS Os, + OUT gckALLOCATOR * Allocator + ); + +#if LINUX_CMA_FSL +gceSTATUS +_CMAFSLAlloctorInit( + IN gckOS Os, + OUT gckALLOCATOR * Allocator + ); +#endif + +gcsALLOCATOR_DESC allocatorArray[] = +{ +#if LINUX_CMA_FSL + gcmkDEFINE_ALLOCATOR_DESC("cmafsl", _CMAFSLAlloctorInit), +#endif + /* Default allocator. */ + gcmkDEFINE_ALLOCATOR_DESC("default", _DefaultAlloctorInit), +}; + + diff --git a/drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_cma.c b/drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_cma.c new file mode 100644 index 00000000000000..03e540f0c0d75c --- /dev/null +++ b/drivers/gpu/galcore/allocator/freescale/gc_hal_kernel_allocator_cma.c @@ -0,0 +1,386 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" +#include "gc_hal_kernel_allocator.h" + +#include +#include +#include +#include +#include +#include +#include + +#define _GC_OBJ_ZONE gcvZONE_OS + +typedef struct _gcsCMA_PRIV * gcsCMA_PRIV_PTR; +typedef struct _gcsCMA_PRIV { + gctUINT32 cmasize; +} +gcsCMA_PRIV; + +struct mdl_cma_priv { + gctPOINTER kvaddr; + dma_addr_t physical; +}; + +int gc_cma_usage_show(struct seq_file* m, void* data) +{ + gcsINFO_NODE *node = m->private; + gckALLOCATOR Allocator = node->device; + gcsCMA_PRIV_PTR priv = Allocator->privateData; + + seq_printf(m, "cma: %u bytes\n", priv->cmasize); + + return 0; +} + +static gcsINFO InfoList[] = +{ + {"cmausage", gc_cma_usage_show}, +}; + +static void +_DefaultAllocatorDebugfsInit( + IN gckALLOCATOR Allocator, + IN gckDEBUGFS_DIR Root + ) +{ + gcmkVERIFY_OK( + gckDEBUGFS_DIR_Init(&Allocator->debugfsDir, Root->root, "cma")); + + gcmkVERIFY_OK(gckDEBUGFS_DIR_CreateFiles( + &Allocator->debugfsDir, + InfoList, + gcmCOUNTOF(InfoList), + Allocator + )); +} + +static void +_DefaultAllocatorDebugfsCleanup( + IN gckALLOCATOR Allocator + ) +{ + gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles( + &Allocator->debugfsDir, + InfoList, + gcmCOUNTOF(InfoList) + )); + + gckDEBUGFS_DIR_Deinit(&Allocator->debugfsDir); +} + +static gceSTATUS +_CMAFSLAlloc( + IN gckALLOCATOR Allocator, + INOUT PLINUX_MDL Mdl, + IN gctSIZE_T NumPages, + IN gctUINT32 Flags + ) +{ + gceSTATUS status; + gcsCMA_PRIV_PTR priv = (gcsCMA_PRIV_PTR)Allocator->privateData; + + struct mdl_cma_priv *mdl_priv=gcvNULL; + gckOS os = Allocator->os; + + gcmkHEADER_ARG("Mdl=%p NumPages=%d", Mdl, NumPages); + + gcmkONERROR(gckOS_Allocate(os, sizeof(struct mdl_cma_priv), (gctPOINTER *)&mdl_priv)); + mdl_priv->kvaddr = gcvNULL; + + mdl_priv->kvaddr = dma_alloc_writecombine(gcvNULL, + NumPages * PAGE_SIZE, + &mdl_priv->physical, + GFP_KERNEL | gcdNOWARN); + + if (mdl_priv->kvaddr == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + Mdl->priv = mdl_priv; + priv->cmasize += NumPages * PAGE_SIZE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if(mdl_priv) + gckOS_Free(os, mdl_priv); + gcmkFOOTER(); + return status; +} + +static void +_CMAFSLFree( + IN gckALLOCATOR Allocator, + IN OUT PLINUX_MDL Mdl + ) +{ + gckOS os = Allocator->os; + struct mdl_cma_priv *mdl_priv=(struct mdl_cma_priv *)Mdl->priv; + gcsCMA_PRIV_PTR priv = (gcsCMA_PRIV_PTR)Allocator->privateData; + dma_free_writecombine(gcvNULL, + Mdl->numPages * PAGE_SIZE, + mdl_priv->kvaddr, + mdl_priv->physical); + gckOS_Free(os, mdl_priv); + priv->cmasize -= Mdl->numPages * PAGE_SIZE; +} + +gctINT +_CMAFSLMapUser( + gckALLOCATOR Allocator, + PLINUX_MDL Mdl, + PLINUX_MDL_MAP MdlMap, + gctBOOL Cacheable + ) +{ + + PLINUX_MDL mdl = Mdl; + PLINUX_MDL_MAP mdlMap = MdlMap; + struct mdl_cma_priv *mdl_priv=(struct mdl_cma_priv *)Mdl->priv; + + gcmkHEADER_ARG("Allocator=%p Mdl=%p MdlMap=%p gctBOOL=%d", Allocator, Mdl, MdlMap, Cacheable); + + mdlMap->vmaAddr = (gctSTRING)vm_mmap(gcvNULL, + 0L, + mdl->numPages * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + 0); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): vmaAddr->0x%X for phys_addr->0x%X", + __FUNCTION__, __LINE__, + (gctUINT32)(gctUINTPTR_T)mdlMap->vmaAddr, + (gctUINT32)(gctUINTPTR_T)mdl + ); + + if (IS_ERR(mdlMap->vmaAddr)) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): do_mmap_pgoff error", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + down_write(¤t->mm->mmap_sem); + + mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr); + + if (mdlMap->vma == gcvNULL) + { + up_write(¤t->mm->mmap_sem); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): find_vma error", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } + + /* Now map all the vmalloc pages to this user address. */ + if (mdl->contiguous) + { + /* map kernel memory to user space.. */ + if (dma_mmap_writecombine(gcvNULL, + mdlMap->vma, + mdl_priv->kvaddr, + mdl_priv->physical, + mdl->numPages * PAGE_SIZE) < 0) + { + up_write(¤t->mm->mmap_sem); + + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): dma_mmap_attrs error", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + } + else + { + gckOS_Print("incorrect mdl:conti%d\n",mdl->contiguous); + } + + up_write(¤t->mm->mmap_sem); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +void +_CMAUnmapUser( + IN gckALLOCATOR Allocator, + IN gctPOINTER Logical, + IN gctUINT32 Size + ) +{ + if (unlikely(current->mm == gcvNULL)) + { + /* Do nothing if process is exiting. */ + return; + } + + if (vm_munmap((unsigned long)Logical, Size) < 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): vm_munmap failed", + __FUNCTION__, __LINE__ + ); + } +} + +gceSTATUS +_CMAMapKernel( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + OUT gctPOINTER *Logical + ) +{ + struct mdl_cma_priv *mdl_priv=(struct mdl_cma_priv *)Mdl->priv; + *Logical =mdl_priv->kvaddr; + return gcvSTATUS_OK; +} + +gceSTATUS +_CMAUnmapKernel( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical + ) +{ + return gcvSTATUS_OK; +} + +extern gceSTATUS +_DefaultLogicalToPhysical( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + OUT gctUINT32_PTR Physical + ); + +extern gceSTATUS +_DefaultCache( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical, + IN gctUINT32 Physical, + IN gctUINT32 Bytes, + IN gceCACHEOPERATION Operation + ); + +gceSTATUS +_CMAPhysical( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctUINT32 Offset, + OUT gctUINT32_PTR Physical + ) +{ + struct mdl_cma_priv *mdl_priv=(struct mdl_cma_priv *)Mdl->priv; + gcmkASSERT(!Offset); + *Physical = mdl_priv->physical; + + return gcvSTATUS_OK; +} + + +extern void +_DefaultAllocatorDestructor( + IN void* PrivateData + ); + +/* Default allocator operations. */ +gcsALLOCATOR_OPERATIONS CMAFSLAllocatorOperations = { + .Alloc = _CMAFSLAlloc, + .Free = _CMAFSLFree, + .MapUser = _CMAFSLMapUser, + .UnmapUser = _CMAUnmapUser, + .MapKernel = _CMAMapKernel, + .UnmapKernel = _CMAUnmapKernel, + .LogicalToPhysical = _DefaultLogicalToPhysical, + .Cache = _DefaultCache, + .Physical = _CMAPhysical, +}; + +/* Default allocator entry. */ +gceSTATUS +_CMAFSLAlloctorInit( + IN gckOS Os, + OUT gckALLOCATOR * Allocator + ) +{ + gceSTATUS status; + gckALLOCATOR allocator; + gcsCMA_PRIV_PTR priv = gcvNULL; + + gcmkONERROR( + gckALLOCATOR_Construct(Os, &CMAFSLAllocatorOperations, &allocator)); + + priv = kzalloc(gcmSIZEOF(gcsCMA_PRIV), GFP_KERNEL | gcdNOWARN); + + if (!priv) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Register private data. */ + allocator->privateData = priv; + allocator->privateDataDestructor = _DefaultAllocatorDestructor; + + allocator->debugfsInit = _DefaultAllocatorDebugfsInit; + allocator->debugfsCleanup = _DefaultAllocatorDebugfsCleanup; + + allocator->capability = gcvALLOC_FLAG_CONTIGUOUS; + + *Allocator = allocator; + + return gcvSTATUS_OK; + +OnError: + return status; +} + diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c new file mode 100644 index 00000000000000..08644a7cf5e52e --- /dev/null +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c @@ -0,0 +1,2317 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal.h" +#include "gc_hal_kernel.h" +#include "gc_hal_kernel_context.h" +#include "gc_hal_kernel_buffer.h" + +/******************************************************************************\ +******************************** Debugging Macro ******************************* +\******************************************************************************/ + +/* Zone used for header/footer. */ +#define _GC_OBJ_ZONE gcvZONE_HARDWARE + + +/******************************************************************************\ +************************** Context State Buffer Helpers ************************ +\******************************************************************************/ + +#define _STATE(reg) \ + _State(\ + Context, index, \ + reg ## _Address >> 2, \ + reg ## _ResetValue, \ + reg ## _Count, \ + gcvFALSE, gcvFALSE \ + ) + +#define _STATE_COUNT(reg, count) \ + _State(\ + Context, index, \ + reg ## _Address >> 2, \ + reg ## _ResetValue, \ + count, \ + gcvFALSE, gcvFALSE \ + ) + +#define _STATE_COUNT_OFFSET(reg, offset, count) \ + _State(\ + Context, index, \ + (reg ## _Address >> 2) + offset, \ + reg ## _ResetValue, \ + count, \ + gcvFALSE, gcvFALSE \ + ) + +#define _STATE_MIRROR_COUNT(reg, mirror, count) \ + _StateMirror(\ + Context, \ + reg ## _Address >> 2, \ + count, \ + mirror ## _Address >> 2 \ + ) + +#define _STATE_HINT(reg) \ + _State(\ + Context, index, \ + reg ## _Address >> 2, \ + reg ## _ResetValue, \ + reg ## _Count, \ + gcvFALSE, gcvTRUE \ + ) + +#define _STATE_HINT_BLOCK(reg, block, count) \ + _State(\ + Context, index, \ + (reg ## _Address >> 2) + (block << reg ## _BLK), \ + reg ## _ResetValue, \ + count, \ + gcvFALSE, gcvTRUE \ + ) + +#define _STATE_COUNT_OFFSET_HINT(reg, offset, count) \ + _State(\ + Context, index, \ + (reg ## _Address >> 2) + offset, \ + reg ## _ResetValue, \ + count, \ + gcvFALSE, gcvTRUE \ + ) + +#define _STATE_X(reg) \ + _State(\ + Context, index, \ + reg ## _Address >> 2, \ + reg ## _ResetValue, \ + reg ## _Count, \ + gcvTRUE, gcvFALSE \ + ) + +#define _STATE_INIT_VALUE(reg, value) \ + _State(\ + Context, index, \ + reg ## _Address >> 2, \ + value, \ + reg ## _Count, \ + gcvFALSE, gcvFALSE \ + ) + +#define _CLOSE_RANGE() \ + _TerminateStateBlock(Context, index) + +#define _ENABLE(reg, field) \ + do \ + { \ + if (gcmVERIFYFIELDVALUE(data, reg, MASK_ ## field, ENABLED)) \ + { \ + enable |= gcmFIELDMASK(reg, field); \ + } \ + } \ + while (gcvFALSE) + +#define _BLOCK_COUNT(reg) \ + ((reg ## _Count) >> (reg ## _BLK)) + + +/******************************************************************************\ +*********************** Support Functions and Definitions ********************** +\******************************************************************************/ + +#define gcdSTATE_MASK \ + (((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x03 | 0xC0FFEE & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27)))) + +#if gcdENABLE_3D +static gctUINT32 +_TerminateStateBlock( + IN gckCONTEXT Context, + IN gctUINT32 Index + ) +{ + gctUINT32_PTR buffer; + gctUINT32 align; + + /* Determine if we need alignment. */ + align = (Index & 1) ? 1 : 0; + + /* Address correct index. */ + buffer = (Context->buffer == gcvNULL) + ? gcvNULL + : Context->buffer->logical; + + /* Flush the current state block; make sure no pairing with the states + to follow happens. */ + if (align && (buffer != gcvNULL)) + { + buffer[Index] = 0xDEADDEAD; + } + + /* Reset last address. */ + Context->lastAddress = ~0U; + + /* Return alignment requirement. */ + return align; +} +#endif + + +#if (gcdENABLE_3D || gcdENABLE_2D) +static gctUINT32 +_FlushPipe( + IN gckCONTEXT Context, + IN gctUINT32 Index, + IN gcePIPE_SELECT Pipe + ) +{ + gctBOOL fcFlushStall; + gctUINT32 flushSlots; + gctBOOL iCacheInvalidate; + + fcFlushStall + = gckHARDWARE_IsFeatureAvailable(Context->hardware, gcvFEATURE_FC_FLUSH_STALL); + + iCacheInvalidate + = ((((gctUINT32) (Context->hardware->identity.chipMinorFeatures3)) >> (0 ? 3:3) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))); + + flushSlots = 6; + + if (fcFlushStall) + { + /* Flush tile status cache. */ + flushSlots += 6; + } + + if (iCacheInvalidate) + { + flushSlots += 12; + } + + if (Context->buffer != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Address correct index. */ + buffer = Context->buffer->logical + Index; + + /* Flush the current pipe. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = (Pipe == gcvPIPE_2D) + ? ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) + : ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); + + /* Semaphore from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Stall from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + if (fcFlushStall) + { + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0594) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + /* Semaphore from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Stall from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + } + + if (iCacheInvalidate) + { + /* Invalidate I$ after pipe is stalled */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0218) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x021A) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0218) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x021A) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); + + /* Semaphore from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Stall from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + } + } + + /* Number of slots taken by flushing pipe. */ + return flushSlots; +} +#endif + +#if gcdENABLE_3D +static gctUINT32 +_SemaphoreStall( + IN gckCONTEXT Context, + IN gctUINT32 Index + ) +{ + if (Context->buffer != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Address correct index. */ + buffer = Context->buffer->logical + Index; + + /* Semaphore from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Stall from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + } + + /* Semaphore/stall takes 4 slots. */ + return 4; +} +#endif + +#if (gcdENABLE_3D || gcdENABLE_2D) +static gctUINT32 +_SwitchPipe( + IN gckCONTEXT Context, + IN gctUINT32 Index, + IN gcePIPE_SELECT Pipe + ) +{ + gctUINT32 slots = 6; + + if (Context->buffer != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Address correct index. */ + buffer = Context->buffer->logical + Index; + + /* LoadState(AQPipeSelect, 1), pipe. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E00) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ + = (Pipe == gcvPIPE_2D) + ? 0x1 + : 0x0; + + /* Semaphore from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Stall from FE to PE. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + } + + Context->pipeSelectBytes = slots * gcmSIZEOF(gctUINT32); + + return slots; +} +#endif + +#if gcdENABLE_3D +static gctUINT32 +_State( + IN gckCONTEXT Context, + IN gctUINT32 Index, + IN gctUINT32 Address, + IN gctUINT32 Value, + IN gctUINT32 Size, + IN gctBOOL FixedPoint, + IN gctBOOL Hinted + ) +{ + gctUINT32_PTR buffer; + gctUINT32 align; + gctUINT32 i; + + /* Determine if we need alignment. */ + align = (Index & 1) ? 1 : 0; + + /* Address correct index. */ + buffer = (Context->buffer == gcvNULL) + ? gcvNULL + : Context->buffer->logical; + + if ((buffer == gcvNULL) && (Address + Size > Context->stateCount)) + { + /* Determine maximum state. */ + Context->stateCount = Address + Size; + } + + /* Do we need a new entry? */ + if ((Address != Context->lastAddress) || (FixedPoint != Context->lastFixed)) + { + if (buffer != gcvNULL) + { + if (align) + { + /* Add filler. */ + buffer[Index++] = 0xDEADDEAD; + } + + /* LoadState(Address, Count). */ + gcmkASSERT((Index & 1) == 0); + + if (FixedPoint) + { + buffer[Index] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1))))))) << (0 ? 26:26))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1))))))) << (0 ? 26:26))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (Size) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (Address) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + } + else + { + buffer[Index] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1))))))) << (0 ? 26:26))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1))))))) << (0 ? 26:26))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (Size) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (Address) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + } + + /* Walk all the states. */ + for (i = 0; i < (gctUINT32)Size; i += 1) + { + /* Set state to uninitialized value. */ + buffer[Index + 1 + i] = Value; + + /* Set index in state mapping table. */ + Context->map[Address + i].index = (gctUINT)Index + 1 + i; + +#if gcdSECURE_USER + /* Save hint. */ + if (Context->hint != gcvNULL) + { + Context->hint[Address + i] = Hinted; + } +#endif + } + } + + /* Save information for this LoadState. */ + Context->lastIndex = (gctUINT)Index; + Context->lastAddress = Address + (gctUINT32)Size; + Context->lastSize = Size; + Context->lastFixed = FixedPoint; + + /* Return size for load state. */ + return align + 1 + Size; + } + + /* Append this state to the previous one. */ + if (buffer != gcvNULL) + { + /* Update last load state. */ + buffer[Context->lastIndex] = + ((((gctUINT32) (buffer[Context->lastIndex])) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (Context->lastSize + Size) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + /* Walk all the states. */ + for (i = 0; i < (gctUINT32)Size; i += 1) + { + /* Set state to uninitialized value. */ + buffer[Index + i] = Value; + + /* Set index in state mapping table. */ + Context->map[Address + i].index = (gctUINT)Index + i; + +#if gcdSECURE_USER + /* Save hint. */ + if (Context->hint != gcvNULL) + { + Context->hint[Address + i] = Hinted; + } +#endif + } + } + + /* Update last address and size. */ + Context->lastAddress += (gctUINT32)Size; + Context->lastSize += Size; + + /* Return number of slots required. */ + return Size; +} + +static gctUINT32 +_StateMirror( + IN gckCONTEXT Context, + IN gctUINT32 Address, + IN gctUINT32 Size, + IN gctUINT32 AddressMirror + ) +{ + gctUINT32 i; + + /* Process when buffer is set. */ + if (Context->buffer != gcvNULL) + { + /* Walk all states. */ + for (i = 0; i < Size; i++) + { + /* Copy the mapping address. */ + Context->map[Address + i].index = + Context->map[AddressMirror + i].index; + } + } + + /* Return the number of required maps. */ + return Size; +} +#endif + +#if (gcdENABLE_3D || gcdENABLE_2D) +static gceSTATUS +_InitializeContextBuffer( + IN gckCONTEXT Context + ) +{ + gctUINT32_PTR buffer; + gctUINT32 index; + +#if gcdENABLE_3D + gctBOOL halti0, halti1, halti2, halti3; + gctUINT i; + gctUINT vertexUniforms, fragmentUniforms, vsConstBase, psConstBase, constMax; + gctBOOL unifiedUniform; + gctUINT fe2vsCount; +#endif + + /* Reset the buffer index. */ + index = 0; + + /* Reset the last state address. */ + Context->lastAddress = ~0U; + + /* Get the buffer pointer. */ + buffer = (Context->buffer == gcvNULL) + ? gcvNULL + : Context->buffer->logical; + + + /**************************************************************************/ + /* Build 2D states. *******************************************************/ + + +#if gcdENABLE_3D + /**************************************************************************/ + /* Build 3D states. *******************************************************/ + + halti0 = (((((gctUINT32) (Context->hardware->identity.chipMinorFeatures1)) >> (0 ? 23:23)) & ((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1)))))) ); + halti1 = (((((gctUINT32) (Context->hardware->identity.chipMinorFeatures2)) >> (0 ? 11:11)) & ((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1)))))) ); + halti2 = (((((gctUINT32) (Context->hardware->identity.chipMinorFeatures4)) >> (0 ? 16:16)) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) ); + halti3 = (((((gctUINT32) (Context->hardware->identity.chipMinorFeatures5)) >> (0 ? 9:9)) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1)))))) ); + + /* Query how many uniforms can support for non-unified uniform mode. */ + {if (Context->hardware->identity.numConstants > 256){ unifiedUniform = gcvTRUE; vsConstBase = 0xC000; psConstBase = 0xC000; constMax = Context->hardware->identity.numConstants; vertexUniforms = 256; fragmentUniforms = constMax - vertexUniforms;}else if (Context->hardware->identity.numConstants == 256){ if (Context->hardware->identity.chipModel == gcv2000 && Context->hardware->identity.chipRevision == 0x5118) { unifiedUniform = gcvFALSE; vsConstBase = 0x1400; psConstBase = 0x1C00; vertexUniforms = 256; fragmentUniforms = 64; constMax = 320; } else { unifiedUniform = gcvFALSE; vsConstBase = 0x1400; psConstBase = 0x1C00; vertexUniforms = 256; fragmentUniforms = 256; constMax = 512; }}else{ unifiedUniform = gcvFALSE; vsConstBase = 0x1400; psConstBase = 0x1C00; vertexUniforms = 168; fragmentUniforms = 64; constMax = 232;}}; + +#if !gcdENABLE_UNIFIED_CONSTANT + if (Context->hardware->identity.numConstants > 256) + { + unifiedUniform = gcvTRUE; + } + else + { + unifiedUniform = gcvFALSE; + } +#endif + + /* Store the 3D entry index. */ + Context->entryOffset3D = (gctUINT)index * gcmSIZEOF(gctUINT32); + + /* Switch to 3D pipe. */ + index += _SwitchPipe(Context, index, gcvPIPE_3D); + + /* Current context pointer. */ +#if gcdDEBUG + index += _State(Context, index, 0x03850 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); +#endif + + index += _FlushPipe(Context, index, gcvPIPE_3D); + + /* Global states. */ + index += _State(Context, index, 0x03814 >> 2, 0x00000001, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03818 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0381C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03820 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03828 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0382C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03834 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03838 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03854 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0384C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + /* Front End states. */ + fe2vsCount = 12; + if (halti0) + { + fe2vsCount = 16; + } + index += _State(Context, index, 0x00600 >> 2, 0x00000000, fe2vsCount, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + index += _State(Context, index, 0x00644 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x00648 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0064C >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x00650 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00680 >> 2, 0x00000000, 8, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x006A0 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00674 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00670 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00678 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0067C >> 2, 0xFFFFFFFF, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x006C0 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00700 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00740 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00780 >> 2, 0x3F800000, 16, gcvFALSE, gcvFALSE); + + if (halti2) + { + index += _State(Context, index, 0x14600 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14640 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14680 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + } + + /* This register is programed by all chips, which program all DECODE_SELECT as VS + ** except SAMPLER_DECODE_SELECT. + */ + index += _State(Context, index, 0x00860 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + if (((((gctUINT32) (Context->hardware->identity.chipMinorFeatures3)) >> (0 ? 3:3) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))))) + { + /* I-Cache states. */ + index += _State(Context, index, 0x00868 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0086C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0304C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01028 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + if (halti3) + { + index += _State(Context, index, 0x00890 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0104C >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _CLOSE_RANGE(); + } + } + + /* Vertex Shader states. */ + index += _State(Context, index, 0x00804 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00808 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0080C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00810 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00820 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00830 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + index += _CLOSE_RANGE(); + + /* Primitive Assembly states. */ + index += _State(Context, index, 0x00A00 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00A04 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00A08 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A0C >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00A10 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00A14 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A18 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A1C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A28 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A2C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A30 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A40 >> 2, 0x00000000, Context->hardware->identity.varyingsCount, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A34 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A38 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A3C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A80 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A84 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00A8C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00A88 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + +#if gcdMULTI_GPU + index += _State(Context, index, 0x03A00 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03A04 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x03A08 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); +#endif + /* Setup states. */ + index += _State(Context, index, 0x00C00 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00C04 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00C08 >> 2, 0x45000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00C0C >> 2, 0x45000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00C10 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00C14 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00C18 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00C1C >> 2, 0x42000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00C20 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + index += _State(Context, index, 0x00C24 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); + + /* Raster states. */ + index += _State(Context, index, 0x00E00 >> 2, 0x00000001, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00E10 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00E04 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00E40 >> 2, 0x00000000, 16, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00E08 >> 2, 0x00000031, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00E24 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00E20 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + if (halti2) + { + index += _State(Context, index, 0x00E0C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + } + + /* Pixel Shader states. */ + index += _State(Context, index, 0x01004 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01008 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0100C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01010 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01030 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + index += _CLOSE_RANGE(); + + /* Texture states. */ + index += _State(Context, index, 0x02000 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02040 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02080 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x020C0 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02100 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02140 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02180 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x021C0 >> 2, 0x00321000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02200 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x02240 >> 2, 0x00000000, 12, gcvFALSE, gcvFALSE); + index += _State(Context, index, (0x02400 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02440 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02480 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x024C0 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02500 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02540 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02580 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x025C0 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02600 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02640 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02680 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x026C0 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02700 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x02740 >> 2) + (0 << 4), 0x00000000, 12, gcvFALSE, gcvTRUE); + index += _CLOSE_RANGE(); + + if ((((((gctUINT32) (Context->hardware->identity.chipMinorFeatures1)) >> (0 ? 22:22)) & ((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1)))))) )) + { + /* + * Linear stride LODn will overwrite LOD0 on GC880,GC2000. + * And only LOD0 is valid for this register. + */ + gctUINT count = halti1 ? 14 : 1; + + for (i = 0; i < 12; i += 1) + { + index += _State(Context, index, (0x02C00 >> 2) + i * 16, 0x00000000, count, gcvFALSE, gcvFALSE); + } + } + + if (halti1) + { + gctUINT texBlockCount; + gctUINT gcregTXLogSizeResetValue; + + /* Enable the integer filter pipe for all texture samplers + so that the floating point filter clock will shut off until + we start using the floating point filter. + */ + gcregTXLogSizeResetValue = ((((gctUINT32) (0x00000000)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 29:29) - (0 ? 29:29) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 29:29) - (0 ? 29:29) + 1))))))) << (0 ? 29:29))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 29:29) - (0 ? 29:29) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 29:29) - (0 ? 29:29) + 1))))))) << (0 ? 29:29))); + + /* New texture block. */ + index += _State(Context, index, 0x10000 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10080 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10100 >> 2, gcregTXLogSizeResetValue, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10180 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10200 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10280 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10300 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10380 >> 2, 0x00321000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10400 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10480 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + + if ((((((gctUINT32) (Context->hardware->identity.chipMinorFeatures2)) >> (0 ? 15:15)) & ((gctUINT32) ((((1 ? 15:15) - (0 ? 15:15) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:15) - (0 ? 15:15) + 1)))))) )) + { + index += _State(Context, index, 0x12000 >> 2, 0x00000000, 256, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x12400 >> 2, 0x00000000, 256, gcvFALSE, gcvFALSE); + } + + texBlockCount = ((512) >> (4)); + + for (i = 0; i < texBlockCount; i += 1) + { + index += _State(Context, index, (0x10800 >> 2) + (i << 4), 0x00000000, 14, gcvFALSE, gcvTRUE); + } + } + + if (halti2) + { + index += _State(Context, index, 0x10700 >> 2, 0x00000F00, 32, gcvFALSE, gcvFALSE); + } + + if (halti3) + { + index += _State(Context, index, 0x10780 >> 2, 0x00030000, 32, gcvFALSE, gcvFALSE); + } + + /* ASTC */ + if ((((((gctUINT32) (Context->hardware->identity.chipMinorFeatures4)) >> (0 ? 13:13)) & ((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1)))))) )) + { + index += _State(Context, index, 0x10500 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10580 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10600 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x10680 >> 2, 0x00000000, 32, gcvFALSE, gcvFALSE); + } + + /* YUV. */ + index += _State(Context, index, 0x01678 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0167C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01680 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01684 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01688 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0168C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01690 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01694 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01698 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0169C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + /* Thread walker states. */ + index += _State(Context, index, 0x00900 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00904 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00908 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0090C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00910 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00914 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00918 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0091C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00924 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + if (((((gctUINT32) (Context->hardware->identity.chipMinorFeatures3)) >> (0 ? 21:21) & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))))) + { + index += _State(Context, index, 0x00940 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00944 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00948 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0094C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00950 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00954 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + } + + index += _CLOSE_RANGE(); + + if (!halti3) + { + if (Context->hardware->identity.instructionCount > 1024) + { + /* New Shader instruction PC registers. */ + index += _State(Context, index, 0x0085C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0101C >> 2, 0x00000100, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + for (i = 0; + i < Context->hardware->identity.instructionCount << 2; + i += 256 << 2 + ) + { + index += _State(Context, index, (0x20000 >> 2) + i, 0x00000000, 256 << 2, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + } + } + else if (Context->hardware->identity.instructionCount > 256) + { + /* New Shader instruction PC registers. */ + index += _State(Context, index, 0x0085C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0101C >> 2, 0x00000100, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + /* VX instruction memory. */ + for (i = 0; + i < Context->hardware->identity.instructionCount << 2; + i += 256 << 2 + ) + { + index += _State(Context, index, (0x0C000 >> 2) + i, 0x00000000, 256 << 2, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + } + + _StateMirror(Context, 0x08000 >> 2, Context->hardware->identity.instructionCount << 2 , 0x0C000 >> 2); + } + else /* if (Context->hardware->identity.instructionCount <= 256) */ + { + /* old shader instruction PC registers */ + index += _State(Context, index, 0x00800 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00838 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + index += _State(Context, index, 0x01000 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01018 >> 2, 0x01000000, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + index += _State(Context, index, 0x04000 >> 2, 0x00000000, 1024, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + index += _State(Context, index, 0x06000 >> 2, 0x00000000, 1024, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + } + } + /* I cache use the new instruction PC registers */ + else + { + /* New Shader instruction PC registers. */ + index += _State(Context, index, 0x0085C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0101C >> 2, 0x00000100, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + } + + if (unifiedUniform) + { + gctINT numConstants = Context->hardware->identity.numConstants; + + index += _State(Context, index, 0x01024 >> 2, 0x00000100, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x00864 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + for (i = 0; + numConstants > 0; + i += 256 << 2, + numConstants -= 256 + ) + { + if (numConstants >= 256) + { + index += _State(Context, index, (0x30000 >> 2) + i, 0x00000000, 256 << 2, gcvFALSE, gcvFALSE); + } + else + { + index += _State(Context, index, (0x30000 >> 2) + i, 0x00000000, numConstants << 2, gcvFALSE, gcvFALSE); + } + index += _CLOSE_RANGE(); + } + } +#if gcdENABLE_UNIFIED_CONSTANT + else +#endif + { + index += _State(Context, index, 0x05000 >> 2, 0x00000000, vertexUniforms * 4, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x07000 >> 2, 0x00000000, fragmentUniforms * 4, gcvFALSE, gcvFALSE); + } + + /* Store the index of the "XD" entry. */ + Context->entryOffsetXDFrom3D = (gctUINT)index * gcmSIZEOF(gctUINT32); + + + /* Pixel Engine states. */ + index += _State(Context, index, 0x01400 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01404 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01408 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0140C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01414 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01418 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0141C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01420 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01424 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01428 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0142C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01434 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01454 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01458 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0145C >> 2, 0x00000010, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014A0 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014A8 >> 2, 0xFFFFFFFF, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014AC >> 2, 0xFFFFFFFF, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014B0 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014B4 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014A4 >> 2, 0x000E400C, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01580 >> 2, 0x00000000, 3, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x014B8 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + /* Composition states. */ + index += _State(Context, index, 0x03008 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + if (Context->hardware->identity.pixelPipes == 1) + { + index += _State(Context, index, 0x01460 >> 2, 0x00000000, 8, gcvFALSE, gcvTRUE); + + index += _State(Context, index, 0x01430 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01410 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + } + else + { + index += _State(Context, index, (0x01460 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE); + } + + if (Context->hardware->identity.pixelPipes > 1 || halti0) + { + index += _State(Context, index, (0x01480 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE); + } + + for (i = 0; i < 3; i++) + { + index += _State(Context, index, (0x01500 >> 2) + (i << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE); + } + + if (halti2) + { + for (i = 0; i < 7; i++) + { + index += _State(Context, index, (0x14800 >> 2) + (i << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE); + } + index += _State(Context, index, 0x14900 >> 2, 0x00000000, 7, gcvFALSE, gcvFALSE); + } + + + if (halti3) + { + index += _State(Context, index, 0x014BC >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + } + + /* Resolve states. */ + index += _State(Context, index, 0x01604 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01608 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0160C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01610 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01614 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01620 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01630 >> 2, 0x00000000, 2, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01640 >> 2, 0x00000000, 4, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x0163C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x016A0 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x016B4 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + + if ((Context->hardware->identity.pixelPipes > 1) || halti1) + { + index += _State(Context, index, (0x016C0 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE); + + index += _State(Context, index, (0x016E0 >> 2) + (0 << 3), 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvTRUE); + + index += _State(Context, index, 0x01700 >> 2, 0x00000000, Context->hardware->identity.pixelPipes, gcvFALSE, gcvFALSE); + } + +#if gcd3DBLIT + index += _State(Context, index, (0x14000 >> 2) + (0 << 1), 0x00000000, 2, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x14008 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1400C >> 2, 0x0001C800, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14010 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x14014 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, (0x14018 >> 2) + (0 << 1), 0x00000000, 2, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x14020 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x14024 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14028 >> 2, 0x0001C800, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1402C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14030 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14034 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14038 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1403C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14040 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14044 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14048 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1404C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14050 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14058 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1405C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14054 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14100 >> 2, 0x00000000, 64, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14200 >> 2, 0x00000000, 64, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14064 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14068 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + index += _State(Context, index, 0x1406C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14070 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14074 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14078 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1407C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14080 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14084 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14088 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x1408C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14090 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + + index += _State(Context, index, 0x14094 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x14098 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); +#endif + + /* Tile status. */ + index += _State(Context, index, 0x01654 >> 2, 0x00200000, 1, gcvFALSE, gcvFALSE); + + index += _CLOSE_RANGE(); + index += _State(Context, index, 0x01658 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0165C >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01660 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01664 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01668 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x0166C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01670 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01674 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x016A4 >> 2, 0x00000000, 1, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x016AC >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x016A8 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01720 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x01740 >> 2, 0x00000000, 8, gcvFALSE, gcvTRUE); + index += _State(Context, index, 0x01760 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE); + + + if (halti2) + { + index += _State(Context, index, 0x01780 >> 2, 0x00000000, 8, gcvFALSE, gcvFALSE); + index += _State(Context, index, 0x016BC >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); + index += _State(Context, index, (0x017A0 >> 2) + 1, 0x00000000, 7, gcvFALSE, gcvFALSE); + index += _State(Context, index, (0x017C0 >> 2) + 1, 0x00000000, 7, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x017E0 >> 2) + 1, 0x00000000, 7, gcvFALSE, gcvTRUE); + index += _State(Context, index, (0x01A00 >> 2) + 1, 0x00000000, 7, gcvFALSE, gcvFALSE); + index += _State(Context, index, (0x01A20 >> 2) + 1, 0x00000000, 7, gcvFALSE, gcvFALSE); + index += _State(Context, index, (0x01A40 >> 2) + 1, 0x00000000, 7, gcvFALSE, gcvFALSE); + } + + index += _CLOSE_RANGE(); + + if(((((gctUINT32) (Context->hardware->identity.chipMinorFeatures4)) >> (0 ? 25:25) & ((gctUINT32) ((((1 ? 25:25) - (0 ? 25:25) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:25) - (0 ? 25:25) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 25:25) - (0 ? 25:25) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:25) - (0 ? 25:25) + 1)))))))) + { + index += _State(Context, index, 0x03860 >> 2, 0x6, 1, gcvFALSE, gcvFALSE); + index += _CLOSE_RANGE(); + } + + if (halti3) + { + index += _State(Context, index, 0x01A80 >> 2, 0x00000000, 8, gcvFALSE, gcvTRUE); + index += _CLOSE_RANGE(); + } + + /* Semaphore/stall. */ + index += _SemaphoreStall(Context, index); +#endif + + /**************************************************************************/ + /* Link to another address. ***********************************************/ + + Context->linkIndex3D = (gctUINT)index; + + if (buffer != gcvNULL) + { + buffer[index + 0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[index + 1] + = 0; + } + + index += 2; + + /* Store the end of the context buffer. */ + Context->bufferSize = index * gcmSIZEOF(gctUINT32); + + + /**************************************************************************/ + /* Pipe switch for the case where neither 2D nor 3D are used. *************/ + + /* Store the 3D entry index. */ + Context->entryOffsetXDFrom2D = (gctUINT)index * gcmSIZEOF(gctUINT32); + + /* Flush 2D pipe. */ + index += _FlushPipe(Context, index, gcvPIPE_2D); + + /* Switch to 3D pipe. */ + index += _SwitchPipe(Context, index, gcvPIPE_3D); + + /* Store the location of the link. */ + Context->linkIndexXD = (gctUINT)index; + + if (buffer != gcvNULL) + { + buffer[index + 0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[index + 1] + = 0; + } + + index += 2; + + + /**************************************************************************/ + /* Save size for buffer. **************************************************/ + + Context->totalSize = index * gcmSIZEOF(gctUINT32); + + + /* Success. */ + return gcvSTATUS_OK; +} +#endif + +static gceSTATUS +_DestroyContext( + IN gckCONTEXT Context + ) +{ + gceSTATUS status = gcvSTATUS_OK; + + if (Context != gcvNULL) + { + gcsCONTEXT_PTR bufferHead; + + /* Free context buffers. */ + for (bufferHead = Context->buffer; Context->buffer != gcvNULL;) + { + /* Get a shortcut to the current buffer. */ + gcsCONTEXT_PTR buffer = Context->buffer; + + /* Get the next buffer. */ + gcsCONTEXT_PTR next = buffer->next; + + /* Last item? */ + if (next == bufferHead) + { + next = gcvNULL; + } + + /* Destroy the signal. */ + if (buffer->signal != gcvNULL) + { + gcmkONERROR(gckOS_DestroySignal( + Context->os, buffer->signal + )); + + buffer->signal = gcvNULL; + } + + /* Free state delta map. */ + if (buffer->logical != gcvNULL) + { + if (Context->hardware->kernel->virtualCommandBuffer) + { + gcmkONERROR(gckEVENT_DestroyVirtualCommandBuffer( + Context->hardware->kernel->eventObj, + Context->totalSize, + buffer->physical, + buffer->logical, + gcvKERNEL_PIXEL + )); + } + else + { + gcmkONERROR(gckEVENT_FreeContiguousMemory( + Context->hardware->kernel->eventObj, + Context->totalSize, + buffer->physical, + buffer->logical, + gcvKERNEL_PIXEL + )); + } + + buffer->logical = gcvNULL; + } + + /* Free context buffer. */ + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, buffer)); + + /* Remove from the list. */ + Context->buffer = next; + } + +#if gcdSECURE_USER + /* Free the hint array. */ + if (Context->hint != gcvNULL) + { + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->hint)); + } +#endif + /* Free record array copy. */ +#if REMOVE_DUPLICATED_COPY_FROM_USER + if (Context->recordArrayMap != gcvNULL) + { + gcsRECORD_ARRAY_MAP_PTR map = Context->recordArrayMap; + + do + { + /* Free record array. */ + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, map->kData)); + map = map->next; + } + while (map != Context->recordArrayMap); + + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->recordArrayMap)); + } +#else + if (Context->recordArray != gcvNULL) + { + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->recordArray)); + } +#endif + + /* Free the state mapping. */ + if (Context->map != gcvNULL) + { + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->map)); + } + + /* Mark the gckCONTEXT object as unknown. */ + Context->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckCONTEXT object. */ + gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context)); + } + +OnError: + return status; +} + + +/******************************************************************************\ +**************************** Context Management API **************************** +\******************************************************************************/ + +/******************************************************************************\ +** +** gckCONTEXT_Construct +** +** Construct a new gckCONTEXT object. +** +** INPUT: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctUINT32 ProcessID +** Current process ID. +** +** gckHARDWARE Hardware +** Pointer to gckHARDWARE object. +** +** OUTPUT: +** +** gckCONTEXT * Context +** Pointer to a variable thet will receive the gckCONTEXT object +** pointer. +*/ +#if (gcdENABLE_3D || gcdENABLE_2D) +gceSTATUS +gckCONTEXT_Construct( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gctUINT32 ProcessID, + OUT gckCONTEXT * Context + ) +{ + gceSTATUS status; + gckCONTEXT context = gcvNULL; + gctUINT32 allocationSize; + gctUINT i; + gctPOINTER pointer = gcvNULL; + gctUINT32 address; + + gcmkHEADER_ARG("Os=0x%08X Hardware=0x%08X", Os, Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Context != gcvNULL); + + + /**************************************************************************/ + /* Allocate and initialize basic fields of gckCONTEXT. ********************/ + + /* The context object size. */ + allocationSize = gcmSIZEOF(struct _gckCONTEXT); + + /* Allocate the object. */ + gcmkONERROR(gckOS_Allocate( + Os, allocationSize, &pointer + )); + + context = pointer; + + /* Reset the entire object. */ + gcmkONERROR(gckOS_ZeroMemory(context, allocationSize)); + + /* Initialize the gckCONTEXT object. */ + context->object.type = gcvOBJ_CONTEXT; + context->os = Os; + context->hardware = Hardware; + + +#if !gcdENABLE_3D + context->entryPipe = gcvPIPE_2D; + context->exitPipe = gcvPIPE_2D; +#elif gcdCMD_NO_2D_CONTEXT + context->entryPipe = gcvPIPE_3D; + context->exitPipe = gcvPIPE_3D; +#else + context->entryPipe + = (((((gctUINT32) (context->hardware->identity.chipFeatures)) >> (0 ? 9:9)) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1)))))) ) + ? gcvPIPE_2D + : gcvPIPE_3D; + context->exitPipe = gcvPIPE_3D; +#endif + + /* Get the command buffer requirements. */ + gcmkONERROR(gckHARDWARE_QueryCommandBuffer( + Hardware, + &context->alignment, + &context->reservedHead, + &context->reservedTail + )); + + /* Mark the context as dirty to force loading of the entire state table + the first time. */ + context->dirty = gcvTRUE; + + + /**************************************************************************/ + /* Get the size of the context buffer. ************************************/ + + gcmkONERROR(_InitializeContextBuffer(context)); + + + /**************************************************************************/ + /* Compute the size of the record array. **********************************/ + + context->recordArraySize + = gcmSIZEOF(gcsSTATE_DELTA_RECORD) * (gctUINT)context->stateCount; + + + if (context->stateCount > 0) + { + /**************************************************************************/ + /* Allocate and reset the state mapping table. ****************************/ + + /* Allocate the state mapping table. */ + gcmkONERROR(gckOS_Allocate( + Os, + gcmSIZEOF(gcsSTATE_MAP) * context->stateCount, + &pointer + )); + + context->map = pointer; + + /* Zero the state mapping table. */ + gcmkONERROR(gckOS_ZeroMemory( + context->map, gcmSIZEOF(gcsSTATE_MAP) * context->stateCount + )); + + + /**************************************************************************/ + /* Allocate the hint array. ***********************************************/ + +#if gcdSECURE_USER + /* Allocate hints. */ + gcmkONERROR(gckOS_Allocate( + Os, + gcmSIZEOF(gctBOOL) * context->stateCount, + &pointer + )); + + context->hint = pointer; +#endif + } + + /**************************************************************************/ + /* Allocate the context and state delta buffers. **************************/ + + for (i = 0; i < gcdCONTEXT_BUFFER_COUNT; i += 1) + { + /* Allocate a context buffer. */ + gcsCONTEXT_PTR buffer; + + gctSIZE_T totalSize = context->totalSize; + + /* Allocate the context buffer structure. */ + gcmkONERROR(gckOS_Allocate( + Os, + gcmSIZEOF(gcsCONTEXT), + &pointer + )); + + buffer = pointer; + + /* Reset the context buffer structure. */ + gcmkVERIFY_OK(gckOS_ZeroMemory( + buffer, gcmSIZEOF(gcsCONTEXT) + )); + + /* Append to the list. */ + if (context->buffer == gcvNULL) + { + buffer->next = buffer; + context->buffer = buffer; + } + else + { + buffer->next = context->buffer->next; + context->buffer->next = buffer; + } + + /* Set the number of delta in the order of creation. */ +#if gcmIS_DEBUG(gcdDEBUG_CODE) + buffer->num = i; +#endif + + /* Create the busy signal. */ + gcmkONERROR(gckOS_CreateSignal( + Os, gcvFALSE, &buffer->signal + )); + + /* Set the signal, buffer is currently not busy. */ + gcmkONERROR(gckOS_Signal( + Os, buffer->signal, gcvTRUE + )); + + /* Create a new physical context buffer. */ + if (context->hardware->kernel->virtualCommandBuffer) + { + gcmkONERROR(gckKERNEL_AllocateVirtualCommandBuffer( + context->hardware->kernel, + gcvFALSE, + &totalSize, + &buffer->physical, + &pointer + )); + + gcmkONERROR(gckKERNEL_GetGPUAddress( + context->hardware->kernel, + pointer, + gcvFALSE, + &address + )); + } + else + { + gcmkONERROR(gckOS_AllocateContiguous( + Os, + gcvFALSE, + &totalSize, + &buffer->physical, + &pointer + )); + + gcmkONERROR(gckHARDWARE_ConvertLogical( + context->hardware, + pointer, + gcvFALSE, + &address + )); + } + + buffer->logical = pointer; + buffer->address = address; + + /* Set gckEVENT object pointer. */ + buffer->eventObj = Hardware->kernel->eventObj; + + /* Set the pointers to the LINK commands. */ + if (context->linkIndex2D != 0) + { + buffer->link2D = &buffer->logical[context->linkIndex2D]; + } + + if (context->linkIndex3D != 0) + { + buffer->link3D = &buffer->logical[context->linkIndex3D]; + } + + if (context->linkIndexXD != 0) + { + gctPOINTER xdLink; + gctUINT32 xdEntryAddress; + gctUINT32 xdEntrySize; + gctUINT32 linkBytes; + + /* Determine LINK parameters. */ + xdLink + = &buffer->logical[context->linkIndexXD]; + + xdEntryAddress + = buffer->address + + context->entryOffsetXDFrom3D; + + xdEntrySize + = context->bufferSize + - context->entryOffsetXDFrom3D; + + /* Query LINK size. */ + gcmkONERROR(gckHARDWARE_Link( + Hardware, gcvNULL, 0, 0, &linkBytes + )); + + /* Generate a LINK. */ + gcmkONERROR(gckHARDWARE_Link( + Hardware, + xdLink, + xdEntryAddress, + xdEntrySize, + &linkBytes + )); + } + } + + + /**************************************************************************/ + /* Initialize the context buffers. ****************************************/ + + /* Initialize the current context buffer. */ + gcmkONERROR(_InitializeContextBuffer(context)); + + /* Make all created contexts equal. */ + { + gcsCONTEXT_PTR currContext, tempContext; + + /* Set the current context buffer. */ + currContext = context->buffer; + + /* Get the next context buffer. */ + tempContext = currContext->next; + + /* Loop through all buffers. */ + while (tempContext != currContext) + { + if (tempContext == gcvNULL) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + /* Copy the current context. */ + gckOS_MemCopy( + tempContext->logical, + currContext->logical, + context->totalSize + ); + + /* Get the next context buffer. */ + tempContext = tempContext->next; + } + } + + /* Return pointer to the gckCONTEXT object. */ + *Context = context; + + /* Success. */ + gcmkFOOTER_ARG("*Context=0x%08X", *Context); + return gcvSTATUS_OK; + +OnError: + /* Roll back on error. */ + gcmkVERIFY_OK(_DestroyContext(context)); + + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************\ +** +** gckCONTEXT_Destroy +** +** Destroy a gckCONTEXT object. +** +** INPUT: +** +** gckCONTEXT Context +** Pointer to an gckCONTEXT object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCONTEXT_Destroy( + IN gckCONTEXT Context + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Context=0x%08X", Context); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Context, gcvOBJ_CONTEXT); + + /* Destroy the context and all related objects. */ + status = _DestroyContext(Context); + + /* Success. */ + gcmkFOOTER_NO(); + return status; +} + +/******************************************************************************\ +** +** gckCONTEXT_Update +** +** Merge all pending state delta buffers into the current context buffer. +** +** INPUT: +** +** gckCONTEXT Context +** Pointer to an gckCONTEXT object. +** +** gctUINT32 ProcessID +** Current process ID. +** +** gcsSTATE_DELTA_PTR StateDelta +** Pointer to the state delta. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCONTEXT_Update( + IN gckCONTEXT Context, + IN gctUINT32 ProcessID, + IN gcsSTATE_DELTA_PTR StateDelta + ) +{ +#if gcdENABLE_3D + gceSTATUS status = gcvSTATUS_OK; + gcsSTATE_DELTA _stateDelta; + gckKERNEL kernel; + gcsCONTEXT_PTR buffer; + gcsSTATE_MAP_PTR map; + gctBOOL needCopy = gcvFALSE; + gcsSTATE_DELTA_PTR nDelta; + gcsSTATE_DELTA_PTR uDelta = gcvNULL; + gcsSTATE_DELTA_PTR kDelta = gcvNULL; + gcsSTATE_DELTA_RECORD_PTR record; + gcsSTATE_DELTA_RECORD_PTR recordArray = gcvNULL; +#if REMOVE_DUPLICATED_COPY_FROM_USER + gcsRECORD_ARRAY_MAP_PTR recordArrayMap = gcvNULL; +#endif + gctUINT elementCount; + gctUINT address; + gctUINT32 mask; + gctUINT32 data; + gctUINT index; + gctUINT i, j; + +#if gcdSECURE_USER + gcskSECURE_CACHE_PTR cache; +#endif + + gcmkHEADER_ARG( + "Context=0x%08X ProcessID=%d StateDelta=0x%08X", + Context, ProcessID, StateDelta + ); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Context, gcvOBJ_CONTEXT); + + /* Get a shortcut to the kernel object. */ + kernel = Context->hardware->kernel; + + /* Check wehther we need to copy the structures or not. */ + gcmkONERROR(gckOS_QueryNeedCopy(Context->os, ProcessID, &needCopy)); + + /* Allocate the copy buffer for the user record array. */ +#if REMOVE_DUPLICATED_COPY_FROM_USER + if (needCopy && (Context->recordArrayMap == gcvNULL)) + { + /* Allocate enough maps. */ + gcmkONERROR(gckOS_Allocate( + Context->os, + gcmSIZEOF(gcsRECORD_ARRAY_MAP_PTR) * gcdCONTEXT_BUFFER_COUNT, + (gctPOINTER *) &Context->recordArrayMap + )); + + for (i = 0; i < gcdCONTEXT_BUFFER_COUNT; i++) + { + /* Next mapping id. */ + gctUINT n = (i + 1) % gcdCONTEXT_BUFFER_COUNT; + + recordArrayMap = &Context->recordArrayMap[i]; + + /* Allocate the buffer. */ + gcmkONERROR(gckOS_Allocate( + Context->os, + Context->recordArraySize, + (gctPOINTER *) &recordArrayMap->kData + )); + + /* Initialize fields. */ + recordArrayMap->key = 0; + recordArrayMap->next = &Context->recordArrayMap[n]; + } + } +#else + if (needCopy && (Context->recordArray == gcvNULL)) + { + /* Allocate the buffer. */ + gcmkONERROR(gckOS_Allocate( + Context->os, + Context->recordArraySize, + (gctPOINTER *) &Context->recordArray + )); + } +#endif + + /* Get the current context buffer. */ + buffer = Context->buffer; + + /* Wait until the context buffer becomes available; this will + also reset the signal and mark the buffer as busy. */ + gcmkONERROR(gckOS_WaitSignal( + Context->os, buffer->signal, gcvINFINITE + )); + +#if gcdSECURE_USER + /* Get the cache form the database. */ + gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache)); +#endif + +#if gcmIS_DEBUG(gcdDEBUG_CODE) && 1 && gcdENABLE_3D + /* Update current context token. */ + buffer->logical[Context->map[0x0E14].index] + = (gctUINT32)gcmPTR2INT32(Context); +#endif + + /* Are there any pending deltas? */ + if (buffer->deltaCount != 0) + { + /* Get the state map. */ + map = Context->map; + + /* Get the first delta item. */ + uDelta = buffer->delta; + + /* Reset the vertex stream count. */ + elementCount = 0; + + /* Merge all pending deltas. */ + for (i = 0; i < buffer->deltaCount; i += 1) + { + /* Get access to the state delta. */ + gcmkONERROR(gckKERNEL_OpenUserData( + kernel, needCopy, + &_stateDelta, + uDelta, gcmSIZEOF(gcsSTATE_DELTA), + (gctPOINTER *) &kDelta + )); + +#if REMOVE_DUPLICATED_COPY_FROM_USER + if (needCopy) + { + recordArray = gcvNULL; + recordArrayMap = Context->recordArrayMap; + + do + { + /* Check if recordArray is alreay opened. */ + if (recordArrayMap->key == kDelta->recordArray) + { + /* Found. */ + recordArray = recordArrayMap->kData; + break; + } + + recordArrayMap = recordArrayMap->next; + } + while (recordArrayMap != Context->recordArrayMap); + + if (recordArray == gcvNULL) + { + while (recordArrayMap->key != 0) + { + /* Found an empty slot. */ + recordArrayMap = recordArrayMap->next; + } + + /* Get access to the state records. */ + gcmkONERROR(gckOS_CopyFromUserData( + kernel->os, + recordArrayMap->kData, + gcmUINT64_TO_PTR(kDelta->recordArray), + Context->recordArraySize + )); + + /* Save user pointer as key. */ + recordArrayMap->key = kDelta->recordArray; + recordArray = recordArrayMap->kData; + } + } + else + { + /* Get access to the state records. */ + gcmkONERROR(gckOS_MapUserPointer( + kernel->os, + gcmUINT64_TO_PTR(kDelta->recordArray), + Context->recordArraySize, + (gctPOINTER *) &recordArray + )); + } +#else + /* Get access to the state records. */ + gcmkONERROR(gckKERNEL_OpenUserData( + kernel, needCopy, + Context->recordArray, + gcmUINT64_TO_PTR(kDelta->recordArray), Context->recordArraySize, + (gctPOINTER *) &recordArray + )); +#endif + + /* Merge all pending states. */ + for (j = 0; j < kDelta->recordCount; j += 1) + { + if (j >= Context->stateCount) + { + break; + } + + /* Get the current state record. */ + record = &recordArray[j]; + + /* Get the state address. */ + address = record->address; + + /* Make sure the state is a part of the mapping table. */ + if (address >= Context->stateCount) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): State 0x%04X is not mapped.\n", + __FUNCTION__, __LINE__, + address + ); + + continue; + } + + /* Get the state index. */ + index = map[address].index; + + /* Skip the state if not mapped. */ + if (index == 0) + { + continue; + } + + /* Get the data mask. */ + mask = record->mask; + + /* Masked states that are being completly reset or regular states. */ + if ((mask == 0) || (mask == ~0U)) + { + /* Get the new data value. */ + data = record->data; + + /* Process special states. */ + if (address == 0x0595) + { + /* Force auto-disable to be disabled. */ + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1))))))) << (0 ? 13:13))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1))))))) << (0 ? 13:13))); + } + +#if gcdSECURE_USER + /* Do we need to convert the logical address? */ + if (Context->hint[address]) + { + /* Map handle into physical address. */ + gcmkONERROR(gckKERNEL_MapLogicalToPhysical( + kernel, cache, (gctPOINTER) &data + )); + } +#endif + + /* Set new data. */ + buffer->logical[index] = data; + } + + /* Masked states that are being set partially. */ + else + { + buffer->logical[index] + = (~mask & buffer->logical[index]) + | (mask & record->data); + } + } + + /* Get the element count. */ + if (kDelta->elementCount != 0) + { + elementCount = kDelta->elementCount; + } + + /* Dereference delta. */ + kDelta->refCount -= 1; + gcmkASSERT(kDelta->refCount >= 0); + + /* Get the next state delta. */ + nDelta = gcmUINT64_TO_PTR(kDelta->next); + +#if REMOVE_DUPLICATED_COPY_FROM_USER + if (needCopy) + { + if (kDelta->refCount == 0) + { + /* No other reference, reset the mapping. */ + recordArrayMap->key = 0; + } + } + else + { + /* Close access to the state records. */ + gcmkONERROR(gckOS_UnmapUserPointer( + kernel->os, + gcmUINT64_TO_PTR(kDelta->recordArray), + Context->recordArraySize, + (gctPOINTER *) recordArray + )); + + recordArray = gcvNULL; + } +#else + /* Get access to the state records. */ + gcmkONERROR(gckKERNEL_CloseUserData( + kernel, needCopy, + gcvFALSE, + gcmUINT64_TO_PTR(kDelta->recordArray), Context->recordArraySize, + (gctPOINTER *) &recordArray + )); +#endif + + /* Close access to the current state delta. */ + gcmkONERROR(gckKERNEL_CloseUserData( + kernel, needCopy, + gcvTRUE, + uDelta, gcmSIZEOF(gcsSTATE_DELTA), + (gctPOINTER *) &kDelta + )); + + /* Update the user delta pointer. */ + uDelta = nDelta; + } + + /* Hardware disables all input streams when the stream 0 is programmed, + it then reenables those streams that were explicitely programmed by + the software. Because of this we cannot program the entire array of + values, otherwise we'll get all streams reenabled, but rather program + only those that are actully needed by the software. */ + if (elementCount != 0) + { + gctUINT base; + gctUINT nopCount; + gctUINT32_PTR nop; + gctUINT fe2vsCount = 12; + + if ((((((gctUINT32) (Context->hardware->identity.chipMinorFeatures1)) >> (0 ? 23:23)) & ((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1)))))) )) + { + fe2vsCount = 16; + } + + /* Determine the base index of the vertex stream array. */ + base = map[0x0180].index; + + /* Set the proper state count. */ + buffer->logical[base - 1] + = ((((gctUINT32) (buffer->logical[base - 1])) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (elementCount ) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + /* Determine the number of NOP commands. */ + nopCount + = (fe2vsCount / 2) + - (elementCount / 2); + + /* Determine the location of the first NOP. */ + nop = &buffer->logical[base + (elementCount | 1)]; + + /* Fill the unused space with NOPs. */ + for (i = 0; i < nopCount; i += 1) + { + if (nop >= buffer->logical + Context->totalSize) + { + break; + } + + /* Generate a NOP command. */ + *nop = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x03 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + /* Advance. */ + nop += 2; + } + } + + /* Reset pending deltas. */ + buffer->deltaCount = 0; + buffer->delta = gcvNULL; + } + + /* Set state delta user pointer. */ + uDelta = StateDelta; + + /* Get access to the state delta. */ + gcmkONERROR(gckKERNEL_OpenUserData( + kernel, needCopy, + &_stateDelta, + uDelta, gcmSIZEOF(gcsSTATE_DELTA), + (gctPOINTER *) &kDelta + )); + + /* State delta cannot be attached to anything yet. */ + if (kDelta->refCount != 0) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): kDelta->refCount = %d (has to be 0).\n", + __FUNCTION__, __LINE__, + kDelta->refCount + ); + } + + /* Attach to all contexts. */ + buffer = Context->buffer; + + do + { + /* Attach to the context if nothing is attached yet. If a delta + is allready attached, all we need to do is to increment + the number of deltas in the context. */ + if (buffer->delta == gcvNULL) + { + buffer->delta = uDelta; + } + + /* Update reference count. */ + kDelta->refCount += 1; + + /* Update counters. */ + buffer->deltaCount += 1; + + /* Get the next context buffer. */ + buffer = buffer->next; + + if (buffer == gcvNULL) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + } + while (Context->buffer != buffer); + + /* Close access to the current state delta. */ + gcmkONERROR(gckKERNEL_CloseUserData( + kernel, needCopy, + gcvTRUE, + uDelta, gcmSIZEOF(gcsSTATE_DELTA), + (gctPOINTER *) &kDelta + )); + + /* Schedule an event to mark the context buffer as available. */ + gcmkONERROR(gckEVENT_Signal( + buffer->eventObj, buffer->signal, gcvKERNEL_PIXEL + )); + + /* Advance to the next context buffer. */ + Context->buffer = buffer->next; + + /* Return the status. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + /* Get access to the state records. */ + if (kDelta != gcvNULL) + { + gcmkVERIFY_OK(gckKERNEL_CloseUserData( + kernel, needCopy, + gcvFALSE, + gcmUINT64_TO_PTR(kDelta->recordArray), Context->recordArraySize, + (gctPOINTER *) &recordArray + )); + } + + /* Close access to the current state delta. */ + gcmkVERIFY_OK(gckKERNEL_CloseUserData( + kernel, needCopy, + gcvTRUE, + uDelta, gcmSIZEOF(gcsSTATE_DELTA), + (gctPOINTER *) &kDelta + )); + + /* Return the status. */ + gcmkFOOTER(); + return status; +#else + return gcvSTATUS_OK; +#endif +} + +gceSTATUS +gckCONTEXT_MapBuffer( + IN gckCONTEXT Context, + OUT gctUINT32 *Physicals, + OUT gctUINT64 *Logicals, + OUT gctUINT32 *Bytes + ) +{ + gceSTATUS status; + int i = 0; + gctSIZE_T pageCount; + gckVIRTUAL_COMMAND_BUFFER_PTR commandBuffer; + gckKERNEL kernel = Context->hardware->kernel; + gctPOINTER logical; + gctPHYS_ADDR physical; + + gcsCONTEXT_PTR buffer; + + gcmkHEADER(); + + gcmkVERIFY_OBJECT(Context, gcvOBJ_CONTEXT); + + buffer = Context->buffer; + + for (i = 0; i < gcdCONTEXT_BUFFER_COUNT; i++) + { + if (kernel->virtualCommandBuffer) + { + commandBuffer = (gckVIRTUAL_COMMAND_BUFFER_PTR)buffer->physical; + physical = commandBuffer->physical; + + gcmkONERROR(gckOS_CreateUserVirtualMapping( + kernel->os, + physical, + Context->totalSize, + &logical, + &pageCount)); + } + else + { + physical = buffer->physical; + + gcmkONERROR(gckOS_MapMemory( + kernel->os, + physical, + Context->totalSize, + &logical)); + } + + Physicals[i] = gcmPTR_TO_NAME(physical); + + Logicals[i] = gcmPTR_TO_UINT64(logical); + + buffer = buffer->next; + } + + *Bytes = (gctUINT)Context->totalSize; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.h b/drivers/gpu/galcore/arch/gc_hal_kernel_context.h new file mode 100644 index 00000000000000..29e6a5321e0da0 --- /dev/null +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.h @@ -0,0 +1,183 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_context_h_ +#define __gc_hal_kernel_context_h_ + +#include "gc_hal_kernel_buffer.h" + +/* Exprimental optimization. */ +#define REMOVE_DUPLICATED_COPY_FROM_USER 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Maps state locations within the context buffer. */ +typedef struct _gcsSTATE_MAP * gcsSTATE_MAP_PTR; +typedef struct _gcsSTATE_MAP +{ + /* Index of the state in the context buffer. */ + gctUINT index; + + /* State mask. */ + gctUINT32 mask; +} +gcsSTATE_MAP; + +/* Context buffer. */ +typedef struct _gcsCONTEXT * gcsCONTEXT_PTR; +typedef struct _gcsCONTEXT +{ + /* For debugging: the number of context buffer in the order of creation. */ +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gctUINT num; +#endif + + /* Pointer to gckEVENT object. */ + gckEVENT eventObj; + + /* Context busy signal. */ + gctSIGNAL signal; + + /* Physical address of the context buffer. */ + gctPHYS_ADDR physical; + + /* Logical address of the context buffer. */ + gctUINT32_PTR logical; + + /* Hardware address of the context buffer. */ + gctUINT32 address; + + /* Pointer to the LINK commands. */ + gctPOINTER link2D; + gctPOINTER link3D; + + /* The number of pending state deltas. */ + gctUINT deltaCount; + + /* Pointer to the first delta to be applied. */ + gcsSTATE_DELTA_PTR delta; + + /* Next context buffer. */ + gcsCONTEXT_PTR next; +} +gcsCONTEXT; + +typedef struct _gcsRECORD_ARRAY_MAP * gcsRECORD_ARRAY_MAP_PTR; +struct _gcsRECORD_ARRAY_MAP +{ + /* User pointer key. */ + gctUINT64 key; + + /* Kernel memory buffer. */ + gcsSTATE_DELTA_RECORD_PTR kData; + + /* Next map. */ + gcsRECORD_ARRAY_MAP_PTR next; + +}; + +/* gckCONTEXT structure that hold the current context. */ +struct _gckCONTEXT +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Pointer to gckHARDWARE object. */ + gckHARDWARE hardware; + + /* Command buffer alignment. */ + gctUINT32 alignment; + gctUINT32 reservedHead; + gctUINT32 reservedTail; + + /* Context buffer metrics. */ + gctSIZE_T stateCount; + gctUINT32 totalSize; + gctUINT32 bufferSize; + gctUINT32 linkIndex2D; + gctUINT32 linkIndex3D; + gctUINT32 linkIndexXD; + gctUINT32 entryOffset3D; + gctUINT32 entryOffsetXDFrom2D; + gctUINT32 entryOffsetXDFrom3D; + + /* Dirty flags. */ + gctBOOL dirty; + gctBOOL dirty2D; + gctBOOL dirty3D; + gcsCONTEXT_PTR dirtyBuffer; + + /* State mapping. */ + gcsSTATE_MAP_PTR map; + + /* List of context buffers. */ + gcsCONTEXT_PTR buffer; + + /* A copy of the user record array. */ + gctUINT recordArraySize; +#if REMOVE_DUPLICATED_COPY_FROM_USER + gcsRECORD_ARRAY_MAP_PTR recordArrayMap; +#else + gcsSTATE_DELTA_RECORD_PTR recordArray; +#endif + + /* Requested pipe select for context. */ + gcePIPE_SELECT entryPipe; + gcePIPE_SELECT exitPipe; + + /* Variables used for building state buffer. */ + gctUINT32 lastAddress; + gctSIZE_T lastSize; + gctUINT32 lastIndex; + gctBOOL lastFixed; + + gctUINT32 pipeSelectBytes; + + /* Hint array. */ +#if gcdSECURE_USER + gctBOOL_PTR hint; +#endif + +#if VIVANTE_PROFILER_CONTEXT + gcsPROFILER_COUNTERS latestProfiler; + gcsPROFILER_COUNTERS histroyProfiler; + gctUINT32 prevVSInstCount; + gctUINT32 prevVSBranchInstCount; + gctUINT32 prevVSTexInstCount; + gctUINT32 prevVSVertexCount; + gctUINT32 prevPSInstCount; + gctUINT32 prevPSBranchInstCount; + gctUINT32 prevPSTexInstCount; + gctUINT32 prevPSPixelCount; +#endif +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_kernel_context_h_ */ + diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c new file mode 100644 index 00000000000000..3a225a9a208db1 --- /dev/null +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -0,0 +1,8036 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal.h" +#include "gc_hal_kernel.h" +#if VIVANTE_PROFILER_CONTEXT +#include "gc_hal_kernel_context.h" +#endif + +#define gcdDISABLE_FE_L2 1 + +#define _GC_OBJ_ZONE gcvZONE_HARDWARE + +#define gcmSEMAPHORESTALL(buffer) \ + do \ + { \ + /* Arm the PE-FE Semaphore. */ \ + *buffer++ \ + = gcmSETFIELDVALUE(0, AQ_COMMAND_LOAD_STATE_COMMAND, OPCODE, LOAD_STATE) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, COUNT, 1) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, ADDRESS, 0x0E02); \ + \ + *buffer++ \ + = gcmSETFIELDVALUE(0, AQ_SEMAPHORE, SOURCE, FRONT_END) \ + | gcmSETFIELDVALUE(0, AQ_SEMAPHORE, DESTINATION, PIXEL_ENGINE);\ + \ + /* STALL FE until PE is done flushing. */ \ + *buffer++ \ + = gcmSETFIELDVALUE(0, STALL_COMMAND, OPCODE, STALL); \ + \ + *buffer++ \ + = gcmSETFIELDVALUE(0, STALL_STALL, SOURCE, FRONT_END) \ + | gcmSETFIELDVALUE(0, STALL_STALL, DESTINATION, PIXEL_ENGINE); \ + } while(0) + +typedef struct _gcsiDEBUG_REGISTERS * gcsiDEBUG_REGISTERS_PTR; +typedef struct _gcsiDEBUG_REGISTERS +{ + gctSTRING module; + gctUINT index; + gctUINT shift; + gctUINT data; + gctUINT count; + gctUINT32 signature; +} +gcsiDEBUG_REGISTERS; + +/******************************************************************************\ +********************************* Support Code ********************************* +\******************************************************************************/ +static gctBOOL +_IsHardwareMatch( + IN gckHARDWARE Hardware, + IN gctINT32 ChipModel, + IN gctUINT32 ChipRevision + ) +{ + return ((Hardware->identity.chipModel == ChipModel) && + (Hardware->identity.chipRevision == ChipRevision)); +} + +static gceSTATUS +_ResetGPU( + IN gckHARDWARE Hardware, + IN gckOS Os, + IN gceCORE Core + ); + +static gceSTATUS +_IdentifyHardware( + IN gckOS Os, + IN gceCORE Core, + OUT gcsHAL_QUERY_CHIP_IDENTITY_PTR Identity + ) +{ + gceSTATUS status; + + gctUINT32 chipIdentity; + + gctUINT32 streamCount = 0; + gctUINT32 registerMax = 0; + gctUINT32 threadCount = 0; + gctUINT32 shaderCoreCount = 0; + gctUINT32 vertexCacheSize = 0; + gctUINT32 vertexOutputBufferSize = 0; + gctUINT32 pixelPipes = 0; + gctUINT32 instructionCount = 0; + gctUINT32 numConstants = 0; + gctUINT32 bufferSize = 0; + gctUINT32 varyingsCount = 0; +#if gcdMULTI_GPU + gctUINT32 gpuCoreCount = 0; +#endif + + gcmkHEADER_ARG("Os=0x%x", Os); + + /*************************************************************************** + ** Get chip ID and revision. + */ + + /* Read chip identity register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00018, + &chipIdentity)); + + /* Special case for older graphic cores. */ + if (((((gctUINT32) (chipIdentity)) >> (0 ? 31:24) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1)))))) == (0x01 & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1)))))))) + { + Identity->chipModel = gcv500; + Identity->chipRevision = (((((gctUINT32) (chipIdentity)) >> (0 ? 15:12)) & ((gctUINT32) ((((1 ? 15:12) - (0 ? 15:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:12) - (0 ? 15:12) + 1)))))) ); + } + + else + { + /* Read chip identity register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00020, + (gctUINT32_PTR) &Identity->chipModel)); + + if (((Identity->chipModel & 0xFF00) == 0x0400) + && (Identity->chipModel != 0x0420) + && (Identity->chipModel != 0x0428)) + { + Identity->chipModel = (gceCHIPMODEL) (Identity->chipModel & 0x0400); + } + + /* Read CHIP_REV register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00024, + &Identity->chipRevision)); + + if ((Identity->chipModel == gcv300) + && (Identity->chipRevision == 0x2201) + ) + { + gctUINT32 chipDate; + gctUINT32 chipTime; + + /* Read date and time registers. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00028, + &chipDate)); + + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x0002C, + &chipTime)); + + if ((chipDate == 0x20080814) && (chipTime == 0x12051100)) + { + /* This IP has an ECO; put the correct revision in it. */ + Identity->chipRevision = 0x1051; + } + } + + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x000A8, + &Identity->productID)); + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipModel=%X", + Identity->chipModel); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipRevision=%X", + Identity->chipRevision); + + + /*************************************************************************** + ** Get chip features. + */ + + /* Read chip feature register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x0001C, + &Identity->chipFeatures)); + +#if gcdENABLE_3D + /* Disable fast clear on GC700. */ + if (Identity->chipModel == gcv700) + { + Identity->chipFeatures + = ((((gctUINT32) (Identity->chipFeatures)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + } +#endif + + if (((Identity->chipModel == gcv500) && (Identity->chipRevision < 2)) + || ((Identity->chipModel == gcv300) && (Identity->chipRevision < 0x2000)) + ) + { + /* GC500 rev 1.x and GC300 rev < 2.0 doesn't have these registers. */ + Identity->chipMinorFeatures = 0; + Identity->chipMinorFeatures1 = 0; + Identity->chipMinorFeatures2 = 0; + Identity->chipMinorFeatures3 = 0; + Identity->chipMinorFeatures4 = 0; + Identity->chipMinorFeatures5 = 0; + } + else + { + /* Read chip minor feature register #0. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00034, + &Identity->chipMinorFeatures)); + + if (((((gctUINT32) (Identity->chipMinorFeatures)) >> (0 ? 21:21) & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1))))))) + ) + { + /* Read chip minor featuress register #1. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00074, + &Identity->chipMinorFeatures1)); + + /* Read chip minor featuress register #2. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00084, + &Identity->chipMinorFeatures2)); + + /*Identity->chipMinorFeatures2 &= ~(0x1 << 3);*/ + + /* Read chip minor featuress register #1. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00088, + &Identity->chipMinorFeatures3)); + + + /* Read chip minor featuress register #4. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00094, + &Identity->chipMinorFeatures4)); + + /* Read chip minor featuress register #5. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x000A0, + &Identity->chipMinorFeatures5)); + } + else + { + /* Chip doesn't has minor features register #1 or 2 or 3 or 4. */ + Identity->chipMinorFeatures1 = 0; + Identity->chipMinorFeatures2 = 0; + Identity->chipMinorFeatures3 = 0; + Identity->chipMinorFeatures4 = 0; + Identity->chipMinorFeatures5 = 0; + } + } + + /* Get the Supertile layout in the hardware. */ + if (((((gctUINT32) (Identity->chipMinorFeatures3)) >> (0 ? 26:26) & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1))))))) + || ((((gctUINT32) (Identity->chipMinorFeatures3)) >> (0 ? 8:8) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))))) + { + Identity->superTileMode = 2; + } + else if (((((gctUINT32) (Identity->chipMinorFeatures)) >> (0 ? 27:27) & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1)))))))) + { + Identity->superTileMode = 1; + } + else + { + Identity->superTileMode = 0; + } + + /* Exception for GC1000, revision 5035 & GC800, revision 4612 */ + if (((Identity->chipModel == gcv1000) && ((Identity->chipRevision == 0x5035) + || (Identity->chipRevision == 0x5036) + || (Identity->chipRevision == 0x5037) + || (Identity->chipRevision == 0x5039) + || (Identity->chipRevision >= 0x5040))) + || ((Identity->chipModel == gcv800) && (Identity->chipRevision == 0x4612)) + || ((Identity->chipModel == gcv600) && (Identity->chipRevision >= 0x4650)) + || ((Identity->chipModel == gcv860) && (Identity->chipRevision == 0x4647)) + || ((Identity->chipModel == gcv400) && (Identity->chipRevision >= 0x4633))) + { + Identity->superTileMode = 1; + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipFeatures=0x%08X", + Identity->chipFeatures); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipMinorFeatures=0x%08X", + Identity->chipMinorFeatures); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipMinorFeatures1=0x%08X", + Identity->chipMinorFeatures1); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipMinorFeatures2=0x%08X", + Identity->chipMinorFeatures2); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipMinorFeatures3=0x%08X", + Identity->chipMinorFeatures3); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipMinorFeatures4=0x%08X", + Identity->chipMinorFeatures4); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipMinorFeatures5=0x%08X", + Identity->chipMinorFeatures5); + + /*************************************************************************** + ** Get chip specs. + */ + + if (((((gctUINT32) (Identity->chipMinorFeatures)) >> (0 ? 21:21) & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))))) + { + gctUINT32 specs, specs2, specs3, specs4; + + /* Read gcChipSpecs register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00048, + &specs)); + + /* Extract the fields. */ + registerMax = (((((gctUINT32) (specs)) >> (0 ? 7:4)) & ((gctUINT32) ((((1 ? 7:4) - (0 ? 7:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:4) - (0 ? 7:4) + 1)))))) ); + threadCount = (((((gctUINT32) (specs)) >> (0 ? 11:8)) & ((gctUINT32) ((((1 ? 11:8) - (0 ? 11:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:8) - (0 ? 11:8) + 1)))))) ); + shaderCoreCount = (((((gctUINT32) (specs)) >> (0 ? 24:20)) & ((gctUINT32) ((((1 ? 24:20) - (0 ? 24:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:20) - (0 ? 24:20) + 1)))))) ); + vertexCacheSize = (((((gctUINT32) (specs)) >> (0 ? 16:12)) & ((gctUINT32) ((((1 ? 16:12) - (0 ? 16:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:12) - (0 ? 16:12) + 1)))))) ); + vertexOutputBufferSize = (((((gctUINT32) (specs)) >> (0 ? 31:28)) & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1)))))) ); + pixelPipes = (((((gctUINT32) (specs)) >> (0 ? 27:25)) & ((gctUINT32) ((((1 ? 27:25) - (0 ? 27:25) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:25) - (0 ? 27:25) + 1)))))) ); + + /* Read gcChipSpecs2 register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x00080, + &specs2)); + + instructionCount = (((((gctUINT32) (specs2)) >> (0 ? 15:8)) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1)))))) ); + numConstants = (((((gctUINT32) (specs2)) >> (0 ? 31:16)) & ((gctUINT32) ((((1 ? 31:16) - (0 ? 31:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:16) - (0 ? 31:16) + 1)))))) ); + bufferSize = (((((gctUINT32) (specs2)) >> (0 ? 7:0)) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1)))))) ); + + /* Read gcChipSpecs3 register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x0008C, + &specs3)); + + varyingsCount = (((((gctUINT32) (specs3)) >> (0 ? 8:4)) & ((gctUINT32) ((((1 ? 8:4) - (0 ? 8:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:4) - (0 ? 8:4) + 1)))))) ); +#if gcdMULTI_GPU + gpuCoreCount = (((((gctUINT32) (specs3)) >> (0 ? 2:0)) & ((gctUINT32) ((((1 ? 2:0) - (0 ? 2:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:0) - (0 ? 2:0) + 1)))))) ); +#endif + + /* Read gcChipSpecs4 register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Os, Core, + 0x0009C, + &specs4)); + + + streamCount = (((((gctUINT32) (specs4)) >> (0 ? 16:12)) & ((gctUINT32) ((((1 ? 16:12) - (0 ? 16:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:12) - (0 ? 16:12) + 1)))))) ); + if (streamCount == 0) + { + /* Extract stream count from older register. */ + streamCount = (((((gctUINT32) (specs)) >> (0 ? 3:0)) & ((gctUINT32) ((((1 ? 3:0) - (0 ? 3:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:0) - (0 ? 3:0) + 1)))))) ); + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipSpecs1=0x%08X", + specs); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipSpecs2=0x%08X", + specs2); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipSpecs3=0x%08X", + specs3); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Identity: chipSpecs4=0x%08X", + specs4); + } + + /* Get the number of pixel pipes. */ + Identity->pixelPipes = gcmMAX(pixelPipes, 1); + + /* Get the stream count. */ + Identity->streamCount = (streamCount != 0) + ? streamCount + : (Identity->chipModel >= gcv1000) ? 4 : 1; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: streamCount=%u%s", + Identity->streamCount, + (streamCount == 0) ? " (default)" : ""); + + /* Get the vertex output buffer size. */ + Identity->vertexOutputBufferSize = (vertexOutputBufferSize != 0) + ? 1 << vertexOutputBufferSize + : (Identity->chipModel == gcv400) + ? (Identity->chipRevision < 0x4000) ? 512 + : (Identity->chipRevision < 0x4200) ? 256 + : 128 + : (Identity->chipModel == gcv530) + ? (Identity->chipRevision < 0x4200) ? 512 + : 128 + : 512; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: vertexOutputBufferSize=%u%s", + Identity->vertexOutputBufferSize, + (vertexOutputBufferSize == 0) ? " (default)" : ""); + + /* Get the maximum number of threads. */ + Identity->threadCount = (threadCount != 0) + ? 1 << threadCount + : (Identity->chipModel == gcv400) ? 64 + : (Identity->chipModel == gcv500) ? 128 + : (Identity->chipModel == gcv530) ? 128 + : 256; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: threadCount=%u%s", + Identity->threadCount, + (threadCount == 0) ? " (default)" : ""); + + /* Get the number of shader cores. */ + Identity->shaderCoreCount = (shaderCoreCount != 0) + ? shaderCoreCount + : (Identity->chipModel >= gcv1000) ? 2 + : 1; + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: shaderCoreCount=%u%s", + Identity->shaderCoreCount, + (shaderCoreCount == 0) ? " (default)" : ""); + + /* Get the vertex cache size. */ + Identity->vertexCacheSize = (vertexCacheSize != 0) + ? vertexCacheSize + : 8; + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: vertexCacheSize=%u%s", + Identity->vertexCacheSize, + (vertexCacheSize == 0) ? " (default)" : ""); + + /* Get the maximum number of temporary registers. */ + Identity->registerMax = (registerMax != 0) + /* Maximum of registerMax/4 registers are accessible to 1 shader */ + ? 1 << registerMax + : (Identity->chipModel == gcv400) ? 32 + : 64; + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: registerMax=%u%s", + Identity->registerMax, + (registerMax == 0) ? " (default)" : ""); + + /* Get the instruction count. */ + Identity->instructionCount = (instructionCount == 0) ? 256 + : (instructionCount == 1) ? 1024 + : (instructionCount == 2) ? 2048 + : (instructionCount == 0xFF) ? 512 + : 256; + + if (Identity->instructionCount == 256) + { + if ((Identity->chipModel == gcv2000 && Identity->chipRevision == 0x5108) + || Identity->chipModel == gcv880) + { + Identity->instructionCount = 512; + } + else if (((((gctUINT32) (Identity->chipMinorFeatures3)) >> (0 ? 3:3) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))))) + { + Identity->instructionCount = 512; + } + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: instructionCount=%u%s", + Identity->instructionCount, + (instructionCount == 0) ? " (default)" : ""); + + /* Get the number of constants. */ + Identity->numConstants = (numConstants == 0) ? 168 : numConstants; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: numConstants=%u%s", + Identity->numConstants, + (numConstants == 0) ? " (default)" : ""); + + /* Get the buffer size. */ + Identity->bufferSize = bufferSize; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Specs: bufferSize=%u%s", + Identity->bufferSize, + (bufferSize == 0) ? " (default)" : ""); + + + if (varyingsCount != 0) + { + Identity->varyingsCount = varyingsCount; + } + else if (((((gctUINT32) (Identity->chipMinorFeatures1)) >> (0 ? 23:23) & ((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1)))))))) + { + Identity->varyingsCount = 12; + } + else + { + Identity->varyingsCount = 8; + } + + /* For some cores, it consumes two varying for position, so the max varying vectors should minus one. */ + if ((Identity->chipModel == gcv5000 && Identity->chipRevision == 0x5434) || + (Identity->chipModel == gcv4000 && Identity->chipRevision == 0x5222) || + (Identity->chipModel == gcv4000 && Identity->chipRevision == 0x5208) || + (Identity->chipModel == gcv4000 && Identity->chipRevision == 0x5245) || + (Identity->chipModel == gcv3000 && Identity->chipRevision == 0x5435) || + (Identity->chipModel == gcv2200 && Identity->chipRevision == 0x5244) || + (Identity->chipModel == gcv1500 && Identity->chipRevision == 0x5246) || + ((Identity->chipModel == gcv2100 || Identity->chipModel == gcv2000) && Identity->chipRevision == 0x5108) || + (Identity->chipModel == gcv880 && (Identity->chipRevision == 0x5107 || Identity->chipRevision == 0x5106))) + { + Identity->varyingsCount -= 1; + } + + Identity->chip2DControl = 0; + if (Identity->chipModel == gcv320) + { + gctUINT32 data; + + gcmkONERROR( + gckOS_ReadRegisterEx(Os, + Core, + 0x0002C, + &data)); + + if ((data != 33956864) && + ((Identity->chipRevision == 0x5007) || + (Identity->chipRevision == 0x5220))) + { + Identity->chip2DControl |= 0xFF & + (Identity->chipRevision == 0x5220 ? 8 : + (Identity->chipRevision == 0x5007 ? 12 : 0)); + } + + if (Identity->chipRevision == 0x5007) + { + /* Disable splitting rectangle. */ + Identity->chip2DControl |= 0x100; + + /* Enable 2D Flush. */ + Identity->chip2DControl |= 0x200; + } + } + +#if gcdMULTI_GPU +#if gcdMULTI_GPU > 1 + Identity->gpuCoreCount = gpuCoreCount + 1; +#else + Identity->gpuCoreCount = 1; +#endif +#endif + + /* Success. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#define gcdDEBUG_MODULE_CLOCK_GATING 0 +#define gcdDISABLE_MODULE_CLOCK_GATING 0 +#define gcdDISABLE_FE_CLOCK_GATING 0 +#define gcdDISABLE_PE_CLOCK_GATING 0 +#define gcdDISABLE_SH_CLOCK_GATING 0 +#define gcdDISABLE_PA_CLOCK_GATING 0 +#define gcdDISABLE_SE_CLOCK_GATING 0 +#define gcdDISABLE_RA_CLOCK_GATING 0 +#define gcdDISABLE_RA_EZ_CLOCK_GATING 0 +#define gcdDISABLE_RA_HZ_CLOCK_GATING 0 +#define gcdDISABLE_TX_CLOCK_GATING 0 + +#if gcdDEBUG_MODULE_CLOCK_GATING +gceSTATUS +_ConfigureModuleLevelClockGating( + gckHARDWARE Hardware + ) +{ + gctUINT32 data; + + gcmkVERIFY_OK( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + &data)); + +#if gcdDISABLE_FE_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); +#endif + +#if gcdDISABLE_PE_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))); +#endif + +#if gcdDISABLE_SH_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))); +#endif + +#if gcdDISABLE_PA_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); +#endif + +#if gcdDISABLE_SE_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); +#endif + +#if gcdDISABLE_RA_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); +#endif + +#if gcdDISABLE_TX_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))); +#endif + +#if gcdDISABLE_RA_EZ_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))); +#endif + +#if gcdDISABLE_RA_HZ_CLOCK_GATING + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))); +#endif + + gcmkVERIFY_OK( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + data)); + +#if gcdDISABLE_MODULE_CLOCK_GATING + gcmkVERIFY_OK( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00100, + &data)); + + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + + gcmkVERIFY_OK( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00100, + data)); +#endif + + return gcvSTATUS_OK; +} +#endif + +#if gcdPOWEROFF_TIMEOUT +void +_PowerTimerFunction( + gctPOINTER Data + ) +{ + gckHARDWARE hardware = (gckHARDWARE)Data; + gcmkVERIFY_OK( + gckHARDWARE_SetPowerManagementState(hardware, gcvPOWER_OFF_TIMEOUT)); +} +#endif + +static gceSTATUS +_VerifyDMA( + IN gckOS Os, + IN gceCORE Core, + gctUINT32_PTR Address1, + gctUINT32_PTR Address2, + gctUINT32_PTR State1, + gctUINT32_PTR State2 + ) +{ + gceSTATUS status; + gctUINT32 i; + + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x660, State1)); + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x664, Address1)); + + for (i = 0; i < 500; i += 1) + { + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x660, State2)); + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x664, Address2)); + + if (*Address1 != *Address2) + { + break; + } + + if (*State1 != *State2) + { + break; + } + } + +OnError: + return status; +} + +static gceSTATUS +_DumpDebugRegisters( + IN gckOS Os, + IN gceCORE Core, + IN gcsiDEBUG_REGISTERS_PTR Descriptor + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctUINT32 select; + gctUINT32 data = 0; + gctUINT i; + + gcmkHEADER_ARG("Os=0x%X Descriptor=0x%X", Os, Descriptor); + + gcmkPRINT_N(4, " %s debug registers:\n", Descriptor->module); + + for (i = 0; i < Descriptor->count; i += 1) + { + select = i << Descriptor->shift; + + gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, Descriptor->index, select)); +#if gcdFPGA_BUILD + gcmkONERROR(gckOS_Delay(Os, 1000)); +#endif + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, Descriptor->data, &data)); + + gcmkPRINT_N(12, " [0x%02X] 0x%08X\n", i, data); + } + + select = 0xF << Descriptor->shift; + + for (i = 0; i < 500; i += 1) + { + gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, Descriptor->index, select)); +#if gcdFPGA_BUILD + gcmkONERROR(gckOS_Delay(Os, 1000)); +#endif + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, Descriptor->data, &data)); + + if (data == Descriptor->signature) + { + break; + } + } + + if (i == 500) + { + gcmkPRINT_N(4, " failed to obtain the signature (read 0x%08X).\n", data); + } + else + { + gcmkPRINT_N(8, " signature = 0x%08X (%d read attempt(s))\n", data, i + 1); + } + +OnError: + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +static gceSTATUS +_IsGPUPresent( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gcsHAL_QUERY_CHIP_IDENTITY identity; + gctUINT32 control; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + &control)); + + control = ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))); + control = ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + control)); + + /* Identify the hardware. */ + gcmkONERROR(_IdentifyHardware(Hardware->os, + Hardware->core, + &identity)); + + /* Check if these are the same values as saved before. */ + if ((Hardware->identity.chipModel != identity.chipModel) + || (Hardware->identity.chipRevision != identity.chipRevision) + || (Hardware->identity.chipFeatures != identity.chipFeatures) + || (Hardware->identity.chipMinorFeatures != identity.chipMinorFeatures) + || (Hardware->identity.chipMinorFeatures1 != identity.chipMinorFeatures1) + || (Hardware->identity.chipMinorFeatures2 != identity.chipMinorFeatures2) + ) + { + gcmkPRINT("[galcore]: GPU is not present."); + gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +_FlushCache( + gckHARDWARE Hardware, + gckCOMMAND Command + ) +{ + gceSTATUS status; + gctUINT32 bytes, requested; + gctPOINTER buffer; + + /* Get the size of the flush command. */ + gcmkONERROR(gckHARDWARE_Flush(Hardware, + gcvFLUSH_ALL, + gcvNULL, + &requested)); + + /* Reserve space in the command queue. */ + gcmkONERROR(gckCOMMAND_Reserve(Command, + requested, + &buffer, + &bytes)); + + /* Append a flush. */ + gcmkONERROR(gckHARDWARE_Flush( + Hardware, gcvFLUSH_ALL, buffer, &bytes + )); + + /* Execute the command queue. */ + gcmkONERROR(gckCOMMAND_Execute(Command, requested)); + + return gcvSTATUS_OK; + +OnError: + return status; +} + +gctBOOL +_IsGPUIdle( + IN gctUINT32 Idle + ) +{ + return (((((gctUINT32) (Idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 1:1)) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 3:3)) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 4:4)) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 5:5)) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 6:6)) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 7:7)) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1)))))) ) + && (((((gctUINT32) (Idle)) >> (0 ? 2:2)) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1)))))) ) + ; +} + +/******************************************************************************\ +****************************** gckHARDWARE API code ***************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckHARDWARE_Construct +** +** Construct a new gckHARDWARE object. +** +** INPUT: +** +** gckOS Os +** Pointer to an initialized gckOS object. +** +** gceCORE Core +** Specified core. +** +** OUTPUT: +** +** gckHARDWARE * Hardware +** Pointer to a variable that will hold the pointer to the gckHARDWARE +** object. +*/ +gceSTATUS +gckHARDWARE_Construct( + IN gckOS Os, + IN gceCORE Core, + OUT gckHARDWARE * Hardware + ) +{ + gceSTATUS status; + gckHARDWARE hardware = gcvNULL; + gctUINT16 data = 0xff00; + gctPOINTER pointer = gcvNULL; +#if gcdMULTI_GPU_AFFINITY + gctUINT32 control; +#endif + + gcmkHEADER_ARG("Os=0x%x", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Hardware != gcvNULL); + + /* Enable the GPU. */ + gcmkONERROR(gckOS_SetGPUPower(Os, Core, gcvTRUE, gcvTRUE)); + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + 0x00000900)); + + /* Allocate the gckHARDWARE object. */ + gcmkONERROR(gckOS_Allocate(Os, + gcmSIZEOF(struct _gckHARDWARE), + &pointer)); + + hardware = (gckHARDWARE) pointer; + + /* Initialize the gckHARDWARE object. */ + hardware->object.type = gcvOBJ_HARDWARE; + hardware->os = Os; + hardware->core = Core; + + /* Identify the hardware. */ + gcmkONERROR(_IdentifyHardware(Os, Core, &hardware->identity)); + + /* Determine the hardware type */ + switch (hardware->identity.chipModel) + { + case gcv350: + case gcv355: + hardware->type = gcvHARDWARE_VG; + break; + + case gcv200: + case gcv300: + case gcv320: + case gcv328: + case gcv420: + case gcv428: + hardware->type = gcvHARDWARE_2D; + break; + + default: +#if gcdMULTI_GPU_AFFINITY + hardware->type = (Core == gcvCORE_MAJOR) ? gcvHARDWARE_3D : gcvHARDWARE_OCL; +#else + hardware->type = gcvHARDWARE_3D; +#endif + + if(hardware->identity.chipModel == gcv880 && hardware->identity.chipRevision == 0x5107) + { + /*set outstanding limit*/ + gctUINT32 axi_ot; + gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, 0x00414, &axi_ot)); + axi_ot = (axi_ot & (~0xFF)) | 0x00010; + gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, 0x00414, axi_ot)); + } + + + if ((((((gctUINT32) (hardware->identity.chipFeatures)) >> (0 ? 9:9)) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1)))))) )) + { + hardware->type = (gceHARDWARE_TYPE) (hardware->type | gcvHARDWARE_2D); + } + } + + hardware->powerBaseAddress + = ((hardware->identity.chipModel == gcv300) + && (hardware->identity.chipRevision < 0x2000)) + ? 0x0100 + : 0x0000; + + /* _ResetGPU need powerBaseAddress. */ + status = _ResetGPU(hardware, Os, Core); + + if (status != gcvSTATUS_OK) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "_ResetGPU failed: status=%d\n", status); + } + +#if gcdMULTI_GPU + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x0055C, +#if gcdDISABLE_FE_L2 + 0x00FFFFFF)); +#else + 0x00FFFF05)); +#endif + +#elif gcdMULTI_GPU_AFFINITY + control = ((((gctUINT32) (0x00FF0A05)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) << (0 ? 27:27))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) << (0 ? 27:27))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x0055C, + control)); +#endif + + hardware->powerMutex = gcvNULL; + + hardware->mmuVersion + = (((((gctUINT32) (hardware->identity.chipMinorFeatures1)) >> (0 ? 28:28)) & ((gctUINT32) ((((1 ? 28:28) - (0 ? 28:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 28:28) - (0 ? 28:28) + 1)))))) ); + + /* Determine whether bug fixes #1 are present. */ + hardware->extraEventStates = ((((gctUINT32) (hardware->identity.chipMinorFeatures1)) >> (0 ? 3:3) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) == (0x0 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))); + + /* Check if big endian */ + hardware->bigEndian = (*(gctUINT8 *)&data == 0xff); + + /* Initialize the fast clear. */ + gcmkONERROR(gckHARDWARE_SetFastClear(hardware, -1, -1)); + +#if !gcdENABLE_128B_MERGE + + if (((((gctUINT32) (hardware->identity.chipMinorFeatures2)) >> (0 ? 21:21) & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1)))))))) + { + /* 128B merge is turned on by default. Disable it. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, Core, 0x00558, 0)); + } + +#endif + + /* Set power state to ON. */ + hardware->chipPowerState = gcvPOWER_ON; + hardware->clockState = gcvTRUE; + hardware->powerState = gcvTRUE; + hardware->lastWaitLink = ~0U; + hardware->lastEnd = ~0U; + hardware->globalSemaphore = gcvNULL; +#if gcdENABLE_FSCALE_VAL_ADJUST + hardware->powerOnFscaleVal = 64; +#endif + + gcmkONERROR(gckOS_CreateMutex(Os, &hardware->powerMutex)); + gcmkONERROR(gckOS_CreateSemaphore(Os, &hardware->globalSemaphore)); + hardware->startIsr = gcvNULL; + hardware->stopIsr = gcvNULL; + +#if gcdPOWEROFF_TIMEOUT + hardware->powerOffTimeout = gcdPOWEROFF_TIMEOUT; + + gcmkVERIFY_OK(gckOS_CreateTimer(Os, + _PowerTimerFunction, + (gctPOINTER)hardware, + &hardware->powerOffTimer)); +#endif + + gcmkONERROR(gckOS_AtomConstruct(Os, &hardware->pageTableDirty)); + gcmkONERROR(gckOS_AtomConstruct(Os, &hardware->pendingEvent)); + +#if gcdLINK_QUEUE_SIZE + hardware->linkQueue.front = 0; + hardware->linkQueue.rear = 0; + hardware->linkQueue.count = 0; +#endif + + /* Enable power management by default. */ + hardware->powerManagement = gcvTRUE; + + /* Disable profiler by default */ + hardware->gpuProfiler = gcvFALSE; + +#if defined(LINUX) || defined(__QNXNTO__) || defined(UNDERCE) + if (hardware->mmuVersion) + { + hardware->endAfterFlushMmuCache = gcvTRUE; + } + else +#endif + { + hardware->endAfterFlushMmuCache = gcvFALSE; + } + + gcmkONERROR(gckOS_QueryOption(Os, "mmu", (gctUINT32_PTR)&hardware->enableMMU)); + + hardware->minFscaleValue = 1; + + /* Return pointer to the gckHARDWARE object. */ + *Hardware = hardware; + + /* Success. */ + gcmkFOOTER_ARG("*Hardware=0x%x", *Hardware); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (hardware != gcvNULL) + { + /* Turn off the power. */ + gcmkVERIFY_OK(gckOS_SetGPUPower(Os, Core, gcvFALSE, gcvFALSE)); + + if (hardware->globalSemaphore != gcvNULL) + { + /* Destroy the global semaphore. */ + gcmkVERIFY_OK(gckOS_DestroySemaphore(Os, + hardware->globalSemaphore)); + } + + if (hardware->powerMutex != gcvNULL) + { + /* Destroy the power mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, hardware->powerMutex)); + } + +#if gcdPOWEROFF_TIMEOUT + if (hardware->powerOffTimer != gcvNULL) + { + gcmkVERIFY_OK(gckOS_StopTimer(Os, hardware->powerOffTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Os, hardware->powerOffTimer)); + } +#endif + + if (hardware->pageTableDirty != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Os, hardware->pageTableDirty)); + } + + if (hardware->pendingEvent != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Os, hardware->pendingEvent)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, hardware)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_Destroy +** +** Destroy an gckHARDWARE object. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object that needs to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_Destroy( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Destroy the power semaphore. */ + gcmkVERIFY_OK(gckOS_DestroySemaphore(Hardware->os, + Hardware->globalSemaphore)); + + /* Destroy the power mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Hardware->os, Hardware->powerMutex)); + +#if gcdPOWEROFF_TIMEOUT + gcmkVERIFY_OK(gckOS_StopTimer(Hardware->os, Hardware->powerOffTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Hardware->os, Hardware->powerOffTimer)); +#endif + + gcmkVERIFY_OK(gckOS_AtomDestroy(Hardware->os, Hardware->pageTableDirty)); + + gcmkVERIFY_OK(gckOS_AtomDestroy(Hardware->os, Hardware->pendingEvent)); + + gcmkVERIFY_OK(gckOS_FreeNonPagedMemory( + Hardware->os, + Hardware->functionBytes, + Hardware->functionPhysical, + Hardware->functionLogical + )); + + /* Mark the object as unknown. */ + Hardware->object.type = gcvOBJ_UNKNOWN; + + /* Free the object. */ + gcmkONERROR(gcmkOS_SAFE_FREE(Hardware->os, Hardware)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_GetType +** +** Get the hardware type. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** OUTPUT: +** +** gceHARDWARE_TYPE * Type +** Pointer to a variable that receives the type of hardware object. +*/ +gceSTATUS +gckHARDWARE_GetType( + IN gckHARDWARE Hardware, + OUT gceHARDWARE_TYPE * Type + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + gcmkVERIFY_ARGUMENT(Type != gcvNULL); + + *Type = Hardware->type; + + gcmkFOOTER_ARG("*Type=%d", *Type); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_InitializeHardware +** +** Initialize the hardware. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_InitializeHardware( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gctUINT32 baseAddress; + gctUINT32 chipRev; + gctUINT32 control; + gctUINT32 data; + gctUINT32 regPMC = 0; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Read the chip revision register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00024, + &chipRev)); + + if (chipRev != Hardware->identity.chipRevision) + { + /* Chip is not there! */ + gcmkONERROR(gcvSTATUS_CONTEXT_LOSSED); + } + + /* Disable isolate GPU bit. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (0x00000900)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))))); + + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + &control)); + + /* Enable debug register. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1))))))) << (0 ? 11:11))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1))))))) << (0 ? 11:11))))); + + /* Reset memory counters. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0003C, + ~0U)); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0003C, + 0)); + + /* Get the system's physical base address. */ + gcmkONERROR(gckOS_GetBaseAddress(Hardware->os, &baseAddress)); + + /* Program the base addesses. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0041C, + baseAddress)); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00418, + baseAddress)); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00428, + baseAddress)); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00420, + baseAddress)); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00424, + baseAddress)); + + { + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00100, + &data)); + + /* Enable clock gating. */ + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + if ((Hardware->identity.chipRevision == 0x4301) + || (Hardware->identity.chipRevision == 0x4302) + ) + { + /* Disable stall module level clock gating for 4.3.0.1 and 4.3.0.2 + ** revisions. */ + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))); + } + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00100, + data)); + +#if gcdENABLE_3D + /* Disable PE clock gating on revs < 5.0 when HZ is present without a + ** bug fix. */ + if ((Hardware->identity.chipRevision < 0x5000) + && gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_HZ) + && ((((gctUINT32) (Hardware->identity.chipMinorFeatures1)) >> (0 ? 9:9) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1)))))) == (0x0 & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) + ) + { + if (regPMC == 0) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + ®PMC)); + } + + /* Disable PE clock gating. */ + regPMC = ((((gctUINT32) (regPMC)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))); + } + +#endif + } + + if (Hardware->identity.chipModel == gcv4000 && + ((Hardware->identity.chipRevision == 0x5208) || (Hardware->identity.chipRevision == 0x5222))) + { + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + ((((gctUINT32) (0x01590880)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1))))))) << (0 ? 23:23))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1))))))) << (0 ? 23:23))))); + } + + if (Hardware->identity.chipModel == gcv1000 && + (Hardware->identity.chipRevision == 0x5039 || + Hardware->identity.chipRevision == 0x5040)) + { + gctUINT32 pulseEater; + + pulseEater = ((((gctUINT32) (0x01590880)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))); + + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + ((((gctUINT32) (pulseEater)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))))); + } + + if ((gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_HALTI2) == gcvSTATUS_FALSE) + || (Hardware->identity.chipRevision < 0x5422) + ) + { + if (regPMC == 0) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + ®PMC)); + } + + regPMC = ((((gctUINT32) (regPMC)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:15) - (0 ? 15:15) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:15) - (0 ? 15:15) + 1))))))) << (0 ? 15:15))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 15:15) - (0 ? 15:15) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:15) - (0 ? 15:15) + 1))))))) << (0 ? 15:15))); + } + + if (_IsHardwareMatch(Hardware, gcv2000, 0x5108)) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00480, + &data)); + + /* Set FE bus to one, TX bus to zero */ + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))); + + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00480, + data)); + } + + gcmkONERROR( + gckHARDWARE_SetMMU(Hardware, + Hardware->kernel->mmu->pageTableLogical)); + + if (Hardware->identity.chipModel >= gcv400 + && Hardware->identity.chipModel != gcv420) + { + if (regPMC == 0) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + ®PMC)); + } + + /* Disable PA clock gating. */ + regPMC = ((((gctUINT32) (regPMC)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); + } + + /* Limit 2D outstanding request. */ + if (_IsHardwareMatch(Hardware, gcv880, 0x5107)) + { + gctUINT32 axi_ot; + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00414, &axi_ot)); + axi_ot = (axi_ot & (~0xFF)) | 0x00010; + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00414, axi_ot)); + } + + if (Hardware->identity.chip2DControl & 0xFF) + { + gctUINT32 data; + + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00414, + &data)); + + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (Hardware->identity.chip2DControl & 0xFF) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))); + + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00414, + data)); + } + + if (_IsHardwareMatch(Hardware, gcv1000, 0x5035)) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00414, + &data)); + + /* Disable HZ-L2. */ + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))); + + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00414, + data)); + } + + if (_IsHardwareMatch(Hardware, gcv4000, 0x5222)) + { + if (regPMC == 0) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + ®PMC)); + } + + /* Disable TX clock gating. */ + regPMC = ((((gctUINT32) (regPMC)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))); + } + + if (_IsHardwareMatch(Hardware, gcv880, 0x5106)) + { + Hardware->kernel->timeOut = 140 * 1000; + } + + if (regPMC == 0) + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + ®PMC)); + } + + /* Disable RA HZ clock gating. */ + regPMC = ((((gctUINT32) (regPMC)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))); + + /* Disable RA EZ clock gating. */ + regPMC = ((((gctUINT32) (regPMC)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))); + + if (regPMC != 0) + { + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + regPMC)); + } + + if (_IsHardwareMatch(Hardware, gcv2000, 0x5108) + || _IsHardwareMatch(Hardware, gcv320, 0x5007) + || _IsHardwareMatch(Hardware, gcv880, 0x5106) + || _IsHardwareMatch(Hardware, gcv400, 0x4645) + ) + { + /* Update GPU AXI cache atttribute. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00008, + 0x00002200)); + } + + + if ((Hardware->identity.chipRevision > 0x5420) + && gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_PIPE_3D)) + { + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + &data)); + + /* Disable internal DFS. */ + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))) << (0 ? 18:18))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))) << (0 ? 18:18))); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + data)); + } + +#if gcdDEBUG_MODULE_CLOCK_GATING + _ConfigureModuleLevelClockGating(Hardware); +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_QueryMemory +** +** Query the amount of memory available on the hardware. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** OUTPUT: +** +** gctSIZE_T * InternalSize +** Pointer to a variable that will hold the size of the internal video +** memory in bytes. If 'InternalSize' is gcvNULL, no information of the +** internal memory will be returned. +** +** gctUINT32 * InternalBaseAddress +** Pointer to a variable that will hold the hardware's base address for +** the internal video memory. This pointer cannot be gcvNULL if +** 'InternalSize' is also non-gcvNULL. +** +** gctUINT32 * InternalAlignment +** Pointer to a variable that will hold the hardware's base address for +** the internal video memory. This pointer cannot be gcvNULL if +** 'InternalSize' is also non-gcvNULL. +** +** gctSIZE_T * ExternalSize +** Pointer to a variable that will hold the size of the external video +** memory in bytes. If 'ExternalSize' is gcvNULL, no information of the +** external memory will be returned. +** +** gctUINT32 * ExternalBaseAddress +** Pointer to a variable that will hold the hardware's base address for +** the external video memory. This pointer cannot be gcvNULL if +** 'ExternalSize' is also non-gcvNULL. +** +** gctUINT32 * ExternalAlignment +** Pointer to a variable that will hold the hardware's base address for +** the external video memory. This pointer cannot be gcvNULL if +** 'ExternalSize' is also non-gcvNULL. +** +** gctUINT32 * HorizontalTileSize +** Number of horizontal pixels per tile. If 'HorizontalTileSize' is +** gcvNULL, no horizontal pixel per tile will be returned. +** +** gctUINT32 * VerticalTileSize +** Number of vertical pixels per tile. If 'VerticalTileSize' is +** gcvNULL, no vertical pixel per tile will be returned. +*/ +gceSTATUS +gckHARDWARE_QueryMemory( + IN gckHARDWARE Hardware, + OUT gctSIZE_T * InternalSize, + OUT gctUINT32 * InternalBaseAddress, + OUT gctUINT32 * InternalAlignment, + OUT gctSIZE_T * ExternalSize, + OUT gctUINT32 * ExternalBaseAddress, + OUT gctUINT32 * ExternalAlignment, + OUT gctUINT32 * HorizontalTileSize, + OUT gctUINT32 * VerticalTileSize + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (InternalSize != gcvNULL) + { + /* No internal memory. */ + *InternalSize = 0; + } + + if (ExternalSize != gcvNULL) + { + /* No external memory. */ + *ExternalSize = 0; + } + + if (HorizontalTileSize != gcvNULL) + { + /* 4x4 tiles. */ + *HorizontalTileSize = 4; + } + + if (VerticalTileSize != gcvNULL) + { + /* 4x4 tiles. */ + *VerticalTileSize = 4; + } + + /* Success. */ + gcmkFOOTER_ARG("*InternalSize=%lu *InternalBaseAddress=0x%08x " + "*InternalAlignment=0x%08x *ExternalSize=%lu " + "*ExternalBaseAddress=0x%08x *ExtenalAlignment=0x%08x " + "*HorizontalTileSize=%u *VerticalTileSize=%u", + gcmOPT_VALUE(InternalSize), + gcmOPT_VALUE(InternalBaseAddress), + gcmOPT_VALUE(InternalAlignment), + gcmOPT_VALUE(ExternalSize), + gcmOPT_VALUE(ExternalBaseAddress), + gcmOPT_VALUE(ExternalAlignment), + gcmOPT_VALUE(HorizontalTileSize), + gcmOPT_VALUE(VerticalTileSize)); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_QueryChipIdentity +** +** Query the identity of the hardware. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** OUTPUT: +** +** gcsHAL_QUERY_CHIP_IDENTITY_PTR Identity +** Pointer to the identity structure. +** +*/ +gceSTATUS +gckHARDWARE_QueryChipIdentity( + IN gckHARDWARE Hardware, + OUT gcsHAL_QUERY_CHIP_IDENTITY_PTR Identity + ) +{ + gctUINT32 features; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Identity != gcvNULL); + + /* Return chip model and revision. */ + Identity->chipModel = Hardware->identity.chipModel; + Identity->chipRevision = Hardware->identity.chipRevision; + + /* Return feature set. */ + features = Hardware->identity.chipFeatures; + + if ((((((gctUINT32) (features)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) )) + { + /* Override fast clear by command line. */ + features = ((((gctUINT32) (features)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (Hardware->allowFastClear) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + } + + if ((((((gctUINT32) (features)) >> (0 ? 5:5)) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1)))))) )) + { + /* Override compression by command line. */ + features = ((((gctUINT32) (features)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) ((gctUINT32) (Hardware->allowCompression) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); + } + + /* Mark 2D pipe as available for GC500.0 through GC500.2 and GC300, + ** since they did not have this bit. */ + if (((Hardware->identity.chipModel == gcv500) && (Hardware->identity.chipRevision <= 2)) + || (Hardware->identity.chipModel == gcv300) + ) + { + features = ((((gctUINT32) (features)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))); + } + + Identity->chipFeatures = features; + + /* Return minor features. */ + Identity->chipMinorFeatures = Hardware->identity.chipMinorFeatures; + Identity->chipMinorFeatures1 = Hardware->identity.chipMinorFeatures1; + Identity->chipMinorFeatures2 = Hardware->identity.chipMinorFeatures2; + Identity->chipMinorFeatures3 = Hardware->identity.chipMinorFeatures3; + Identity->chipMinorFeatures4 = Hardware->identity.chipMinorFeatures4; + Identity->chipMinorFeatures5 = Hardware->identity.chipMinorFeatures5; + + /* Return chip specs. */ + Identity->streamCount = Hardware->identity.streamCount; + Identity->registerMax = Hardware->identity.registerMax; + Identity->threadCount = Hardware->identity.threadCount; + Identity->shaderCoreCount = Hardware->identity.shaderCoreCount; + Identity->vertexCacheSize = Hardware->identity.vertexCacheSize; + Identity->vertexOutputBufferSize = Hardware->identity.vertexOutputBufferSize; + Identity->pixelPipes = Hardware->identity.pixelPipes; + Identity->instructionCount = Hardware->identity.instructionCount; + Identity->numConstants = Hardware->identity.numConstants; + Identity->bufferSize = Hardware->identity.bufferSize; + Identity->varyingsCount = Hardware->identity.varyingsCount; + Identity->superTileMode = Hardware->identity.superTileMode; +#if gcdMULTI_GPU + Identity->gpuCoreCount = Hardware->identity.gpuCoreCount; +#endif + Identity->chip2DControl = Hardware->identity.chip2DControl; + + Identity->productID = Hardware->identity.productID; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_SplitMemory +** +** Split a hardware specific memory address into a pool and offset. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** gctUINT32 Address +** Address in hardware specific format. +** +** OUTPUT: +** +** gcePOOL * Pool +** Pointer to a variable that will hold the pool type for the address. +** +** gctUINT32 * Offset +** Pointer to a variable that will hold the offset for the address. +*/ +gceSTATUS +gckHARDWARE_SplitMemory( + IN gckHARDWARE Hardware, + IN gctUINT32 Address, + OUT gcePOOL * Pool, + OUT gctUINT32 * Offset + ) +{ + gcmkHEADER_ARG("Hardware=0x%x Addres=0x%08x", Hardware, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Pool != gcvNULL); + gcmkVERIFY_ARGUMENT(Offset != gcvNULL); + + if (Hardware->mmuVersion == 0) + { + /* Dispatch on memory type. */ + switch ((((((gctUINT32) (Address)) >> (0 ? 31:31)) & ((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1)))))) )) + { + case 0x0: + /* System memory. */ + *Pool = gcvPOOL_SYSTEM; + break; + + case 0x1: + /* Virtual memory. */ + *Pool = gcvPOOL_VIRTUAL; + break; + + default: + /* Invalid memory type. */ + gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); + return gcvSTATUS_INVALID_ARGUMENT; + } + + /* Return offset of address. */ + *Offset = (((((gctUINT32) (Address)) >> (0 ? 30:0)) & ((gctUINT32) ((((1 ? 30:0) - (0 ? 30:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 30:0) - (0 ? 30:0) + 1)))))) ); + } + else + { + *Pool = gcvPOOL_SYSTEM; + *Offset = Address; + } + + /* Success. */ + gcmkFOOTER_ARG("*Pool=%d *Offset=0x%08x", *Pool, *Offset); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_Execute +** +** Kickstart the hardware's command processor with an initialized command +** buffer. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** gctUINT32 Address +** Hardware address of command buffer. +** +** gctSIZE_T Bytes +** Number of bytes for the prefetch unit (until after the first LINK). +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_Execute( + IN gckHARDWARE Hardware, + IN gctUINT32 Address, + IN gctSIZE_T Bytes + ) +{ + gceSTATUS status; + gctUINT32 control; + + gcmkHEADER_ARG("Hardware=0x%x Address=0x%x Bytes=%lu", + Hardware, Address, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Enable all events. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00014, ~0U)); + + /* Write address register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00654, Address)); + + /* Build control register. */ + control = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) ((Bytes + 7) >> 3) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + /* Set big endian */ + if (Hardware->bigEndian) + { + control |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 21:20) - (0 ? 21:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:20) - (0 ? 21:20) + 1))))))) << (0 ? 21:20))) | (((gctUINT32) (0x2 & ((gctUINT32) ((((1 ? 21:20) - (0 ? 21:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:20) - (0 ? 21:20) + 1))))))) << (0 ? 21:20))); + } + + /* Write control register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00658, control)); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Started command buffer @ 0x%08x", + Address); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_WaitLink +** +** Append a WAIT/LINK command sequence at the specified location in the command +** queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** WAIT/LINK command sequence at or gcvNULL just to query the size of the +** WAIT/LINK command sequence. +** +** gctUINT32 Offset +** Offset into command buffer required for alignment. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the WAIT/LINK command +** sequence. If 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** by the WAIT/LINK command sequence. If 'Bytes' is gcvNULL, nothing will +** be returned. +** +** gctUINT32 * WaitOffset +** Pointer to a variable that will receive the offset of the WAIT command +** from the specified logcial pointer. +** If 'WaitOffset' is gcvNULL nothing will be returned. +** +** gctSIZE_T * WaitSize +** Pointer to a variable that will receive the number of bytes used by +** the WAIT command. If 'LinkSize' is gcvNULL nothing will be returned. +*/ +gceSTATUS +gckHARDWARE_WaitLink( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT32 Offset, + IN OUT gctUINT32 * Bytes, + OUT gctUINT32 * WaitOffset, + OUT gctUINT32 * WaitSize + ) +{ + static const gctUINT waitCount = 200; + + gceSTATUS status; + gctUINT32 address; + gctUINT32_PTR logical; + gctUINT32 bytes; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Offset=0x%08x *Bytes=%lu", + Hardware, Logical, Offset, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical != gcvNULL) || (Bytes != gcvNULL)); + +#if gcdMULTI_GPU && !gcdDISABLE_FE_L2 + bytes = gcmALIGN(Offset + 40, 8) - Offset; +#else + /* Compute number of bytes required. */ + bytes = gcmALIGN(Offset + 16, 8) - Offset; +#endif + /* Cast the input pointer. */ + logical = (gctUINT32_PTR) Logical; + + if (logical != gcvNULL) + { + /* Not enough space? */ + if (*Bytes < bytes) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + /* Convert logical into hardware specific address. */ + gcmkONERROR(gckHARDWARE_ConvertLogical(Hardware, logical, gcvFALSE, &address)); + + /* Store the WAIT/LINK address. */ + Hardware->lastWaitLink = address; + + /* Append WAIT(count). */ + logical[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (waitCount) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + +#if gcdMULTI_GPU && !gcdDISABLE_FE_L2 + logical[2] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x0D & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | gcvCORE_3D_0_MASK; + + logical[3] = 0; + + /* LoadState(AQFlush, 1), flush. */ + logical[4] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + logical[5] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + + logical[6] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x0D & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | gcvCORE_3D_ALL_MASK; + + logical[7] = 0; + + /* Append LINK(2, address). */ + logical[8] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (bytes >> 3) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + logical[9] = address; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%08x: WAIT %u", address, waitCount + ); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: FLUSH 0x%x", address + 8, logical[3]); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%08x: LINK 0x%08x, #%lu", + address + 16, address, bytes + ); +#else + + /* Append LINK(2, address). */ + logical[2] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (bytes >> 3) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + logical[3] = address; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%08x: WAIT %u", address, waitCount + ); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%08x: LINK 0x%08x, #%lu", + address + 8, address, bytes + ); +#endif + if (WaitOffset != gcvNULL) + { + /* Return the offset pointer to WAIT command. */ + *WaitOffset = 0; + } + + if (WaitSize != gcvNULL) + { + /* Return number of bytes used by the WAIT command. */ +#if gcdMULTI_GPU && !gcdDISABLE_FE_L2 + *WaitSize = 32; +#else + *WaitSize = 8; +#endif + } + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the WAIT/LINK command + ** sequence. */ + *Bytes = bytes; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu *WaitOffset=0x%x *WaitSize=%lu", + gcmOPT_VALUE(Bytes), gcmOPT_VALUE(WaitOffset), + gcmOPT_VALUE(WaitSize)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_End +** +** Append an END command at the specified location in the command queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** END command at or gcvNULL just to query the size of the END command. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the END command. If +** 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the END command. If 'Bytes' is gcvNULL, nothing will be returned. +*/ +gceSTATUS +gckHARDWARE_End( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ) +{ + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + gctUINT32 address; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x *Bytes=%lu", + Hardware, Logical, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); + + if (Logical != gcvNULL) + { + if (*Bytes < 8) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + /* Append END. */ + logical[0] = + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x02 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "0x%x: END", Logical); + + /* Make sure the CPU writes out the data to memory. */ + gcmkONERROR( + gckOS_MemoryBarrier(Hardware->os, Logical)); + + gcmkONERROR(gckHARDWARE_ConvertLogical(Hardware, logical, gcvFALSE, &address)); + + Hardware->lastEnd = address; + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the END command. */ + *Bytes = 8; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdMULTI_GPU +gceSTATUS +gckHARDWARE_ChipEnable( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gceCORE_3D_MASK ChipEnable, + IN OUT gctSIZE_T * Bytes + ) +{ + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x ChipEnable=0x%x *Bytes=%lu", + Hardware, Logical, ChipEnable, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); + + if (Logical != gcvNULL) + { + if (*Bytes < 8) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + /* Append CHIPENABLE. */ + logical[0] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x0D & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ChipEnable; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "0x%x: CHIPENABLE 0x%x", Logical, ChipEnable); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the CHIPENABLE command. */ + *Bytes = 8; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckHARDWARE_Nop +** +** Append a NOP command at the specified location in the command queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** NOP command at or gcvNULL just to query the size of the NOP command. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the NOP command. If +** 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the NOP command. If 'Bytes' is gcvNULL, nothing will be returned. +*/ +gceSTATUS +gckHARDWARE_Nop( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN OUT gctSIZE_T * Bytes + ) +{ + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x *Bytes=%lu", + Hardware, Logical, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); + + if (Logical != gcvNULL) + { + if (*Bytes < 8) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + /* Append NOP. */ + logical[0] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x03 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "0x%x: NOP", Logical); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the NOP command. */ + *Bytes = 8; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_Event +** +** Append an EVENT command at the specified location in the command queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** the EVENT command at or gcvNULL just to query the size of the EVENT +** command. +** +** gctUINT8 Event +** Event ID to program. +** +** gceKERNEL_WHERE FromWhere +** Location of the pipe to send the event. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the EVENT command. If +** 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the EVENT command. If 'Bytes' is gcvNULL, nothing will be +** returned. +*/ +gceSTATUS +gckHARDWARE_Event( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT8 Event, + IN gceKERNEL_WHERE FromWhere, + IN OUT gctUINT32 * Bytes + ) +{ + gctUINT size; + gctUINT32 destination = 0; + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Event=%u FromWhere=%d *Bytes=%lu", + Hardware, Logical, Event, FromWhere, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); + gcmkVERIFY_ARGUMENT(Event < 32); + +#if gcdMULTI_GPU + if (FromWhere == gcvKERNEL_COMMAND) FromWhere = gcvKERNEL_PIXEL; +#endif + + /* Determine the size of the command. */ + + size = (Hardware->extraEventStates && (FromWhere == gcvKERNEL_PIXEL)) + ? gcmALIGN(8 + (1 + 5) * 4, 8) /* EVENT + 5 STATES */ + : 8; + + if (Logical != gcvNULL) + { + if (*Bytes < size) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + switch (FromWhere) + { + case gcvKERNEL_COMMAND: + /* From command processor. */ + destination = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); + break; + + case gcvKERNEL_PIXEL: + /* From pixel engine. */ + destination = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + break; + + default: + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Append EVENT(Event, destiantion). */ + logical[0] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E01) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + logical[1] = ((((gctUINT32) (destination)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (Event) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))); + + /* Make sure the event ID gets written out before GPU can access it. */ + gcmkONERROR( + gckOS_MemoryBarrier(Hardware->os, logical + 1)); + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + { + gctUINT32 phys; + gckOS_GetPhysicalAddress(Hardware->os, Logical, &phys); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%08x: EVENT %d", phys, Event); + } +#endif + + /* Append the extra states. These are needed for the chips that do not + ** support back-to-back events due to the async interface. The extra + ** states add the necessary delay to ensure that event IDs do not + ** collide. */ + if (size > 8) + { + logical[2] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0100) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (5) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + logical[3] = 0; + logical[4] = 0; + logical[5] = 0; + logical[6] = 0; + logical[7] = 0; + } + +#if gcdINTERRUPT_STATISTIC + if (Event < gcmCOUNTOF(Hardware->kernel->eventObj->queues)) + { + gckOS_AtomSetMask(Hardware->pendingEvent, 1 << Event); + } +#endif + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the EVENT command. */ + *Bytes = size; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_PipeSelect +** +** Append a PIPESELECT command at the specified location in the command queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** the PIPESELECT command at or gcvNULL just to query the size of the +** PIPESELECT command. +** +** gcePIPE_SELECT Pipe +** Pipe value to select. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the PIPESELECT command. +** If 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the PIPESELECT command. If 'Bytes' is gcvNULL, nothing will be +** returned. +*/ +gceSTATUS +gckHARDWARE_PipeSelect( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gcePIPE_SELECT Pipe, + IN OUT gctUINT32 * Bytes + ) +{ + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Pipe=%d *Bytes=%lu", + Hardware, Logical, Pipe, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); + + /* Append a PipeSelect. */ + if (Logical != gcvNULL) + { + gctUINT32 flush, stall; + + if (*Bytes < 32) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + flush = (Pipe == gcvPIPE_2D) + ? ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + : ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))); + + stall = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* LoadState(AQFlush, 1), flush. */ + logical[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + logical[1] + = flush; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: FLUSH 0x%x", logical, flush); + + /* LoadState(AQSempahore, 1), stall. */ + logical[2] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + logical[3] + = stall; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: SEMAPHORE 0x%x", logical + 2, stall); + + /* Stall, stall. */ + logical[4] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + logical[5] = stall; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: STALL 0x%x", logical + 4, stall); + + /* LoadState(AQPipeSelect, 1), pipe. */ + logical[6] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E00) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + logical[7] = (Pipe == gcvPIPE_2D) + ? 0x1 + : 0x0; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: PIPE %d", logical + 6, Pipe); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the PIPESELECT command. */ + *Bytes = 32; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_Link +** +** Append a LINK command at the specified location in the command queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** the LINK command at or gcvNULL just to query the size of the LINK +** command. +** +** gctUINT32 FetchAddress +** Hardware address of destination of LINK. +** +** gctSIZE_T FetchSize +** Number of bytes in destination of LINK. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the LINK command. If +** 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the LINK command. If 'Bytes' is gcvNULL, nothing will be returned. +*/ +gceSTATUS +gckHARDWARE_Link( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT32 FetchSize, + IN OUT gctUINT32 * Bytes + ) +{ + gceSTATUS status; + gctSIZE_T bytes; + gctUINT32 link; + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x FetchAddress=0x%x FetchSize=%lu " + "*Bytes=%lu", + Hardware, Logical, FetchAddress, FetchSize, + gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); + + if (Logical != gcvNULL) + { + if (*Bytes < 8) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + gcmkONERROR( + gckOS_WriteMemory(Hardware->os, logical + 1, FetchAddress)); + + /* Make sure the address got written before the LINK command. */ + gcmkONERROR( + gckOS_MemoryBarrier(Hardware->os, logical + 1)); + + /* Compute number of 64-byte aligned bytes to fetch. */ + bytes = gcmALIGN(FetchAddress + FetchSize, 64) - FetchAddress; + + /* Append LINK(bytes / 8), FetchAddress. */ + link = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (bytes >> 3) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + gcmkONERROR( + gckOS_WriteMemory(Hardware->os, logical, link)); + + /* Memory barrier. */ + gcmkONERROR( + gckOS_MemoryBarrier(Hardware->os, logical)); + +#if gcdLINK_QUEUE_SIZE && !gcdPROCESS_ADDRESS_SPACE + if ((Hardware->kernel->virtualCommandBuffer) + && (Hardware->kernel->stuckDump > 2) + ) + { + gctBOOL in; + + gcmkVERIFY_OK(gckCOMMAND_AddressInKernelCommandBuffer( + Hardware->kernel->command, FetchAddress, &in)); + + if (in == gcvFALSE) + { + /* Record user command buffer and context buffer link + ** information for stuck dump. + **/ + gckLINKQUEUE_Enqueue( + &Hardware->linkQueue, FetchAddress, FetchAddress + (gctUINT)bytes); + } + } +#endif + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the LINK command. */ + *Bytes = 8; + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_UpdateQueueTail +** +** Update the tail of the command queue. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Logical address of the start of the command queue. +** +** gctUINT32 Offset +** Offset into the command queue of the tail (last command). +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_UpdateQueueTail( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT32 Offset + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x Offset=0x%08x", + Hardware, Logical, Offset); + + /* Verify the hardware. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Force a barrier. */ + gcmkONERROR( + gckOS_MemoryBarrier(Hardware->os, Logical)); + + /* Notify gckKERNEL object of change. */ +#if gcdMULTI_GPU + gcmkONERROR( + gckKERNEL_Notify(Hardware->kernel, + 0, + gcvNOTIFY_COMMAND_QUEUE, + gcvFALSE)); +#else + gcmkONERROR( + gckKERNEL_Notify(Hardware->kernel, + gcvNOTIFY_COMMAND_QUEUE, + gcvFALSE)); +#endif + + if (status == gcvSTATUS_CHIP_NOT_READY) + { + gcmkONERROR(gcvSTATUS_DEVICE); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_ConvertLogical +** +** Convert a logical system address into a hardware specific address. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Logical address to convert. +** +** gctBOOL InUserSpace +** gcvTRUE if the memory in user space. +** +** gctUINT32* Address +** Return hardware specific address. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_ConvertLogical( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + OUT gctUINT32 * Address + ) +{ + gctUINT32 address; + gceSTATUS status; + gctUINT32 baseAddress; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x InUserSpace=%d", + Hardware, Logical, InUserSpace); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + /* Convert logical address into a physical address. */ + if (InUserSpace) + { + gcmkONERROR(gckOS_UserLogicalToPhysical(Hardware->os, Logical, &address)); + } + else + { + gcmkONERROR(gckOS_GetPhysicalAddress(Hardware->os, Logical, &address)); + } + + /* For old MMU, get GPU address according to baseAddress. */ + if (Hardware->mmuVersion == 0) + { + gcmkONERROR(gckOS_GetBaseAddress(Hardware->os, &baseAddress)); + + /* Subtract base address to get a GPU address. */ + gcmkASSERT(address >= baseAddress); + address -= baseAddress; + } + + /* Return hardware specific address. */ + *Address = (Hardware->mmuVersion == 0) + ? ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))) << (0 ? 31:31))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))) << (0 ? 31:31))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 30:0) - (0 ? 30:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 30:0) - (0 ? 30:0) + 1))))))) << (0 ? 30:0))) | (((gctUINT32) ((gctUINT32) (address) & ((gctUINT32) ((((1 ? 30:0) - (0 ? 30:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 30:0) - (0 ? 30:0) + 1))))))) << (0 ? 30:0))) + : address; + + /* Success. */ + gcmkFOOTER_ARG("*Address=0x%08x", *Address); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_Interrupt +** +** Process an interrupt. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctBOOL InterruptValid +** If gcvTRUE, this function will read the interrupt acknowledge +** register, stores the data, and return whether or not the interrupt +** is ours or not. If gcvFALSE, this functions will read the interrupt +** acknowledge register and combine it with any stored value to handle +** the event notifications. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_Interrupt( + IN gckHARDWARE Hardware, +#if gcdMULTI_GPU + IN gctUINT CoreId, +#endif + IN gctBOOL InterruptValid + ) +{ + gckEVENT eventObj; + gctUINT32 data = 0; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x InterruptValid=%d", Hardware, InterruptValid); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Extract gckEVENT object. */ + eventObj = Hardware->kernel->eventObj; + gcmkVERIFY_OBJECT(eventObj, gcvOBJ_EVENT); + + if (InterruptValid) + { + /* Read AQIntrAcknowledge register. */ +#if gcdMULTI_GPU + if (Hardware->core == gcvCORE_MAJOR) + { + gcmkONERROR( + gckOS_ReadRegisterByCoreId(Hardware->os, + Hardware->core, + CoreId, + 0x00010, + &data)); + } + else + { + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00010, + &data)); + } +#else + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00010, + &data)); +#endif + + if (data == 0) + { + /* Not our interrupt. */ + status = gcvSTATUS_NOT_OUR_INTERRUPT; + } + else + { + +#if gcdINTERRUPT_STATISTIC + gckOS_AtomClearMask(Hardware->pendingEvent, data); +#endif + + /* Inform gckEVENT of the interrupt. */ + status = gckEVENT_Interrupt(eventObj, +#if gcdMULTI_GPU + CoreId, +#endif + data); + } + } + else + { + /* Handle events. */ + status = gckEVENT_Notify(eventObj, 0); + } + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_QueryCommandBuffer +** +** Query the command buffer alignment and number of reserved bytes. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** OUTPUT: +** +** gctSIZE_T * Alignment +** Pointer to a variable receiving the alignment for each command. +** +** gctSIZE_T * ReservedHead +** Pointer to a variable receiving the number of reserved bytes at the +** head of each command buffer. +** +** gctSIZE_T * ReservedTail +** Pointer to a variable receiving the number of bytes reserved at the +** tail of each command buffer. +*/ +gceSTATUS +gckHARDWARE_QueryCommandBuffer( + IN gckHARDWARE Hardware, + OUT gctUINT32 * Alignment, + OUT gctUINT32 * ReservedHead, + OUT gctUINT32 * ReservedTail + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (Alignment != gcvNULL) + { + /* Align every 8 bytes. */ + *Alignment = 8; + } + + if (ReservedHead != gcvNULL) + { + /* Reserve space for SelectPipe(). */ + *ReservedHead = 32; + } + + if (ReservedTail != gcvNULL) + { + /* Reserve space for Link(). */ + *ReservedTail = 8; + } + + /* Success. */ + gcmkFOOTER_ARG("*Alignment=%lu *ReservedHead=%lu *ReservedTail=%lu", + gcmOPT_VALUE(Alignment), gcmOPT_VALUE(ReservedHead), + gcmOPT_VALUE(ReservedTail)); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_QuerySystemMemory +** +** Query the command buffer alignment and number of reserved bytes. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** OUTPUT: +** +** gctSIZE_T * SystemSize +** Pointer to a variable that receives the maximum size of the system +** memory. +** +** gctUINT32 * SystemBaseAddress +** Poinetr to a variable that receives the base address for system +** memory. +*/ +gceSTATUS +gckHARDWARE_QuerySystemMemory( + IN gckHARDWARE Hardware, + OUT gctSIZE_T * SystemSize, + OUT gctUINT32 * SystemBaseAddress + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (SystemSize != gcvNULL) + { + /* Maximum system memory can be 2GB. */ + *SystemSize = 1U << 31; + } + + if (SystemBaseAddress != gcvNULL) + { + /* Set system memory base address. */ + *SystemBaseAddress = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))) << (0 ? 31:31))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))) << (0 ? 31:31))); + } + + /* Success. */ + gcmkFOOTER_ARG("*SystemSize=%lu *SystemBaseAddress=%lu", + gcmOPT_VALUE(SystemSize), gcmOPT_VALUE(SystemBaseAddress)); + return gcvSTATUS_OK; +} + +#if gcdENABLE_3D +/******************************************************************************* +** +** gckHARDWARE_QueryShaderCaps +** +** Query the shader capabilities. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** gctUINT * VertexUniforms +** Pointer to a variable receiving the number of uniforms in the vertex +** shader. +** +** gctUINT * FragmentUniforms +** Pointer to a variable receiving the number of uniforms in the +** fragment shader. +** +** gctBOOL * UnifiedUnforms +** Pointer to a variable receiving whether the uniformas are unified. +*/ +gceSTATUS +gckHARDWARE_QueryShaderCaps( + IN gckHARDWARE Hardware, + OUT gctUINT * VertexUniforms, + OUT gctUINT * FragmentUniforms, + OUT gctBOOL * UnifiedUnforms + ) +{ + gctBOOL unifiedConst; + gctUINT32 vsConstMax; + gctUINT32 psConstMax; + gctUINT32 vsConstBase; + gctUINT32 psConstBase; + gctUINT32 ConstMax; + + gcmkHEADER_ARG("Hardware=0x%x VertexUniforms=0x%x " + "FragmentUniforms=0x%x UnifiedUnforms=0x%x", + Hardware, VertexUniforms, + FragmentUniforms, UnifiedUnforms); + + {if (Hardware->identity.numConstants > 256){ unifiedConst = gcvTRUE; vsConstBase = 0xC000; psConstBase = 0xC000; ConstMax = Hardware->identity.numConstants; vsConstMax = 256; psConstMax = ConstMax - vsConstMax;}else if (Hardware->identity.numConstants == 256){ if (Hardware->identity.chipModel == gcv2000 && Hardware->identity.chipRevision == 0x5118) { unifiedConst = gcvFALSE; vsConstBase = 0x1400; psConstBase = 0x1C00; vsConstMax = 256; psConstMax = 64; ConstMax = 320; } else { unifiedConst = gcvFALSE; vsConstBase = 0x1400; psConstBase = 0x1C00; vsConstMax = 256; psConstMax = 256; ConstMax = 512; }}else{ unifiedConst = gcvFALSE; vsConstBase = 0x1400; psConstBase = 0x1C00; vsConstMax = 168; psConstMax = 64; ConstMax = 232;}}; + + if (VertexUniforms != gcvNULL) + { + /* Return the vs shader const count. */ + *VertexUniforms = vsConstMax; + } + + if (FragmentUniforms != gcvNULL) + { + /* Return the ps shader const count. */ + *FragmentUniforms = psConstMax; + } + + if (UnifiedUnforms != gcvNULL) + { + /* Return whether the uniformas are unified. */ + *UnifiedUnforms = unifiedConst; + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif + +/******************************************************************************* +** +** gckHARDWARE_SetMMU +** +** Set the page table base address. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Logical address of the page table. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_SetMMU( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical + ) +{ + gceSTATUS status; + gctUINT32 address = 0; + gctUINT32 idle; + gctUINT32 timer = 0, delay = 1; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x", Hardware, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (Hardware->mmuVersion == 0) + { + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + /* Convert the logical address into physical address. */ + gcmkONERROR(gckOS_GetPhysicalAddress(Hardware->os, Logical, &address)); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Setting page table to 0x%08X", + address); + + /* Write the AQMemoryFePageTable register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00400, + address)); + + /* Write the AQMemoryRaPageTable register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00410, + address)); + + /* Write the AQMemoryTxPageTable register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00404, + address)); + + + /* Write the AQMemoryPePageTable register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00408, + address)); + + /* Write the AQMemoryPezPageTable register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0040C, + address)); + } + else if (Hardware->enableMMU == gcvTRUE) + { + /* Execute prepared command sequence. */ + gcmkONERROR(gckHARDWARE_Execute( + Hardware, + Hardware->functions[gcvHARDWARE_FUNCTION_MMU].address, + Hardware->functions[gcvHARDWARE_FUNCTION_MMU].bytes + )); + + /* Wait until MMU configure finishes. */ + do + { + gckOS_Delay(Hardware->os, delay); + + gcmkONERROR(gckOS_ReadRegisterEx( + Hardware->os, + Hardware->core, + 0x00004, + &idle)); + + timer += delay; + delay *= 2; + +#if gcdGPU_TIMEOUT + if (timer >= Hardware->kernel->timeOut) + { + /* Even if hardware is not reset correctly, let software + ** continue to avoid software stuck. Software will timeout again + ** and try to recover GPU in next timeout. + */ + gcmkONERROR(gcvSTATUS_DEVICE); + } +#endif + } + while (!(((((gctUINT32) (idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) )); + + /* Enable MMU. */ + gcmkONERROR(gckOS_WriteRegisterEx( + Hardware->os, + Hardware->core, + 0x0018C, + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (gcvTRUE) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + )); + } + + /* Return the status. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_FlushMMU +** +** Flush the page table. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_FlushMMU( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gckCOMMAND command; + gctUINT32_PTR buffer; + gctUINT32 bufferSize; + gctPOINTER pointer = gcvNULL; + gctUINT32 flushSize; + gctUINT32 count; + gctUINT32 physical; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Verify the gckCOMMAND object pointer. */ + command = Hardware->kernel->command; + + /* Flush the memory controller. */ + if (Hardware->mmuVersion == 0) + { + gcmkONERROR(gckCOMMAND_Reserve( + command, 8, &pointer, &bufferSize + )); + + buffer = (gctUINT32_PTR) pointer; + + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E04) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); + + gcmkONERROR(gckCOMMAND_Execute(command, 8)); + } + else + { + flushSize = 16 * 4; + + gcmkONERROR(gckCOMMAND_Reserve( + command, flushSize, &pointer, &bufferSize + )); + + buffer = (gctUINT32_PTR) pointer; + + count = ((gctUINT)bufferSize - flushSize + 7) >> 3; + + gcmkONERROR(gckOS_GetPhysicalAddress(command->os, buffer, &physical)); + + /* Flush cache. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + + /* Arm the PE-FE Semaphore. */ + buffer[2] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[3] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* STALL FE until PE is done flushing. */ + buffer[4] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + buffer[5] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* LINK to next slot to flush FE FIFO. */ + buffer[6] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (4) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[7] + = physical + 8 * gcmSIZEOF(gctUINT32); + + /* Flush MMU cache. */ + buffer[8] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0061) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + buffer[9] + = (((((gctUINT32) (~0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) & ((((gctUINT32) (~0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7)))); + + /* Arm the PE-FE Semaphore. */ + buffer[10] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[11] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* STALL FE until PE is done flushing. */ + buffer[12] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + buffer[13] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* LINK to next slot to flush FE FIFO. */ + buffer[14] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (count) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[15] + = physical + flushSize; + + gcmkONERROR(gckCOMMAND_Execute(command, flushSize)); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_SetMMUStates( + IN gckHARDWARE Hardware, + IN gctPOINTER MtlbAddress, + IN gceMMU_MODE Mode, + IN gctPOINTER SafeAddress, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ) +{ + gceSTATUS status; + gctUINT32 config, address; + gctUINT32_PTR buffer; + gctBOOL ace; + gctUINT32 reserveBytes = 16 + 4 * 4; + + gctBOOL config2D; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Hardware->mmuVersion != 0); + + ace = gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_ACE); + + if (ace) + { + reserveBytes += 8; + } + + config2D = gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_PIPE_3D) + && gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_PIPE_2D); + + if (config2D) + { + reserveBytes += + /* Pipe Select. */ + 4 * 4 + /* Configure MMU States. */ + + 4 * 4 + /* Semaphore stall */ + + 4 * 8; + } + + /* Convert logical address into physical address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Hardware->os, MtlbAddress, &config)); + + gcmkONERROR( + gckOS_GetPhysicalAddress(Hardware->os, SafeAddress, &address)); + + if (address & 0x3F) + { + gcmkONERROR(gcvSTATUS_NOT_ALIGNED); + } + + switch (Mode) + { + case gcvMMU_MODE_1K: + if (config & 0x3FF) + { + gcmkONERROR(gcvSTATUS_NOT_ALIGNED); + } + + config |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + break; + + case gcvMMU_MODE_4K: + if (config & 0xFFF) + { + gcmkONERROR(gcvSTATUS_NOT_ALIGNED); + } + + config |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + break; + + default: + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (Logical != gcvNULL) + { + buffer = Logical; + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0061) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = config; + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0060) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = address; + + if (ace) + { + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0068) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = 0; + } + + do{*buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8)));} while(0);; + + if (config2D) + { + /* LoadState(AQPipeSelect, 1), pipe. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E00) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = 0x1; + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0061) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = config; + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0060) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = address; + + do{*buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8)));} while(0);; + + /* LoadState(AQPipeSelect, 1), pipe. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E00) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ = 0x0; + + do{*buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); *buffer++ = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8)));} while(0);; + } + + } + + if (Bytes != gcvNULL) + { + *Bytes = reserveBytes; + } + + /* Return the status. */ + gcmkFOOTER_NO(); + return status; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdPROCESS_ADDRESS_SPACE +/******************************************************************************* +** +** gckHARDWARE_ConfigMMU +** +** Append a MMU Configuration command sequence at the specified location in the command +** queue. That command sequence consists of mmu configuration, LINK and WAIT/LINK. +** LINK is fetched and paresed with new mmu configuration. +** +** If MMU Configuration is not changed between commit, change last WAIT/LINK to +** link to ENTRY. +** +** -+-----------+-----------+----------------------------------------- +** | WAIT/LINK | WAIT/LINK | +** -+-----------+-----------+----------------------------------------- +** | /|\ +** \|/ | +** +--------------------+ +** | ENTRY | ... | LINK | +** +--------------------+ +** +** If MMU Configuration is changed between commit, change last WAIT/LINK to +** link to MMU CONFIGURATION command sequence, and there are an EVNET and +** an END at the end of this command sequence, when interrupt handler +** receives this event, it will start FE at ENTRY to continue the command +** buffer execution. +** +** -+-----------+-------------------+---------+---------+-----------+-- +** | WAIT/LINK | MMU CONFIGURATION | EVENT | END | WAIT/LINK | +** -+-----------+-------------------+---------+---------+-----------+-- +** | /|\ /|\ +** +-------------+ | +** +--------------------+ +** | ENTRY | ... | LINK | +** +--------------------+ +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command queue to append +** command sequence at or gcvNULL just to query the size of the +** command sequence. +** +** gctPOINTER MtlbLogical +** Pointer to the current Master TLB. +** +** gctUINT32 Offset +** Offset into command buffer required for alignment. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the command +** sequence. If 'Logical' is gcvNULL, this argument will be ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** by the command sequence. If 'Bytes' is gcvNULL, nothing will +** be returned. +** +** gctUINT32 * WaitLinkOffset +** Pointer to a variable that will receive the offset of the WAIT/LINK command +** from the specified logcial pointer. +** If 'WaitLinkOffset' is gcvNULL nothing will be returned. +** +** gctSIZE_T * WaitLinkBytes +** Pointer to a variable that will receive the number of bytes used by +** the WAIT command. +** If 'WaitLinkBytes' is gcvNULL nothing will be returned. +*/ +gceSTATUS +gckHARDWARE_ConfigMMU( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctPOINTER MtlbLogical, + IN gctUINT32 Offset, + IN OUT gctSIZE_T * Bytes, + OUT gctSIZE_T * WaitLinkOffset, + OUT gctSIZE_T * WaitLinkBytes + ) +{ + gceSTATUS status; + gctSIZE_T bytes, bytesAligned; + gctUINT32 config; + gctUINT32_PTR buffer = (gctUINT32_PTR) Logical; + gctUINT32 physical; + gctUINT32 event; + + gcmkHEADER_ARG("Hardware=0x%08X Logical=0x%08x MtlbLogical=0x%08X", + Hardware, Logical, MtlbLogical); + + bytes + /* Flush cache states. */ + = 18 * 4 + /* MMU configuration states. */ + + 6 * 4 + /* EVENT. */ + + 2 * 4 + /* END. */ + + 2 * 4 + /* WAIT/LINK. */ + + 4 * 4; + + /* Compute number of bytes required. */ + bytesAligned = gcmALIGN(Offset + bytes, 8) - Offset; + + if (buffer != gcvNULL) + { + if (MtlbLogical == gcvNULL) + { + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Get physical address of this command buffer segment. */ + gcmkONERROR(gckOS_GetPhysicalAddress(Hardware->os, buffer, &physical)); + + /* Get physical address of Master TLB. */ + gcmkONERROR(gckOS_GetPhysicalAddress(Hardware->os, MtlbLogical, &config)); + + config |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); + + /* Flush cache. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + + /* Flush tile status cache. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0594) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + /* Arm the PE-FE Semaphore. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* STALL FE until PE is done flushing. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* LINK to next slot to flush FE FIFO. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (4) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = physical + 10 * gcmSIZEOF(gctUINT32); + + /* Configure MMU. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0061) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ + = (((((gctUINT32) (~0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) & ((((gctUINT32) (~0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7)))); + + /* Arm the PE-FE Semaphore. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* STALL FE until PE is done flushing. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* LINK to next slot to flush FE FIFO. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (5) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = physical + 18 * 4; + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0061) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *buffer++ + = config; + + /* Arm the PE-FE Semaphore. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* STALL FE until PE is done flushing. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Event 29. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E01) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + event = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + event = ((((gctUINT32) (event)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (29) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))); + + *buffer++ + = event; + + /* Append END. */ + *buffer++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x02 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + } + + if (Bytes != gcvNULL) + { + *Bytes = bytesAligned; + } + + if (WaitLinkOffset != gcvNULL) + { + *WaitLinkOffset = bytes - 4 * 4; + } + + if (WaitLinkBytes != gcvNULL) + { +#if gcdMULTI_GPU + *WaitLinkBytes = 40; +#else + *WaitLinkBytes = 4 * 4; +#endif + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckHARDWARE_BuildVirtualAddress +** +** Build a virtual address. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gctUINT32 Index +** Index into page table. +** +** gctUINT32 Offset +** Offset into page. +** +** OUTPUT: +** +** gctUINT32 * Address +** Pointer to a variable receiving te hardware address. +*/ +gceSTATUS +gckHARDWARE_BuildVirtualAddress( + IN gckHARDWARE Hardware, + IN gctUINT32 Index, + IN gctUINT32 Offset, + OUT gctUINT32 * Address + ) +{ + gcmkHEADER_ARG("Hardware=0x%x Index=%u Offset=%u", Hardware, Index, Offset); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + /* Build virtual address. */ + *Address = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))) << (0 ? 31:31))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))) << (0 ? 31:31))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 30:0) - (0 ? 30:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 30:0) - (0 ? 30:0) + 1))))))) << (0 ? 30:0))) | (((gctUINT32) ((gctUINT32) (Offset | (Index << 12)) & ((gctUINT32) ((((1 ? 30:0) - (0 ? 30:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 30:0) - (0 ? 30:0) + 1))))))) << (0 ? 30:0))); + + /* Success. */ + gcmkFOOTER_ARG("*Address=0x%08x", *Address); + return gcvSTATUS_OK; +} + +gceSTATUS +gckHARDWARE_GetIdle( + IN gckHARDWARE Hardware, + IN gctBOOL Wait, + OUT gctUINT32 * Data + ) +{ + gceSTATUS status; + gctUINT32 idle = 0; + gctINT retry, poll, pollCount; + gctUINT32 address; + + gcmkHEADER_ARG("Hardware=0x%x Wait=%d", Hardware, Wait); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Data != gcvNULL); + + + /* If we have to wait, try 100 polls per millisecond. */ + pollCount = Wait ? 100 : 1; + + /* At most, try for 1 second. */ + for (retry = 0; retry < 1000; ++retry) + { + /* If we have to wait, try 100 polls per millisecond. */ + for (poll = pollCount; poll > 0; --poll) + { + /* Read register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00004, &idle)); + + /* Read the current FE address. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00664, + &address)); + + + /* See if we have to wait for FE idle. */ + if (_IsGPUIdle(idle) + && (address == Hardware->lastEnd + 8) + ) + { + /* FE is idle. */ + break; + } + } + + /* Check if we need to wait for FE and FE is busy. */ + if (Wait && !_IsGPUIdle(idle)) + { + /* Wait a little. */ + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "%s: Waiting for idle: 0x%08X", + __FUNCTION__, idle); + + gcmkVERIFY_OK(gckOS_Delay(Hardware->os, 1)); + } + else + { + break; + } + } + + /* Return idle to caller. */ + *Data = idle; + +#if defined(EMULATOR) + /* Wait a little while until CModel FE gets END. + * END is supposed to be appended by caller. + */ + gckOS_Delay(gcvNULL, 100); +#endif + + /* Success. */ + gcmkFOOTER_ARG("*Data=0x%08x", *Data); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/* Flush the caches. */ +gceSTATUS +gckHARDWARE_Flush( + IN gckHARDWARE Hardware, + IN gceKERNEL_FLUSH Flush, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ) +{ + gctUINT32 pipe; + gctUINT32 flush = 0; + gctBOOL flushTileStatus; + gctUINT32_PTR logical = (gctUINT32_PTR) Logical; + gceSTATUS status; + gctUINT32 reserveBytes + /* Semaphore/Stall */ + = 4 * gcmSIZEOF(gctUINT32); + + gcmkHEADER_ARG("Hardware=0x%x Flush=0x%x Logical=0x%x *Bytes=%lu", + Hardware, Flush, Logical, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Get current pipe. */ + pipe = Hardware->kernel->command->pipeSelect; + + /* Flush tile status cache. */ + flushTileStatus = Flush & gcvFLUSH_TILE_STATUS; + + /* Flush 3D color cache. */ + if ((Flush & gcvFLUSH_COLOR) && (pipe == 0x0)) + { + flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))); + } + + /* Flush 3D depth cache. */ + if ((Flush & gcvFLUSH_DEPTH) && (pipe == 0x0)) + { + flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + } + + /* Flush 3D texture cache. */ + if ((Flush & gcvFLUSH_TEXTURE) && (pipe == 0x0)) + { + flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))); + } + + /* Flush 2D cache. */ + if ((Flush & gcvFLUSH_2D) && (pipe == 0x1)) + { + flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))); + } + +#if gcdMULTI_GPU + /* Flush L2 cache. */ + if ((Flush & gcvFLUSH_L2) && (pipe == 0x0)) + { + flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + } +#endif + + /* Determine reserve bytes. */ + if (flush) + { + reserveBytes += 2 * gcmSIZEOF(gctUINT32); + } + + if (flushTileStatus) + { + reserveBytes += 2 * gcmSIZEOF(gctUINT32); + } + + /* See if there is a valid flush. */ + if ((flush == 0) && (flushTileStatus == gcvFALSE)) + { + if (Bytes != gcvNULL) + { + /* No bytes required. */ + *Bytes = 0; + } + } + + else + { + /* Copy to command queue. */ + if (Logical != gcvNULL) + { + if (*Bytes < reserveBytes) + { + /* Command queue too small. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + + if (flush) + { + /* Append LOAD_STATE to AQFlush. */ + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *logical++ + = flush; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: FLUSH 0x%x", logical - 1, flush); + } + + if (flushTileStatus) + { + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0594) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "0x%x: FLUSH TILE STATUS 0x%x", logical - 1, logical[-1]); + } + + /* Semaphore. */ + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E02) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + + /* Stall. */ + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x09 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + + *logical++ + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) (0x05 & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + } + + if (Bytes != gcvNULL) + { + /* bytes required. */ + *Bytes = reserveBytes; + } + } + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_SetFastClear( + IN gckHARDWARE Hardware, + IN gctINT Enable, + IN gctINT Compression + ) +{ +#if gcdENABLE_3D + gctUINT32 debug; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Enable=%d Compression=%d", + Hardware, Enable, Compression); + + /* Only process if fast clear is available. */ + if ((((((gctUINT32) (Hardware->identity.chipFeatures)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) )) + { + if (Enable == -1) + { + /* Determine automatic value for fast clear. */ + Enable = ((Hardware->identity.chipModel != gcv500) + || (Hardware->identity.chipRevision >= 3) + ) ? 1 : 0; + } + + if (Compression == -1) + { + /* Determine automatic value for compression. */ + Compression = Enable + & (((((gctUINT32) (Hardware->identity.chipFeatures)) >> (0 ? 5:5)) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1)))))) ); + } + + /* Read AQMemoryDebug register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00414, &debug)); + + /* Set fast clear bypass. */ + debug = ((((gctUINT32) (debug)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:20) - (0 ? 20:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:20) - (0 ? 20:20) + 1))))))) << (0 ? 20:20))) | (((gctUINT32) ((gctUINT32) (Enable == 0) & ((gctUINT32) ((((1 ? 20:20) - (0 ? 20:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:20) - (0 ? 20:20) + 1))))))) << (0 ? 20:20))); + + if ( + ((((gctUINT32) (Hardware->identity.chipMinorFeatures2)) >> (0 ? 27:27) & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) || + (Hardware->identity.chipModel >= gcv4000)) + { + /* Set compression bypass. */ + debug = ((((gctUINT32) (debug)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1))))))) << (0 ? 21:21))) | (((gctUINT32) ((gctUINT32) (Compression == 0) & ((gctUINT32) ((((1 ? 21:21) - (0 ? 21:21) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 21:21) - (0 ? 21:21) + 1))))))) << (0 ? 21:21))); + } + + /* Write back AQMemoryDebug register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00414, + debug)); + + /* Store fast clear and comprersison flags. */ + Hardware->allowFastClear = Enable; + Hardware->allowCompression = Compression; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "FastClear=%d Compression=%d", Enable, Compression); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +#else + return gcvSTATUS_OK; +#endif +} + +typedef enum +{ + gcvPOWER_FLAG_INITIALIZE = 1 << 0, + gcvPOWER_FLAG_STALL = 1 << 1, + gcvPOWER_FLAG_STOP = 1 << 2, + gcvPOWER_FLAG_START = 1 << 3, + gcvPOWER_FLAG_RELEASE = 1 << 4, + gcvPOWER_FLAG_DELAY = 1 << 5, + gcvPOWER_FLAG_SAVE = 1 << 6, + gcvPOWER_FLAG_ACQUIRE = 1 << 7, + gcvPOWER_FLAG_POWER_OFF = 1 << 8, + gcvPOWER_FLAG_CLOCK_OFF = 1 << 9, + gcvPOWER_FLAG_CLOCK_ON = 1 << 10, +} +gcePOWER_FLAGS; + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +static gctCONST_STRING +_PowerEnum(gceCHIPPOWERSTATE State) +{ + const gctCONST_STRING states[] = + { + gcmSTRING(gcvPOWER_ON), + gcmSTRING(gcvPOWER_OFF), + gcmSTRING(gcvPOWER_IDLE), + gcmSTRING(gcvPOWER_SUSPEND), + gcmSTRING(gcvPOWER_SUSPEND_ATPOWERON), + gcmSTRING(gcvPOWER_OFF_ATPOWERON), + gcmSTRING(gcvPOWER_IDLE_BROADCAST), + gcmSTRING(gcvPOWER_SUSPEND_BROADCAST), + gcmSTRING(gcvPOWER_OFF_BROADCAST), + gcmSTRING(gcvPOWER_OFF_RECOVERY), + gcmSTRING(gcvPOWER_OFF_TIMEOUT), + gcmSTRING(gcvPOWER_ON_AUTO) + }; + + if ((State >= gcvPOWER_ON) && (State <= gcvPOWER_ON_AUTO)) + { + return states[State - gcvPOWER_ON]; + } + + return "unknown"; +} +#endif + +/******************************************************************************* +** +** gckHARDWARE_SetPowerManagementState +** +** Set GPU to a specified power state. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gceCHIPPOWERSTATE State +** Power State. +** +*/ +gceSTATUS +gckHARDWARE_SetPowerManagementState( + IN gckHARDWARE Hardware, + IN gceCHIPPOWERSTATE State + ) +{ + gceSTATUS status; + gckCOMMAND command = gcvNULL; + gckOS os; + gctUINT flag, clock; + gctPOINTER buffer; + gctUINT32 bytes, requested; + gctBOOL acquired = gcvFALSE; + gctBOOL mutexAcquired = gcvFALSE; + gctBOOL stall = gcvTRUE; + gctBOOL broadcast = gcvFALSE; +#if gcdPOWEROFF_TIMEOUT + gctBOOL timeout = gcvFALSE; + gctBOOL isAfter = gcvFALSE; + gctUINT32 currentTime; +#endif + gctUINT32 process, thread; + gctBOOL commitEntered = gcvFALSE; + gctBOOL commandStarted = gcvFALSE; + gctBOOL isrStarted = gcvFALSE; + +#if gcdENABLE_PROFILING + gctUINT64 time, freq, mutexTime, onTime, stallTime, stopTime, delayTime, + initTime, offTime, startTime, totalTime; +#endif + gctBOOL global = gcvFALSE; + gctBOOL globalAcquired = gcvFALSE; + gctBOOL configMmu = gcvFALSE; + + /* State transition flags. */ + static const gctUINT flags[4][4] = + { + /* gcvPOWER_ON */ + { /* ON */ 0, + /* OFF */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STALL | + gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_POWER_OFF | + gcvPOWER_FLAG_CLOCK_OFF, + /* IDLE */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STALL, + /* SUSPEND */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STALL | + gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_CLOCK_OFF, + }, + + /* gcvPOWER_OFF */ + { /* ON */ gcvPOWER_FLAG_INITIALIZE | + gcvPOWER_FLAG_START | + gcvPOWER_FLAG_RELEASE | + gcvPOWER_FLAG_DELAY, + /* OFF */ 0, + /* IDLE */ gcvPOWER_FLAG_INITIALIZE | + gcvPOWER_FLAG_START | + gcvPOWER_FLAG_DELAY, + /* SUSPEND */ gcvPOWER_FLAG_INITIALIZE | + gcvPOWER_FLAG_CLOCK_OFF, + }, + + /* gcvPOWER_IDLE */ + { /* ON */ gcvPOWER_FLAG_RELEASE, + /* OFF */ gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_POWER_OFF | + gcvPOWER_FLAG_CLOCK_OFF, + /* IDLE */ 0, + /* SUSPEND */ gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_CLOCK_OFF, + }, + + /* gcvPOWER_SUSPEND */ + { /* ON */ gcvPOWER_FLAG_START | + gcvPOWER_FLAG_RELEASE | + gcvPOWER_FLAG_DELAY | + gcvPOWER_FLAG_CLOCK_ON, + /* OFF */ gcvPOWER_FLAG_SAVE | + gcvPOWER_FLAG_POWER_OFF | + gcvPOWER_FLAG_CLOCK_OFF, + /* IDLE */ gcvPOWER_FLAG_START | + gcvPOWER_FLAG_DELAY | + gcvPOWER_FLAG_CLOCK_ON, + /* SUSPEND */ 0, + }, + }; + + /* Clocks. */ + static const gctUINT clocks[4] = + { + /* gcvPOWER_ON */ + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (64) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))), + + /* gcvPOWER_OFF */ + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))), + + /* gcvPOWER_IDLE */ + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))), + + /* gcvPOWER_SUSPEND */ + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))), + }; + + gcmkHEADER_ARG("Hardware=0x%x State=%d", Hardware, State); +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Switching to power state %d(%s)", + State, _PowerEnum(State)); +#endif + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Get the gckOS object pointer. */ + os = Hardware->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Get the gckCOMMAND object pointer. */ + gcmkVERIFY_OBJECT(Hardware->kernel, gcvOBJ_KERNEL); + command = Hardware->kernel->command; + gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND); + + /* Start profiler. */ + gcmkPROFILE_INIT(freq, time); + + /* Convert the broadcast power state. */ + switch (State) + { + case gcvPOWER_SUSPEND_ATPOWERON: + /* Convert to SUSPEND and don't wait for STALL. */ + State = gcvPOWER_SUSPEND; + stall = gcvFALSE; + break; + + case gcvPOWER_OFF_ATPOWERON: + /* Convert to OFF and don't wait for STALL. */ + State = gcvPOWER_OFF; + stall = gcvFALSE; + break; + + case gcvPOWER_IDLE_BROADCAST: + /* Convert to IDLE and note we are inside broadcast. */ + State = gcvPOWER_IDLE; + broadcast = gcvTRUE; + break; + + case gcvPOWER_SUSPEND_BROADCAST: + /* Convert to SUSPEND and note we are inside broadcast. */ + State = gcvPOWER_SUSPEND; + broadcast = gcvTRUE; + break; + + case gcvPOWER_OFF_BROADCAST: + /* Convert to OFF and note we are inside broadcast. */ + State = gcvPOWER_OFF; + broadcast = gcvTRUE; + break; + + case gcvPOWER_OFF_RECOVERY: + /* Convert to OFF and note we are inside recovery. */ + State = gcvPOWER_OFF; + stall = gcvFALSE; + broadcast = gcvTRUE; + break; + + case gcvPOWER_ON_AUTO: + /* Convert to ON and note we are inside recovery. */ + State = gcvPOWER_ON; + break; + + case gcvPOWER_ON: + case gcvPOWER_IDLE: + case gcvPOWER_SUSPEND: + case gcvPOWER_OFF: + /* Mark as global power management. */ + global = gcvTRUE; + break; + +#if gcdPOWEROFF_TIMEOUT + case gcvPOWER_OFF_TIMEOUT: + /* Convert to OFF and note we are inside broadcast. */ + State = gcvPOWER_OFF; + broadcast = gcvTRUE; + /* Check time out */ + timeout = gcvTRUE; + break; +#endif + + default: + break; + } + + if (Hardware->powerManagement == gcvFALSE + && State != gcvPOWER_ON + ) + { + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Get current process and thread IDs. */ + gcmkONERROR(gckOS_GetProcessID(&process)); + gcmkONERROR(gckOS_GetThreadID(&thread)); + + if (broadcast) + { + /* Try to acquire the power mutex. */ + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0); + + if (status == gcvSTATUS_TIMEOUT) + { + /* Check if we already own this mutex. */ + if ((Hardware->powerProcess == process) + && (Hardware->powerThread == thread) + ) + { + /* Bail out on recursive power management. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + else if (State != gcvPOWER_ON) + { + /* Called from IST, + ** so waiting here will cause deadlock, + ** if lock holder call gckCOMMAND_Stall() */ + status = gcvSTATUS_INVALID_REQUEST; + goto OnError; + } + else + { + /* Acquire the power mutex. */ + gcmkONERROR(gckOS_AcquireMutex(os, + Hardware->powerMutex, + gcvINFINITE)); + } + } + } + else + { + /* Acquire the power mutex. */ + gcmkONERROR(gckOS_AcquireMutex(os, Hardware->powerMutex, gcvINFINITE)); + } + + /* Get time until mtuex acquired. */ + gcmkPROFILE_QUERY(time, mutexTime); + + Hardware->powerProcess = process; + Hardware->powerThread = thread; + mutexAcquired = gcvTRUE; + + /* Grab control flags and clock. */ + flag = flags[Hardware->chipPowerState][State]; + clock = clocks[State]; + +#if gcdENABLE_FSCALE_VAL_ADJUST + if (State == gcvPOWER_ON) + { + clock = ((((gctUINT32) (clock)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (Hardware->powerOnFscaleVal) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))); + } +#endif + + if (State == gcvPOWER_SUSPEND && Hardware->chipPowerState == gcvPOWER_OFF && broadcast) + { +#if gcdPOWER_SUSPEND_WHEN_IDLE + /* Do nothing */ + + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +#else + /* Clock should be on when switch power from off to suspend */ + clock = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) ; +#endif + } + +#if gcdPOWEROFF_TIMEOUT + if (timeout) + { + gcmkONERROR(gckOS_GetTicks(¤tTime)); + + gcmkONERROR( + gckOS_TicksAfter(Hardware->powerOffTime, currentTime, &isAfter)); + + /* powerOffTime is pushed forward, give up.*/ + if (isAfter + /* Expect a transition start from IDLE or SUSPEND. */ + || (Hardware->chipPowerState == gcvPOWER_ON) + || (Hardware->chipPowerState == gcvPOWER_OFF) + ) + { + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* No need to do anything. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Power Off GPU[%d] at %u [supposed to be at %u]", + Hardware->core, currentTime, Hardware->powerOffTime); + } +#endif + + if (flag == 0) + { + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* No need to do anything. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* If this is an internal power management, we have to check if we can grab + ** the global power semaphore. If we cannot, we have to wait until the + ** external world changes power management. */ + if (!global) + { + /* Try to acquire the global semaphore. */ + status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore); + if (status == gcvSTATUS_TIMEOUT) + { + if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND) + { + /* Called from thread routine which should NEVER sleep.*/ + gcmkONERROR(gcvSTATUS_INVALID_REQUEST); + } + + /* Release the power mutex. */ + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Releasing the power mutex."); + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + mutexAcquired = gcvFALSE; + + /* Wait for the semaphore. */ + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Waiting for global semaphore."); + gcmkONERROR(gckOS_AcquireSemaphore(os, Hardware->globalSemaphore)); + globalAcquired = gcvTRUE; + + /* Acquire the power mutex. */ + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Reacquiring the power mutex."); + gcmkONERROR(gckOS_AcquireMutex(os, + Hardware->powerMutex, + gcvINFINITE)); + mutexAcquired = gcvTRUE; + + /* chipPowerState may be changed by external world during the time + ** we give up powerMutex, so updating flag now is necessary. */ + flag = flags[Hardware->chipPowerState][State]; + + if (flag == 0) + { + gcmkONERROR(gckOS_ReleaseSemaphore(os, Hardware->globalSemaphore)); + globalAcquired = gcvFALSE; + + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + mutexAcquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + } + else + { + /* Error. */ + gcmkONERROR(status); + } + + /* Release the global semaphore again. */ + gcmkONERROR(gckOS_ReleaseSemaphore(os, Hardware->globalSemaphore)); + globalAcquired = gcvFALSE; + } + else + { + if (State == gcvPOWER_OFF || State == gcvPOWER_SUSPEND || State == gcvPOWER_IDLE) + { + /* Acquire the global semaphore if it has not been acquired. */ + status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore); + if (status == gcvSTATUS_OK) + { + globalAcquired = gcvTRUE; + } + else if (status != gcvSTATUS_TIMEOUT) + { + /* Other errors. */ + gcmkONERROR(status); + } + /* Ignore gcvSTATUS_TIMEOUT and leave globalAcquired as gcvFALSE. + ** gcvSTATUS_TIMEOUT means global semaphore has already + ** been acquired before this operation, so even if we fail, + ** we should not release it in our error handling. It should be + ** released by the next successful global gcvPOWER_ON. */ + } + + /* Global power management can't be aborted, so sync with + ** proceeding last commit. */ + if (flag & gcvPOWER_FLAG_ACQUIRE) + { + /* Acquire the power management semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(os, command->powerSemaphore)); + acquired = gcvTRUE; + + /* avoid acquiring again. */ + flag &= ~gcvPOWER_FLAG_ACQUIRE; + } + } + + if (flag & (gcvPOWER_FLAG_INITIALIZE | gcvPOWER_FLAG_CLOCK_ON)) + { + /* Turn on the power. */ + gcmkONERROR(gckOS_SetGPUPower(os, Hardware->core, gcvTRUE, gcvTRUE)); + + /* Mark clock and power as enabled. */ + Hardware->clockState = gcvTRUE; + Hardware->powerState = gcvTRUE; + + for (;;) + { + /* Check if GPU is present and awake. */ + status = _IsGPUPresent(Hardware); + + /* Check if the GPU is not responding. */ + if (status == gcvSTATUS_GPU_NOT_RESPONDING) + { + /* Turn off the power and clock. */ + gcmkONERROR(gckOS_SetGPUPower(os, Hardware->core, gcvFALSE, gcvFALSE)); + + Hardware->clockState = gcvFALSE; + Hardware->powerState = gcvFALSE; + + /* Wait a little. */ + gckOS_Delay(os, 1); + + /* Turn on the power and clock. */ + gcmkONERROR(gckOS_SetGPUPower(os, Hardware->core, gcvTRUE, gcvTRUE)); + + Hardware->clockState = gcvTRUE; + Hardware->powerState = gcvTRUE; + + /* We need to initialize the hardware and start the command + * processor. */ + flag |= gcvPOWER_FLAG_INITIALIZE | gcvPOWER_FLAG_START; + } + else + { + /* Test for error. */ + gcmkONERROR(status); + + /* Break out of loop. */ + break; + } + } + } + + /* Get time until powered on. */ + gcmkPROFILE_QUERY(time, onTime); + + if ((flag & gcvPOWER_FLAG_STALL) && stall) + { + gctBOOL idle; + gctINT32 atomValue; + + /* For global operation, all pending commits have already been + ** blocked by globalSemaphore or powerSemaphore.*/ + if (!global) + { + /* Check commit atom. */ + gcmkONERROR(gckOS_AtomGet(os, command->atomCommit, &atomValue)); + + if (atomValue > 0) + { + /* Commits are pending - abort power management. */ + status = broadcast ? gcvSTATUS_CHIP_NOT_READY + : gcvSTATUS_MORE_DATA; + goto OnError; + } + } + + if (broadcast) + { + /* Check for idle. */ + gcmkONERROR(gckHARDWARE_QueryIdle(Hardware, &idle)); + + if (!idle) + { + status = gcvSTATUS_CHIP_NOT_READY; + goto OnError; + } + } + + else + { + /* Acquire the command queue. */ + gcmkONERROR(gckCOMMAND_EnterCommit(command, gcvTRUE)); + commitEntered = gcvTRUE; + + /* Get the size of the flush command. */ + gcmkONERROR(gckHARDWARE_Flush(Hardware, + gcvFLUSH_ALL, + gcvNULL, + &requested)); + + /* Reserve space in the command queue. */ + gcmkONERROR(gckCOMMAND_Reserve(command, + requested, + &buffer, + &bytes)); + + /* Append a flush. */ + gcmkONERROR(gckHARDWARE_Flush( + Hardware, gcvFLUSH_ALL, buffer, &bytes + )); + + /* Execute the command queue. */ + gcmkONERROR(gckCOMMAND_Execute(command, requested)); + + /* Release the command queue. */ + gcmkONERROR(gckCOMMAND_ExitCommit(command, gcvTRUE)); + commitEntered = gcvFALSE; + + /* Wait to finish all commands. */ +#if gcdMULTI_GPU + gcmkONERROR(gckCOMMAND_Stall(command, gcvTRUE, gcvCORE_3D_ALL_MASK)); +#else + gcmkONERROR(gckCOMMAND_Stall(command, gcvTRUE)); +#endif + } + } + + /* Get time until stalled. */ + gcmkPROFILE_QUERY(time, stallTime); + + if (flag & gcvPOWER_FLAG_ACQUIRE) + { + /* Acquire the power management semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(os, command->powerSemaphore)); + acquired = gcvTRUE; + } + + if (flag & gcvPOWER_FLAG_STOP) + { + /* Stop the command parser. */ + gcmkONERROR(gckCOMMAND_Stop(command, gcvFALSE)); + + /* Stop the Isr. */ + if (Hardware->stopIsr) + { + gcmkONERROR(Hardware->stopIsr(Hardware->isrContext)); + } + } + + /* Flush Cache before Power Off. */ + if (flag & gcvPOWER_FLAG_POWER_OFF) + { + if (Hardware->clockState == gcvFALSE) + { + /* Turn off the GPU power. */ + gcmkONERROR( + gckOS_SetGPUPower(os, + Hardware->core, + gcvTRUE, + gcvTRUE)); + + Hardware->clockState = gcvTRUE; + + if (gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_DYNAMIC_FREQUENCY_SCALING) != gcvTRUE) + { + /* Write the clock control register. */ + gcmkONERROR(gckOS_WriteRegisterEx(os, + Hardware->core, + 0x00000, + clocks[0])); + + /* Done loading the frequency scaler. */ + gcmkONERROR(gckOS_WriteRegisterEx(os, + Hardware->core, + 0x00000, + ((((gctUINT32) (clocks[0])) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))))); + } + } + + gcmkONERROR(gckCOMMAND_Start(command)); + + gcmkONERROR(_FlushCache(Hardware, command)); + + gckOS_Delay(gcvNULL, 1); + + /* Stop the command parser. */ + gcmkONERROR(gckCOMMAND_Stop(command, gcvFALSE)); + + flag |= gcvPOWER_FLAG_CLOCK_OFF; + } + + /* Get time until stopped. */ + gcmkPROFILE_QUERY(time, stopTime); + + /* Only process this when hardware is enabled. */ + if (Hardware->clockState && Hardware->powerState + /* Don't touch clock control if dynamic frequency scaling is available. */ + && gckHARDWARE_IsFeatureAvailable(Hardware, gcvFEATURE_DYNAMIC_FREQUENCY_SCALING) != gcvTRUE + ) + { + if (flag & (gcvPOWER_FLAG_POWER_OFF | gcvPOWER_FLAG_CLOCK_OFF)) + { + if (Hardware->identity.chipModel == gcv4000 + && ((Hardware->identity.chipRevision == 0x5208) || (Hardware->identity.chipRevision == 0x5222))) + { + clock &= ~2U; + } + } + + /* Write the clock control register. */ + gcmkONERROR(gckOS_WriteRegisterEx(os, + Hardware->core, + 0x00000, + clock)); + + /* Done loading the frequency scaler. */ + gcmkONERROR(gckOS_WriteRegisterEx(os, + Hardware->core, + 0x00000, + ((((gctUINT32) (clock)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))))); + } + + if (flag & gcvPOWER_FLAG_DELAY) + { + /* Wait for the specified amount of time to settle coming back from + ** power-off or suspend state. */ + gcmkONERROR(gckOS_Delay(os, gcdPOWER_CONTROL_DELAY)); + } + + /* Get time until delayed. */ + gcmkPROFILE_QUERY(time, delayTime); + + if (flag & gcvPOWER_FLAG_INITIALIZE) + { + /* Initialize hardware. */ + gcmkONERROR(gckHARDWARE_InitializeHardware(Hardware)); + + gcmkONERROR(gckHARDWARE_SetFastClear(Hardware, + Hardware->allowFastClear, + Hardware->allowCompression)); + + /* Force the command queue to reload the next context. */ + command->currContext = gcvNULL; + + /* Need to config mmu after command start. */ + configMmu = gcvTRUE; + } + + /* Get time until initialized. */ + gcmkPROFILE_QUERY(time, initTime); + + if (flag & (gcvPOWER_FLAG_POWER_OFF | gcvPOWER_FLAG_CLOCK_OFF)) + { + /* Turn off the GPU power. */ + gcmkONERROR( + gckOS_SetGPUPower(os, + Hardware->core, + (flag & gcvPOWER_FLAG_CLOCK_OFF) ? gcvFALSE + : gcvTRUE, + (flag & gcvPOWER_FLAG_POWER_OFF) ? gcvFALSE + : gcvTRUE)); + + /* Save current hardware power and clock states. */ + Hardware->clockState = (flag & gcvPOWER_FLAG_CLOCK_OFF) ? gcvFALSE + : gcvTRUE; + Hardware->powerState = (flag & gcvPOWER_FLAG_POWER_OFF) ? gcvFALSE + : gcvTRUE; + } + + /* Get time until off. */ + gcmkPROFILE_QUERY(time, offTime); + + if (flag & gcvPOWER_FLAG_START) + { + /* Start the command processor. */ + gcmkONERROR(gckCOMMAND_Start(command)); + commandStarted = gcvTRUE; + + if (Hardware->startIsr) + { + /* Start the Isr. */ + gcmkONERROR(Hardware->startIsr(Hardware->isrContext)); + isrStarted = gcvTRUE; + } + } + + /* Get time until started. */ + gcmkPROFILE_QUERY(time, startTime); + + if (flag & gcvPOWER_FLAG_RELEASE) + { + /* Release the power management semaphore. */ + gcmkONERROR(gckOS_ReleaseSemaphore(os, command->powerSemaphore)); + acquired = gcvFALSE; + + if (global) + { + /* Verify global semaphore has been acquired already before + ** we release it. + ** If it was acquired, gckOS_TryAcquireSemaphore will return + ** gcvSTATUS_TIMEOUT and we release it. Otherwise, global + ** semaphore will be acquried now, but it still is released + ** immediately. */ + status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore); + if (status != gcvSTATUS_TIMEOUT) + { + gcmkONERROR(status); + } + + /* Release the global semaphore. */ + gcmkONERROR(gckOS_ReleaseSemaphore(os, Hardware->globalSemaphore)); + globalAcquired = gcvFALSE; + } + } + + /* Save the new power state. */ + Hardware->chipPowerState = State; + +#if gcdDVFS + if (State == gcvPOWER_ON && Hardware->kernel->dvfs) + { + gckDVFS_Start(Hardware->kernel->dvfs); + } +#endif + +#if gcdPOWEROFF_TIMEOUT + /* Reset power off time */ + gcmkONERROR(gckOS_GetTicks(¤tTime)); + + Hardware->powerOffTime = currentTime + Hardware->powerOffTimeout; + + if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND) + { + /* Start a timer to power off GPU when GPU enters IDLE or SUSPEND. */ + gcmkVERIFY_OK(gckOS_StartTimer(os, + Hardware->powerOffTimer, + Hardware->powerOffTimeout)); + } + else + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "Cancel powerOfftimer"); + + /* Cancel running timer when GPU enters ON or OFF. */ + gcmkVERIFY_OK(gckOS_StopTimer(os, Hardware->powerOffTimer)); + } +#endif + + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* Get total time. */ + gcmkPROFILE_QUERY(time, totalTime); +#if gcdENABLE_PROFILING + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "PROF(%llu): mutex:%llu on:%llu stall:%llu stop:%llu", + freq, mutexTime, onTime, stallTime, stopTime); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + " delay:%llu init:%llu off:%llu start:%llu total:%llu", + delayTime, initTime, offTime, startTime, totalTime); +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (commandStarted) + { + gcmkVERIFY_OK(gckCOMMAND_Stop(command, gcvFALSE)); + } + + if (isrStarted) + { + gcmkVERIFY_OK(Hardware->stopIsr(Hardware->isrContext)); + } + + if (commitEntered) + { + /* Release the command queue mutex. */ + gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, gcvTRUE)); + } + + if (acquired) + { + /* Release semaphore. */ + gcmkVERIFY_OK(gckOS_ReleaseSemaphore(Hardware->os, + command->powerSemaphore)); + } + + if (globalAcquired) + { + gcmkVERIFY_OK(gckOS_ReleaseSemaphore(Hardware->os, + Hardware->globalSemaphore)); + } + + if (mutexAcquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_QueryPowerManagementState +** +** Get GPU power state. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gceCHIPPOWERSTATE* State +** Power State. +** +*/ +gceSTATUS +gckHARDWARE_QueryPowerManagementState( + IN gckHARDWARE Hardware, + OUT gceCHIPPOWERSTATE* State + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(State != gcvNULL); + + /* Return the statue. */ + *State = Hardware->chipPowerState; + + /* Success. */ + gcmkFOOTER_ARG("*State=%d", *State); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_SetPowerManagement +** +** Configure GPU power management function. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gctBOOL PowerManagement +** Power Mangement State. +** +*/ +gceSTATUS +gckHARDWARE_SetPowerManagement( + IN gckHARDWARE Hardware, + IN gctBOOL PowerManagement + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + if(!Hardware->powerManagementLock) + { + gcmkVERIFY_OK( + gckOS_AcquireMutex(Hardware->os, Hardware->powerMutex, gcvINFINITE)); + + Hardware->powerManagement = PowerManagement; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex)); + } + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_SetPowerManagementLock +** +** Disable dynamic GPU power management switch. +** Only used in driver initialization stage. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gctBOOL Lock +** Power Mangement Lock State. +** +*/ +gceSTATUS +gckHARDWARE_SetPowerManagementLock( + IN gckHARDWARE Hardware, + IN gctBOOL Lock + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + Hardware->powerManagementLock = Lock; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +/******************************************************************************* +** +** gckHARDWARE_SetGpuProfiler +** +** Configure GPU profiler function. +** Only used in driver initialization stage. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gctBOOL GpuProfiler +** GOU Profiler State. +** +*/ +gceSTATUS +gckHARDWARE_SetGpuProfiler( + IN gckHARDWARE Hardware, + IN gctBOOL GpuProfiler + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (GpuProfiler == gcvTRUE) + { + gctUINT32 data = 0; + + /* Need to disable clock gating when doing profiling. */ + gcmkVERIFY_OK( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00100, + &data)); + + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + + + gcmkVERIFY_OK( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00100, + data)); + } + + Hardware->gpuProfiler = GpuProfiler; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +#if gcdENABLE_FSCALE_VAL_ADJUST +gceSTATUS +gckHARDWARE_SetFscaleValue( + IN gckHARDWARE Hardware, + IN gctUINT32 FscaleValue + ) +{ + gceSTATUS status; + gctUINT32 clock; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Hardware=0x%x FscaleValue=%d", Hardware, FscaleValue); + + gcmkVERIFY_ARGUMENT(FscaleValue > 0 && FscaleValue <= 64); + + gcmkONERROR( + gckOS_AcquireMutex(Hardware->os, Hardware->powerMutex, gcvINFINITE)); + acquired = gcvTRUE; + + Hardware->powerOnFscaleVal = FscaleValue; + + if (Hardware->chipPowerState == gcvPOWER_ON) + { + gctUINT32 data; + + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + &data)); + + /* Disable all clock gating. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1))))))) << (0 ? 7:7))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1))))))) << (0 ? 8:8))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1))))))) << (0 ? 8:8))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1))))))) << (0 ? 11:11))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1))))))) << (0 ? 11:11))))); + + clock = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) | (((gctUINT32) ((gctUINT32) (FscaleValue) & ((gctUINT32) ((((1 ? 8:2) - (0 ? 8:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:2) - (0 ? 8:2) + 1))))))) << (0 ? 8:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + clock)); + + /* Done loading the frequency scaler. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (clock)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))))); + + /* Restore all clock gating. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + Hardware->powerBaseAddress + + 0x00104, + data)); + } + + gcmkVERIFY(gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY(gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_GetFscaleValue( + IN gckHARDWARE Hardware, + IN gctUINT * FscaleValue, + IN gctUINT * MinFscaleValue, + IN gctUINT * MaxFscaleValue + ) +{ + *FscaleValue = Hardware->powerOnFscaleVal; + *MinFscaleValue = Hardware->minFscaleValue; + *MaxFscaleValue = 64; + + return gcvSTATUS_OK; +} + +gceSTATUS +gckHARDWARE_SetMinFscaleValue( + IN gckHARDWARE Hardware, + IN gctUINT MinFscaleValue + ) +{ + if (MinFscaleValue >= 1 && MinFscaleValue <= 64) + { + Hardware->minFscaleValue = MinFscaleValue; + } + + return gcvSTATUS_OK; +} +#endif + +#if gcdPOWEROFF_TIMEOUT +gceSTATUS +gckHARDWARE_SetPowerOffTimeout( + IN gckHARDWARE Hardware, + IN gctUINT32 Timeout +) +{ + gcmkHEADER_ARG("Hardware=0x%x Timeout=%d", Hardware, Timeout); + + Hardware->powerOffTimeout = Timeout; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + + +gceSTATUS +gckHARDWARE_QueryPowerOffTimeout( + IN gckHARDWARE Hardware, + OUT gctUINT32* Timeout +) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + *Timeout = Hardware->powerOffTimeout; + + gcmkFOOTER_ARG("*Timeout=%d", *Timeout); + return gcvSTATUS_OK; +} +#endif + +gceSTATUS +gckHARDWARE_QueryIdle( + IN gckHARDWARE Hardware, + OUT gctBOOL_PTR IsIdle + ) +{ + gceSTATUS status; + gctUINT32 idle, address; + gctBOOL isIdle; +#if gcdMULTI_GPU > 1 + gctUINT32 idle3D1 = 0; + gctUINT32 address3D1; + gctBOOL isIdle3D1 = gcvFALSE; +#endif + +#if gcdINTERRUPT_STATISTIC + gctINT32 pendingInterrupt; +#endif + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(IsIdle != gcvNULL); + + /* We are idle when the power is not ON. */ + if (Hardware->chipPowerState != gcvPOWER_ON) + { + isIdle = gcvTRUE; +#if gcdMULTI_GPU > 1 + isIdle3D1 = gcvTRUE; +#endif + } + + else + { + /* Read idle register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00004, &idle)); + +#if gcdMULTI_GPU > 1 + if (Hardware->core == gcvCORE_MAJOR) + { + gcmkONERROR( + gckOS_ReadRegisterByCoreId(Hardware->os, + Hardware->core, + gcvCORE_3D_1_ID, + 0x00004, + &idle3D1)); + } +#endif + + /* Pipe must be idle. */ + if (((((((gctUINT32) (idle)) >> (0 ? 1:1)) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 3:3)) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 4:4)) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 5:5)) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 6:6)) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 7:7)) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 2:2)) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1)))))) ) != 1) + ) + { + /* Something is busy. */ + isIdle = gcvFALSE; + } + + else + { +#if gcdSECURITY + isIdle = gcvTRUE; + address = 0; +#else + /* Read the current FE address. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00664, + &address)); + + /* Test if address is inside the last WAIT/LINK sequence. */ + if ((address >= Hardware->lastWaitLink) +#if gcdMULTI_GPU + && (address <= Hardware->lastWaitLink + 40) +#else + && (address <= Hardware->lastWaitLink + 16) +#endif + ) + { + /* FE is in last WAIT/LINK and the pipe is idle. */ + isIdle = gcvTRUE; + } + else + { + /* FE is not in WAIT/LINK yet. */ + isIdle = gcvFALSE; + } +#endif + } + +#if gcdMULTI_GPU > 1 + if (Hardware->core == gcvCORE_MAJOR) + { + /* Pipe must be idle. */ + if (((((((gctUINT32) (idle3D1)) >> (0 ? 1:1)) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle3D1)) >> (0 ? 3:3)) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle3D1)) >> (0 ? 4:4)) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle3D1)) >> (0 ? 5:5)) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle3D1)) >> (0 ? 6:6)) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle3D1)) >> (0 ? 7:7)) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle3D1)) >> (0 ? 2:2)) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1)))))) ) != 1) + ) + { + /* Something is busy. */ + isIdle3D1 = gcvFALSE; + } + + else + { + /* Read the current FE address. */ + gcmkONERROR(gckOS_ReadRegisterByCoreId(Hardware->os, + Hardware->core, + gcvCORE_3D_1_ID, + 0x00664, + &address3D1)); + + /* Test if address is inside the last WAIT/LINK sequence. */ + if ((address3D1 >= Hardware->lastWaitLink) + && (address3D1 <= Hardware->lastWaitLink + 40) + ) + { + /* FE is in last WAIT/LINK and the pipe is idle. */ + isIdle3D1 = gcvTRUE; + } + else + { + /* FE is not in WAIT/LINK yet. */ + isIdle3D1 = gcvFALSE; + } + } + } +#endif + + } + +#if gcdINTERRUPT_STATISTIC + gcmkONERROR(gckOS_AtomGet( + Hardware->os, + Hardware->kernel->eventObj->interruptCount, + &pendingInterrupt + )); + + if (pendingInterrupt) + { + isIdle = gcvFALSE; + } +#endif + +#if gcdMULTI_GPU > 1 + if (Hardware->core == gcvCORE_MAJOR) + { + *IsIdle = (isIdle & isIdle3D1); + } + else +#endif + { + *IsIdle = isIdle; + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** Handy macros that will help in reading those debug registers. +*/ + +#define gcmkREAD_DEBUG_REGISTER(control, block, index, data) \ + gcmkONERROR(\ + gckOS_WriteRegisterEx(Hardware->os, \ + Hardware->core, \ + GC_DEBUG_CONTROL##control##_Address, \ + gcmSETFIELD(0, \ + GC_DEBUG_CONTROL##control, \ + block, \ + index))); \ + gcmkONERROR(\ + gckOS_ReadRegisterEx(Hardware->os, \ + Hardware->core, \ + GC_DEBUG_SIGNALS_##block##_Address, \ + &profiler->data)) + +#define gcmkREAD_DEBUG_REGISTER_N(control, block, index, data) \ + gcmkONERROR(\ + gckOS_WriteRegisterEx(Hardware->os, \ + Hardware->core, \ + GC_DEBUG_CONTROL##control##_Address, \ + gcmSETFIELD(0, \ + GC_DEBUG_CONTROL##control, \ + block, \ + index))); \ + gcmkONERROR(\ + gckOS_ReadRegisterEx(Hardware->os, \ + Hardware->core, \ + GC_DEBUG_SIGNALS_##block##_Address, \ + &data)) + +#define gcmkRESET_DEBUG_REGISTER(control, block) \ + gcmkONERROR(\ + gckOS_WriteRegisterEx(Hardware->os, \ + Hardware->core, \ + GC_DEBUG_CONTROL##control##_Address, \ + gcmSETFIELD(0, \ + GC_DEBUG_CONTROL##control, \ + block, \ + 15))); \ + gcmkONERROR(\ + gckOS_WriteRegisterEx(Hardware->os, \ + Hardware->core, \ + GC_DEBUG_CONTROL##control##_Address, \ + gcmSETFIELD(0, \ + GC_DEBUG_CONTROL##control, \ + block, \ + 0))) + +/******************************************************************************* +** +** gckHARDWARE_ProfileEngine2D +** +** Read the profile registers available in the 2D engine and sets them in the +** profile. The function will also reset the pixelsRendered counter every time. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** OPTIONAL gcs2D_PROFILE_PTR Profile +** Pointer to a gcs2D_Profile structure. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_ProfileEngine2D( + IN gckHARDWARE Hardware, + OPTIONAL gcs2D_PROFILE_PTR Profile + ) +{ + gceSTATUS status; + gcs2D_PROFILE_PTR profiler = Profile; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (Profile != gcvNULL) + { + /* Read the cycle count. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00438, + &Profile->cycleCount)); + + /* Read pixels rendered by 2D engine. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (11) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &profiler->pixelsRendered)); + + /* Reset counter. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) +)); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if VIVANTE_PROFILER +gceSTATUS +gckHARDWARE_QueryProfileRegisters( + IN gckHARDWARE Hardware, + IN gctBOOL Reset, + OUT gcsPROFILER_COUNTERS * Counters + ) +{ + gceSTATUS status; + gcsPROFILER_COUNTERS * profiler = Counters; + gctUINT i, clock; + gctUINT32 colorKilled, colorDrawn, depthKilled, depthDrawn; + gctUINT32 totalRead, totalWrite; + + gcmkHEADER_ARG("Hardware=0x%x Counters=0x%x", Hardware, Counters); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Read the counters. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00438, + &profiler->gpuCyclesCounter)); + + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00078, + &profiler->gpuTotalCyclesCounter)); + + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0007C, + &profiler->gpuIdleCyclesCounter)); + + + /* Read clock control register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + &clock)); + + profiler->gpuTotalRead64BytesPerFrame = 0; + profiler->gpuTotalWrite64BytesPerFrame = 0; + profiler->pe_pixel_count_killed_by_color_pipe = 0; + profiler->pe_pixel_count_killed_by_depth_pipe = 0; + profiler->pe_pixel_count_drawn_by_color_pipe = 0; + profiler->pe_pixel_count_drawn_by_depth_pipe = 0; + + /* Walk through all avaiable pixel pipes. */ + for (i = 0; i < Hardware->identity.pixelPipes; ++i) + { + /* Select proper pipe. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (clock)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:20) - (0 ? 23:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:20) - (0 ? 23:20) + 1))))))) << (0 ? 23:20))) | (((gctUINT32) ((gctUINT32) (i) & ((gctUINT32) ((((1 ? 23:20) - (0 ? 23:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:20) - (0 ? 23:20) + 1))))))) << (0 ? 23:20))))); + + /* BW */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00040, + &totalRead)); + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00044, + &totalWrite)); + + profiler->gpuTotalRead64BytesPerFrame += totalRead; + profiler->gpuTotalWrite64BytesPerFrame += totalWrite; + + /* PE */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &colorKilled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &depthKilled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &colorDrawn)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &depthDrawn)); + + profiler->pe_pixel_count_killed_by_color_pipe += colorKilled; + profiler->pe_pixel_count_killed_by_depth_pipe += depthKilled; + profiler->pe_pixel_count_drawn_by_color_pipe += colorDrawn; + profiler->pe_pixel_count_drawn_by_depth_pipe += depthDrawn; + } + + /* Reset clock control register. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + clock)); + + /* Reset counters. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x0003C, 1)); + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x0003C, 0)); + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00438, 0)); + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00078, 0)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) +)); + + /* SH */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (7) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->ps_inst_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (8) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->rendered_pixel_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (9) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vs_inst_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (10) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->rendered_vertice_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (11) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vtx_branch_inst_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (12) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vtx_texld_inst_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (13) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->pxl_branch_inst_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (14) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->pxl_texld_inst_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) +)); + + /* PA */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_input_vtx_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (4) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_input_prim_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (5) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_output_prim_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (6) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_depth_clipped_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (7) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_trivial_rejected_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (8) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_culled_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) +)); + + /* SE */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00464, &profiler->se_culled_triangle_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00464, &profiler->se_culled_lines_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) +)); + + /* RA */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_valid_pixel_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_total_quad_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_valid_quad_count_after_early_z)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_total_primitive_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (9) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_pipe_cache_miss_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (10) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_prefetch_cache_miss_counter)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) +)); + + /* TX */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_bilinear_requests)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_trilinear_requests)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_discarded_texture_requests)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_texture_requests)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (5) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_mem_read_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (6) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_mem_read_in_8B_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (7) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_miss_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (8) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_hit_texel_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (9) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_miss_texel_count)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) +)); + + /* MC */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_read_req_8B_from_pipeline)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_read_req_8B_from_IP)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_write_req_8B_from_pipeline)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) +)); + + /* HI */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_read_request_stalled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_write_request_stalled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_write_data_stalled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) +)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + + +#if VIVANTE_PROFILER_CONTEXT +#define gcmkUPDATE_PROFILE_DATA(data) \ + profilerHistroy->data += profiler->data + +gceSTATUS +gckHARDWARE_QueryContextProfile( + IN gckHARDWARE Hardware, + IN gctBOOL Reset, + IN gckCONTEXT Context, + OUT gcsPROFILER_COUNTERS * Counters + ) +{ + gceSTATUS status; + gckCOMMAND command = Hardware->kernel->command; + gcsPROFILER_COUNTERS * profiler = Counters; + + gcmkHEADER_ARG("Hardware=0x%x Counters=0x%x", Hardware, Counters); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Acquire the context sequnence mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + command->os, command->mutexContextSeq, gcvINFINITE + )); + + /* Read the counters. */ + gcmkVERIFY_OK(gckOS_MemCopy( + profiler, &Context->histroyProfiler, gcmSIZEOF(gcsPROFILER_COUNTERS) + )); + + /* Reset counters. */ + gcmkVERIFY_OK(gckOS_ZeroMemory( + &Context->histroyProfiler, gcmSIZEOF(gcsPROFILER_COUNTERS) + )); + + gcmkVERIFY_OK(gckOS_ReleaseMutex( + command->os, command->mutexContextSeq + )); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +static gctUINT32 +CalcDelta( + IN gctUINT32 new, + IN gctUINT32 old + ) +{ + if (new >= old) + { + return new - old; + } + else + { + return (gctUINT32)((gctUINT64)new + 0x100000000ll - old); + } +} + +gceSTATUS +gckHARDWARE_UpdateContextProfile( + IN gckHARDWARE Hardware, + IN gckCONTEXT Context + ) +{ + gceSTATUS status; + gcsPROFILER_COUNTERS * profiler = &Context->latestProfiler; + gcsPROFILER_COUNTERS * profilerHistroy = &Context->histroyProfiler; + gctUINT i, clock; + gctUINT32 colorKilled = 0, colorDrawn = 0, depthKilled = 0, depthDrawn = 0; + gctUINT32 totalRead, totalWrite; + gceCHIPMODEL chipModel; + gctUINT32 chipRevision; + gctUINT32 temp; + gctBOOL needResetShader = gcvFALSE; + + gcmkHEADER_ARG("Hardware=0x%x Context=0x%x", Hardware, Context); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_OBJECT(Context, gcvOBJ_CONTEXT); + + chipModel = Hardware->identity.chipModel; + chipRevision = Hardware->identity.chipRevision; + if (chipModel == gcv2000 || (chipModel == gcv2100 && chipRevision == 0x5118)) + { + needResetShader = gcvTRUE; + } + + /* Read the counters. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00438, + &profiler->gpuCyclesCounter)); + gcmkUPDATE_PROFILE_DATA(gpuCyclesCounter); + + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00078, + &profiler->gpuTotalCyclesCounter)); + gcmkUPDATE_PROFILE_DATA(gpuTotalCyclesCounter); + + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0007C, + &profiler->gpuIdleCyclesCounter)); + gcmkUPDATE_PROFILE_DATA(gpuIdleCyclesCounter); + + /* Read clock control register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + &clock)); + + profiler->gpuTotalRead64BytesPerFrame = 0; + profiler->gpuTotalWrite64BytesPerFrame = 0; + profiler->pe_pixel_count_killed_by_color_pipe = 0; + profiler->pe_pixel_count_killed_by_depth_pipe = 0; + profiler->pe_pixel_count_drawn_by_color_pipe = 0; + profiler->pe_pixel_count_drawn_by_depth_pipe = 0; + + /* Walk through all avaiable pixel pipes. */ + for (i = 0; i < Hardware->identity.pixelPipes; ++i) + { + /* Select proper pipe. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (clock)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:20) - (0 ? 23:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:20) - (0 ? 23:20) + 1))))))) << (0 ? 23:20))) | (((gctUINT32) ((gctUINT32) (i) & ((gctUINT32) ((((1 ? 23:20) - (0 ? 23:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:20) - (0 ? 23:20) + 1))))))) << (0 ? 23:20))))); + + /* BW */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00040, + &totalRead)); + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00044, + &totalWrite)); + + profiler->gpuTotalRead64BytesPerFrame += totalRead; + profiler->gpuTotalWrite64BytesPerFrame += totalWrite; + gcmkUPDATE_PROFILE_DATA(gpuTotalRead64BytesPerFrame); + gcmkUPDATE_PROFILE_DATA(gpuTotalWrite64BytesPerFrame); + + /* PE */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &colorKilled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &depthKilled)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &colorDrawn)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16)))));gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00454, &depthDrawn)); + + profiler->pe_pixel_count_killed_by_color_pipe += colorKilled; + profiler->pe_pixel_count_killed_by_depth_pipe += depthKilled; + profiler->pe_pixel_count_drawn_by_color_pipe += colorDrawn; + profiler->pe_pixel_count_drawn_by_depth_pipe += depthDrawn; + gcmkUPDATE_PROFILE_DATA(pe_pixel_count_killed_by_color_pipe); + gcmkUPDATE_PROFILE_DATA(pe_pixel_count_killed_by_depth_pipe); + gcmkUPDATE_PROFILE_DATA(pe_pixel_count_drawn_by_color_pipe); + gcmkUPDATE_PROFILE_DATA(pe_pixel_count_drawn_by_depth_pipe); + } + + /* Reset clock control register. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + clock)); + + + /* Reset counters. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x0003C, 1)); + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x0003C, 0)); + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00438, 0)); + gcmkONERROR( + gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00078, 0)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) +)); + + /* SH */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (7) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->ps_inst_counter)); + if (needResetShader) + { + temp = profiler->ps_inst_counter; + profiler->ps_inst_counter = CalcDelta(temp, Context->prevPSInstCount); + Context->prevPSInstCount = temp; + } + gcmkUPDATE_PROFILE_DATA(ps_inst_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (8) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->rendered_pixel_counter)); + if (needResetShader) + { + temp = profiler->rendered_pixel_counter; + profiler->rendered_pixel_counter = CalcDelta(temp, Context->prevPSPixelCount); + Context->prevPSPixelCount = temp; + } + gcmkUPDATE_PROFILE_DATA(rendered_pixel_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (9) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vs_inst_counter)); + if (needResetShader) + { + temp = profiler->vs_inst_counter; + profiler->vs_inst_counter = CalcDelta(temp, Context->prevVSInstCount); + Context->prevVSInstCount = temp; + } + gcmkUPDATE_PROFILE_DATA(vs_inst_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (10) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->rendered_vertice_counter)); + if (needResetShader) + { + temp = profiler->rendered_vertice_counter; + profiler->rendered_vertice_counter = CalcDelta(temp, Context->prevVSVertexCount); + Context->prevVSVertexCount = temp; + } + gcmkUPDATE_PROFILE_DATA(rendered_vertice_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (11) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vtx_branch_inst_counter)); + if (needResetShader) + { + temp = profiler->vtx_branch_inst_counter; + profiler->vtx_branch_inst_counter = CalcDelta(temp, Context->prevVSBranchInstCount); + Context->prevVSBranchInstCount = temp; + } + gcmkUPDATE_PROFILE_DATA(vtx_branch_inst_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (12) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->vtx_texld_inst_counter)); + if (needResetShader) + { + temp = profiler->vtx_texld_inst_counter; + profiler->vtx_texld_inst_counter = CalcDelta(temp, Context->prevVSTexInstCount); + Context->prevVSTexInstCount = temp; + } + gcmkUPDATE_PROFILE_DATA(vtx_texld_inst_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (13) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->pxl_branch_inst_counter)); + if (needResetShader) + { + temp = profiler->pxl_branch_inst_counter; + profiler->pxl_branch_inst_counter = CalcDelta(temp, Context->prevPSBranchInstCount); + Context->prevPSBranchInstCount = temp; + } + gcmkUPDATE_PROFILE_DATA(pxl_branch_inst_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (14) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0045C, &profiler->pxl_texld_inst_counter)); + if (needResetShader) + { + temp = profiler->pxl_texld_inst_counter; + profiler->pxl_texld_inst_counter = CalcDelta(temp, Context->prevPSTexInstCount); + Context->prevPSTexInstCount = temp; + } + gcmkUPDATE_PROFILE_DATA(pxl_texld_inst_counter); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00470, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) +)); + + /* PA */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_input_vtx_counter)); + gcmkUPDATE_PROFILE_DATA(pa_input_vtx_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (4) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_input_prim_counter)); + gcmkUPDATE_PROFILE_DATA(pa_input_prim_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (5) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_output_prim_counter)); + gcmkUPDATE_PROFILE_DATA(pa_output_prim_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (6) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_depth_clipped_counter)); + gcmkUPDATE_PROFILE_DATA(pa_depth_clipped_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (7) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_trivial_rejected_counter)); + gcmkUPDATE_PROFILE_DATA(pa_trivial_rejected_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (8) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00460, &profiler->pa_culled_counter)); + gcmkUPDATE_PROFILE_DATA(pa_culled_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) +)); + + /* SE */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00464, &profiler->se_culled_triangle_count)); + gcmkUPDATE_PROFILE_DATA(se_culled_triangle_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00464, &profiler->se_culled_lines_count)); + gcmkUPDATE_PROFILE_DATA(se_culled_lines_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) +)); + + /* RA */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_valid_pixel_count)); + gcmkUPDATE_PROFILE_DATA(ra_valid_pixel_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_total_quad_count)); + gcmkUPDATE_PROFILE_DATA(ra_total_quad_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_valid_quad_count_after_early_z)); + gcmkUPDATE_PROFILE_DATA(ra_valid_quad_count_after_early_z); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_total_primitive_count)); + gcmkUPDATE_PROFILE_DATA(ra_total_primitive_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (9) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_pipe_cache_miss_counter)); + gcmkUPDATE_PROFILE_DATA(ra_pipe_cache_miss_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (10) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00448, &profiler->ra_prefetch_cache_miss_counter)); + gcmkUPDATE_PROFILE_DATA(ra_prefetch_cache_miss_counter); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 23:16) - (0 ? 23:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:16) - (0 ? 23:16) + 1))))))) << (0 ? 23:16))) +)); + + /* TX */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_bilinear_requests)); + gcmkUPDATE_PROFILE_DATA(tx_total_bilinear_requests); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_trilinear_requests)); + gcmkUPDATE_PROFILE_DATA(tx_total_trilinear_requests); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_discarded_texture_requests)); + gcmkUPDATE_PROFILE_DATA(tx_total_discarded_texture_requests); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_total_texture_requests)); + gcmkUPDATE_PROFILE_DATA(tx_total_texture_requests); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (5) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_mem_read_count)); + gcmkUPDATE_PROFILE_DATA(tx_mem_read_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (6) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_mem_read_in_8B_count)); + gcmkUPDATE_PROFILE_DATA(tx_mem_read_in_8B_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (7) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_miss_count)); + gcmkUPDATE_PROFILE_DATA(tx_cache_miss_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (8) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_hit_texel_count)); + gcmkUPDATE_PROFILE_DATA(tx_cache_hit_texel_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (9) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0044C, &profiler->tx_cache_miss_texel_count)); + gcmkUPDATE_PROFILE_DATA(tx_cache_miss_texel_count); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00474, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1))))))) << (0 ? 31:24))) +)); + + /* MC */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_read_req_8B_from_pipeline)); + gcmkUPDATE_PROFILE_DATA(mc_total_read_req_8B_from_pipeline); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_read_req_8B_from_IP)); + gcmkUPDATE_PROFILE_DATA(mc_total_read_req_8B_from_IP); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (3) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00468, &profiler->mc_total_write_req_8B_from_pipeline)); + gcmkUPDATE_PROFILE_DATA(mc_total_write_req_8B_from_pipeline); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 7:0) - (0 ? 7:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:0) - (0 ? 7:0) + 1))))))) << (0 ? 7:0))) +)); + + /* HI */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_read_request_stalled)); + gcmkUPDATE_PROFILE_DATA(hi_axi_cycles_read_request_stalled); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_write_request_stalled)); + gcmkUPDATE_PROFILE_DATA(hi_axi_cycles_write_request_stalled); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (2) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x0046C, &profiler->hi_axi_cycles_write_data_stalled)); + gcmkUPDATE_PROFILE_DATA(hi_axi_cycles_write_data_stalled); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (15) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) )); +gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, 0x00478, ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) +)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + + +#if VIVANTE_PROFILER_NEW +gceSTATUS +gckHARDWARE_InitProfiler( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gctUINT32 control; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + &control)); + /* Enable debug register. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1))))))) << (0 ? 11:11))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1))))))) << (0 ? 11:11))))); + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +static gceSTATUS +_ResetGPU( + IN gckHARDWARE Hardware, + IN gckOS Os, + IN gceCORE Core + ) +{ + gctUINT32 control, idle; + gceSTATUS status; + + for (;;) + { + /* Disable clock gating. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + Hardware->powerBaseAddress + + 0x00104, + 0x00000000)); + + control = ((((gctUINT32) (0x01590880)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1))))))) << (0 ? 17:17))); + + /* Disable pulse-eater. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x0010C, + control)); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x0010C, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x0010C, + control)); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + ((((gctUINT32) (0x00000900)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + 0x00000900)); + + /* Wait for clock being stable. */ + gcmkONERROR(gckOS_Delay(Os, 1)); + + /* Isolate the GPU. */ + control = ((((gctUINT32) (0x00000900)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + control)); + + /* Set soft reset. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))))); + + /* Wait for reset. */ + gcmkONERROR(gckOS_Delay(Os, 1)); + + /* Reset soft reset bit. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))))); + + /* Reset GPU isolation. */ + control = ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + Core, + 0x00000, + control)); + + /* Read idle register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Os, + Core, + 0x00004, + &idle)); + + if ((((((gctUINT32) (idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) ) == 0) + { + continue; + } + +#if gcdMULTI_GPU > 1 + if (Core == gcvCORE_MAJOR) + { + /* Read idle register. */ + gcmkONERROR(gckOS_ReadRegisterByCoreId(Os, + Core, + gcvCORE_3D_1_ID, + 0x00004, + &idle)); + + if ((((((gctUINT32) (idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) ) == 0) + { + continue; + } + } +#endif + /* Read reset register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Os, + Core, + 0x00000, + &control)); + + if (((((((gctUINT32) (control)) >> (0 ? 16:16)) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) ) == 0) + || ((((((gctUINT32) (control)) >> (0 ? 17:17)) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1)))))) ) == 0) + ) + { + continue; + } + +#if gcdMULTI_GPU > 1 + if (Core == gcvCORE_MAJOR) + { + /* Read reset register. */ + gcmkONERROR(gckOS_ReadRegisterByCoreId(Os, + Core, + gcvCORE_3D_1_ID, + 0x00000, + &control)); + + if (((((((gctUINT32) (control)) >> (0 ? 16:16)) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) ) == 0) + || ((((((gctUINT32) (control)) >> (0 ? 17:17)) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1)))))) ) == 0) + ) + { + continue; + } + } +#endif + /* GPU is idle. */ + break; + } + + /* Success. */ + return gcvSTATUS_OK; + +OnError: + + /* Return the error. */ + return status; +} + +gceSTATUS +gckHARDWARE_Reset( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_OBJECT(Hardware->kernel, gcvOBJ_KERNEL); + + /* Hardware reset. */ + status = gckOS_ResetGPU(Hardware->os, Hardware->core); + + if (gcmIS_ERROR(status)) + { + if (Hardware->identity.chipRevision < 0x4600) + { + /* Not supported - we need the isolation bit. */ + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + + /* Soft reset. */ + gcmkONERROR(_ResetGPU(Hardware, Hardware->os, Hardware->core)); + } + + /* Initialize hardware. */ + gcmkONERROR(gckHARDWARE_InitializeHardware(Hardware)); + + /* Jump to address into which GPU should run if it doesn't stuck. */ + gcmkONERROR(gckHARDWARE_Execute(Hardware, Hardware->kernel->restoreAddress, 16)); + + gcmkPRINT("[galcore]: recovery done"); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkPRINT("[galcore]: Hardware not reset successfully, give up"); + + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_GetBaseAddress( + IN gckHARDWARE Hardware, + OUT gctUINT32_PTR BaseAddress + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(BaseAddress != gcvNULL); + + /* Test if we have a new Memory Controller. */ + if (((((gctUINT32) (Hardware->identity.chipMinorFeatures)) >> (0 ? 22:22) & ((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1)))))))) + { + /* No base address required. */ + *BaseAddress = 0; + } + else + { + /* Get the base address from the OS. */ + gcmkONERROR(gckOS_GetBaseAddress(Hardware->os, BaseAddress)); + } + + /* Success. */ + gcmkFOOTER_ARG("*BaseAddress=0x%08x", *BaseAddress); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_NeedBaseAddress( + IN gckHARDWARE Hardware, + IN gctUINT32 State, + OUT gctBOOL_PTR NeedBase + ) +{ + gctBOOL need = gcvFALSE; + + gcmkHEADER_ARG("Hardware=0x%x State=0x%08x", Hardware, State); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(NeedBase != gcvNULL); + + /* Make sure this is a load state. */ + if (((((gctUINT32) (State)) >> (0 ? 31:27) & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1)))))) == (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1)))))))) + { +#if gcdENABLE_3D + /* Get the state address. */ + switch ((((((gctUINT32) (State)) >> (0 ? 15:0)) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1)))))) )) + { + case 0x0596: + case 0x0597: + case 0x0599: + case 0x059A: + case 0x05A9: + /* These states need a TRUE physical address. */ + need = gcvTRUE; + break; + } +#else + /* 2D addresses don't need a base address. */ +#endif + } + + /* Return the flag. */ + *NeedBase = need; + + /* Success. */ + gcmkFOOTER_ARG("*NeedBase=%d", *NeedBase); + return gcvSTATUS_OK; +} + +gceSTATUS +gckHARDWARE_SetIsrManager( + IN gckHARDWARE Hardware, + IN gctISRMANAGERFUNC StartIsr, + IN gctISRMANAGERFUNC StopIsr, + IN gctPOINTER Context + ) +{ + gceSTATUS status = gcvSTATUS_OK; + + gcmkHEADER_ARG("Hardware=0x%x, StartIsr=0x%x, StopIsr=0x%x, Context=0x%x", + Hardware, StartIsr, StopIsr, Context); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (StartIsr == gcvNULL || + StopIsr == gcvNULL || + Context == gcvNULL) + { + status = gcvSTATUS_INVALID_ARGUMENT; + + gcmkFOOTER(); + return status; + } + + Hardware->startIsr = StartIsr; + Hardware->stopIsr = StopIsr; + Hardware->isrContext = Context; + + /* Success. */ + gcmkFOOTER(); + + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_Compose +** +** Start a composition. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_Compose( + IN gckHARDWARE Hardware, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Offset, + IN gctSIZE_T Size, + IN gctUINT8 EventID + ) +{ +#if gcdENABLE_3D + gceSTATUS status; + gctUINT32_PTR triggerState; + + gcmkHEADER_ARG("Hardware=0x%x Physical=0x%x Logical=0x%x" + " Offset=%d Size=%d EventID=%d", + Hardware, Physical, Logical, Offset, Size, EventID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(((Size + 8) & 63) == 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + /* Program the trigger state. */ + triggerState = (gctUINT32_PTR) ((gctUINT8_PTR) Logical + Offset + Size); + triggerState[0] = 0x0C03; + triggerState[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:4) - (0 ? 5:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:4) - (0 ? 5:4) + 1))))))) << (0 ? 5:4))) | (((gctUINT32) (0x3 & ((gctUINT32) ((((1 ? 5:4) - (0 ? 5:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:4) - (0 ? 5:4) + 1))))))) << (0 ? 5:4))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1))))))) << (0 ? 8:8))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1))))))) << (0 ? 8:8))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 24:24) - (0 ? 24:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:24) - (0 ? 24:24) + 1))))))) << (0 ? 24:24))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 24:24) - (0 ? 24:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:24) - (0 ? 24:24) + 1))))))) << (0 ? 24:24))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:16) - (0 ? 20:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:16) - (0 ? 20:16) + 1))))))) << (0 ? 20:16))) | (((gctUINT32) ((gctUINT32) (EventID) & ((gctUINT32) ((((1 ? 20:16) - (0 ? 20:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:16) - (0 ? 20:16) + 1))))))) << (0 ? 20:16))) + ; + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache for the wait/link. */ + gcmkONERROR(gckOS_CacheClean( + Hardware->os, ProcessID, gcvNULL, + (gctUINT32)Physical, Logical, Offset + Size + )); +#endif + + /* Start composition. */ + gcmkONERROR(gckOS_WriteRegisterEx( + Hardware->os, Hardware->core, 0x00554, + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) (0x3 & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) + )); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +#else + /* Return the status. */ + return gcvSTATUS_NOT_SUPPORTED; +#endif +} + +/******************************************************************************* +** +** gckHARDWARE_IsFeatureAvailable +** +** Verifies whether the specified feature is available in hardware. +** +** INPUT: +** +** gckHARDWARE Hardware +** Pointer to an gckHARDWARE object. +** +** gceFEATURE Feature +** Feature to be verified. +*/ +gceSTATUS +gckHARDWARE_IsFeatureAvailable( + IN gckHARDWARE Hardware, + IN gceFEATURE Feature + ) +{ + gctBOOL available; + + gcmkHEADER_ARG("Hardware=0x%x Feature=%d", Hardware, Feature); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Only features needed by common kernel logic added here. */ + switch (Feature) + { + case gcvFEATURE_END_EVENT: + /*available = gcmVERIFYFIELDVALUE(Hardware->identity.chipMinorFeatures2, + GC_MINOR_FEATURES2, END_EVENT, AVAILABLE + );*/ + available = gcvFALSE; + break; + + case gcvFEATURE_MC20: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures)) >> (0 ? 22:22) & ((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1))))))); + break; + + case gcvFEATURE_EARLY_Z: + available = ((((gctUINT32) (Hardware->identity.chipFeatures)) >> (0 ? 16:16) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) == (0x0 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))); + break; + + case gcvFEATURE_HZ: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures)) >> (0 ? 27:27) & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))); + break; + + case gcvFEATURE_NEW_HZ: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures3)) >> (0 ? 26:26) & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 26:26) - (0 ? 26:26) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:26) - (0 ? 26:26) + 1))))))); + break; + + case gcvFEATURE_FAST_MSAA: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures3)) >> (0 ? 8:8) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1))))))); + break; + + case gcvFEATURE_SMALL_MSAA: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures4)) >> (0 ? 18:18) & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))); + break; + + case gcvFEATURE_DYNAMIC_FREQUENCY_SCALING: + /* This feature doesn't apply for 2D cores. */ + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures2)) >> (0 ? 14:14) & ((gctUINT32) ((((1 ? 14:14) - (0 ? 14:14) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 14:14) - (0 ? 14:14) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 14:14) - (0 ? 14:14) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 14:14) - (0 ? 14:14) + 1))))))) + && ((((gctUINT32) (Hardware->identity.chipFeatures)) >> (0 ? 2:2) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))); + + if (Hardware->identity.chipModel == gcv1000 && + (Hardware->identity.chipRevision == 0x5039 || + Hardware->identity.chipRevision == 0x5040)) + { + available = gcvFALSE; + } + break; + + case gcvFEATURE_ACE: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures3)) >> (0 ? 18:18) & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))); + break; + + case gcvFEATURE_HALTI2: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures4)) >> (0 ? 16:16) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))); + break; + + case gcvFEATURE_PIPE_2D: + available = ((((gctUINT32) (Hardware->identity.chipFeatures)) >> (0 ? 9:9) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))); + break; + + case gcvFEATURE_PIPE_3D: +#if gcdENABLE_3D + available = ((((gctUINT32) (Hardware->identity.chipFeatures)) >> (0 ? 2:2) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))); +#else + available = gcvFALSE; +#endif + break; + + case gcvFEATURE_FC_FLUSH_STALL: + available = ((((gctUINT32) (Hardware->identity.chipMinorFeatures1)) >> (0 ? 31:31) & ((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 31:31) - (0 ? 31:31) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:31) - (0 ? 31:31) + 1))))))); + break; + + default: + gcmkFATAL("Invalid feature has been requested."); + available = gcvFALSE; + } + + /* Return result. */ + gcmkFOOTER_ARG("%d", available ? gcvSTATUS_TRUE : gcvSTATUS_FALSE); + return available ? gcvSTATUS_TRUE : gcvSTATUS_FALSE; +} + +/******************************************************************************* +** +** gckHARDWARE_DumpMMUException +** +** Dump the MMU debug info on an MMU exception. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_DumpMMUException( + IN gckHARDWARE Hardware + ) +{ + gctUINT32 mmu = 0; + gctUINT32 mmuStatus = 0; + gctUINT32 address = 0; + gctUINT32 i = 0; + gctUINT32 mtlb = 0; + gctUINT32 stlb = 0; + gctUINT32 offset = 0; +#if gcdPROCESS_ADDRESS_SPACE + gcsDATABASE_PTR database; +#endif + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + gcmkPRINT("GPU[%d](ChipModel=0x%x ChipRevision=0x%x):\n", + Hardware->core, + Hardware->identity.chipModel, + Hardware->identity.chipRevision); + + gcmkPRINT("**************************\n"); + gcmkPRINT("*** MMU ERROR DUMP ***\n"); + gcmkPRINT("**************************\n"); + + gcmkVERIFY_OK( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00188, + &mmuStatus)); + + gcmkPRINT(" MMU status = 0x%08X\n", mmuStatus); + + for (i = 0; i < 4; i += 1) + { + mmu = mmuStatus & 0xF; + mmuStatus >>= 4; + + if (mmu == 0) + { + continue; + } + + switch (mmu) + { + case 1: + gcmkPRINT(" MMU%d: slave not present\n", i); + break; + + case 2: + gcmkPRINT(" MMU%d: page not present\n", i); + break; + + case 3: + gcmkPRINT(" MMU%d: write violation\n", i); + break; + + default: + gcmkPRINT(" MMU%d: unknown state\n", i); + } + + gcmkVERIFY_OK( + gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00190 + i * 4, + &address)); + + mtlb = (address & gcdMMU_MTLB_MASK) >> gcdMMU_MTLB_SHIFT; + stlb = (address & gcdMMU_STLB_4K_MASK) >> gcdMMU_STLB_4K_SHIFT; + offset = address & gcdMMU_OFFSET_4K_MASK; + + gcmkPRINT(" MMU%d: exception address = 0x%08X\n", i, address); + + gcmkPRINT(" MTLB entry = %d\n", mtlb); + + gcmkPRINT(" STLB entry = %d\n", stlb); + + gcmkPRINT(" Offset = 0x%08X (%d)\n", offset, offset); + + gckMMU_DumpPageTableEntry(Hardware->kernel->mmu, address); + +#if gcdPROCESS_ADDRESS_SPACE + for (i = 0; i < gcmCOUNTOF(Hardware->kernel->db->db); ++i) + { + for (database = Hardware->kernel->db->db[i]; + database != gcvNULL; + database = database->next) + { + gcmkPRINT(" database [%d] :", database->processID); + gckMMU_DumpPageTableEntry(database->mmu, address); + } + } +#endif + } + + gckHARDWARE_DumpGPUState(Hardware); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHARDWARE_DumpGPUState +** +** Dump the GPU debug registers. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHARDWARE_DumpGPUState( + IN gckHARDWARE Hardware + ) +{ + static gctCONST_STRING _cmdState[] = + { + "PAR_IDLE_ST", "PAR_DEC_ST", "PAR_ADR0_ST", "PAR_LOAD0_ST", + "PAR_ADR1_ST", "PAR_LOAD1_ST", "PAR_3DADR_ST", "PAR_3DCMD_ST", + "PAR_3DCNTL_ST", "PAR_3DIDXCNTL_ST", "PAR_INITREQDMA_ST", + "PAR_DRAWIDX_ST", "PAR_DRAW_ST", "PAR_2DRECT0_ST", "PAR_2DRECT1_ST", + "PAR_2DDATA0_ST", "PAR_2DDATA1_ST", "PAR_WAITFIFO_ST", "PAR_WAIT_ST", + "PAR_LINK_ST", "PAR_END_ST", "PAR_STALL_ST" + }; + + static gctCONST_STRING _cmdDmaState[] = + { + "CMD_IDLE_ST", "CMD_START_ST", "CMD_REQ_ST", "CMD_END_ST" + }; + + static gctCONST_STRING _cmdFetState[] = + { + "FET_IDLE_ST", "FET_RAMVALID_ST", "FET_VALID_ST" + }; + + static gctCONST_STRING _reqDmaState[] = + { + "REQ_IDLE_ST", "REQ_WAITIDX_ST", "REQ_CAL_ST" + }; + + static gctCONST_STRING _calState[] = + { + "CAL_IDLE_ST", "CAL_LDADR_ST", "CAL_IDXCALC_ST" + }; + + static gctCONST_STRING _veReqState[] = + { + "VER_IDLE_ST", "VER_CKCACHE_ST", "VER_MISS_ST" + }; + + static gcsiDEBUG_REGISTERS _dbgRegs[] = + { + { "RA", 0x474, 16, 0x448, 16, 0x12344321 }, + { "TX", 0x474, 24, 0x44C, 16, 0x12211221 }, + { "FE", 0x470, 0, 0x450, 16, 0xBABEF00D }, + { "PE", 0x470, 16, 0x454, 16, 0xBABEF00D }, + { "DE", 0x470, 8, 0x458, 16, 0xBABEF00D }, + { "SH", 0x470, 24, 0x45C, 16, 0xDEADBEEF }, + { "PA", 0x474, 0, 0x460, 16, 0x0000AAAA }, + { "SE", 0x474, 8, 0x464, 16, 0x5E5E5E5E }, + { "MC", 0x478, 0, 0x468, 16, 0x12345678 }, + { "HI", 0x478, 8, 0x46C, 16, 0xAAAAAAAA } + }; + + static gctUINT32 _otherRegs[] = + { + 0x040, 0x044, 0x04C, 0x050, 0x054, 0x058, 0x05C, 0x060, + 0x43c, 0x440, 0x444, 0x414, + }; + + gceSTATUS status; + gckKERNEL kernel = gcvNULL; + gctUINT32 idle = 0, axi = 0; + gctUINT32 dmaAddress1 = 0, dmaAddress2 = 0; + gctUINT32 dmaState1 = 0, dmaState2 = 0; + gctUINT32 dmaLow = 0, dmaHigh = 0; + gctUINT32 cmdState = 0, cmdDmaState = 0, cmdFetState = 0; + gctUINT32 dmaReqState = 0, calState = 0, veReqState = 0; + gctUINT i; + gctUINT pipe = 0, pixelPipes = 0; + gctUINT32 control = 0, oldControl = 0; + gckOS os = Hardware->os; + gceCORE core = Hardware->core; + + gcmkHEADER_ARG("Hardware=0x%X", Hardware); + + kernel = Hardware->kernel; + + gcmkPRINT_N(12, "GPU[%d](ChipModel=0x%x ChipRevision=0x%x):\n", + core, + Hardware->identity.chipModel, + Hardware->identity.chipRevision); + + pixelPipes = Hardware->identity.pixelPipes + ? Hardware->identity.pixelPipes + : 1; + + /* Reset register values. */ + idle = axi = + dmaState1 = dmaState2 = + dmaAddress1 = dmaAddress2 = + dmaLow = dmaHigh = 0; + + /* Verify whether DMA is running. */ + gcmkONERROR(_VerifyDMA( + os, core, &dmaAddress1, &dmaAddress2, &dmaState1, &dmaState2 + )); + + cmdState = dmaState2 & 0x1F; + cmdDmaState = (dmaState2 >> 8) & 0x03; + cmdFetState = (dmaState2 >> 10) & 0x03; + dmaReqState = (dmaState2 >> 12) & 0x03; + calState = (dmaState2 >> 14) & 0x03; + veReqState = (dmaState2 >> 16) & 0x03; + + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x004, &idle)); + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x00C, &axi)); + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x668, &dmaLow)); + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x66C, &dmaHigh)); + + gcmkPRINT_N(0, "**************************\n"); + gcmkPRINT_N(0, "*** GPU STATE DUMP ***\n"); + gcmkPRINT_N(0, "**************************\n"); + + gcmkPRINT_N(4, " axi = 0x%08X\n", axi); + + gcmkPRINT_N(4, " idle = 0x%08X\n", idle); + if ((idle & 0x00000001) == 0) gcmkPRINT_N(0, " FE not idle\n"); + if ((idle & 0x00000002) == 0) gcmkPRINT_N(0, " DE not idle\n"); + if ((idle & 0x00000004) == 0) gcmkPRINT_N(0, " PE not idle\n"); + if ((idle & 0x00000008) == 0) gcmkPRINT_N(0, " SH not idle\n"); + if ((idle & 0x00000010) == 0) gcmkPRINT_N(0, " PA not idle\n"); + if ((idle & 0x00000020) == 0) gcmkPRINT_N(0, " SE not idle\n"); + if ((idle & 0x00000040) == 0) gcmkPRINT_N(0, " RA not idle\n"); + if ((idle & 0x00000080) == 0) gcmkPRINT_N(0, " TX not idle\n"); + if ((idle & 0x00000100) == 0) gcmkPRINT_N(0, " VG not idle\n"); + if ((idle & 0x00000200) == 0) gcmkPRINT_N(0, " IM not idle\n"); + if ((idle & 0x00000400) == 0) gcmkPRINT_N(0, " FP not idle\n"); + if ((idle & 0x00000800) == 0) gcmkPRINT_N(0, " TS not idle\n"); + if ((idle & 0x80000000) != 0) gcmkPRINT_N(0, " AXI low power mode\n"); + + if ( + (dmaAddress1 == dmaAddress2) + && (dmaState1 == dmaState2) + ) + { + gcmkPRINT_N(0, " DMA appears to be stuck at this address:\n"); + gcmkPRINT_N(4, " 0x%08X\n", dmaAddress1); + } + else + { + if (dmaAddress1 == dmaAddress2) + { + gcmkPRINT_N(0, " DMA address is constant, but state is changing:\n"); + gcmkPRINT_N(4, " 0x%08X\n", dmaState1); + gcmkPRINT_N(4, " 0x%08X\n", dmaState2); + } + else + { + gcmkPRINT_N(0, " DMA is running; known addresses are:\n"); + gcmkPRINT_N(4, " 0x%08X\n", dmaAddress1); + gcmkPRINT_N(4, " 0x%08X\n", dmaAddress2); + } + } + + gcmkPRINT_N(4, " dmaLow = 0x%08X\n", dmaLow); + gcmkPRINT_N(4, " dmaHigh = 0x%08X\n", dmaHigh); + gcmkPRINT_N(4, " dmaState = 0x%08X\n", dmaState2); + gcmkPRINT_N(8, " command state = %d (%s)\n", cmdState, _cmdState [cmdState]); + gcmkPRINT_N(8, " command DMA state = %d (%s)\n", cmdDmaState, _cmdDmaState[cmdDmaState]); + gcmkPRINT_N(8, " command fetch state = %d (%s)\n", cmdFetState, _cmdFetState[cmdFetState]); + gcmkPRINT_N(8, " DMA request state = %d (%s)\n", dmaReqState, _reqDmaState[dmaReqState]); + gcmkPRINT_N(8, " cal state = %d (%s)\n", calState, _calState [calState]); + gcmkPRINT_N(8, " VE request state = %d (%s)\n", veReqState, _veReqState [veReqState]); + + /* Record control. */ + gckOS_ReadRegisterEx(os, core, 0x0, &oldControl); + + for (pipe = 0; pipe < pixelPipes; pipe++) + { + gcmkPRINT_N(4, " Debug registers of pipe[%d]:\n", pipe); + + /* Switch pipe. */ + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x0, &control)); + control &= ~(0xF << 20); + control |= (pipe << 20); + gcmkONERROR(gckOS_WriteRegisterEx(os, core, 0x0, control)); + + for (i = 0; i < gcmCOUNTOF(_dbgRegs); i += 1) + { + gcmkONERROR(_DumpDebugRegisters(os, core, &_dbgRegs[i])); + } + + gcmkPRINT_N(0, " Other Registers:\n"); + for (i = 0; i < gcmCOUNTOF(_otherRegs); i += 1) + { + gctUINT32 read; + gcmkONERROR(gckOS_ReadRegisterEx(os, core, _otherRegs[i], &read)); + gcmkPRINT_N(12, " [0x%04X] 0x%08X\n", _otherRegs[i], read); + } + } + + if (kernel->hardware->identity.chipFeatures & (1 << 4)) + { + gctUINT32 read0, read1, write; + + read0 = read1 = write = 0; + + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x43C, &read0)); + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x440, &read1)); + gcmkONERROR(gckOS_ReadRegisterEx(os, core, 0x444, &write)); + + gcmkPRINT_N(4, " read0 = 0x%08X\n", read0); + gcmkPRINT_N(4, " read1 = 0x%08X\n", read1); + gcmkPRINT_N(4, " write = 0x%08X\n", write); + } + + /* Restore control. */ + gcmkONERROR(gckOS_WriteRegisterEx(os, core, 0x0, oldControl)); + + /* dump stack. */ + gckOS_DumpCallStack(os); + +OnError: + + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +static gceSTATUS +gckHARDWARE_ReadPerformanceRegister( + IN gckHARDWARE Hardware, + IN gctUINT PerformanceAddress, + IN gctUINT IndexAddress, + IN gctUINT IndexShift, + IN gctUINT Index, + OUT gctUINT32_PTR Value + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x PerformanceAddress=0x%x IndexAddress=0x%x " + "IndexShift=%u Index=%u", + Hardware, PerformanceAddress, IndexAddress, IndexShift, + Index); + + /* Write the index. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + IndexAddress, + Index << IndexShift)); + + /* Read the register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + PerformanceAddress, + Value)); + + /* Test for reset. */ + if (Index == 15) + { + /* Index another register to get out of reset. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, Hardware->core, IndexAddress, 0)); + } + + /* Success. */ + gcmkFOOTER_ARG("*Value=0x%x", *Value); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_GetFrameInfo( + IN gckHARDWARE Hardware, + OUT gcsHAL_FRAME_INFO * FrameInfo + ) +{ + gceSTATUS status; + gctUINT i, clock; + gcsHAL_FRAME_INFO info; +#if gcdFRAME_DB_RESET + gctUINT reset; +#endif + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Get profile tick. */ + gcmkONERROR(gckOS_GetProfileTick(&info.ticks)); + + /* Read SH counters and reset them. */ + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0045C, + 0x00470, + 24, + 4, + &info.shaderCycles)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0045C, + 0x00470, + 24, + 9, + &info.vsInstructionCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0045C, + 0x00470, + 24, + 12, + &info.vsTextureCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0045C, + 0x00470, + 24, + 7, + &info.psInstructionCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0045C, + 0x00470, + 24, + 14, + &info.psTextureCount)); +#if gcdFRAME_DB_RESET + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0045C, + 0x00470, + 24, + 15, + &reset)); +#endif + + /* Read PA counters and reset them. */ + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 3, + &info.vertexCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 4, + &info.primitiveCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 7, + &info.rejectedPrimitives)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 8, + &info.culledPrimitives)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 6, + &info.clippedPrimitives)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 5, + &info.outPrimitives)); +#if gcdFRAME_DB_RESET + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00460, + 0x00474, + 0, + 15, + &reset)); +#endif + + /* Read RA counters and reset them. */ + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00448, + 0x00474, + 16, + 3, + &info.inPrimitives)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00448, + 0x00474, + 16, + 11, + &info.culledQuadCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00448, + 0x00474, + 16, + 1, + &info.totalQuadCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00448, + 0x00474, + 16, + 2, + &info.quadCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00448, + 0x00474, + 16, + 0, + &info.totalPixelCount)); +#if gcdFRAME_DB_RESET + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00448, + 0x00474, + 16, + 15, + &reset)); +#endif + + /* Read TX counters and reset them. */ + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0044C, + 0x00474, + 24, + 0, + &info.bilinearRequests)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0044C, + 0x00474, + 24, + 1, + &info.trilinearRequests)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0044C, + 0x00474, + 24, + 8, + &info.txHitCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0044C, + 0x00474, + 24, + 9, + &info.txMissCount)); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0044C, + 0x00474, + 24, + 6, + &info.txBytes8)); +#if gcdFRAME_DB_RESET + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x0044C, + 0x00474, + 24, + 15, + &reset)); +#endif + + /* Read clock control register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + &clock)); + + /* Walk through all avaiable pixel pipes. */ + for (i = 0; i < Hardware->identity.pixelPipes; ++i) + { + /* Select proper pipe. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + ((((gctUINT32) (clock)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:20) - (0 ? 23:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:20) - (0 ? 23:20) + 1))))))) << (0 ? 23:20))) | (((gctUINT32) ((gctUINT32) (i) & ((gctUINT32) ((((1 ? 23:20) - (0 ? 23:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:20) - (0 ? 23:20) + 1))))))) << (0 ? 23:20))))); + + /* Read cycle registers. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00078, + &info.cycles[i])); + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0007C, + &info.idleCycles[i])); + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00438, + &info.mcCycles[i])); + + /* Read bandwidth registers. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0005C, + &info.readRequests[i])); + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00040, + &info.readBytes8[i])); + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00050, + &info.writeRequests[i])); + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00044, + &info.writeBytes8[i])); + + /* Read PE counters. */ + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00454, + 0x00470, + 16, + 0, + &info.colorKilled[i])); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00454, + 0x00470, + 16, + 2, + &info.colorDrawn[i])); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00454, + 0x00470, + 16, + 1, + &info.depthKilled[i])); + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00454, + 0x00470, + 16, + 3, + &info.depthDrawn[i])); + } + + /* Zero out remaning reserved counters. */ + for (; i < 8; ++i) + { + info.readBytes8[i] = 0; + info.writeBytes8[i] = 0; + info.cycles[i] = 0; + info.idleCycles[i] = 0; + info.mcCycles[i] = 0; + info.readRequests[i] = 0; + info.writeRequests[i] = 0; + info.colorKilled[i] = 0; + info.colorDrawn[i] = 0; + info.depthKilled[i] = 0; + info.depthDrawn[i] = 0; + } + + /* Reset clock control register. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00000, + clock)); + + /* Reset cycle and bandwidth counters. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0003C, + 1)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0003C, + 0)); + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00078, + 0)); + +#if gcdFRAME_DB_RESET + /* Reset PE counters. */ + gcmkONERROR(gckHARDWARE_ReadPerformanceRegister( + Hardware, + 0x00454, + 0x00470, + 16, + 15, + &reset)); +#endif + + /* Copy to user. */ + gcmkONERROR(gckOS_CopyToUserData(Hardware->os, + &info, + FrameInfo, + gcmSIZEOF(info))); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdDVFS +#define READ_FROM_EATER1 0 + +gceSTATUS +gckHARDWARE_QueryLoad( + IN gckHARDWARE Hardware, + OUT gctUINT32 * Load + ) +{ + gctUINT32 debug1; + gceSTATUS status; + gcmkHEADER_ARG("Hardware=0x%X", Hardware); + + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Load != gcvNULL); + + gckOS_AcquireMutex(Hardware->os, Hardware->powerMutex, gcvINFINITE); + + if (Hardware->chipPowerState == gcvPOWER_ON) + { + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00110, + Load)); +#if READ_FROM_EATER1 + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00134, + Load)); +#endif + + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00114, + &debug1)); + + /* Patch result of 0x110 with result of 0x114. */ + if ((debug1 & 0xFF) == 1) + { + *Load &= ~0xFF; + *Load |= 1; + } + + if (((debug1 & 0xFF00) >> 8) == 1) + { + *Load &= ~(0xFF << 8); + *Load |= 1 << 8; + } + + if (((debug1 & 0xFF0000) >> 16) == 1) + { + *Load &= ~(0xFF << 16); + *Load |= 1 << 16; + } + + if (((debug1 & 0xFF000000) >> 24) == 1) + { + *Load &= ~(0xFF << 24); + *Load |= 1 << 24; + } + } + else + { + status = gcvSTATUS_INVALID_REQUEST; + } + +OnError: + + gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex); + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_SetDVFSPeroid( + IN gckHARDWARE Hardware, + OUT gctUINT32 Frequency + ) +{ + gceSTATUS status; + gctUINT32 period; + gctUINT32 eater; + +#if READ_FROM_EATER1 + gctUINT32 period1; + gctUINT32 eater1; +#endif + + gcmkHEADER_ARG("Hardware=0x%X Frequency=%d", Hardware, Frequency); + + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + period = 0; + + while((64 << period) < (gcdDVFS_ANAYLSE_WINDOW * Frequency * 1000) ) + { + period++; + } + +#if READ_FROM_EATER1 + /* + * Peroid = F * 1000 * 1000 / (60 * 16 * 1024); + */ + period1 = Frequency * 6250 / 6114; +#endif + + gckOS_AcquireMutex(Hardware->os, Hardware->powerMutex, gcvINFINITE); + + if (Hardware->chipPowerState == gcvPOWER_ON) + { + /* Get current configure. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + &eater)); + + /* Change peroid. */ + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + ((((gctUINT32) (eater)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))) | (((gctUINT32) ((gctUINT32) (period) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1))))))) << (0 ? 15:8))))); + +#if READ_FROM_EATER1 + /* Config eater1. */ + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x00130, + &eater1)); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x00130, + ((((gctUINT32) (eater1)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:16) - (0 ? 31:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:16) - (0 ? 31:16) + 1))))))) << (0 ? 31:16))) | (((gctUINT32) ((gctUINT32) (period1) & ((gctUINT32) ((((1 ? 31:16) - (0 ? 31:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:16) - (0 ? 31:16) + 1))))))) << (0 ? 31:16))))); +#endif + } + else + { + status = gcvSTATUS_INVALID_REQUEST; + } + +OnError: + gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex); + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckHARDWARE_InitDVFS( + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gctUINT32 data; + + gcmkHEADER_ARG("Hardware=0x%X", Hardware); + + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + &data)); + + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))) << (0 ? 18:18))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))) << (0 ? 18:18))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:20) - (0 ? 20:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:20) - (0 ? 20:20) + 1))))))) << (0 ? 20:20))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 20:20) - (0 ? 20:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:20) - (0 ? 20:20) + 1))))))) << (0 ? 20:20))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1))))))) << (0 ? 23:23))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 23:23) - (0 ? 23:23) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 23:23) - (0 ? 23:23) + 1))))))) << (0 ? 23:23))); + data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1))))))) << (0 ? 22:22))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 22:22) - (0 ? 22:22) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 22:22) - (0 ? 22:22) + 1))))))) << (0 ? 22:22))); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "DVFS Configure=0x%X", + data); + + gcmkONERROR(gckOS_WriteRegisterEx(Hardware->os, + Hardware->core, + 0x0010C, + data)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckHARDWARE_PrepareFunctions +** +** Generate command buffer snippets which will be used by gckHARDWARE, by which +** gckHARDWARE can manipulate GPU by FE command without using gckCOMMAND to avoid +** race condition and deadlock. +** +** Notice: +** 1. Each snippet can only be executed when GPU is idle. +** 2. Execution is triggered by AHB (0x658) +** 3. Each snippet followed by END so software can sync with GPU by checking GPU +** idle +** 4. It is transparent to gckCOMMAND command buffer. +** +** Existing Snippets: +** 1. MMU Configure +** For new MMU, after GPU is reset, FE execute this command sequence to enble MMU. +*/ +gceSTATUS +gckHARDWARE_PrepareFunctions( + gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gckOS os; + gctUINT32 offset = 0; + gctUINT32 mmuBytes; + gctUINT32 endBytes; + gctUINT8_PTR logical; + + gcmkHEADER_ARG("%x", Hardware); + + os = Hardware->os; + + gcmkVERIFY_OK(gckOS_GetPageSize(os, &Hardware->functionBytes)); + + /* Allocate a command buffer. */ + gcmkONERROR(gckOS_AllocateNonPagedMemory( + os, + gcvFALSE, + &Hardware->functionBytes, + &Hardware->functionPhysical, + &Hardware->functionLogical + )); + + gcmkONERROR(gckOS_GetPhysicalAddress( + os, + Hardware->functionLogical, + &Hardware->functionAddress + )); + + if (Hardware->mmuVersion > 0) + { + /* MMU configure command sequence. */ + logical = (gctUINT8_PTR)Hardware->functionLogical + offset; + + Hardware->functions[gcvHARDWARE_FUNCTION_MMU].address + = Hardware->functionAddress + offset; + + gcmkONERROR(gckHARDWARE_SetMMUStates( + Hardware, + Hardware->kernel->mmu->mtlbLogical, + gcvMMU_MODE_4K, + (gctUINT8_PTR)Hardware->kernel->mmu->mtlbLogical + gcdMMU_MTLB_SIZE, + logical, + &mmuBytes + )); + + offset += mmuBytes; + + logical = (gctUINT8_PTR)Hardware->functionLogical + offset; + + gcmkONERROR(gckHARDWARE_End( + Hardware, + gcvNULL, + &endBytes + )); + + gcmkONERROR(gckHARDWARE_End( + Hardware, + logical, + &endBytes + )); + + offset += endBytes; + + Hardware->functions[gcvHARDWARE_FUNCTION_MMU].bytes = mmuBytes + endBytes; + } + + gcmkASSERT(offset < Hardware->functionBytes); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + + diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.h b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.h new file mode 100644 index 00000000000000..2ffa1468b5e430 --- /dev/null +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.h @@ -0,0 +1,160 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_hardware_h_ +#define __gc_hal_kernel_hardware_h_ + +#if gcdENABLE_VG +#include "gc_hal_kernel_hardware_vg.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + gcvHARDWARE_FUNCTION_MMU, + gcvHARDWARE_FUNCTION_FLUSH, + + gcvHARDWARE_FUNCTION_NUM, +} +gceHARDWARE_FUNCTION; + + +typedef struct _gcsHARWARE_FUNCTION +{ + /* Entry of the function. */ + gctUINT32 address; + + /* Bytes of the function. */ + gctUINT32 bytes; +} +gcsHARDWARE_FUNCTION; + +/* gckHARDWARE object. */ +struct _gckHARDWARE +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to gctKERNEL object. */ + gckKERNEL kernel; + + /* Pointer to gctOS object. */ + gckOS os; + + /* Core */ + gceCORE core; + + /* Chip characteristics. */ + gcsHAL_QUERY_CHIP_IDENTITY identity; + gctBOOL allowFastClear; + gctBOOL allowCompression; + gctUINT32 powerBaseAddress; + gctBOOL extraEventStates; + + /* Big endian */ + gctBOOL bigEndian; + + /* Chip status */ + gctPOINTER powerMutex; + gctUINT32 powerProcess; + gctUINT32 powerThread; + gceCHIPPOWERSTATE chipPowerState; + gctUINT32 lastWaitLink; + gctUINT32 lastEnd; + gctBOOL clockState; + gctBOOL powerState; + gctPOINTER globalSemaphore; + + gctISRMANAGERFUNC startIsr; + gctISRMANAGERFUNC stopIsr; + gctPOINTER isrContext; + + gctUINT32 mmuVersion; + + /* Whether use new MMU. It is meaningless + ** for old MMU since old MMU is always enabled. + */ + gctBOOL enableMMU; + + /* Type */ + gceHARDWARE_TYPE type; + +#if gcdPOWEROFF_TIMEOUT + gctUINT32 powerOffTime; + gctUINT32 powerOffTimeout; + gctPOINTER powerOffTimer; +#endif + +#if gcdENABLE_FSCALE_VAL_ADJUST + gctUINT32 powerOnFscaleVal; +#endif + gctPOINTER pageTableDirty; + +#if gcdLINK_QUEUE_SIZE + struct _gckLINKQUEUE linkQueue; +#endif + + gctBOOL powerManagement; + gctBOOL powerManagementLock; + gctBOOL gpuProfiler; + + gctBOOL endAfterFlushMmuCache; + + gctUINT32 minFscaleValue; + + gctPOINTER pendingEvent; + + /* Function used by gckHARDWARE. */ + gctPHYS_ADDR functionPhysical; + gctPOINTER functionLogical; + gctUINT32 functionAddress; + gctSIZE_T functionBytes; + + gcsHARDWARE_FUNCTION functions[gcvHARDWARE_FUNCTION_NUM]; +}; + +gceSTATUS +gckHARDWARE_GetBaseAddress( + IN gckHARDWARE Hardware, + OUT gctUINT32_PTR BaseAddress + ); + +gceSTATUS +gckHARDWARE_NeedBaseAddress( + IN gckHARDWARE Hardware, + IN gctUINT32 State, + OUT gctBOOL_PTR NeedBase + ); + +gceSTATUS +gckHARDWARE_GetFrameInfo( + IN gckHARDWARE Hardware, + OUT gcsHAL_FRAME_INFO * FrameInfo + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_kernel_hardware_h_ */ + diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_recorder.c b/drivers/gpu/galcore/arch/gc_hal_kernel_recorder.c new file mode 100644 index 00000000000000..087fa8353ff18f --- /dev/null +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_recorder.c @@ -0,0 +1,679 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal.h" +#include "gc_hal_kernel.h" +#include "gc_hal_kernel_context.h" + +/* + * ----------------------- + * HARDWARE STATE RECORDER + * ----------------------- + * + * State mirror buffer is used to 'mirror' hardware states since hardware + * states can't be dumpped. It is a context buffer which stores 'global' + * context. + * + * For each commit, state recorder + * 1) Records context buffer (if there is) and command buffers in this commit. + * 2) Parse those buffers to estimate the state changed. + * 3) Stores result to a mirror buffer. + * + * == Commit 0 ==================================================================== + * + * Context Buffer 0 + * + * Command Buffer 0 + * + * Mirror Buffer 0 <- Context Buffer 0 + Command Buffer 0 + * + * == Commit 1 ==================================================================== + * + * Command Buffer 1 + * + * Mirror Buffer 1 <- Command buffer 1 + Mirror Buffer 0 + * + * == Commit 2 ==================================================================== + * + * Context Buffer 2 (optional) + * + * Command Buffer 2 + * + * Mirror Buffer 2 <- Command buffer 2 + Context Buffer 2 + Mirror Buffer 1 + * + * == Commit N ==================================================================== + * + * For Commit N, these buffers are needed to reproduce hardware's behavior in + * this commit. + * + * Mirror Buffer [N - 1] : State Mirror accumlated by past commits, + * which is used to restore hardware state. + * Context Buffer [N] : + * Command Buffer [N] : Command buffer executed by hardware in this commit. + * + * If sequence of states programming matters, hardware's behavior can't be reproduced, + * but the state values stored in mirror buffer are assuring. + */ + +/* Queue size. */ +#define gcdNUM_RECORDS 6 + +typedef struct _gcsPARSER_HANDLER * gckPARSER_HANDLER; + +typedef void +(*HandlerFunction)( + IN gckPARSER_HANDLER Handler, + IN gctUINT32 Addr, + IN gctUINT32 Data + ); + +typedef struct _gcsPARSER_HANDLER +{ + gctUINT32 type; + gctUINT32 cmd; + gctPOINTER private; + HandlerFunction function; +} +gcsPARSER_HANDLER; + +typedef struct _gcsPARSER * gckPARSER; +typedef struct _gcsPARSER +{ + gctUINT8_PTR currentCmdBufferAddr; + + /* Current command. */ + gctUINT32 lo; + gctUINT32 hi; + + gctUINT8 cmdOpcode; + gctUINT16 cmdAddr; + gctUINT32 cmdSize; + gctUINT32 cmdRectCount; + gctUINT8 skip; + gctUINT32 skipCount; + + gctBOOL allow; + + /* Callback used by parser to handle a command. */ + gckPARSER_HANDLER commandHandler; +} +gcsPARSER; + +typedef struct _gcsMIRROR +{ + gctUINT32_PTR logical[gcdNUM_RECORDS]; + gctUINT32 bytes; + gcsSTATE_MAP_PTR map; + gctUINT32 stateCount; +} +gcsMIRROR; + +typedef struct _gcsDELTA +{ + gctUINT64 commitStamp; + gctUINT32_PTR command; + gctUINT32 commandBytes; + gctUINT32_PTR context; + gctUINT32 contextBytes; +} +gcsDELTA; + +typedef struct _gcsRECORDER +{ + gckOS os; + gcsMIRROR mirror; + gcsDELTA deltas[gcdNUM_RECORDS]; + + /* Index of current record. */ + gctUINT index; + + /* Number of records. */ + gctUINT num; + + /* Plugin used by gckPARSER. */ + gcsPARSER_HANDLER recorderHandler; + gckPARSER parser; +} +gcsRECORDER; + + +/******************************************************************************\ +***************************** Command Buffer Parser **************************** +\******************************************************************************/ + +/* +** Command buffer parser checks command buffer in FE's view to make sure there +** is no format error. +** +** Parser provide a callback mechnisam, so plug-in can be added to implement +** other functions. +*/ + +static void +_HandleLoadState( + IN OUT gckPARSER Parser + ) +{ + gctUINT i; + gctUINT32_PTR data = (gctUINT32_PTR)Parser->currentCmdBufferAddr; + gctUINT32 cmdAddr = Parser->cmdAddr; + + if (Parser->commandHandler == gcvNULL + || Parser->commandHandler->cmd != 0x01 + ) + { + /* No handler for this command. */ + return; + } + + for (i = 0; i < Parser->cmdSize; i++) + { + Parser->commandHandler->function(Parser->commandHandler, cmdAddr, *data); + + /* Advance to next state. */ + cmdAddr++; + data++; + } +} + +static void +_GetCommand( + IN OUT gckPARSER Parser + ) +{ + gctUINT32 * buffer = (gctUINT32 *)Parser->currentCmdBufferAddr; + + gctUINT16 cmdRectCount; + gctUINT16 cmdDataCount; + + Parser->hi = buffer[0]; + Parser->lo = buffer[1]; + + Parser->cmdOpcode = (((((gctUINT32) (Parser->hi)) >> (0 ? 31:27)) & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1)))))) ); + Parser->cmdRectCount = 1; + + switch (Parser->cmdOpcode) + { + case 0x01: + /* Extract count. */ + Parser->cmdSize = (((((gctUINT32) (Parser->hi)) >> (0 ? 25:16)) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1)))))) ); + if (Parser->cmdSize == 0) + { + /* 0 means 1024. */ + Parser->cmdSize = 1024; + } + Parser->skip = (Parser->cmdSize & 0x1) ? 0 : 1; + + /* Extract address. */ + Parser->cmdAddr = (((((gctUINT32) (Parser->hi)) >> (0 ? 15:0)) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1)))))) ); + + Parser->currentCmdBufferAddr = Parser->currentCmdBufferAddr + 4; + Parser->skipCount = Parser->cmdSize + Parser->skip; + break; + + case 0x05: + Parser->cmdSize = 4; + Parser->skipCount = gcmALIGN(Parser->cmdSize, 2); + break; + + case 0x06: + Parser->cmdSize = 5; + Parser->skipCount = gcmALIGN(Parser->cmdSize, 2); + break; + + case 0x0C: + Parser->cmdSize = 3; + Parser->skipCount = gcmALIGN(Parser->cmdSize, 2); + break; + + case 0x09: + Parser->cmdSize = 2; + Parser->cmdAddr = 0x0F16; + Parser->skipCount = gcmALIGN(Parser->cmdSize, 2); + break; + + case 0x04: + Parser->cmdSize = 1; + Parser->cmdAddr = 0x0F06; + + cmdRectCount = (((((gctUINT32) (Parser->hi)) >> (0 ? 15:8)) & ((gctUINT32) ((((1 ? 15:8) - (0 ? 15:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:8) - (0 ? 15:8) + 1)))))) ); + cmdDataCount = (((((gctUINT32) (Parser->hi)) >> (0 ? 26:16)) & ((gctUINT32) ((((1 ? 26:16) - (0 ? 26:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 26:16) - (0 ? 26:16) + 1)))))) ); + + Parser->skipCount = gcmALIGN(Parser->cmdSize, 2) + + cmdRectCount * 2 + + gcmALIGN(cmdDataCount, 2); + + Parser->cmdRectCount = cmdRectCount; + break; + + case 0x03: + Parser->currentCmdBufferAddr = Parser->currentCmdBufferAddr + 8; + Parser->skipCount = 0; + break; + + case 0x02: + Parser->currentCmdBufferAddr = Parser->currentCmdBufferAddr + 8; + Parser->skipCount = 0; + break; + + default: + /* Unknown command is a risk. */ + Parser->allow = gcvFALSE; + break; + } +} + +static void +_ParseCommand( + IN OUT gckPARSER Parser + ) +{ + switch(Parser->cmdOpcode) + { + case 0x01: + _HandleLoadState(Parser); + break; + case 0x05: + case 0x06: + case 0x0C: + break; + case 0x04: + break; + default: + break; + } + + /* Advance to next command. */ + Parser->currentCmdBufferAddr = Parser->currentCmdBufferAddr + + (Parser->skipCount << 2); +} + +gceSTATUS +gckPARSER_Parse( + IN gckPARSER Parser, + IN gctUINT8_PTR Buffer, + IN gctUINT32 Bytes + ) +{ + gckPARSER parser = Parser; + gctUINT8_PTR end = (gctUINT8_PTR)Buffer + Bytes; + + /* Initialize parser. */ + parser->currentCmdBufferAddr = (gctUINT8_PTR)Buffer; + parser->skip = 0; + parser->allow = gcvTRUE; + + /* Go through command buffer until reaching the end + ** or meeting an error. */ + do + { + _GetCommand(parser); + + _ParseCommand(parser); + } + while ((parser->currentCmdBufferAddr < end) && (parser->allow == gcvTRUE)); + + if (parser->allow == gcvFALSE) + { + /* Error detected. */ + return gcvSTATUS_NOT_SUPPORTED; + } + + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckPARSER_RegisterCommandHandler +** +** Register a command handler which will be called when parser get a command. +** +*/ +gceSTATUS +gckPARSER_RegisterCommandHandler( + IN gckPARSER Parser, + IN gckPARSER_HANDLER Handler + ) +{ + Parser->commandHandler = Handler; + + return gcvSTATUS_OK; +} + +gceSTATUS +gckPARSER_Construct( + IN gckOS Os, + IN gckPARSER_HANDLER Handler, + OUT gckPARSER * Parser + ) +{ + gceSTATUS status; + gckPARSER pointer; + + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsPARSER), (gctPOINTER *)&pointer)); + + /* Put it here temp, should have a more general plug-in mechnisam. */ + pointer->commandHandler = Handler; + + *Parser = pointer; + + return gcvSTATUS_OK; + +OnError: + return status; +} + +void +gckPARSER_Destroy( + IN gckOS Os, + IN gckPARSER Parser + ) +{ + gcmkOS_SAFE_FREE(Os, Parser); +} + +/******************************************************************************\ +**************************** Hardware States Recorder ************************** +\******************************************************************************/ + +static void +_RecodeState( + IN gckPARSER_HANDLER Handler, + IN gctUINT32 Addr, + IN gctUINT32 Data + ) +{ + gcmkVERIFY_OK(gckRECORDER_UpdateMirror(Handler->private, Addr, Data)); +} + +static gctUINT +_Previous( + IN gctUINT Index + ) +{ + if (Index == 0) + { + return gcdNUM_RECORDS - 1; + } + + return Index - 1; +} + +static gctUINT +_Next( + IN gctUINT Index + ) +{ + return (Index + 1) % gcdNUM_RECORDS; +} + +gceSTATUS +gckRECORDER_Construct( + IN gckOS Os, + IN gckHARDWARE Hardware, + OUT gckRECORDER * Recorder + ) +{ + gceSTATUS status; + gckCONTEXT context = gcvNULL; + gckRECORDER recorder = gcvNULL; + gctUINT32 mapSize; + gctUINT i; + gctBOOL virtualCommandBuffer = Hardware->kernel->virtualCommandBuffer; + + /* TODO: We only need context buffer and state map, it should be able to get without construct a + ** new context. + ** Now it is leaked, since we can't free it when command buffer is gone. + */ + + /* MMU is not ready now. */ + Hardware->kernel->virtualCommandBuffer = gcvFALSE; + + gcmkONERROR(gckCONTEXT_Construct(Os, Hardware, 0, &context)); + + /* Restore. */ + Hardware->kernel->virtualCommandBuffer = virtualCommandBuffer; + + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsRECORDER), (gctPOINTER *)&recorder)); + + gckOS_ZeroMemory(recorder, gcmSIZEOF(gcsRECORDER)); + + /* Copy state map. */ + recorder->mirror.stateCount = context->stateCount; + + mapSize = context->stateCount * gcmSIZEOF(gcsSTATE_MAP); + + gcmkONERROR(gckOS_Allocate(Os, mapSize, (gctPOINTER *)&recorder->mirror.map)); + + gckOS_MemCopy(recorder->mirror.map, context->map, mapSize); + + /* Copy context buffer. */ + recorder->mirror.bytes = context->totalSize; + + for (i = 0; i < gcdNUM_RECORDS; i++) + { + gcmkONERROR(gckOS_Allocate(Os, context->totalSize, (gctPOINTER *)&recorder->mirror.logical[i])); + gckOS_MemCopy(recorder->mirror.logical[i], context->buffer->logical, context->totalSize); + } + + for (i = 0; i < gcdNUM_RECORDS; i++) + { + /* TODO : Optimize size. */ + gcmkONERROR(gckOS_Allocate(Os, gcdCMD_BUFFER_SIZE, (gctPOINTER *)&recorder->deltas[i].command)); + gcmkONERROR(gckOS_Allocate(Os, context->totalSize, (gctPOINTER *)&recorder->deltas[i].context)); + } + + recorder->index = 0; + recorder->num = 0; + + /* Initialize Parser plugin. */ + recorder->recorderHandler.cmd = 0x01; + recorder->recorderHandler.private = recorder; + recorder->recorderHandler.function = _RecodeState; + + gcmkONERROR(gckPARSER_Construct(Os, &recorder->recorderHandler, &recorder->parser)); + + recorder->os = Os; + + *Recorder = recorder; + + return gcvSTATUS_OK; + +OnError: + if (recorder) + { + gckRECORDER_Destory(Os, recorder); + } + + return status; +} + +gceSTATUS +gckRECORDER_Destory( + IN gckOS Os, + IN gckRECORDER Recorder + ) +{ + gctUINT i; + + if (Recorder->mirror.map) + { + gcmkOS_SAFE_FREE(Os, Recorder->mirror.map); + } + + for (i = 0; i < gcdNUM_RECORDS; i++) + { + if (Recorder->mirror.logical[i]) + { + gcmkOS_SAFE_FREE(Os, Recorder->mirror.logical[i]); + } + } + + for (i = 0; i < gcdNUM_RECORDS; i++) + { + if (Recorder->deltas[i].command) + { + gcmkOS_SAFE_FREE(Os, Recorder->deltas[i].command); + } + + if (Recorder->deltas[i].context) + { + gcmkOS_SAFE_FREE(Os, Recorder->deltas[i].context); + } + } + + if (Recorder->parser) + { + gckPARSER_Destroy(Os, Recorder->parser); + } + + gcmkOS_SAFE_FREE(Os, Recorder); + + return gcvSTATUS_OK; +} + +gceSTATUS +gckRECORDER_UpdateMirror( + IN gckRECORDER Recorder, + IN gctUINT32 State, + IN gctUINT32 Data + ) +{ + gctUINT32 index; + gcsSTATE_MAP_PTR map = Recorder->mirror.map; + gctUINT32_PTR buffer = Recorder->mirror.logical[Recorder->index]; + + if (State >= Recorder->mirror.stateCount) + { + /* Ignore them just like HW does. */ + return gcvSTATUS_OK; + } + + index = map[State].index; + + if (index) + { + buffer[index] = Data; + } + + return gcvSTATUS_OK; +} + +void +gckRECORDER_AdvanceIndex( + IN gckRECORDER Recorder, + IN gctUINT64 CommitStamp + ) +{ + /* Get next record. */ + gctUINT next = (Recorder->index + 1) % gcdNUM_RECORDS; + + /* Record stamp of this commit. */ + Recorder->deltas[Recorder->index].commitStamp = CommitStamp; + + /* Mirror of next record is mirror of this record and delta in next record. */ + gckOS_MemCopy(Recorder->mirror.logical[next], + Recorder->mirror.logical[Recorder->index], Recorder->mirror.bytes); + + /* Advance to next record. */ + Recorder->index = next; + + Recorder->num = gcmMIN(Recorder->num + 1, gcdNUM_RECORDS - 1); + + + /* Reset delta. */ + Recorder->deltas[Recorder->index].commandBytes = 0; + Recorder->deltas[Recorder->index].contextBytes = 0; +} + +void +gckRECORDER_Record( + IN gckRECORDER Recorder, + IN gctUINT8_PTR CommandBuffer, + IN gctUINT32 CommandBytes, + IN gctUINT8_PTR ContextBuffer, + IN gctUINT32 ContextBytes + ) +{ + gcsDELTA * delta = &Recorder->deltas[Recorder->index]; + + if (CommandBytes != 0xFFFFFFFF) + { + gckPARSER_Parse(Recorder->parser, CommandBuffer, CommandBytes); + gckOS_MemCopy(delta->command, CommandBuffer, CommandBytes); + delta->commandBytes = CommandBytes; + } + + if (ContextBytes != 0xFFFFFFFF) + { + gckPARSER_Parse(Recorder->parser, ContextBuffer, ContextBytes); + gckOS_MemCopy(delta->context, ContextBuffer, ContextBytes); + delta->contextBytes = ContextBytes; + } +} + +void +gckRECORDER_Dump( + IN gckRECORDER Recorder + ) +{ + gctUINT last = Recorder->index; + gctUINT previous; + gctUINT i; + gcsMIRROR *mirror = &Recorder->mirror; + gcsDELTA *delta; + gckOS os = Recorder->os; + + for (i = 0; i < Recorder->num; i++) + { + last = _Previous(last); + } + + for (i = 0; i < Recorder->num; i++) + { + delta = &Recorder->deltas[last]; + + /* Dump record */ + gcmkPRINT("#[commit %llu]", delta->commitStamp); + + if (delta->commitStamp) + { + previous = _Previous(last); + + gcmkPRINT("#[mirror]"); + gckOS_DumpBuffer(os, mirror->logical[previous], mirror->bytes, gceDUMP_BUFFER_CONTEXT, gcvTRUE); + gcmkPRINT("@[kernel.execute]"); + } + + if (delta->contextBytes) + { + gckOS_DumpBuffer(os, delta->context, delta->contextBytes, gceDUMP_BUFFER_CONTEXT, gcvTRUE); + gcmkPRINT("@[kernel.execute]"); + } + + gckOS_DumpBuffer(os, delta->command, delta->commandBytes, gceDUMP_BUFFER_USER, gcvTRUE); + gcmkPRINT("@[kernel.execute]"); + + last = _Next(last); + } +} + + diff --git a/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.c b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.c new file mode 100644 index 00000000000000..0013e63b9a53f9 --- /dev/null +++ b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.c @@ -0,0 +1,932 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal.h" +#include "gc_hal_kernel.h" + +#if gcdENABLE_VG + +#include "gc_hal_kernel_hardware_command_vg.h" + +#define _GC_OBJ_ZONE gcvZONE_COMMAND + +/******************************************************************************\ +****************************** gckVGCOMMAND API code ***************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckVGCOMMAND_InitializeInfo +** +** Initialize architecture dependent command buffer information. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to the Command object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVGCOMMAND_InitializeInfo( + IN gckVGCOMMAND Command + ) +{ + gceSTATUS status; + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + do + { + /* Reset interrupts. */ + Command->info.feBufferInt = -1; + Command->info.tsOverflowInt = -1; + + /* Set command buffer attributes. */ + Command->info.addressAlignment = 64; + Command->info.commandAlignment = 8; + + /* Determine command alignment address mask. */ + Command->info.addressMask = ((((gctUINT32) (Command->info.addressAlignment - 1)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) ((gctUINT32) (0 ) & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))); + + /* Query the number of bytes needed by the STATE command. */ + gcmkERR_BREAK(gckVGCOMMAND_StateCommand( + Command, 0x0, gcvNULL, (gctUINT32)~0, 0, + &Command->info.stateCommandSize + )); + + /* Query the number of bytes needed by the RESTART command. */ + gcmkERR_BREAK(gckVGCOMMAND_RestartCommand( + Command, gcvNULL, (gctUINT32)~0, 0, + &Command->info.restartCommandSize + )); + + /* Query the number of bytes needed by the FETCH command. */ + gcmkERR_BREAK(gckVGCOMMAND_FetchCommand( + Command, gcvNULL, (gctUINT32)~0, 0, + &Command->info.fetchCommandSize + )); + + /* Query the number of bytes needed by the CALL command. */ + gcmkERR_BREAK(gckVGCOMMAND_CallCommand( + Command, gcvNULL, (gctUINT32)~0, 0, + &Command->info.callCommandSize + )); + + /* Query the number of bytes needed by the RETURN command. */ + gcmkERR_BREAK(gckVGCOMMAND_ReturnCommand( + Command, gcvNULL, + &Command->info.returnCommandSize + )); + + /* Query the number of bytes needed by the EVENT command. */ + gcmkERR_BREAK(gckVGCOMMAND_EventCommand( + Command, gcvNULL, gcvBLOCK_PIXEL, -1, + &Command->info.eventCommandSize + )); + + /* Query the number of bytes needed by the END command. */ + gcmkERR_BREAK(gckVGCOMMAND_EndCommand( + Command, gcvNULL, -1, + &Command->info.endCommandSize + )); + + /* Determine the tail reserve size. */ + Command->info.staticTailSize = gcmMAX( + Command->info.fetchCommandSize, + gcmMAX( + Command->info.returnCommandSize, + Command->info.endCommandSize + ) + ); + + /* Determine the maximum tail size. */ + Command->info.dynamicTailSize + = Command->info.staticTailSize + + Command->info.eventCommandSize * gcvBLOCK_COUNT; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +/******************************************************************************* +** +** gckVGCOMMAND_StateCommand +** +** Append a STATE command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to an gckVGCOMMAND object. +** +** gctUINT32 Pipe +** Harwdare destination pipe. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** STATE command at or gcvNULL to query the size of the command. +** +** gctUINT32 Address +** Starting register address of the state buffer. +** If 'Logical' is gcvNULL, this argument is ignored. +** +** gctUINT32 Count +** Number of states in state buffer. +** If 'Logical' is gcvNULL, this argument is ignored. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the STATE command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the STATE command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_StateCommand( + IN gckVGCOMMAND Command, + IN gctUINT32 Pipe, + IN gctPOINTER Logical, + IN gctUINT32 Address, + IN gctUINT32 Count, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Pipe=0x%x Logical=0x%x Address=0x%x Count=0x%x Bytes = 0x%x", + Command, Pipe, Logical, Address, Count, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append STATE. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x3 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 11:0) - (0 ? 11:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:0) - (0 ? 11:0) + 1))))))) << (0 ? 11:0))) | (((gctUINT32) ((gctUINT32) (Address) & ((gctUINT32) ((((1 ? 11:0) - (0 ? 11:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:0) - (0 ? 11:0) + 1))))))) << (0 ? 11:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 27:16) - (0 ? 27:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:16) - (0 ? 27:16) + 1))))))) << (0 ? 27:16))) | (((gctUINT32) ((gctUINT32) (Count) & ((gctUINT32) ((((1 ? 27:16) - (0 ? 27:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:16) - (0 ? 27:16) + 1))))))) << (0 ? 27:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 13:12) - (0 ? 13:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:12) - (0 ? 13:12) + 1))))))) << (0 ? 13:12))) | (((gctUINT32) ((gctUINT32) (Pipe) & ((gctUINT32) ((((1 ? 13:12) - (0 ? 13:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:12) - (0 ? 13:12) + 1))))))) << (0 ? 13:12))); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the STATE command. */ + *Bytes = 4 * (Count + 1); + } + } + else + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append LOAD_STATE. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (Count) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (Address) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the STATE command. */ + *Bytes = 4 * (Count + 1); + } + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGCOMMAND_RestartCommand +** +** Form a RESTART command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to an gckVGCOMMAND object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** RESTART command at or gcvNULL to query the size of the command. +** +** gctUINT32 FetchAddress +** The address of another command buffer to be executed by this RESTART +** command. If 'Logical' is gcvNULL, this argument is ignored. +** +** gctUINT FetchCount +** The number of 64-bit data quantities in another command buffer to +** be executed by this RESTART command. If 'Logical' is gcvNULL, this +** argument is ignored. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the RESTART command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the RESTART command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_RestartCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT FetchCount, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Logical=0x%x FetchAddress=0x%x FetchCount=0x%x Bytes = 0x%x", + Command, Logical, FetchAddress, FetchCount, Bytes); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + gctUINT32 beginEndMark; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Determine Begin/End flag. */ + beginEndMark = (FetchCount > 0) + ? ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 24:24) - (0 ? 24:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:24) - (0 ? 24:24) + 1))))))) << (0 ? 24:24))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 24:24) - (0 ? 24:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:24) - (0 ? 24:24) + 1))))))) << (0 ? 24:24))) + : ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 24:24) - (0 ? 24:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:24) - (0 ? 24:24) + 1))))))) << (0 ? 24:24))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 24:24) - (0 ? 24:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 24:24) - (0 ? 24:24) + 1))))))) << (0 ? 24:24))); + + /* Append RESTART. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x9 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:0) - (0 ? 20:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:0) - (0 ? 20:0) + 1))))))) << (0 ? 20:0))) | (((gctUINT32) ((gctUINT32) (FetchCount) & ((gctUINT32) ((((1 ? 20:0) - (0 ? 20:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:0) - (0 ? 20:0) + 1))))))) << (0 ? 20:0))) + | beginEndMark; + + buffer[1] + = FetchAddress; + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the RESTART command. */ + *Bytes = 8; + } + } + else + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGCOMMAND_FetchCommand +** +** Form a FETCH command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to an gckVGCOMMAND object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** FETCH command at or gcvNULL to query the size of the command. +** +** gctUINT32 FetchAddress +** The address of another command buffer to be executed by this FETCH +** command. If 'Logical' is gcvNULL, this argument is ignored. +** +** gctUINT FetchCount +** The number of 64-bit data quantities in another command buffer to +** be executed by this FETCH command. If 'Logical' is gcvNULL, this +** argument is ignored. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the FETCH command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the FETCH command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_FetchCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT FetchCount, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Logical=0x%x FetchAddress=0x%x FetchCount=0x%x Bytes = 0x%x", + Command, Logical, FetchAddress, FetchCount, Bytes); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append FETCH. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x5 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:0) - (0 ? 20:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:0) - (0 ? 20:0) + 1))))))) << (0 ? 20:0))) | (((gctUINT32) ((gctUINT32) (FetchCount) & ((gctUINT32) ((((1 ? 20:0) - (0 ? 20:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:0) - (0 ? 20:0) + 1))))))) << (0 ? 20:0))); + + buffer[1] + = gcmkFIXADDRESS(FetchAddress); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the FETCH command. */ + *Bytes = 8; + } + } + else + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append LINK. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (FetchCount) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); + + buffer[1] + = gcmkFIXADDRESS(FetchAddress); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the LINK command. */ + *Bytes = 8; + } + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGCOMMAND_CallCommand +** +** Append a CALL command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to an gckVGCOMMAND object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** CALL command at or gcvNULL to query the size of the command. +** +** gctUINT32 FetchAddress +** The address of another command buffer to be executed by this CALL +** command. If 'Logical' is gcvNULL, this argument is ignored. +** +** gctUINT FetchCount +** The number of 64-bit data quantities in another command buffer to +** be executed by this CALL command. If 'Logical' is gcvNULL, this +** argument is ignored. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the CALL command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the CALL command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_CallCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT FetchCount, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Logical=0x%x FetchAddress=0x%x FetchCount=0x%x Bytes = 0x%x", + Command, Logical, FetchAddress, FetchCount, Bytes); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append CALL. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x6 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:0) - (0 ? 20:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:0) - (0 ? 20:0) + 1))))))) << (0 ? 20:0))) | (((gctUINT32) ((gctUINT32) (FetchCount) & ((gctUINT32) ((((1 ? 20:0) - (0 ? 20:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:0) - (0 ? 20:0) + 1))))))) << (0 ? 20:0))); + + buffer[1] + = gcmkFIXADDRESS(FetchAddress); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the CALL command. */ + *Bytes = 8; + } + } + else + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGCOMMAND_ReturnCommand +** +** Append a RETURN command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to an gckVGCOMMAND object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** RETURN command at or gcvNULL to query the size of the command. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the RETURN command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the RETURN command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_ReturnCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Logical=0x%x Bytes = 0x%x", + Command, Logical, Bytes); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append RETURN. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x7 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the RETURN command. */ + *Bytes = 8; + } + } + else + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGCOMMAND_EventCommand +** +** Form an EVENT command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to the Command object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** EVENT command at or gcvNULL to query the size of the command. +** +** gctINT32 InterruptId +** The ID of the interrupt to generate. +** If 'Logical' is gcvNULL, this argument is ignored. +** +** gceBLOCK Block +** Block that will generate the interrupt. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the EVENT command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the END command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_EventCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gceBLOCK Block, + IN gctINT32 InterruptId, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Logical=0x%x Block=0x%x InterruptId=0x%x Bytes = 0x%x", + Command, Logical, Block, InterruptId, Bytes); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + typedef struct _gcsEVENTSTATES + { + /* Chips before VG21 use these values. */ + gctUINT eventFromFE; + gctUINT eventFromPE; + + /* VG21 chips and later use SOURCE field. */ + gctUINT eventSource; + } + gcsEVENTSTATES; + + static gcsEVENTSTATES states[] = + { + /* gcvBLOCK_COMMAND */ + { + (gctUINT)~0, + (gctUINT)~0, + (gctUINT)~0 + }, + + /* gcvBLOCK_TESSELLATOR */ + { + 0x0, + 0x1, + 0x10 + }, + + /* gcvBLOCK_TESSELLATOR2 */ + { + 0x0, + 0x1, + 0x12 + }, + + /* gcvBLOCK_TESSELLATOR3 */ + { + 0x0, + 0x1, + 0x14 + }, + + /* gcvBLOCK_RASTER */ + { + 0x0, + 0x1, + 0x07, + }, + + /* gcvBLOCK_VG */ + { + 0x0, + 0x1, + 0x0F + }, + + /* gcvBLOCK_VG2 */ + { + 0x0, + 0x1, + 0x11 + }, + + /* gcvBLOCK_VG3 */ + { + 0x0, + 0x1, + 0x13 + }, + + /* gcvBLOCK_PIXEL */ + { + 0x0, + 0x1, + 0x07 + }, + }; + + /* Verify block ID. */ + gcmkVERIFY_ARGUMENT(gcmIS_VALID_INDEX(Block, states)); + + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Verify the event ID. */ + gcmkVERIFY_ARGUMENT(InterruptId >= 0); + gcmkVERIFY_ARGUMENT(InterruptId <= ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1)))))); + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append EVENT. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x3 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 11:0) - (0 ? 11:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:0) - (0 ? 11:0) + 1))))))) << (0 ? 11:0))) | (((gctUINT32) ((gctUINT32) (0x0E01) & ((gctUINT32) ((((1 ? 11:0) - (0 ? 11:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:0) - (0 ? 11:0) + 1))))))) << (0 ? 11:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 27:16) - (0 ? 27:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:16) - (0 ? 27:16) + 1))))))) << (0 ? 27:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 27:16) - (0 ? 27:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:16) - (0 ? 27:16) + 1))))))) << (0 ? 27:16))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 13:12) - (0 ? 13:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:12) - (0 ? 13:12) + 1))))))) << (0 ? 13:12))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 13:12) - (0 ? 13:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:12) - (0 ? 13:12) + 1))))))) << (0 ? 13:12))); + + /* Determine chip version. */ + if (Command->vg21) + { + /* Get the event source for the block. */ + gctUINT eventSource = states[Block].eventSource; + + /* Supported? */ + if (eventSource == ~0) + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (InterruptId) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))) | (((gctUINT32) ((gctUINT32) (eventSource) & ((gctUINT32) ((((1 ? 12:8) - (0 ? 12:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:8) - (0 ? 12:8) + 1))))))) << (0 ? 12:8))); + } + else + { + /* Get the event source for the block. */ + gctUINT eventFromFE = states[Block].eventFromFE; + gctUINT eventFromPE = states[Block].eventFromPE; + + /* Supported? */ + if (eventFromFE == ~0) + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (InterruptId) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) ((gctUINT32) (eventFromFE) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) ((gctUINT32) (eventFromPE) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + } + } + + if (Bytes != gcvNULL) + { + /* Make sure the events are directly supported for the block. */ + if (states[Block].eventSource == ~0) + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + /* Return number of bytes required by the END command. */ + *Bytes = 8; + } + } + else + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Verify the event ID. */ + gcmkVERIFY_ARGUMENT(InterruptId >= 0); + gcmkVERIFY_ARGUMENT(InterruptId <= ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1)))))); + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append EVENT. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E01) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + /* Determine event source. */ + if (Block == gcvBLOCK_COMMAND) + { + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (InterruptId) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1))))))) << (0 ? 5:5))); + } + else + { + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (InterruptId) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + } + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the EVENT and END commands. */ + *Bytes = 8; + } + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGCOMMAND_EndCommand +** +** Form an END command at the specified location in the command buffer. +** +** INPUT: +** +** gckVGCOMMAND Command +** Pointer to the Command object. +** +** gctPOINTER Logical +** Pointer to the current location inside the command buffer to append +** END command at or gcvNULL to query the size of the command. +** +** gctINT32 InterruptId +** The ID of the interrupt to generate. +** If 'Logical' is gcvNULL, this argument will be ignored. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes available for the END command. +** If 'Logical' is gcvNULL, the value from this argument is ignored. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that will receive the number of bytes required +** for the END command. If 'Bytes' is gcvNULL, nothing is returned. +*/ +gceSTATUS +gckVGCOMMAND_EndCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctINT32 InterruptId, + IN OUT gctUINT32 * Bytes + ) +{ + gcmkHEADER_ARG("Command=0x%x Logical=0x%x InterruptId=0x%x Bytes = 0x%x", + Command, Logical, InterruptId, Bytes); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->fe20) + { + if (Logical != gcvNULL) + { + gctUINT32_PTR buffer; + + /* Verify the event ID. */ + gcmkVERIFY_ARGUMENT(InterruptId >= 0); + + /* Cast the buffer pointer. */ + buffer = (gctUINT32_PTR) Logical; + + /* Append END. */ + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 31:28) - (0 ? 31:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:28) - (0 ? 31:28) + 1))))))) << (0 ? 31:28))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (InterruptId) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the END command. */ + *Bytes = 8; + } + } + else + { + if (Logical != gcvNULL) + { + gctUINT32_PTR memory; + + /* Verify the event ID. */ + gcmkVERIFY_ARGUMENT(InterruptId >= 0); + + /* Cast the buffer pointer. */ + memory = (gctUINT32_PTR) Logical; + + /* Append EVENT. */ + memory[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E01) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + memory[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) | (((gctUINT32) ((gctUINT32) (InterruptId) & ((gctUINT32) ((((1 ? 4:0) - (0 ? 4:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:0) - (0 ? 4:0) + 1))))))) << (0 ? 4:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); + + /* Append END. */ + memory[2] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x02 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); + } + + if (Bytes != gcvNULL) + { + /* Return number of bytes required by the EVENT and END commands. */ + *Bytes = 16; + } + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +#endif /* gcdENABLE_VG */ + diff --git a/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.h b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.h new file mode 100644 index 00000000000000..22093a9655a8f6 --- /dev/null +++ b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_command_vg.h @@ -0,0 +1,319 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_hardware_command_vg_h_ +#define __gc_hal_kernel_hardware_command_vg_h_ + +/******************************************************************************\ +******************* Task and Interrupt Management Structures. ****************** +\******************************************************************************/ + +/* Task storage header. */ +typedef struct _gcsTASK_STORAGE * gcsTASK_STORAGE_PTR; +typedef struct _gcsTASK_STORAGE +{ + /* Next allocated storage buffer. */ + gcsTASK_STORAGE_PTR next; +} +gcsTASK_STORAGE; + +/* Task container header. */ +typedef struct _gcsTASK_CONTAINER * gcsTASK_CONTAINER_PTR; +typedef struct _gcsTASK_CONTAINER +{ + /* The number of tasks left to be processed in the container. */ + gctINT referenceCount; + + /* Size of the buffer. */ + gctUINT size; + + /* Link to the previous and the next allocated containers. */ + gcsTASK_CONTAINER_PTR allocPrev; + gcsTASK_CONTAINER_PTR allocNext; + + /* Link to the previous and the next containers in the free list. */ + gcsTASK_CONTAINER_PTR freePrev; + gcsTASK_CONTAINER_PTR freeNext; +} +gcsTASK_CONTAINER; + +/* Kernel space task master table entry. */ +typedef struct _gcsBLOCK_TASK_ENTRY * gcsBLOCK_TASK_ENTRY_PTR; +typedef struct _gcsBLOCK_TASK_ENTRY +{ + /* Pointer to the current task container for the block. */ + gcsTASK_CONTAINER_PTR container; + + /* Pointer to the current task data within the container. */ + gcsTASK_HEADER_PTR task; + + /* Pointer to the last link task within the container. */ + gcsTASK_LINK_PTR link; + + /* Number of interrupts allocated for this block. */ + gctUINT interruptCount; + + /* The index of the current interrupt. */ + gctUINT interruptIndex; + + /* Interrupt semaphore. */ + gctSEMAPHORE interruptSemaphore; + + /* Interrupt value array. */ + gctINT32 interruptArray[32]; +} +gcsBLOCK_TASK_ENTRY; + + +/******************************************************************************\ +********************* Command Queue Management Structures. ********************* +\******************************************************************************/ + +/* Command queue kernel element pointer. */ +typedef struct _gcsKERNEL_CMDQUEUE * gcsKERNEL_CMDQUEUE_PTR; + +/* Command queue object handler function type. */ +typedef gceSTATUS (* gctOBJECT_HANDLER) ( + gckVGKERNEL Kernel, + gcsKERNEL_CMDQUEUE_PTR Entry + ); + +/* Command queue kernel element. */ +typedef struct _gcsKERNEL_CMDQUEUE +{ + /* The number of buffers in the queue. */ + gcsCMDBUFFER_PTR commandBuffer; + + /* Pointer to the object handler function. */ + gctOBJECT_HANDLER handler; +} +gcsKERNEL_CMDQUEUE; + +/* Command queue header. */ +typedef struct _gcsKERNEL_QUEUE_HEADER * gcsKERNEL_QUEUE_HEADER_PTR; +typedef struct _gcsKERNEL_QUEUE_HEADER +{ + /* The size of the buffer in bytes. */ + gctUINT size; + + /* The number of pending entries to be processed. */ + volatile gctUINT pending; + + /* The current command queue entry. */ + gcsKERNEL_CMDQUEUE_PTR currentEntry; + + /* Next buffer. */ + gcsKERNEL_QUEUE_HEADER_PTR next; +} +gcsKERNEL_QUEUE_HEADER; + + +/******************************************************************************\ +******************************* gckVGCOMMAND Object ******************************* +\******************************************************************************/ + +/* gckVGCOMMAND object. */ +struct _gckVGCOMMAND +{ + /*************************************************************************** + ** Object data and pointers. + */ + + gcsOBJECT object; + gckVGKERNEL kernel; + gckOS os; + gckVGHARDWARE hardware; + + /* Features. */ + gctBOOL fe20; + gctBOOL vg20; + gctBOOL vg21; + + + /*************************************************************************** + ** Enable command queue dumping. + */ + + gctBOOL enableDumping; + + + /*************************************************************************** + ** Bus Error interrupt. + */ + + gctINT32 busErrorInt; + + + /*************************************************************************** + ** Command buffer information. + */ + + gcsCOMMAND_BUFFER_INFO info; + + + /*************************************************************************** + ** Synchronization objects. + */ + + gctPOINTER queueMutex; + gctPOINTER taskMutex; + gctPOINTER commitMutex; + + + /*************************************************************************** + ** Task management. + */ + + /* The head of the storage buffer linked list. */ + gcsTASK_STORAGE_PTR taskStorage; + + /* Allocation size. */ + gctUINT taskStorageGranularity; + gctUINT taskStorageUsable; + + /* The free container list. */ + gcsTASK_CONTAINER_PTR taskFreeHead; + gcsTASK_CONTAINER_PTR taskFreeTail; + + /* Task table */ + gcsBLOCK_TASK_ENTRY taskTable[gcvBLOCK_COUNT]; + + + /*************************************************************************** + ** Command queue. + */ + + /* Pointer to the allocated queue memory. */ + gcsKERNEL_QUEUE_HEADER_PTR queue; + + /* Pointer to the current available queue from which new queue entries + will be allocated. */ + gcsKERNEL_QUEUE_HEADER_PTR queueHead; + + /* If different from queueHead, points to the command queue which is + currently being executed by the hardware. */ + gcsKERNEL_QUEUE_HEADER_PTR queueTail; + + /* Points to the queue to merge the tail with when the tail is processed. */ + gcsKERNEL_QUEUE_HEADER_PTR mergeQueue; + + /* Queue overflow counter. */ + gctUINT queueOverflow; + + + /*************************************************************************** + ** Context. + */ + + /* Context counter used for unique ID. */ + gctUINT64 contextCounter; + + /* Current context ID. */ + gctUINT64 currentContext; + + /* Command queue power semaphore. */ + gctPOINTER powerSemaphore; + gctINT32 powerStallInt; + gcsCMDBUFFER_PTR powerStallBuffer; + gctSIGNAL powerStallSignal; + +}; + +/******************************************************************************\ +************************ gckVGCOMMAND Object Internal API. *********************** +\******************************************************************************/ + +/* Initialize architecture dependent command buffer information. */ +gceSTATUS +gckVGCOMMAND_InitializeInfo( + IN gckVGCOMMAND Command + ); + +/* Form a STATE command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_StateCommand( + IN gckVGCOMMAND Command, + IN gctUINT32 Pipe, + IN gctPOINTER Logical, + IN gctUINT32 Address, + IN gctUINT32 Count, + IN OUT gctUINT32 * Bytes + ); + +/* Form a RESTART command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_RestartCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT FetchCount, + IN OUT gctUINT32 * Bytes + ); + +/* Form a FETCH command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_FetchCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT FetchCount, + IN OUT gctUINT32 * Bytes + ); + +/* Form a CALL command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_CallCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT FetchCount, + IN OUT gctUINT32 * Bytes + ); + +/* Form a RETURN command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_ReturnCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ); + +/* Form an EVENT command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_EventCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gceBLOCK Block, + IN gctINT32 InterruptId, + IN OUT gctUINT32 * Bytes + ); + +/* Form an END command at the specified location in the command buffer. */ +gceSTATUS +gckVGCOMMAND_EndCommand( + IN gckVGCOMMAND Command, + IN gctPOINTER Logical, + IN gctINT32 InterruptId, + IN OUT gctUINT32 * Bytes + ); + +#endif /* __gc_hal_kernel_hardware_command_h_ */ + diff --git a/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c new file mode 100644 index 00000000000000..3cb47dd04b6fc6 --- /dev/null +++ b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c @@ -0,0 +1,2119 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal.h" +#include "gc_hal_kernel.h" +#include "gc_hal_kernel_hardware_command_vg.h" + +#if gcdENABLE_VG + +#define _GC_OBJ_ZONE gcvZONE_HARDWARE + +typedef enum +{ + gcvPOWER_FLAG_INITIALIZE = 1 << 0, + gcvPOWER_FLAG_STALL = 1 << 1, + gcvPOWER_FLAG_STOP = 1 << 2, + gcvPOWER_FLAG_START = 1 << 3, + gcvPOWER_FLAG_RELEASE = 1 << 4, + gcvPOWER_FLAG_DELAY = 1 << 5, + gcvPOWER_FLAG_SAVE = 1 << 6, + gcvPOWER_FLAG_ACQUIRE = 1 << 7, + gcvPOWER_FLAG_POWER_OFF = 1 << 8, + gcvPOWER_FLAG_CLOCK_OFF = 1 << 9, + gcvPOWER_FLAG_CLOCK_ON = 1 << 10, + gcvPOWER_FLAG_NOP = 1 << 11, +} +gcePOWER_FLAGS; + +/******************************************************************************\ +********************************* Support Code ********************************* +\******************************************************************************/ +static gceSTATUS +_ResetGPU( + IN gckOS Os + ) +{ + gctUINT32 control, idle; + gceSTATUS status; + + /* Read register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Os, + gcvCORE_VG, + 0x00000, + &control)); + + for (;;) + { + /* Disable clock gating. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + gcvCORE_VG, + 0x00104, + 0x00000000)); + + /* Wait for clock being stable. */ + gcmkONERROR(gckOS_Delay(Os, 1)); + + /* Isolate the GPU. */ + control = ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + gcvCORE_VG, + 0x00000, + control)); + + /* Set soft reset. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + gcvCORE_VG, + 0x00000, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))))); + + /* Wait for reset. */ + gcmkONERROR(gckOS_Delay(Os, 1)); + + /* Reset soft reset bit. */ + gcmkONERROR(gckOS_WriteRegisterEx(Os, + gcvCORE_VG, + 0x00000, + ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 12:12) - (0 ? 12:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 12:12) - (0 ? 12:12) + 1))))))) << (0 ? 12:12))))); + + /* Reset GPU isolation. */ + control = ((((gctUINT32) (control)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 19:19) - (0 ? 19:19) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 19:19) - (0 ? 19:19) + 1))))))) << (0 ? 19:19))); + + gcmkONERROR(gckOS_WriteRegisterEx(Os, + gcvCORE_VG, + 0x00000, + control)); + + /* Read idle register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Os, + gcvCORE_VG, + 0x00004, + &idle)); + + if ((((((gctUINT32) (idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) ) == 0) + { + continue; + } + + /* Read reset register. */ + gcmkONERROR(gckOS_ReadRegisterEx(Os, + gcvCORE_VG, + 0x00000, + &control)); + + if (((((((gctUINT32) (control)) >> (0 ? 16:16)) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) ) == 0) + || ((((((gctUINT32) (control)) >> (0 ? 17:17)) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1)))))) ) == 0) + ) + { + continue; + } + + /* GPU is idle. */ + break; + } + + /* Success. */ + return gcvSTATUS_OK; + +OnError: + + /* Return the error. */ + return status; +} + + +static gceSTATUS +_IdentifyHardware( + IN gckOS Os, + OUT gceCHIPMODEL * ChipModel, + OUT gctUINT32 * ChipRevision, + OUT gctUINT32 * ChipFeatures, + OUT gctUINT32 * ChipMinorFeatures, + OUT gctUINT32 * ChipMinorFeatures2 + ) +{ + gceSTATUS status; + gctUINT32 chipIdentity; + + do + { + /* Read chip identity register. */ + gcmkERR_BREAK(gckOS_ReadRegisterEx(Os, gcvCORE_VG, 0x00018, &chipIdentity)); + + /* Special case for older graphic cores. */ + if (((((gctUINT32) (chipIdentity)) >> (0 ? 31:24) & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1)))))) == (0x01 & ((gctUINT32) ((((1 ? 31:24) - (0 ? 31:24) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:24) - (0 ? 31:24) + 1)))))))) + { + *ChipModel = gcv500; + *ChipRevision = (((((gctUINT32) (chipIdentity)) >> (0 ? 15:12)) & ((gctUINT32) ((((1 ? 15:12) - (0 ? 15:12) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:12) - (0 ? 15:12) + 1)))))) ); + } + + else + { + /* Read chip identity register. */ + gcmkERR_BREAK(gckOS_ReadRegisterEx(Os, gcvCORE_VG, + 0x00020, + (gctUINT32 *) ChipModel)); + + /* Read CHIP_REV register. */ + gcmkERR_BREAK(gckOS_ReadRegisterEx(Os, gcvCORE_VG, + 0x00024, + ChipRevision)); + } + + /* Read chip feature register. */ + gcmkERR_BREAK(gckOS_ReadRegisterEx( + Os, gcvCORE_VG, 0x0001C, ChipFeatures + )); + + /* Read chip minor feature register. */ + gcmkERR_BREAK(gckOS_ReadRegisterEx( + Os, gcvCORE_VG, 0x00034, ChipMinorFeatures + )); + + /* Read chip minor feature register #2. */ + gcmkERR_BREAK(gckOS_ReadRegisterEx( + Os, gcvCORE_VG, 0x00074, ChipMinorFeatures2 + )); + + gcmkTRACE( + gcvLEVEL_VERBOSE, + "ChipModel=0x%08X\n" + "ChipRevision=0x%08X\n" + "ChipFeatures=0x%08X\n" + "ChipMinorFeatures=0x%08X\n" + "ChipMinorFeatures2=0x%08X\n", + *ChipModel, + *ChipRevision, + *ChipFeatures, + *ChipMinorFeatures, + *ChipMinorFeatures2 + ); + + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Return the status. */ + return status; +} + +#if gcdPOWEROFF_TIMEOUT +void +_VGPowerTimerFunction( + gctPOINTER Data + ) +{ + gckVGHARDWARE hardware = (gckVGHARDWARE)Data; + gcmkVERIFY_OK( + gckVGHARDWARE_SetPowerManagementState(hardware, gcvPOWER_OFF_TIMEOUT)); +} +#endif + +/******************************************************************************\ +****************************** gckVGHARDWARE API code ***************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckVGHARDWARE_Construct +** +** Construct a new gckVGHARDWARE object. +** +** INPUT: +** +** gckOS Os +** Pointer to an initialized gckOS object. +** +** OUTPUT: +** +** gckVGHARDWARE * Hardware +** Pointer to a variable that will hold the pointer to the gckVGHARDWARE +** object. +*/ +gceSTATUS +gckVGHARDWARE_Construct( + IN gckOS Os, + OUT gckVGHARDWARE * Hardware + ) +{ + gckVGHARDWARE hardware = gcvNULL; + gceSTATUS status; + gceCHIPMODEL chipModel; + gctUINT32 chipRevision; + gctUINT32 chipFeatures; + gctUINT32 chipMinorFeatures; + gctUINT32 chipMinorFeatures2; + + gcmkHEADER_ARG("Os=0x%x Hardware=0x%x ", Os, Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Hardware != gcvNULL); + + do + { + gcmkERR_BREAK(gckOS_SetGPUPower(Os, gcvCORE_VG, gcvTRUE, gcvTRUE)); + + status = _ResetGPU(Os); + + if (status != gcvSTATUS_OK) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "_ResetGPU failed: status=%d\n", status); + } + + /* Identify the hardware. */ + gcmkERR_BREAK(_IdentifyHardware(Os, + &chipModel, &chipRevision, + &chipFeatures, &chipMinorFeatures, &chipMinorFeatures2 + )); + + /* Allocate the gckVGHARDWARE object. */ + gcmkERR_BREAK(gckOS_Allocate(Os, + gcmSIZEOF(struct _gckVGHARDWARE), (gctPOINTER *) &hardware + )); + + /* Initialize the gckVGHARDWARE object. */ + hardware->object.type = gcvOBJ_HARDWARE; + hardware->os = Os; + + /* Set chip identity. */ + hardware->chipModel = chipModel; + hardware->chipRevision = chipRevision; + hardware->chipFeatures = chipFeatures; + hardware->chipMinorFeatures = chipMinorFeatures; + hardware->chipMinorFeatures2 = chipMinorFeatures2; + + hardware->powerMutex = gcvNULL; + hardware->chipPowerState = gcvPOWER_ON; + hardware->chipPowerStateGlobal = gcvPOWER_ON; + hardware->clockState = gcvTRUE; + hardware->powerState = gcvTRUE; + +#if gcdPOWEROFF_TIMEOUT + hardware->powerOffTime = 0; + hardware->powerOffTimeout = gcdPOWEROFF_TIMEOUT; + + gcmkVERIFY_OK(gckOS_CreateTimer(Os, + _VGPowerTimerFunction, + (gctPOINTER)hardware, + &hardware->powerOffTimer)); +#endif + + /* Determine whether FE 2.0 is present. */ + hardware->fe20 = ((((gctUINT32) (hardware->chipFeatures)) >> (0 ? 28:28) & ((gctUINT32) ((((1 ? 28:28) - (0 ? 28:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 28:28) - (0 ? 28:28) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 28:28) - (0 ? 28:28) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 28:28) - (0 ? 28:28) + 1))))))); + + /* Determine whether VG 2.0 is present. */ + hardware->vg20 = ((((gctUINT32) (hardware->chipMinorFeatures)) >> (0 ? 13:13) & ((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1))))))); + + /* Determine whether VG 2.1 is present. */ + hardware->vg21 = ((((gctUINT32) (hardware->chipMinorFeatures)) >> (0 ? 18:18) & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1)))))) == (0x1 & ((gctUINT32) ((((1 ? 18:18) - (0 ? 18:18) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 18:18) - (0 ? 18:18) + 1))))))); + + /* Set default event mask. */ + hardware->eventMask = 0xFFFFFFFF; + + gcmkERR_BREAK(gckOS_AtomConstruct(Os, &hardware->pageTableDirty)); + + /* Set fast clear to auto. */ + gcmkVERIFY_OK(gckVGHARDWARE_SetFastClear(hardware, -1)); + + gcmkERR_BREAK(gckOS_CreateMutex(Os, &hardware->powerMutex)); + + /* Enable power management by default. */ + hardware->powerManagement = gcvTRUE; + + /* Return pointer to the gckVGHARDWARE object. */ + *Hardware = hardware; + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + +#if gcdPOWEROFF_TIMEOUT + if (hardware->powerOffTimer != gcvNULL) + { + gcmkVERIFY_OK(gckOS_StopTimer(Os, hardware->powerOffTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Os, hardware->powerOffTimer)); + } +#endif + + gcmkVERIFY_OK(gckOS_SetGPUPower(Os, gcvCORE_VG, gcvFALSE, gcvFALSE)); + + if (hardware != gcvNULL && hardware->pageTableDirty != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Os, hardware->pageTableDirty)); + } + + if (hardware != gcvNULL) + { + gcmkVERIFY_OK(gckOS_Free(Os, hardware)); + } + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckVGHARDWARE_Destroy +** +** Destroy an gckVGHARDWARE object. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to the gckVGHARDWARE object that needs to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVGHARDWARE_Destroy( + IN gckVGHARDWARE Hardware + ) +{ + gceSTATUS status; + gcmkHEADER_ARG("Hardware=0x%x ", Hardware); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Mark the object as unknown. */ + Hardware->object.type = gcvOBJ_UNKNOWN; + + if (Hardware->powerMutex != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex( + Hardware->os, Hardware->powerMutex)); + } + +#if gcdPOWEROFF_TIMEOUT + gcmkVERIFY_OK(gckOS_StopTimer(Hardware->os, Hardware->powerOffTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Hardware->os, Hardware->powerOffTimer)); +#endif + + if (Hardware->pageTableDirty != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Hardware->os, Hardware->pageTableDirty)); + } + + /* Free the object. */ + status = gckOS_Free(Hardware->os, Hardware); + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVGHARDWARE_QueryMemory +** +** Query the amount of memory available on the hardware. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to the gckVGHARDWARE object. +** +** OUTPUT: +** +** gctSIZE_T * InternalSize +** Pointer to a variable that will hold the size of the internal video +** memory in bytes. If 'InternalSize' is gcvNULL, no information of the +** internal memory will be returned. +** +** gctUINT32 * InternalBaseAddress +** Pointer to a variable that will hold the hardware's base address for +** the internal video memory. This pointer cannot be gcvNULL if +** 'InternalSize' is also non-gcvNULL. +** +** gctUINT32 * InternalAlignment +** Pointer to a variable that will hold the hardware's base address for +** the internal video memory. This pointer cannot be gcvNULL if +** 'InternalSize' is also non-gcvNULL. +** +** gctSIZE_T * ExternalSize +** Pointer to a variable that will hold the size of the external video +** memory in bytes. If 'ExternalSize' is gcvNULL, no information of the +** external memory will be returned. +** +** gctUINT32 * ExternalBaseAddress +** Pointer to a variable that will hold the hardware's base address for +** the external video memory. This pointer cannot be gcvNULL if +** 'ExternalSize' is also non-gcvNULL. +** +** gctUINT32 * ExternalAlignment +** Pointer to a variable that will hold the hardware's base address for +** the external video memory. This pointer cannot be gcvNULL if +** 'ExternalSize' is also non-gcvNULL. +** +** gctUINT32 * HorizontalTileSize +** Number of horizontal pixels per tile. If 'HorizontalTileSize' is +** gcvNULL, no horizontal pixel per tile will be returned. +** +** gctUINT32 * VerticalTileSize +** Number of vertical pixels per tile. If 'VerticalTileSize' is +** gcvNULL, no vertical pixel per tile will be returned. +*/ +gceSTATUS +gckVGHARDWARE_QueryMemory( + IN gckVGHARDWARE Hardware, + OUT gctSIZE_T * InternalSize, + OUT gctUINT32 * InternalBaseAddress, + OUT gctUINT32 * InternalAlignment, + OUT gctSIZE_T * ExternalSize, + OUT gctUINT32 * ExternalBaseAddress, + OUT gctUINT32 * ExternalAlignment, + OUT gctUINT32 * HorizontalTileSize, + OUT gctUINT32 * VerticalTileSize + ) +{ + gcmkHEADER_ARG("Hardware=0x%x InternalSize=0x%x InternalBaseAddress=0x%x InternalAlignment=0x%x" + "ExternalSize=0x%x ExternalBaseAddress=0x%x ExternalAlignment=0x%x HorizontalTileSize=0x%x VerticalTileSize=0x%x", + Hardware, InternalSize, InternalBaseAddress, InternalAlignment, + ExternalSize, ExternalBaseAddress, ExternalAlignment, HorizontalTileSize, VerticalTileSize); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (InternalSize != gcvNULL) + { + /* No internal memory. */ + *InternalSize = 0; + } + + if (ExternalSize != gcvNULL) + { + /* No external memory. */ + *ExternalSize = 0; + } + + if (HorizontalTileSize != gcvNULL) + { + /* 4x4 tiles. */ + *HorizontalTileSize = 4; + } + + if (VerticalTileSize != gcvNULL) + { + /* 4x4 tiles. */ + *VerticalTileSize = 4; + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_QueryChipIdentity +** +** Query the identity of the hardware. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to the gckVGHARDWARE object. +** +** OUTPUT: +** +** gceCHIPMODEL * ChipModel +** If 'ChipModel' is not gcvNULL, the variable it points to will +** receive the model of the chip. +** +** gctUINT32 * ChipRevision +** If 'ChipRevision' is not gcvNULL, the variable it points to will +** receive the revision of the chip. +** +** gctUINT32 * ChipFeatures +** If 'ChipFeatures' is not gcvNULL, the variable it points to will +** receive the feature set of the chip. +** +** gctUINT32 * ChipMinorFeatures +** If 'ChipMinorFeatures' is not gcvNULL, the variable it points to +** will receive the minor feature set of the chip. +** +** gctUINT32 * ChipMinorFeatures2 +** If 'ChipMinorFeatures2' is not gcvNULL, the variable it points to +** will receive the minor feature set of the chip. +** +*/ +gceSTATUS +gckVGHARDWARE_QueryChipIdentity( + IN gckVGHARDWARE Hardware, + OUT gceCHIPMODEL * ChipModel, + OUT gctUINT32 * ChipRevision, + OUT gctUINT32* ChipFeatures, + OUT gctUINT32* ChipMinorFeatures, + OUT gctUINT32* ChipMinorFeatures2 + ) +{ + gcmkHEADER_ARG("Hardware=0x%x ChipModel=0x%x ChipRevision=0x%x ChipFeatures = 0x%x ChipMinorFeatures = 0x%x ChipMinorFeatures2 = 0x%x", + Hardware, ChipModel, ChipRevision, ChipFeatures, ChipMinorFeatures, ChipMinorFeatures2); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Return chip model. */ + if (ChipModel != gcvNULL) + { + *ChipModel = Hardware->chipModel; + } + + /* Return revision number. */ + if (ChipRevision != gcvNULL) + { + *ChipRevision = Hardware->chipRevision; + } + + /* Return feature set. */ + if (ChipFeatures != gcvNULL) + { + gctUINT32 features = Hardware->chipFeatures; + + if ((((((gctUINT32) (features)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) )) + { + features = ((((gctUINT32) (features)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) ((gctUINT32) (Hardware->allowFastClear) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))); + } + + /* Mark 2D pipe as available for GC500.0 since it did not have this *\ + \* bit. */ + if ((Hardware->chipModel == gcv500) + && (Hardware->chipRevision == 0) + ) + { + features = ((((gctUINT32) (features)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))); + } + + /* Mark 2D pipe as available for GC300 since it did not have this *\ + \* bit. */ + if (Hardware->chipModel == gcv300) + { + features = ((((gctUINT32) (features)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1))))))) << (0 ? 9:9))); + } + + *ChipFeatures = features; + } + + /* Return minor feature set. */ + if (ChipMinorFeatures != gcvNULL) + { + *ChipMinorFeatures = Hardware->chipMinorFeatures; + } + + /* Return minor feature set #2. */ + if (ChipMinorFeatures2 != gcvNULL) + { + *ChipMinorFeatures2 = Hardware->chipMinorFeatures2; + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_ConvertFormat +** +** Convert an API format to hardware parameters. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to the gckVGHARDWARE object. +** +** gceSURF_FORMAT Format +** API format to convert. +** +** OUTPUT: +** +** gctUINT32 * BitsPerPixel +** Pointer to a variable that will hold the number of bits per pixel. +** +** gctUINT32 * BytesPerTile +** Pointer to a variable that will hold the number of bytes per tile. +*/ +gceSTATUS +gckVGHARDWARE_ConvertFormat( + IN gckVGHARDWARE Hardware, + IN gceSURF_FORMAT Format, + OUT gctUINT32 * BitsPerPixel, + OUT gctUINT32 * BytesPerTile + ) +{ + gctUINT32 bitsPerPixel; + gctUINT32 bytesPerTile; + + gcmkHEADER_ARG("Hardware=0x%x Format=0x%x BitsPerPixel=0x%x BytesPerTile = 0x%x", + Hardware, Format, BitsPerPixel, BytesPerTile); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Dispatch on format. */ + switch (Format) + { + case gcvSURF_A1: + case gcvSURF_L1: + /* 1-bpp format. */ + bitsPerPixel = 1; + bytesPerTile = (1 * 4 * 4) / 8; + break; + + case gcvSURF_A4: + /* 4-bpp format. */ + bitsPerPixel = 4; + bytesPerTile = (4 * 4 * 4) / 8; + break; + + case gcvSURF_INDEX8: + case gcvSURF_A8: + case gcvSURF_L8: + /* 8-bpp format. */ + bitsPerPixel = 8; + bytesPerTile = (8 * 4 * 4) / 8; + break; + + case gcvSURF_YV12: + /* 12-bpp planar YUV formats. */ + bitsPerPixel = 12; + bytesPerTile = (12 * 4 * 4) / 8; + break; + + case gcvSURF_NV12: + /* 12-bpp planar YUV formats. */ + bitsPerPixel = 12; + bytesPerTile = (12 * 4 * 4) / 8; + break; + + /* 4444 variations. */ + case gcvSURF_X4R4G4B4: + case gcvSURF_A4R4G4B4: + case gcvSURF_R4G4B4X4: + case gcvSURF_R4G4B4A4: + case gcvSURF_B4G4R4X4: + case gcvSURF_B4G4R4A4: + case gcvSURF_X4B4G4R4: + case gcvSURF_A4B4G4R4: + + /* 1555 variations. */ + case gcvSURF_X1R5G5B5: + case gcvSURF_A1R5G5B5: + case gcvSURF_R5G5B5X1: + case gcvSURF_R5G5B5A1: + case gcvSURF_X1B5G5R5: + case gcvSURF_A1B5G5R5: + case gcvSURF_B5G5R5X1: + case gcvSURF_B5G5R5A1: + + /* 565 variations. */ + case gcvSURF_R5G6B5: + case gcvSURF_B5G6R5: + + case gcvSURF_A8L8: + case gcvSURF_YUY2: + case gcvSURF_UYVY: + case gcvSURF_D16: + /* 16-bpp format. */ + bitsPerPixel = 16; + bytesPerTile = (16 * 4 * 4) / 8; + break; + + case gcvSURF_X8R8G8B8: + case gcvSURF_A8R8G8B8: + case gcvSURF_X8B8G8R8: + case gcvSURF_A8B8G8R8: + case gcvSURF_R8G8B8X8: + case gcvSURF_R8G8B8A8: + case gcvSURF_B8G8R8X8: + case gcvSURF_B8G8R8A8: + case gcvSURF_D32: + /* 32-bpp format. */ + bitsPerPixel = 32; + bytesPerTile = (32 * 4 * 4) / 8; + break; + + case gcvSURF_D24S8: + /* 24-bpp format. */ + bitsPerPixel = 32; + bytesPerTile = (32 * 4 * 4) / 8; + break; + + case gcvSURF_DXT1: + case gcvSURF_ETC1: + bitsPerPixel = 4; + bytesPerTile = (4 * 4 * 4) / 8; + break; + + case gcvSURF_DXT2: + case gcvSURF_DXT3: + case gcvSURF_DXT4: + case gcvSURF_DXT5: + bitsPerPixel = 8; + bytesPerTile = (8 * 4 * 4) / 8; + break; + + default: + /* Invalid format. */ + gcmkFOOTER_NO(); + return gcvSTATUS_INVALID_ARGUMENT; + } + + /* Set the result. */ + if (BitsPerPixel != gcvNULL) + { + * BitsPerPixel = bitsPerPixel; + } + + if (BytesPerTile != gcvNULL) + { + * BytesPerTile = bytesPerTile; + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_SplitMemory +** +** Split a hardware specific memory address into a pool and offset. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to the gckVGHARDWARE object. +** +** gctUINT32 Address +** Address in hardware specific format. +** +** OUTPUT: +** +** gcePOOL * Pool +** Pointer to a variable that will hold the pool type for the address. +** +** gctUINT32 * Offset +** Pointer to a variable that will hold the offset for the address. +*/ +gceSTATUS +gckVGHARDWARE_SplitMemory( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Address, + OUT gcePOOL * Pool, + OUT gctUINT32 * Offset + ) +{ + gcmkHEADER_ARG("Hardware=0x%x Address=0x%x Pool=0x%x Offset = 0x%x", + Hardware, Address, Pool, Offset); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Pool != gcvNULL); + gcmkVERIFY_ARGUMENT(Offset != gcvNULL); + + /* Dispatch on memory type. */ + switch ((((((gctUINT32) (Address)) >> (0 ? 1:0)) & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1)))))) )) + { + case 0x0: + /* System memory. */ + *Pool = gcvPOOL_SYSTEM; + break; + + case 0x2: + /* Virtual memory. */ + *Pool = gcvPOOL_VIRTUAL; + break; + + default: + /* Invalid memory type. */ + gcmkFOOTER_NO(); + return gcvSTATUS_INVALID_ARGUMENT; + } + + /* Return offset of address. */ + *Offset = ((((gctUINT32) (Address)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) ((gctUINT32) (0) & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_Execute +** +** Kickstart the hardware's command processor with an initialized command +** buffer. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to the gckVGHARDWARE object. +** +** gctUINT32 Address +** Address of the command buffer. +** +** gctSIZE_T Count +** Number of command-sized data units to be executed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVGHARDWARE_Execute( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Address, + IN gctUINT32 Count + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Address=0x%x Count=0x%x", + Hardware, Address, Count); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + do + { + /* Enable all events. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx( + Hardware->os, + gcvCORE_VG, + 0x00014, + Hardware->eventMask + )); + + if (Hardware->fe20) + { + /* Write address register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx( + Hardware->os, + gcvCORE_VG, + 0x00500, + gcmkFIXADDRESS(Address) + )); + + /* Write control register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx( + Hardware->os, + gcvCORE_VG, + 0x00504, + Count + )); + } + else + { + /* Write address register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx( + Hardware->os, + gcvCORE_VG, + 0x00654, + gcmkFIXADDRESS(Address) + )); + + /* Write control register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx( + Hardware->os, + gcvCORE_VG, + 0x00658, + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1))))))) << (0 ? 16:16))) | + ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (Count) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + )); + } + + /* Success. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + } + while (gcvFALSE); + + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckVGHARDWARE_AlignToTile +** +** Align the specified width and height to tile boundaries. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to an gckVGHARDWARE object. +** +** gceSURF_TYPE Type +** Type of alignment. +** +** gctUINT32 * Width +** Pointer to the width to be aligned. If 'Width' is gcvNULL, no width +** will be aligned. +** +** gctUINT32 * Height +** Pointer to the height to be aligned. If 'Height' is gcvNULL, no height +** will be aligned. +** +** OUTPUT: +** +** gctUINT32 * Width +** Pointer to a variable that will receive the aligned width. +** +** gctUINT32 * Height +** Pointer to a variable that will receive the aligned height. +*/ +gceSTATUS +gckVGHARDWARE_AlignToTile( + IN gckVGHARDWARE Hardware, + IN gceSURF_TYPE Type, + IN OUT gctUINT32 * Width, + IN OUT gctUINT32 * Height + ) +{ + gcmkHEADER_ARG("Hardware=0x%x Type=0x%x Width=0x%x Height=0x%x", + Hardware, Type, Width, Height); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (Width != gcvNULL) + { + /* Align the width. */ + *Width = gcmALIGN(*Width, (Type == gcvSURF_TEXTURE) ? 4 : 16); + } + + if (Height != gcvNULL) + { + /* Special case for VG images. */ + if ((*Height == 0) && (Type == gcvSURF_IMAGE)) + { + *Height = 4; + } + else + { + /* Align the height. */ + *Height = gcmALIGN(*Height, 4); + } + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_ConvertLogical +** +** Convert a logical system address into a hardware specific address. +** +** INPUT: +** +** gckVGHARDWARE Hardware +** Pointer to an gckVGHARDWARE object. +** +** gctPOINTER Logical +** Logical address to convert. +** +** gctBOOL InUserSpace +** gcvTRUE if the memory in user space. +** +** gctUINT32* Address +** Return hardware specific address. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVGHARDWARE_ConvertLogical( + IN gckVGHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + OUT gctUINT32 * Address + ) +{ + gctUINT32 address; + gceSTATUS status; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x InUserSpace=%d Address=0x%x", + Hardware, Logical, InUserSpace, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + do + { + /* Convert logical address into a physical address. */ + if (InUserSpace) + { + gcmkERR_BREAK(gckOS_UserLogicalToPhysical( + Hardware->os, Logical, &address + )); + } + else + { + gcmkERR_BREAK(gckOS_GetPhysicalAddress( + Hardware->os, Logical, &address + )); + } + + /* Return hardware specific address. */ + *Address = ((((gctUINT32) (address)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))); + + /* Success. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckVGHARDWARE_QuerySystemMemory +** +** Query the command buffer alignment and number of reserved bytes. +** +** INPUT: +** +** gckVGHARDWARE Harwdare +** Pointer to an gckVGHARDWARE object. +** +** OUTPUT: +** +** gctSIZE_T * SystemSize +** Pointer to a variable that receives the maximum size of the system +** memory. +** +** gctUINT32 * SystemBaseAddress +** Poinetr to a variable that receives the base address for system +** memory. +*/ +gceSTATUS gckVGHARDWARE_QuerySystemMemory( + IN gckVGHARDWARE Hardware, + OUT gctSIZE_T * SystemSize, + OUT gctUINT32 * SystemBaseAddress + ) +{ + gcmkHEADER_ARG("Hardware=0x%x SystemSize=0x%x SystemBaseAddress=0x%x", + Hardware, SystemSize, SystemBaseAddress); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + if (SystemSize != gcvNULL) + { + /* Maximum system memory can be 2GB. */ + *SystemSize = (gctSIZE_T)(1 << 31); + } + + if (SystemBaseAddress != gcvNULL) + { + /* Set system memory base address. */ + *SystemBaseAddress = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))); + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_SetMMU +** +** Set the page table base address. +** +** INPUT: +** +** gckVGHARDWARE Harwdare +** Pointer to an gckVGHARDWARE object. +** +** gctPOINTER Logical +** Logical address of the page table. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS gckVGHARDWARE_SetMMU( + IN gckVGHARDWARE Hardware, + IN gctPOINTER Logical + ) +{ + gceSTATUS status; + gctUINT32 address = 0; + + gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x", + Hardware, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + do + { + /* Convert the logical address into an hardware address. */ + gcmkERR_BREAK(gckVGHARDWARE_ConvertLogical(Hardware, Logical, + gcvFALSE, &address)); + + /* Write the AQMemoryFePageTable register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx(Hardware->os, gcvCORE_VG, + 0x00400, + gcmkFIXADDRESS(address))); + + /* Write the AQMemoryTxPageTable register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx(Hardware->os, gcvCORE_VG, + 0x00404, + gcmkFIXADDRESS(address))); + + /* Write the AQMemoryPePageTable register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx(Hardware->os, gcvCORE_VG, + 0x00408, + gcmkFIXADDRESS(address))); + + /* Write the AQMemoryPezPageTable register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx(Hardware->os, gcvCORE_VG, + 0x0040C, + gcmkFIXADDRESS(address))); + + /* Write the AQMemoryRaPageTable register. */ + gcmkERR_BREAK(gckOS_WriteRegisterEx(Hardware->os, gcvCORE_VG, + 0x00410, + gcmkFIXADDRESS(address))); + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckVGHARDWARE_FlushMMU +** +** Flush the page table. +** +** INPUT: +** +** gckVGHARDWARE Harwdare +** Pointer to an gckVGHARDWARE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS gckVGHARDWARE_FlushMMU( + IN gckVGHARDWARE Hardware + ) +{ + gceSTATUS status; + gckVGCOMMAND command; + + gcmkHEADER_ARG("Hardware=0x%x ", Hardware); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + do + { + gcsCMDBUFFER_PTR commandBuffer; + gctUINT32_PTR buffer; + + /* Create a shortcut to the command buffer object. */ + command = Hardware->kernel->command; + + /* Allocate command buffer space. */ + gcmkERR_BREAK(gckVGCOMMAND_Allocate( + command, 8, &commandBuffer, (gctPOINTER *) &buffer + )); + + buffer[0] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E04) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); + + buffer[1] + = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1))))))) << (0 ? 0:0))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1))))))) << (0 ? 1:1))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1))))))) << (0 ? 2:2))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) + | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1))))))) << (0 ? 4:4))); + } + while(gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckVGHARDWARE_BuildVirtualAddress +** +** Build a virtual address. +** +** INPUT: +** +** gckVGHARDWARE Harwdare +** Pointer to an gckVGHARDWARE object. +** +** gctUINT32 Index +** Index into page table. +** +** gctUINT32 Offset +** Offset into page. +** +** OUTPUT: +** +** gctUINT32 * Address +** Pointer to a variable receiving te hardware address. +*/ +gceSTATUS gckVGHARDWARE_BuildVirtualAddress( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Index, + IN gctUINT32 Offset, + OUT gctUINT32 * Address + ) +{ + gctUINT32 address; + + gcmkHEADER_ARG("Hardware=0x%x Index=0x%x Offset=0x%x Address=0x%x", + Hardware, Index, Offset, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + /* Build virtual address. */ + address = (Index << 12) | Offset; + + /* Set virtual type. */ + address = ((((gctUINT32) (address)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))) | (((gctUINT32) (0x2 & ((gctUINT32) ((((1 ? 1:0) - (0 ? 1:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:0) - (0 ? 1:0) + 1))))))) << (0 ? 1:0))); + + /* Set the result. */ + *Address = address; + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +gceSTATUS +gckVGHARDWARE_GetIdle( + IN gckVGHARDWARE Hardware, + OUT gctUINT32 * Data + ) +{ + gceSTATUS status; + gcmkHEADER_ARG("Hardware=0x%x Data=0x%x", Hardware, Data); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Data != gcvNULL); + + /* Read register and return. */ + status = gckOS_ReadRegisterEx(Hardware->os, gcvCORE_VG, 0x00004, Data); + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckVGHARDWARE_SetFastClear( + IN gckVGHARDWARE Hardware, + IN gctINT Enable + ) +{ + gctUINT32 debug; + gceSTATUS status; + + if (!(((((gctUINT32) (Hardware->chipFeatures)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) )) + { + return gcvSTATUS_OK; + } + + do + { + if (Enable == -1) + { + Enable = (Hardware->chipModel > gcv500) || + ((Hardware->chipModel == gcv500) && (Hardware->chipRevision >= 3)); + } + + gcmkERR_BREAK(gckOS_ReadRegisterEx(Hardware->os, gcvCORE_VG, + 0x00414, + &debug)); + + debug = ((((gctUINT32) (debug)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 20:20) - (0 ? 20:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:20) - (0 ? 20:20) + 1))))))) << (0 ? 20:20))) | (((gctUINT32) ((gctUINT32) (Enable == 0) & ((gctUINT32) ((((1 ? 20:20) - (0 ? 20:20) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 20:20) - (0 ? 20:20) + 1))))))) << (0 ? 20:20))); + +#ifdef AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION + debug = ((((gctUINT32) (debug)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) - (0 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) + 1) == 32) ? ~0 : (~(~0 << ((1 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) - (0 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) + 1))))))) << (0 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION))) | (((gctUINT32) ((gctUINT32) (Enable == 0) & ((gctUINT32) ((((1 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) - (0 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) + 1) == 32) ? ~0 : (~(~0 << ((1 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) - (0 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION) + 1))))))) << (0 ? AQ_MEMORY_DEBUG_DISABLE_Z_COMPRESSION))); +#endif + + gcmkERR_BREAK(gckOS_WriteRegisterEx(Hardware->os, gcvCORE_VG, + 0x00414, + debug)); + + Hardware->allowFastClear = Enable; + + status = gcvFALSE; + } + while (gcvFALSE); + + return status; +} + +gceSTATUS +gckVGHARDWARE_ReadInterrupt( + IN gckVGHARDWARE Hardware, + OUT gctUINT32_PTR IDs + ) +{ + gceSTATUS status; + gcmkHEADER_ARG("Hardware=0x%x IDs=0x%x", Hardware, IDs); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(IDs != gcvNULL); + + /* Read AQIntrAcknowledge register. */ + status = gckOS_ReadRegisterEx(Hardware->os, gcvCORE_VG, + 0x00010, + IDs); + gcmkFOOTER(); + return status; +} + +static gceSTATUS _CommandStall( + gckVGHARDWARE Hardware) +{ + gceSTATUS status; + gckVGCOMMAND command; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + do + { + gctUINT32_PTR buffer; + command = Hardware->kernel->command; + + /* Allocate command buffer space. */ + gcmkERR_BREAK(gckVGCOMMAND_Allocate( + command, 8, &command->powerStallBuffer, + (gctPOINTER *) &buffer + )); + + gcmkERR_BREAK(gckVGCOMMAND_EventCommand( + command, buffer, gcvBLOCK_PIXEL, + command->powerStallInt, gcvNULL)); + + gcmkERR_BREAK(gckVGCOMMAND_Execute( + command, + command->powerStallBuffer + )); + + /* Wait the signal. */ + gcmkERR_BREAK(gckOS_WaitSignal( + command->os, + command->powerStallSignal, + command->kernel->kernel->timeOut)); + + + } + while(gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_SetPowerManagementState +** +** Set GPU to a specified power state. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gceCHIPPOWERSTATE State +** Power State. +** +*/ +gceSTATUS +gckVGHARDWARE_SetPowerManagementState( + IN gckVGHARDWARE Hardware, + IN gceCHIPPOWERSTATE State + ) +{ + gceSTATUS status; + gckVGCOMMAND command = gcvNULL; + gckOS os; + gctUINT flag/*, clock*/; + + gctBOOL acquired = gcvFALSE; + gctBOOL stall = gcvTRUE; + gctBOOL commitMutex = gcvFALSE; + gctBOOL mutexAcquired = gcvFALSE; + +#if gcdPOWEROFF_TIMEOUT + gctBOOL timeout = gcvFALSE; + gctBOOL isAfter = gcvFALSE; + gctUINT32 currentTime; +#endif + + gctBOOL broadcast = gcvFALSE; + gctUINT32 process, thread; + gctBOOL global = gcvFALSE; + +#if gcdENABLE_PROFILING + gctUINT64 time, freq, mutexTime, onTime, stallTime, stopTime, delayTime, + initTime, offTime, startTime, totalTime; +#endif + + /* State transition flags. */ + static const gctUINT flags[4][4] = + { + /* gcvPOWER_ON */ + { /* ON */ 0, + /* OFF */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STALL | + gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_POWER_OFF | + gcvPOWER_FLAG_CLOCK_OFF, + /* IDLE */ gcvPOWER_FLAG_NOP, + /* SUSPEND */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STALL | + gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_CLOCK_OFF, + }, + + /* gcvPOWER_OFF */ + { /* ON */ gcvPOWER_FLAG_INITIALIZE | + gcvPOWER_FLAG_START | + gcvPOWER_FLAG_RELEASE | + gcvPOWER_FLAG_DELAY, + /* OFF */ 0, + /* IDLE */ gcvPOWER_FLAG_INITIALIZE | + gcvPOWER_FLAG_START | + gcvPOWER_FLAG_RELEASE | + gcvPOWER_FLAG_DELAY, + /* SUSPEND */ gcvPOWER_FLAG_INITIALIZE | + gcvPOWER_FLAG_CLOCK_OFF, + }, + + /* gcvPOWER_IDLE */ + { /* ON */ gcvPOWER_FLAG_NOP, + /* OFF */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_POWER_OFF | + gcvPOWER_FLAG_CLOCK_OFF, + /* IDLE */ 0, + /* SUSPEND */ gcvPOWER_FLAG_ACQUIRE | + gcvPOWER_FLAG_STOP | + gcvPOWER_FLAG_CLOCK_OFF, + }, + + /* gcvPOWER_SUSPEND */ + { /* ON */ gcvPOWER_FLAG_START | + gcvPOWER_FLAG_RELEASE | + gcvPOWER_FLAG_DELAY | + gcvPOWER_FLAG_CLOCK_ON, + /* OFF */ gcvPOWER_FLAG_SAVE | + gcvPOWER_FLAG_POWER_OFF | + gcvPOWER_FLAG_CLOCK_OFF, + /* IDLE */ gcvPOWER_FLAG_START | + gcvPOWER_FLAG_DELAY | + gcvPOWER_FLAG_RELEASE | + gcvPOWER_FLAG_CLOCK_ON, + /* SUSPEND */ 0, + }, + }; + + gcmkHEADER_ARG("Hardware=0x%x State=%d", Hardware, State); +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "Switching to power state %d", + State); +#endif + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + /* Get the gckOS object pointer. */ + os = Hardware->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Get the gckCOMMAND object pointer. */ + gcmkVERIFY_OBJECT(Hardware->kernel, gcvOBJ_KERNEL); + command = Hardware->kernel->command; + gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND); + + if (Hardware->powerManagement == gcvFALSE) + { + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Start profiler. */ + gcmkPROFILE_INIT(freq, time); + + /* Convert the broadcast power state. */ + switch (State) + { + case gcvPOWER_SUSPEND_ATPOWERON: + /* Convert to SUSPEND and don't wait for STALL. */ + State = gcvPOWER_SUSPEND; + stall = gcvFALSE; + break; + + case gcvPOWER_OFF_ATPOWERON: + /* Convert to OFF and don't wait for STALL. */ + State = gcvPOWER_OFF; + stall = gcvFALSE; + break; + + case gcvPOWER_IDLE_BROADCAST: + /* Convert to IDLE and note we are inside broadcast. */ + State = gcvPOWER_IDLE; + broadcast = gcvTRUE; + break; + + case gcvPOWER_SUSPEND_BROADCAST: + /* Convert to SUSPEND and note we are inside broadcast. */ + State = gcvPOWER_SUSPEND; + broadcast = gcvTRUE; + break; + + case gcvPOWER_OFF_BROADCAST: + /* Convert to OFF and note we are inside broadcast. */ + State = gcvPOWER_OFF; + broadcast = gcvTRUE; + break; + + case gcvPOWER_OFF_RECOVERY: + /* Convert to OFF and note we are inside recovery. */ + State = gcvPOWER_OFF; + stall = gcvFALSE; + broadcast = gcvTRUE; + break; + + case gcvPOWER_ON_AUTO: + /* Convert to ON and note we are inside recovery. */ + State = gcvPOWER_ON; + break; + + case gcvPOWER_ON: + case gcvPOWER_IDLE: + case gcvPOWER_SUSPEND: + case gcvPOWER_OFF: + /* Mark as global power management. */ + global = gcvTRUE; + break; + +#if gcdPOWEROFF_TIMEOUT + case gcvPOWER_OFF_TIMEOUT: + /* Convert to OFF and note we are inside broadcast. */ + State = gcvPOWER_OFF; + broadcast = gcvTRUE; + /* Check time out */ + timeout = gcvTRUE; + break; +#endif + + default: + break; + } + + /* Get current process and thread IDs. */ + gcmkONERROR(gckOS_GetProcessID(&process)); + gcmkONERROR(gckOS_GetThreadID(&thread)); + + /* Acquire the power mutex. */ + if (broadcast) + { + /* Try to acquire the power mutex. */ + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0); + + if (status == gcvSTATUS_TIMEOUT) + { + /* Check if we already own this mutex. */ + if ((Hardware->powerProcess == process) + && (Hardware->powerThread == thread) + ) + { + /* Bail out on recursive power management. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + else if (State == gcvPOWER_IDLE) + { + /* gcvPOWER_IDLE_BROADCAST is from IST, + ** so waiting here will cause deadlock, + ** if lock holder call gckCOMMAND_Stall() */ + gcmkONERROR(gcvSTATUS_INVALID_REQUEST); + } + else + { + /* Acquire the power mutex. */ + gcmkONERROR(gckOS_AcquireMutex(os, + Hardware->powerMutex, + gcvINFINITE)); + } + } + } + else + { + /* Acquire the power mutex. */ + gcmkONERROR(gckOS_AcquireMutex(os, Hardware->powerMutex, gcvINFINITE)); + } + + /* Get time until mtuex acquired. */ + gcmkPROFILE_QUERY(time, mutexTime); + + Hardware->powerProcess = process; + Hardware->powerThread = thread; + mutexAcquired = gcvTRUE; + + /* Grab control flags and clock. */ + flag = flags[Hardware->chipPowerState][State]; + /*clock = clocks[State];*/ + +#if gcdPOWEROFF_TIMEOUT + if (timeout) + { + gcmkONERROR(gckOS_GetTicks(¤tTime)); + + gcmkONERROR( + gckOS_TicksAfter(Hardware->powerOffTime, currentTime, &isAfter)); + + /* powerOffTime is pushed forward, give up.*/ + if (isAfter + /* Expect a transition start from IDLE. */ + || (Hardware->chipPowerState == gcvPOWER_ON) + || (Hardware->chipPowerState == gcvPOWER_OFF) + ) + { + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* No need to do anything. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + } +#endif + + if (flag == 0) + { + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* No need to do anything. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* internal power control */ + if (!global) + { + if (Hardware->chipPowerStateGlobal == gcvPOWER_OFF) + { + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* No need to do anything. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + } + else + { + if (flag & gcvPOWER_FLAG_ACQUIRE) + { + /* Acquire the power management semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(os, command->powerSemaphore)); + acquired = gcvTRUE; + + /* avoid acquiring again. */ + flag &= ~gcvPOWER_FLAG_ACQUIRE; + } + } + + if (flag & (gcvPOWER_FLAG_INITIALIZE | gcvPOWER_FLAG_CLOCK_ON)) + { + /* Turn on the power. */ + gcmkONERROR(gckOS_SetGPUPower(os, gcvCORE_VG, gcvTRUE, gcvTRUE)); + + /* Mark clock and power as enabled. */ + Hardware->clockState = gcvTRUE; + Hardware->powerState = gcvTRUE; + } + + /* Get time until powered on. */ + gcmkPROFILE_QUERY(time, onTime); + + if ((flag & gcvPOWER_FLAG_STALL) && stall) + { + /* Acquire the mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + command->os, + command->commitMutex, + gcvINFINITE + )); + + commitMutex = gcvTRUE; + + gcmkONERROR(_CommandStall(Hardware)); + } + + /* Get time until stalled. */ + gcmkPROFILE_QUERY(time, stallTime); + + if (flag & gcvPOWER_FLAG_ACQUIRE) + { + /* Acquire the power management semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(os, command->powerSemaphore)); + + acquired = gcvTRUE; + } + + + /* Get time until stopped. */ + gcmkPROFILE_QUERY(time, stopTime); + + + if (flag & gcvPOWER_FLAG_DELAY) + { + /* Wait for the specified amount of time to settle coming back from + ** power-off or suspend state. */ + gcmkONERROR(gckOS_Delay(os, gcdPOWER_CONTROL_DELAY)); + } + + /* Get time until delayed. */ + gcmkPROFILE_QUERY(time, delayTime); + + if (flag & gcvPOWER_FLAG_INITIALIZE) + { + + /* Initialize GPU here, replaced by InitializeHardware later */ + gcmkONERROR(gckVGHARDWARE_SetMMU(Hardware, Hardware->kernel->mmu->pageTableLogical)); + gcmkVERIFY_OK(gckVGHARDWARE_SetFastClear(Hardware, -1)); + + /* Force the command queue to reload the next context. */ + command->currentContext = 0; + } + + /* Get time until initialized. */ + gcmkPROFILE_QUERY(time, initTime); + + if (flag & (gcvPOWER_FLAG_POWER_OFF | gcvPOWER_FLAG_CLOCK_OFF)) + { + /* Turn off the GPU power. */ + gcmkONERROR( + gckOS_SetGPUPower(os, + gcvCORE_VG, + (flag & gcvPOWER_FLAG_CLOCK_OFF) ? gcvFALSE + : gcvTRUE, + (flag & gcvPOWER_FLAG_POWER_OFF) ? gcvFALSE + : gcvTRUE)); + + /* Save current hardware power and clock states. */ + Hardware->clockState = (flag & gcvPOWER_FLAG_CLOCK_OFF) ? gcvFALSE + : gcvTRUE; + Hardware->powerState = (flag & gcvPOWER_FLAG_POWER_OFF) ? gcvFALSE + : gcvTRUE; + } + + /* Get time until off. */ + gcmkPROFILE_QUERY(time, offTime); + + + /* Get time until started. */ + gcmkPROFILE_QUERY(time, startTime); + + if (flag & gcvPOWER_FLAG_RELEASE) + { + /* Release the power management semaphore. */ + gcmkONERROR(gckOS_ReleaseSemaphore(os, command->powerSemaphore)); + acquired = gcvFALSE; + } + + /* Save the new power state. */ + Hardware->chipPowerState = State; + + if (global) + { + /* Save the new power state. */ + Hardware->chipPowerStateGlobal = State; + } + + if (commitMutex) + { + /* Acquire the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex( + command->os, + command->commitMutex + )); + } + +#if gcdPOWEROFF_TIMEOUT + /* Reset power off time */ + gcmkONERROR(gckOS_GetTicks(¤tTime)); + + Hardware->powerOffTime = currentTime + Hardware->powerOffTimeout; + + if (State == gcvPOWER_IDLE) + { + /* Start a timer to power off GPU when GPU enters IDLE or SUSPEND. */ + gcmkVERIFY_OK(gckOS_StartTimer(os, + Hardware->powerOffTimer, + Hardware->powerOffTimeout)); + } + else + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "Cancel powerOfftimer"); + + /* Cancel running timer when GPU enters ON or OFF. */ + gcmkVERIFY_OK(gckOS_StopTimer(os, Hardware->powerOffTimer)); + } +#endif + + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + /* Get total time. */ + gcmkPROFILE_QUERY(time, totalTime); +#if gcdENABLE_PROFILING + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + "PROF(%llu): mutex:%llu on:%llu stall:%llu stop:%llu", + freq, mutexTime, onTime, stallTime, stopTime); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + " delay:%llu init:%llu off:%llu start:%llu total:%llu", + delayTime, initTime, offTime, startTime, totalTime); +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + + if (acquired) + { + /* Release semaphore. */ + gcmkVERIFY_OK(gckOS_ReleaseSemaphore(Hardware->os, + command->powerSemaphore)); + } + + if (mutexAcquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Hardware->os, Hardware->powerMutex)); + } + + if (commitMutex) + { + /* Acquire the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex( + command->os, + command->commitMutex + )); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHARDWARE_QueryPowerManagementState +** +** Get GPU power state. +** +** INPUT: +** +** gckHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gceCHIPPOWERSTATE* State +** Power State. +** +*/ +gceSTATUS +gckVGHARDWARE_QueryPowerManagementState( + IN gckVGHARDWARE Hardware, + OUT gceCHIPPOWERSTATE* State + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(State != gcvNULL); + + /* Return the statue. */ + *State = Hardware->chipPowerState; + + /* Success. */ + gcmkFOOTER_ARG("*State=%d", *State); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGHARDWARE_SetPowerManagement +** +** Configure GPU power management function. +** Only used in driver initialization stage. +** +** INPUT: +** +** gckVGHARDWARE Harwdare +** Pointer to an gckHARDWARE object. +** +** gctBOOL PowerManagement +** Power Mangement State. +** +*/ +gceSTATUS +gckVGHARDWARE_SetPowerManagement( + IN gckVGHARDWARE Hardware, + IN gctBOOL PowerManagement + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + Hardware->powerManagement = PowerManagement; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +#if gcdPOWEROFF_TIMEOUT +gceSTATUS +gckVGHARDWARE_SetPowerOffTimeout( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Timeout + ) +{ + gcmkHEADER_ARG("Hardware=0x%x Timeout=%d", Hardware, Timeout); + + Hardware->powerOffTimeout = Timeout; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + + +gceSTATUS +gckVGHARDWARE_QueryPowerOffTimeout( + IN gckVGHARDWARE Hardware, + OUT gctUINT32* Timeout + ) +{ + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + *Timeout = Hardware->powerOffTimeout; + + gcmkFOOTER_ARG("*Timeout=%d", *Timeout); + return gcvSTATUS_OK; +} +#endif + +gceSTATUS +gckVGHARDWARE_QueryIdle( + IN gckVGHARDWARE Hardware, + OUT gctBOOL_PTR IsIdle + ) +{ + gceSTATUS status; + gctUINT32 idle; + + gcmkHEADER_ARG("Hardware=0x%x", Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(IsIdle != gcvNULL); + + /* We are idle when the power is not ON. */ + if (Hardware->chipPowerState != gcvPOWER_ON) + { + *IsIdle = gcvTRUE; + } + + else + { + /* Read idle register. */ + gcmkONERROR( + gckOS_ReadRegisterEx(Hardware->os, gcvCORE_VG, 0x00004, &idle)); + + /* Pipe must be idle. */ + if (((((((gctUINT32) (idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 8:8)) & ((gctUINT32) ((((1 ? 8:8) - (0 ? 8:8) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:8) - (0 ? 8:8) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 9:9)) & ((gctUINT32) ((((1 ? 9:9) - (0 ? 9:9) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 9:9) - (0 ? 9:9) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 10:10)) & ((gctUINT32) ((((1 ? 10:10) - (0 ? 10:10) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 10:10) - (0 ? 10:10) + 1)))))) ) != 1) + || ((((((gctUINT32) (idle)) >> (0 ? 11:11)) & ((gctUINT32) ((((1 ? 11:11) - (0 ? 11:11) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 11:11) - (0 ? 11:11) + 1)))))) ) != 1) + ) + { + /* Something is busy. */ + *IsIdle = gcvFALSE; + } + + else + { + *IsIdle = gcvTRUE; + } + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif /* gcdENABLE_VG */ + diff --git a/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.h b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.h new file mode 100644 index 00000000000000..ce54910545d7a2 --- /dev/null +++ b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.h @@ -0,0 +1,74 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_hardware_vg_h_ +#define __gc_hal_kernel_hardware_vg_h_ + +/* gckHARDWARE object. */ +struct _gckVGHARDWARE +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to gckKERNEL object. */ + gckVGKERNEL kernel; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Chip characteristics. */ + gceCHIPMODEL chipModel; + gctUINT32 chipRevision; + gctUINT32 chipFeatures; + gctUINT32 chipMinorFeatures; + gctUINT32 chipMinorFeatures2; + gctBOOL allowFastClear; + + /* Features. */ + gctBOOL fe20; + gctBOOL vg20; + gctBOOL vg21; + + /* Event mask. */ + gctUINT32 eventMask; + + gctBOOL clockState; + gctBOOL powerState; + gctPOINTER powerMutex; + gctUINT32 powerProcess; + gctUINT32 powerThread; + gceCHIPPOWERSTATE chipPowerState; + gceCHIPPOWERSTATE chipPowerStateGlobal; + gctISRMANAGERFUNC startIsr; + gctISRMANAGERFUNC stopIsr; + gctPOINTER isrContext; + gctPOINTER pageTableDirty; +#if gcdPOWEROFF_TIMEOUT + gctUINT32 powerOffTime; + gctUINT32 powerOffTimeout; + gctPOINTER powerOffTimer; +#endif + + gctBOOL powerManagement; +}; + +#endif /* __gc_hal_kernel_hardware_h_ */ + diff --git a/drivers/gpu/galcore/config b/drivers/gpu/galcore/config new file mode 100644 index 00000000000000..51f3c891101461 --- /dev/null +++ b/drivers/gpu/galcore/config @@ -0,0 +1,37 @@ +############################################################################## +# +# Copyright (C) 2005 - 2014 by Vivante Corp. +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# +############################################################################## + + +ARCH_TYPE ?= arm +SDK_DIR ?= $(AQROOT)/build/sdk +VIVANTE_ENABLE_3D ?= 1 +VIVANTE_ENABLE_2D ?= 1 +VIVANTE_ENABLE_VG ?= 0 +FORCE_ALL_VIDEO_MEMORY_CACHED ?= 0 +NONPAGED_MEMORY_CACHEABLE ?= 0 +NONPAGED_MEMORY_BUFFERABLE ?= 1 +CACHE_FUNCTION_UNIMPLEMENTED ?= 0 +ENABLE_OUTER_CACHE_PATCH ?= 1 +USE_BANK_ALIGNMENT ?= 1 +BANK_BIT_START ?= 13 +BANK_BIT_END ?= 15 +BANK_CHANNEL_BIT ?= 12 +PLATFORM ?= freescale/gc_hal_kernel_platform_imx6q14 +DEBUG ?= 0 diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c new file mode 100644 index 00000000000000..f9d277ae76b783 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -0,0 +1,5041 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_KERNEL + +/******************************************************************************* +***** Version Signature *******************************************************/ + +#define _gcmTXT2STR(t) #t +#define gcmTXT2STR(t) _gcmTXT2STR(t) +const char * _VERSION = "\n\0$VERSION$" + gcmTXT2STR(gcvVERSION_MAJOR) "." + gcmTXT2STR(gcvVERSION_MINOR) "." + gcmTXT2STR(gcvVERSION_PATCH) ":" + gcmTXT2STR(gcvVERSION_BUILD) "$\n"; + +/******************************************************************************\ +******************************* gckKERNEL API Code ****************************** +\******************************************************************************/ + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +#define gcmDEFINE2TEXT(d) #d +gctCONST_STRING _DispatchText[] = +{ + gcmDEFINE2TEXT(gcvHAL_QUERY_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_QUERY_CHIP_IDENTITY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_NON_PAGED_MEMORY), + gcmDEFINE2TEXT(gcvHAL_FREE_NON_PAGED_MEMORY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY), + gcmDEFINE2TEXT(gcvHAL_FREE_CONTIGUOUS_MEMORY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_RELEASE_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_MAP_MEMORY), + gcmDEFINE2TEXT(gcvHAL_UNMAP_MEMORY), + gcmDEFINE2TEXT(gcvHAL_MAP_USER_MEMORY), + gcmDEFINE2TEXT(gcvHAL_UNMAP_USER_MEMORY), + gcmDEFINE2TEXT(gcvHAL_LOCK_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_UNLOCK_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_EVENT_COMMIT), + gcmDEFINE2TEXT(gcvHAL_USER_SIGNAL), + gcmDEFINE2TEXT(gcvHAL_SIGNAL), + gcmDEFINE2TEXT(gcvHAL_WRITE_DATA), + gcmDEFINE2TEXT(gcvHAL_COMMIT), + gcmDEFINE2TEXT(gcvHAL_STALL), + gcmDEFINE2TEXT(gcvHAL_READ_REGISTER), + gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER), + gcmDEFINE2TEXT(gcvHAL_GET_PROFILE_SETTING), + gcmDEFINE2TEXT(gcvHAL_SET_PROFILE_SETTING), + gcmDEFINE2TEXT(gcvHAL_READ_ALL_PROFILE_REGISTERS), + gcmDEFINE2TEXT(gcvHAL_PROFILE_REGISTERS_2D), +#if VIVANTE_PROFILER_PERDRAW + gcvHAL_READ_PROFILER_REGISTER_SETTING, +#endif + gcmDEFINE2TEXT(gcvHAL_SET_POWER_MANAGEMENT_STATE), + gcmDEFINE2TEXT(gcvHAL_QUERY_POWER_MANAGEMENT_STATE), + gcmDEFINE2TEXT(gcvHAL_GET_BASE_ADDRESS), + gcmDEFINE2TEXT(gcvHAL_SET_IDLE), + gcmDEFINE2TEXT(gcvHAL_QUERY_KERNEL_SETTINGS), + gcmDEFINE2TEXT(gcvHAL_RESET), + gcmDEFINE2TEXT(gcvHAL_MAP_PHYSICAL), + gcmDEFINE2TEXT(gcvHAL_DEBUG), + gcmDEFINE2TEXT(gcvHAL_CACHE), + gcmDEFINE2TEXT(gcvHAL_TIMESTAMP), + gcmDEFINE2TEXT(gcvHAL_DATABASE), + gcmDEFINE2TEXT(gcvHAL_VERSION), + gcmDEFINE2TEXT(gcvHAL_CHIP_INFO), + gcmDEFINE2TEXT(gcvHAL_ATTACH), + gcmDEFINE2TEXT(gcvHAL_DETACH), + gcmDEFINE2TEXT(gcvHAL_COMPOSE), + gcmDEFINE2TEXT(gcvHAL_SET_TIMEOUT), + gcmDEFINE2TEXT(gcvHAL_GET_FRAME_INFO), + gcmDEFINE2TEXT(gcvHAL_QUERY_COMMAND_BUFFER), + gcmDEFINE2TEXT(gcvHAL_COMMIT_DONE), + gcmDEFINE2TEXT(gcvHAL_DUMP_GPU_STATE), + gcmDEFINE2TEXT(gcvHAL_DUMP_EVENT), + gcmDEFINE2TEXT(gcvHAL_ALLOCATE_VIRTUAL_COMMAND_BUFFER), + gcmDEFINE2TEXT(gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER), + gcmDEFINE2TEXT(gcvHAL_SET_FSCALE_VALUE), + gcmDEFINE2TEXT(gcvHAL_GET_FSCALE_VALUE), + gcmDEFINE2TEXT(gcvHAL_NAME_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_IMPORT_VIDEO_MEMORY), + gcmDEFINE2TEXT(gcvHAL_QUERY_RESET_TIME_STAMP), + gcmDEFINE2TEXT(gcvHAL_READ_REGISTER_EX), + gcmDEFINE2TEXT(gcvHAL_WRITE_REGISTER_EX), + gcmDEFINE2TEXT(gcvHAL_SYNC_POINT), + gcmDEFINE2TEXT(gcvHAL_CREATE_NATIVE_FENCE), + gcmDEFINE2TEXT(gcvHAL_DESTROY_MMU), + gcmDEFINE2TEXT(gcvHAL_SHBUF), +}; +#endif + +#if gcdGPU_TIMEOUT && gcdINTERRUPT_STATISTIC +void +_MonitorTimerFunction( + gctPOINTER Data + ) +{ + gckKERNEL kernel = (gckKERNEL)Data; + gctUINT32 pendingInterrupt; + gctBOOL reset = gcvFALSE; + gctUINT32 mask; + gctUINT32 advance = kernel->timeOut/2; + +#if gcdENABLE_VG + if (kernel->core == gcvCORE_VG) + { + return; + } +#endif + + if (kernel->monitorTimerStop) + { + /* Stop. */ + return; + } + + gckOS_AtomGet(kernel->os, kernel->eventObj->interruptCount, &pendingInterrupt); + + if (kernel->monitoring == gcvFALSE) + { + if (pendingInterrupt) + { + /* Begin to mointor GPU state. */ + kernel->monitoring = gcvTRUE; + + /* Record current state. */ + kernel->lastCommitStamp = kernel->eventObj->lastCommitStamp; + kernel->restoreAddress = kernel->hardware->lastWaitLink; + gcmkVERIFY_OK(gckOS_AtomGet( + kernel->os, + kernel->hardware->pendingEvent, + &kernel->restoreMask + )); + + /* Clear timeout. */ + kernel->timer = 0; + } + } + else + { + if (pendingInterrupt) + { + gcmkVERIFY_OK(gckOS_AtomGet( + kernel->os, + kernel->hardware->pendingEvent, + &mask + )); + + if (kernel->eventObj->lastCommitStamp == kernel->lastCommitStamp + && kernel->hardware->lastWaitLink == kernel->restoreAddress + && mask == kernel->restoreMask + ) + { + /* GPU state is not changed, accumlate timeout. */ + kernel->timer += advance; + + if (kernel->timer >= kernel->timeOut) + { + /* GPU stuck, trigger reset. */ + reset = gcvTRUE; + } + } + else + { + /* GPU state changed, cancel current timeout.*/ + kernel->monitoring = gcvFALSE; + } + } + else + { + /* GPU finish all jobs, cancel current timeout*/ + kernel->monitoring = gcvFALSE; + } + } + + if (reset) + { + gckKERNEL_Recovery(kernel); + + /* Work in this timeout is done. */ + kernel->monitoring = gcvFALSE; + } + + gcmkVERIFY_OK(gckOS_StartTimer(kernel->os, kernel->monitorTimer, advance)); +} +#endif + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +_MapCommandBuffer( + IN gckKERNEL Kernel + ) +{ + gceSTATUS status; + gctUINT32 i; + gctUINT32 physical; + gckMMU mmu; + + gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu)); + + for (i = 0; i < gcdCOMMAND_QUEUES; i++) + { + gcmkONERROR(gckOS_GetPhysicalAddress( + Kernel->os, + Kernel->command->queues[i].logical, + &physical + )); + + gcmkONERROR(gckMMU_FlatMapping(mmu, physical)); + } + + return gcvSTATUS_OK; + +OnError: + return status; +} +#endif + +void +_DumpDriverConfigure( + IN gckKERNEL Kernel + ) +{ + gcmkPRINT_N(0, "**************************\n"); + gcmkPRINT_N(0, "*** GPU DRV CONFIG ***\n"); + gcmkPRINT_N(0, "**************************\n"); + + gcmkPRINT("Galcore version %d.%d.%d.%d\n", + gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD); + + gckOS_DumpParam(); +} + +void +_DumpState( + IN gckKERNEL Kernel + ) +{ + /* Dump GPU Debug registers. */ + gcmkVERIFY_OK(gckHARDWARE_DumpGPUState(Kernel->hardware)); + + if (Kernel->virtualCommandBuffer) + { + gcmkVERIFY_OK(gckCOMMAND_DumpExecutingBuffer(Kernel->command)); + } + + /* Dump Pending event. */ + gcmkVERIFY_OK(gckEVENT_Dump(Kernel->eventObj)); + + /* Dump Process DB. */ + gcmkVERIFY_OK(gckKERNEL_DumpProcessDB(Kernel)); + +#if gcdRECORD_COMMAND + /* Dump record. */ + gckRECORDER_Dump(Kernel->command->recorder); +#endif +} + +/******************************************************************************* +** +** gckKERNEL_Construct +** +** Construct a new gckKERNEL object. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gceCORE Core +** Specified core. +** +** IN gctPOINTER Context +** Pointer to a driver defined context. +** +** IN gckDB SharedDB, +** Pointer to a shared DB. +** +** OUTPUT: +** +** gckKERNEL * Kernel +** Pointer to a variable that will hold the pointer to the gckKERNEL +** object. +*/ + +gceSTATUS +gckKERNEL_Construct( + IN gckOS Os, + IN gceCORE Core, + IN gctPOINTER Context, + IN gckDB SharedDB, + OUT gckKERNEL * Kernel + ) +{ + gckKERNEL kernel = gcvNULL; + gceSTATUS status; + gctSIZE_T i; + gctPOINTER pointer = gcvNULL; + + gcmkHEADER_ARG("Os=0x%x Context=0x%x", Os, Context); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Kernel != gcvNULL); + + /* Allocate the gckKERNEL object. */ + gcmkONERROR(gckOS_Allocate(Os, + gcmSIZEOF(struct _gckKERNEL), + &pointer)); + + kernel = pointer; + + /* Zero the object pointers. */ + kernel->hardware = gcvNULL; + kernel->command = gcvNULL; + kernel->eventObj = gcvNULL; + kernel->mmu = gcvNULL; +#if gcdDVFS + kernel->dvfs = gcvNULL; +#endif + kernel->monitorTimer = gcvNULL; + + /* Initialize the gckKERNEL object. */ + kernel->object.type = gcvOBJ_KERNEL; + kernel->os = Os; + kernel->core = Core; + + if (SharedDB == gcvNULL) + { + gcmkONERROR(gckOS_Allocate(Os, + gcmSIZEOF(struct _gckDB), + &pointer)); + + kernel->db = pointer; + kernel->dbCreated = gcvTRUE; + kernel->db->freeDatabase = gcvNULL; + kernel->db->freeRecord = gcvNULL; + kernel->db->dbMutex = gcvNULL; + kernel->db->lastDatabase = gcvNULL; + kernel->db->idleTime = 0; + kernel->db->lastIdle = 0; + kernel->db->lastSlowdown = 0; + + for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i) + { + kernel->db->db[i] = gcvNULL; + } + + /* Construct a database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->dbMutex)); + + /* Construct a video memory name database. */ + gcmkONERROR(gckKERNEL_CreateIntegerDatabase(kernel, &kernel->db->nameDatabase)); + + /* Construct a video memory name database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->nameDatabaseMutex)); + + /* Construct a pointer name database. */ + gcmkONERROR(gckKERNEL_CreateIntegerDatabase(kernel, &kernel->db->pointerDatabase)); + + /* Construct a pointer name database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &kernel->db->pointerDatabaseMutex)); + } + else + { + kernel->db = SharedDB; + kernel->dbCreated = gcvFALSE; + } + + for (i = 0; i < gcmCOUNTOF(kernel->timers); ++i) + { + kernel->timers[i].startTime = 0; + kernel->timers[i].stopTime = 0; + } + + /* Save context. */ + kernel->context = Context; + + /* Construct atom holding number of clients. */ + kernel->atomClients = gcvNULL; + gcmkONERROR(gckOS_AtomConstruct(Os, &kernel->atomClients)); + +#if gcdENABLE_VG + kernel->vg = gcvNULL; + + if (Core == gcvCORE_VG) + { + /* Construct the gckMMU object. */ + gcmkONERROR( + gckVGKERNEL_Construct(Os, Context, kernel, &kernel->vg)); + + kernel->timeOut = gcdGPU_TIMEOUT; + } + else +#endif + { + /* Construct the gckHARDWARE object. */ + gcmkONERROR( + gckHARDWARE_Construct(Os, kernel->core, &kernel->hardware)); + + /* Set pointer to gckKERNEL object in gckHARDWARE object. */ + kernel->hardware->kernel = kernel; + + kernel->timeOut = kernel->hardware->type == gcvHARDWARE_2D + ? gcdGPU_2D_TIMEOUT + : gcdGPU_TIMEOUT + ; + + /* Initialize virtual command buffer. */ + /* TODO: Remove platform limitation after porting. */ +#if (defined(LINUX) || defined(__QNXNTO__)) + kernel->virtualCommandBuffer = gcvTRUE; +#else + kernel->virtualCommandBuffer = gcvFALSE; +#endif + +#if gcdSECURITY + kernel->virtualCommandBuffer = gcvFALSE; +#endif + + /* Construct the gckCOMMAND object. */ + gcmkONERROR( + gckCOMMAND_Construct(kernel, &kernel->command)); + + /* Construct the gckEVENT object. */ + gcmkONERROR( + gckEVENT_Construct(kernel, &kernel->eventObj)); + + /* Construct the gckMMU object. */ + gcmkONERROR( + gckMMU_Construct(kernel, gcdMMU_SIZE, &kernel->mmu)); + + gcmkVERIFY_OK(gckOS_GetTime(&kernel->resetTimeStamp)); + + gcmkONERROR(gckHARDWARE_PrepareFunctions(kernel->hardware)); + + /* Initialize the hardware. */ + gcmkONERROR( + gckHARDWARE_InitializeHardware(kernel->hardware)); + +#if gcdDVFS + if (gckHARDWARE_IsFeatureAvailable(kernel->hardware, + gcvFEATURE_DYNAMIC_FREQUENCY_SCALING)) + { + gcmkONERROR(gckDVFS_Construct(kernel->hardware, &kernel->dvfs)); + gcmkONERROR(gckDVFS_Start(kernel->dvfs)); + } +#endif + } + +#if VIVANTE_PROFILER + /* Initialize profile setting */ + kernel->profileEnable = gcvFALSE; + kernel->profileCleanRegister = gcvTRUE; +#endif + +#if gcdANDROID_NATIVE_FENCE_SYNC + gcmkONERROR(gckOS_CreateSyncTimeline(Os, &kernel->timeline)); +#endif + + kernel->recovery = gcvTRUE; + kernel->stuckDump = 1; + + kernel->virtualBufferHead = + kernel->virtualBufferTail = gcvNULL; + + gcmkONERROR( + gckOS_CreateMutex(Os, (gctPOINTER)&kernel->virtualBufferLock)); + +#if gcdSECURITY + /* Connect to security service for this GPU. */ + gcmkONERROR(gckKERNEL_SecurityOpen(kernel, kernel->core, &kernel->securityChannel)); +#endif + +#if gcdGPU_TIMEOUT && gcdINTERRUPT_STATISTIC + if (kernel->timeOut) + { + gcmkVERIFY_OK(gckOS_CreateTimer( + Os, + (gctTIMERFUNCTION)_MonitorTimerFunction, + (gctPOINTER)kernel, + &kernel->monitorTimer + )); + + kernel->monitoring = gcvFALSE; + + kernel->monitorTimerStop = gcvFALSE; + + gcmkVERIFY_OK(gckOS_StartTimer( + Os, + kernel->monitorTimer, + 100 + )); + } +#endif + + /* Return pointer to the gckKERNEL object. */ + *Kernel = kernel; + + /* Success. */ + gcmkFOOTER_ARG("*Kernel=0x%x", *Kernel); + return gcvSTATUS_OK; + +OnError: + if (kernel != gcvNULL) + { +#if gcdENABLE_VG + if (Core != gcvCORE_VG) +#endif + { + if (kernel->eventObj != gcvNULL) + { + gcmkVERIFY_OK(gckEVENT_Destroy(kernel->eventObj)); + } + + if (kernel->command != gcvNULL) + { + gcmkVERIFY_OK(gckCOMMAND_Destroy(kernel->command)); + } + + if (kernel->hardware != gcvNULL) + { + /* Turn off the power. */ + gcmkVERIFY_OK(gckOS_SetGPUPower(kernel->hardware->os, + kernel->hardware->core, + gcvFALSE, + gcvFALSE)); + gcmkVERIFY_OK(gckHARDWARE_Destroy(kernel->hardware)); + } + } + + if (kernel->atomClients != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Os, kernel->atomClients)); + } + + if (kernel->dbCreated && kernel->db != gcvNULL) + { + if (kernel->db->dbMutex != gcvNULL) + { + /* Destroy the database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, kernel->db->dbMutex)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, kernel->db)); + } + + if (kernel->virtualBufferLock != gcvNULL) + { + /* Destroy the virtual command buffer mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, kernel->virtualBufferLock)); + } + +#if gcdDVFS + if (kernel->dvfs) + { + gcmkVERIFY_OK(gckDVFS_Stop(kernel->dvfs)); + gcmkVERIFY_OK(gckDVFS_Destroy(kernel->dvfs)); + } +#endif + +#if gcdANDROID_NATIVE_FENCE_SYNC + if (kernel->timeline) + { + gcmkVERIFY_OK(gckOS_DestroySyncTimeline(Os, kernel->timeline)); + } +#endif + + if (kernel->monitorTimer) + { + gcmkVERIFY_OK(gckOS_StopTimer(Os, kernel->monitorTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Os, kernel->monitorTimer)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, kernel)); + } + + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_Destroy +** +** Destroy an gckKERNEL object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_Destroy( + IN gckKERNEL Kernel + ) +{ + gctSIZE_T i; + gcsDATABASE_PTR database, databaseNext; + gcsDATABASE_RECORD_PTR record, recordNext; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); +#if QNX_SINGLE_THREADED_DEBUGGING + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->debugMutex)); +#endif + + /* Destroy the database. */ + if (Kernel->dbCreated) + { + for (i = 0; i < gcmCOUNTOF(Kernel->db->db); ++i) + { + if (Kernel->db->db[i] != gcvNULL) + { + gcmkVERIFY_OK( + gckKERNEL_DestroyProcessDB(Kernel, Kernel->db->db[i]->processID)); + } + } + + /* Free all databases. */ + for (database = Kernel->db->freeDatabase; + database != gcvNULL; + database = databaseNext) + { + databaseNext = database->next; + + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, database->counterMutex)); + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, database)); + } + + if (Kernel->db->lastDatabase != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->lastDatabase->counterMutex)); + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel->db->lastDatabase)); + } + + /* Free all database records. */ + for (record = Kernel->db->freeRecord; record != gcvNULL; record = recordNext) + { + recordNext = record->next; + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, record)); + } + + /* Destroy the database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Destroy video memory name database. */ + gcmkVERIFY_OK(gckKERNEL_DestroyIntegerDatabase(Kernel, Kernel->db->nameDatabase)); + + /* Destroy video memory name database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->nameDatabaseMutex)); + + + /* Destroy id-pointer database. */ + gcmkVERIFY_OK(gckKERNEL_DestroyIntegerDatabase(Kernel, Kernel->db->pointerDatabase)); + + /* Destroy id-pointer database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + + /* Destroy the database. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel->db)); + + /* Notify stuck timer to quit. */ + Kernel->monitorTimerStop = gcvTRUE; + } + +#if gcdENABLE_VG + if (Kernel->vg) + { + gcmkVERIFY_OK(gckVGKERNEL_Destroy(Kernel->vg)); + } + else +#endif + { + /* Destroy the gckMMU object. */ + gcmkVERIFY_OK(gckMMU_Destroy(Kernel->mmu)); + + /* Destroy the gckCOMMNAND object. */ + gcmkVERIFY_OK(gckCOMMAND_Destroy(Kernel->command)); + + /* Destroy the gckEVENT object. */ + gcmkVERIFY_OK(gckEVENT_Destroy(Kernel->eventObj)); + + /* Destroy the gckHARDWARE object. */ + gcmkVERIFY_OK(gckHARDWARE_Destroy(Kernel->hardware)); + } + + /* Detsroy the client atom. */ + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, Kernel->atomClients)); + + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->virtualBufferLock)); + +#if gcdDVFS + if (Kernel->dvfs) + { + gcmkVERIFY_OK(gckDVFS_Stop(Kernel->dvfs)); + gcmkVERIFY_OK(gckDVFS_Destroy(Kernel->dvfs)); + } +#endif + +#if gcdANDROID_NATIVE_FENCE_SYNC + gcmkVERIFY_OK(gckOS_DestroySyncTimeline(Kernel->os, Kernel->timeline)); +#endif + +#if gcdSECURITY + gcmkVERIFY_OK(gckKERNEL_SecurityClose(Kernel->securityChannel)); +#endif + + if (Kernel->monitorTimer) + { + gcmkVERIFY_OK(gckOS_StopTimer(Kernel->os, Kernel->monitorTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Kernel->os, Kernel->monitorTimer)); + } + + /* Mark the gckKERNEL object as unknown. */ + Kernel->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckKERNEL object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, Kernel)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** _AllocateMemory +** +** Private function to walk all required memory pools to allocate the requested +** amount of video memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_AllocateLinearMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcePOOL * Pool, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + IN gctUINT32 Flag, + OUT gctUINT32 * Node + ) +{ + gcePOOL pool; + gceSTATUS status; + gckVIDMEM videoMemory; + gctINT loopCount; + gcuVIDMEM_NODE_PTR node = gcvNULL; + gctBOOL tileStatusInVirtual; + gctBOOL contiguous = gcvFALSE; + gctBOOL cacheable = gcvFALSE; + gctSIZE_T bytes = Bytes; + gctUINT32 handle = 0; + gceDATABASE_TYPE type; + + gcmkHEADER_ARG("Kernel=0x%x *Pool=%d Bytes=%lu Alignment=%lu Type=%d", + Kernel, *Pool, Bytes, Alignment, Type); + + gcmkVERIFY_ARGUMENT(Pool != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes != 0); + + /* Get basic type. */ + Type &= 0xFF; + + /* Check flags. */ + contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; + cacheable = Flag & gcvALLOC_FLAG_CACHEABLE; + +AllocateMemory: + + /* Get initial pool. */ + switch (pool = *Pool) + { + case gcvPOOL_DEFAULT: + case gcvPOOL_LOCAL: + pool = gcvPOOL_LOCAL_INTERNAL; + loopCount = (gctINT) gcvPOOL_NUMBER_OF_POOLS; + break; + + case gcvPOOL_UNIFIED: + pool = gcvPOOL_SYSTEM; + loopCount = (gctINT) gcvPOOL_NUMBER_OF_POOLS; + break; + + case gcvPOOL_CONTIGUOUS: + loopCount = (gctINT) gcvPOOL_NUMBER_OF_POOLS; + break; + + default: + loopCount = 1; + break; + } + + while (loopCount-- > 0) + { + if (pool == gcvPOOL_VIRTUAL) + { + /* Create a gcuVIDMEM_NODE for virtual memory. */ + gcmkONERROR( + gckVIDMEM_ConstructVirtual(Kernel, Flag | gcvALLOC_FLAG_NON_CONTIGUOUS, Bytes, &node)); + + bytes = node->Virtual.bytes; + node->Virtual.type = Type; + + /* Success. */ + break; + } + + else + if (pool == gcvPOOL_CONTIGUOUS) + { +#if gcdCONTIGUOUS_SIZE_LIMIT + if (Bytes > gcdCONTIGUOUS_SIZE_LIMIT && contiguous == gcvFALSE) + { + status = gcvSTATUS_OUT_OF_MEMORY; + } + else +#endif + { + /* Create a gcuVIDMEM_NODE from contiguous memory. */ + status = gckVIDMEM_ConstructVirtual( + Kernel, + Flag | gcvALLOC_FLAG_CONTIGUOUS, + Bytes, + &node); + } + + if (gcmIS_SUCCESS(status)) + { + bytes = node->Virtual.bytes; + node->Virtual.type = Type; + + /* Memory allocated. */ + break; + } + } + + else + /* gcvPOOL_SYSTEM can't be cacheable. */ + if (cacheable == gcvFALSE) + { + /* Get pointer to gckVIDMEM object for pool. */ + status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory); + + if (gcmIS_SUCCESS(status)) + { + /* Allocate memory. */ +#if defined(gcdLINEAR_SIZE_LIMIT) + /* 512 KB */ + if (Bytes > gcdLINEAR_SIZE_LIMIT) + { + status = gcvSTATUS_OUT_OF_MEMORY; + } + else +#endif + { + status = gckVIDMEM_AllocateLinear(Kernel, + videoMemory, + Bytes, + Alignment, + Type, + (*Pool == gcvPOOL_SYSTEM), + &node); + } + + if (gcmIS_SUCCESS(status)) + { + /* Memory allocated. */ + node->VidMem.pool = pool; + bytes = node->VidMem.bytes; + break; + } + } + } + + if (pool == gcvPOOL_LOCAL_INTERNAL) + { + /* Advance to external memory. */ + pool = gcvPOOL_LOCAL_EXTERNAL; + } + + else + if (pool == gcvPOOL_LOCAL_EXTERNAL) + { + /* Advance to contiguous system memory. */ + pool = gcvPOOL_SYSTEM; + } + + else + if (pool == gcvPOOL_SYSTEM) + { + /* Advance to contiguous memory. */ + pool = gcvPOOL_CONTIGUOUS; + } + + else + if (pool == gcvPOOL_CONTIGUOUS) + { +#if gcdENABLE_VG + if (Kernel->vg) + { + tileStatusInVirtual = gcvFALSE; + } + else +#endif + { + tileStatusInVirtual = + gckHARDWARE_IsFeatureAvailable(Kernel->hardware, + gcvFEATURE_MC20); + } + + if (Type == gcvSURF_TILE_STATUS && tileStatusInVirtual != gcvTRUE) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + if (contiguous) + { + break; + } + + /* Advance to virtual memory. */ + pool = gcvPOOL_VIRTUAL; + } + + else + { + /* Out of pools. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + } + + if (node == gcvNULL) + { + if (contiguous) + { + /* Broadcast OOM message. */ + status = gckOS_Broadcast(Kernel->os, Kernel->hardware, gcvBROADCAST_OUT_OF_MEMORY); + + if (gcmIS_SUCCESS(status)) + { + /* Get some memory. */ + gckOS_Delay(gcvNULL, 1); + goto AllocateMemory; + } + } + + /* Nothing allocated. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Allocate handle for this video memory. */ + gcmkONERROR( + gckVIDMEM_NODE_Allocate(Kernel, node, Type, pool, &handle)); + + /* Return node and pool used for allocation. */ + *Node = handle; + *Pool = pool; + + /* Encode surface type and pool to database type. */ + type = gcvDB_VIDEO_MEMORY + | (Type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) + | (pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); + + /* Record in process db. */ + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + ProcessID, + type, + gcmINT2PTR(handle), + gcvNULL, + bytes)); + + /* Return status. */ + gcmkFOOTER_ARG("*Pool=%d *Node=0x%x", *Pool, *Node); + return gcvSTATUS_OK; + +OnError: + if (handle) + { + /* Destroy handle allocated. */ + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, handle)); + } + + if (node) + { + /* Free video memory allocated. */ + gcmkVERIFY_OK(gckVIDMEM_Free(Kernel, node)); + } + + /* For some case like chrome with webgl test, it needs too much memory so that it invokes oom_killer + * And the case is killed by oom_killer, the user wants not to see the crash and hope the case iteself handles the condition + * So the patch reports the out_of_memory to the case */ + if ( status == gcvSTATUS_OUT_OF_MEMORY && (Flag & gcvALLOC_FLAG_MEMLIMIT) ) + gcmkPRINT("The running case is out_of_memory"); + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_ReleaseVideoMemory +** +** Release handle of a video memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID of current process. +** +** gctUINT32 Handle +** Handle of video memory. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_ReleaseVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject; + gceDATABASE_TYPE type; + + gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d Handle=%d", + Kernel, ProcessID, Handle); + + gcmkONERROR( + gckVIDMEM_HANDLE_Lookup(Kernel, ProcessID, Handle, &nodeObject)); + + type = gcvDB_VIDEO_MEMORY + | (nodeObject->type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) + | (nodeObject->pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); + + gcmkONERROR( + gckKERNEL_RemoveProcessDB(Kernel, + ProcessID, + type, + gcmINT2PTR(Handle))); + + gckVIDMEM_HANDLE_Dereference(Kernel, ProcessID, Handle); + + gckVIDMEM_NODE_Dereference(Kernel, nodeObject); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_LockVideoMemory +** +** Lock a video memory node. It will generate a cpu virtual address used +** by software and a GPU address used by GPU. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gceCORE Core +** GPU to which video memory is locked. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_LockVideoMemory( + IN gckKERNEL Kernel, + IN gceCORE Core, + IN gctUINT32 ProcessID, + IN gctBOOL FromUser, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject = gcvNULL; + gcuVIDMEM_NODE_PTR node = gcvNULL; + gctBOOL locked = gcvFALSE; + gctBOOL asynchronous = gcvFALSE; +#ifndef __QNXNTO__ + gctPOINTER pointer = gcvNULL; +#endif + + gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d", + Kernel, ProcessID); + + gcmkONERROR( + gckVIDMEM_HANDLE_LookupAndReference(Kernel, + Interface->u.LockVideoMemory.node, + &nodeObject)); + + node = nodeObject->node; + + Interface->u.LockVideoMemory.gid = 0; + + /* Lock video memory. */ + gcmkONERROR( + gckVIDMEM_Lock(Kernel, + nodeObject, + Interface->u.LockVideoMemory.cacheable, + &Interface->u.LockVideoMemory.address, + &Interface->u.LockVideoMemory.gid, + &Interface->u.LockVideoMemory.physicalAddress)); + + locked = gcvTRUE; + + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + /* Map video memory address into user space. */ +#ifdef __QNXNTO__ + if (node->VidMem.logical == gcvNULL) + { + gcmkONERROR( + gckKERNEL_MapVideoMemory(Kernel, + FromUser, + Interface->u.LockVideoMemory.address, + ProcessID, + node->VidMem.bytes, + &node->VidMem.logical)); + } + gcmkASSERT(node->VidMem.logical != gcvNULL); + + Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(node->VidMem.logical); +#else + gcmkONERROR( + gckKERNEL_MapVideoMemoryEx(Kernel, + Core, + FromUser, + Interface->u.LockVideoMemory.address, + &pointer)); + + Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(pointer); +#endif + } + else + { + Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(node->Virtual.logical); + + /* Success. */ + status = gcvSTATUS_OK; + } + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckVIDMEM_Node_Lock( + Kernel, + nodeObject, + &Interface->u.LockVideoMemory.address + )); +#endif + + +#if gcdSECURE_USER + /* Return logical address as physical address. */ + Interface->u.LockVideoMemory.address = + (gctUINT32)(Interface->u.LockVideoMemory.memory); +#endif + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + ProcessID, gcvDB_VIDEO_MEMORY_LOCKED, + gcmINT2PTR(Interface->u.LockVideoMemory.node), + gcvNULL, + 0)); + + gckVIDMEM_HANDLE_Reference( + Kernel, ProcessID, (gctUINT32)Interface->u.LockVideoMemory.node); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (locked) + { + /* Roll back the lock. */ + gcmkVERIFY_OK(gckVIDMEM_Unlock(Kernel, + nodeObject, + gcvSURF_TYPE_UNKNOWN, + &asynchronous)); + + if (gcvTRUE == asynchronous) + { + /* Bottom Half */ + gcmkVERIFY_OK(gckVIDMEM_Unlock(Kernel, + nodeObject, + gcvSURF_TYPE_UNKNOWN, + gcvNULL)); + } + } + + if (nodeObject != gcvNULL) + { + gckVIDMEM_NODE_Dereference(Kernel, nodeObject); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_UnlockVideoMemory +** +** Unlock a video memory node. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID of current process. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_UnlockVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject; + gcuVIDMEM_NODE_PTR node; + + gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d", + Kernel, ProcessID); + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup( + Kernel, + ProcessID, + (gctUINT32)Interface->u.UnlockVideoMemory.node, + &nodeObject)); + + node = nodeObject->node; + + /* Unlock video memory. */ +#if gcdSECURE_USER + /* Save node information before it disappears. */ + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + logical = gcvNULL; + bytes = 0; + } + else + { + logical = node->Virtual.logical; + bytes = node->Virtual.bytes; + } +#endif + + /* Unlock video memory. */ + gcmkONERROR(gckVIDMEM_Unlock( + Kernel, + nodeObject, + Interface->u.UnlockVideoMemory.type, + &Interface->u.UnlockVideoMemory.asynchroneous)); + +#if gcdSECURE_USER + /* Flush the translation cache for virtual surfaces. */ + if (logical != gcvNULL) + { + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel, + cache, + logical, + bytes)); + } +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_QueryDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gctINT i; + gcuDATABASE_INFO tmp; + + gceDATABASE_TYPE type[3] = { + gcvDB_VIDEO_MEMORY | (gcvPOOL_SYSTEM << gcdDB_VIDEO_MEMORY_POOL_SHIFT), + gcvDB_VIDEO_MEMORY | (gcvPOOL_CONTIGUOUS << gcdDB_VIDEO_MEMORY_POOL_SHIFT), + gcvDB_VIDEO_MEMORY | (gcvPOOL_VIRTUAL << gcdDB_VIDEO_MEMORY_POOL_SHIFT), + }; + + gcmkHEADER(); + + /* Query video memory. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_VIDEO_MEMORY, + &Interface->u.Database.vidMem)); + + /* Query non-paged memory. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_NON_PAGED, + &Interface->u.Database.nonPaged)); + + /* Query contiguous memory. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_CONTIGUOUS, + &Interface->u.Database.contiguous)); + + /* Query GPU idle time. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_IDLE, + &Interface->u.Database.gpuIdle)); + for (i = 0; i < 3; i++) + { + /* Query each video memory pool. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + type[i], + &Interface->u.Database.vidMemPool[i])); + } + + /* Query virtual command buffer pool. */ + gcmkONERROR( + gckKERNEL_QueryProcessDB(Kernel, + Interface->u.Database.processID, + !Interface->u.Database.validProcessID, + gcvDB_COMMAND_BUFFER, + &tmp)); + + Interface->u.Database.vidMemPool[2].counters.bytes += tmp.counters.bytes; + Interface->u.Database.vidMemPool[2].counters.maxBytes += tmp.counters.maxBytes; + Interface->u.Database.vidMemPool[2].counters.totalBytes += tmp.counters.totalBytes; + + Interface->u.Database.vidMem.counters.bytes += tmp.counters.bytes; + Interface->u.Database.vidMem.counters.maxBytes += tmp.counters.maxBytes; + Interface->u.Database.vidMem.counters.totalBytes += tmp.counters.totalBytes; + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gckKERNEL_DumpVidMemUsage(Kernel, Interface->u.Database.processID); +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_ConfigPowerManagement( + IN gckKERNEL Kernel, + IN OUT gcsHAL_INTERFACE * Interface +) +{ + gceSTATUS status; + gctBOOL enable = Interface->u.ConfigPowerManagement.enable; + + gcmkHEADER(); + + gcmkONERROR(gckHARDWARE_SetPowerManagement(Kernel->hardware, enable)); + + if (enable == gcvTRUE) + { + gcmkONERROR( + gckHARDWARE_SetPowerManagementState(Kernel->hardware, gcvPOWER_ON)); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_Dispatch +** +** Dispatch a command received from the user HAL layer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL FromUser +** whether the call is from the user space. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS +gckKERNEL_Dispatch( + IN gckKERNEL Kernel, + IN gctBOOL FromUser, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctPHYS_ADDR physical = gcvNULL; + gctSIZE_T bytes; + gctPOINTER logical = gcvNULL; + gctPOINTER info = gcvNULL; +#if (gcdENABLE_3D || gcdENABLE_2D) + gckCONTEXT context = gcvNULL; +#endif + gckKERNEL kernel = Kernel; + gctUINT32 address; + gctUINT32 processID; +#if gcdSECURE_USER + gcskSECURE_CACHE_PTR cache; + gctPOINTER logical; +#endif + gctUINT32 paddr = gcvINVALID_ADDRESS; +#if !USE_NEW_LINUX_SIGNAL + gctSIGNAL signal; +#endif + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + + gckVIDMEM_NODE nodeObject; + gctBOOL powerMutexAcquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%x FromUser=%d Interface=0x%x", + Kernel, FromUser, Interface); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Interface != gcvNULL); + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "Dispatching command %d (%s)", + Interface->command, _DispatchText[Interface->command]); +#endif +#if QNX_SINGLE_THREADED_DEBUGGING + gckOS_AcquireMutex(Kernel->os, Kernel->debugMutex, gcvINFINITE); +#endif + + /* Get the current process ID. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + +#if gcdSECURE_USER + gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache)); +#endif + + /* Dispatch on command. */ + switch (Interface->command) + { + case gcvHAL_GET_BASE_ADDRESS: + /* Get base address. */ + gcmkONERROR( + gckOS_GetBaseAddress(Kernel->os, + &Interface->u.GetBaseAddress.baseAddress)); + break; + + case gcvHAL_QUERY_VIDEO_MEMORY: + /* Query video memory size. */ + gcmkONERROR(gckKERNEL_QueryVideoMemory(Kernel, Interface)); + break; + + case gcvHAL_QUERY_CHIP_IDENTITY: + /* Query chip identity. */ + gcmkONERROR( + gckHARDWARE_QueryChipIdentity( + Kernel->hardware, + &Interface->u.QueryChipIdentity)); + break; + + case gcvHAL_MAP_MEMORY: + physical = gcmINT2PTR(Interface->u.MapMemory.physical); + + /* Map memory. */ + gcmkONERROR( + gckKERNEL_MapMemory(Kernel, + physical, + (gctSIZE_T) Interface->u.MapMemory.bytes, + &logical)); + + Interface->u.MapMemory.logical = gcmPTR_TO_UINT64(logical); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_MAP_MEMORY, + logical, + physical, + (gctSIZE_T) Interface->u.MapMemory.bytes)); + break; + + case gcvHAL_UNMAP_MEMORY: + physical = gcmINT2PTR(Interface->u.UnmapMemory.physical); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_MAP_MEMORY, + gcmUINT64_TO_PTR(Interface->u.UnmapMemory.logical))); + + /* Unmap memory. */ + gcmkONERROR( + gckKERNEL_UnmapMemory(Kernel, + physical, + (gctSIZE_T) Interface->u.UnmapMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.UnmapMemory.logical))); + break; + + case gcvHAL_ALLOCATE_NON_PAGED_MEMORY: + bytes = (gctSIZE_T) Interface->u.AllocateNonPagedMemory.bytes; + + /* Allocate non-paged memory. */ + gcmkONERROR( + gckOS_AllocateNonPagedMemory( + Kernel->os, + FromUser, + &bytes, + &physical, + &logical)); + + Interface->u.AllocateNonPagedMemory.bytes = bytes; + Interface->u.AllocateNonPagedMemory.logical = gcmPTR_TO_UINT64(logical); + Interface->u.AllocateNonPagedMemory.physical = gcmPTR_TO_NAME(physical); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_NON_PAGED, + logical, + gcmINT2PTR(Interface->u.AllocateNonPagedMemory.physical), + bytes)); + break; + + case gcvHAL_ALLOCATE_VIRTUAL_COMMAND_BUFFER: + bytes = (gctSIZE_T) Interface->u.AllocateVirtualCommandBuffer.bytes; + + gcmkONERROR( + gckKERNEL_AllocateVirtualCommandBuffer( + Kernel, + FromUser, + &bytes, + &physical, + &logical)); + + Interface->u.AllocateVirtualCommandBuffer.bytes = bytes; + Interface->u.AllocateVirtualCommandBuffer.logical = gcmPTR_TO_UINT64(logical); + Interface->u.AllocateVirtualCommandBuffer.physical = gcmPTR_TO_NAME(physical); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_COMMAND_BUFFER, + logical, + gcmINT2PTR(Interface->u.AllocateVirtualCommandBuffer.physical), + bytes)); + break; + + case gcvHAL_FREE_NON_PAGED_MEMORY: + physical = gcmNAME_TO_PTR(Interface->u.FreeNonPagedMemory.physical); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_NON_PAGED, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + + /* Unmap user logical out of physical memory first. */ + gcmkONERROR(gckOS_UnmapUserLogical(Kernel->os, + physical, + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + + /* Free non-paged memory. */ + gcmkONERROR( + gckOS_FreeNonPagedMemory(Kernel->os, + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes, + physical, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Kernel, + cache, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical), + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes)); +#endif + + gcmRELEASE_NAME(Interface->u.FreeNonPagedMemory.physical); + break; + + case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY: + bytes = (gctSIZE_T) Interface->u.AllocateContiguousMemory.bytes; + + /* Allocate contiguous memory. */ + gcmkONERROR(gckOS_AllocateContiguous( + Kernel->os, + FromUser, + &bytes, + &physical, + &logical)); + + Interface->u.AllocateContiguousMemory.bytes = bytes; + Interface->u.AllocateContiguousMemory.logical = gcmPTR_TO_UINT64(logical); + Interface->u.AllocateContiguousMemory.physical = gcmPTR_TO_NAME(physical); + + gcmkONERROR(gckHARDWARE_ConvertLogical( + Kernel->hardware, + logical, + gcvTRUE, + &Interface->u.AllocateContiguousMemory.address)); + + gcmkVERIFY_OK(gckKERNEL_AddProcessDB( + Kernel, + processID, gcvDB_CONTIGUOUS, + logical, + gcmINT2PTR(Interface->u.AllocateContiguousMemory.physical), + bytes)); + break; + + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + physical = gcmNAME_TO_PTR(Interface->u.FreeContiguousMemory.physical); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_CONTIGUOUS, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + + /* Unmap user logical out of physical memory first. */ + gcmkONERROR(gckOS_UnmapUserLogical(Kernel->os, + physical, + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical))); + + /* Free contiguous memory. */ + gcmkONERROR( + gckOS_FreeContiguous(Kernel->os, + physical, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical), + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes)); + +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Kernel, + cache, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical), + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes)); +#endif + + gcmRELEASE_NAME(Interface->u.FreeContiguousMemory.physical); + break; + + case gcvHAL_ALLOCATE_VIDEO_MEMORY: + + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + + break; + + case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY: + /* Allocate memory. */ + gcmkONERROR( + gckKERNEL_AllocateLinearMemory(Kernel, processID, + &Interface->u.AllocateLinearVideoMemory.pool, + Interface->u.AllocateLinearVideoMemory.bytes, + Interface->u.AllocateLinearVideoMemory.alignment, + Interface->u.AllocateLinearVideoMemory.type, + Interface->u.AllocateLinearVideoMemory.flag, + &Interface->u.AllocateLinearVideoMemory.node)); + break; + + case gcvHAL_RELEASE_VIDEO_MEMORY: + /* Release video memory. */ + gcmkONERROR(gckKERNEL_ReleaseVideoMemory( + Kernel, processID, + (gctUINT32)Interface->u.ReleaseVideoMemory.node + )); + break; + + case gcvHAL_LOCK_VIDEO_MEMORY: + /* Lock video memory. */ + gcmkONERROR(gckKERNEL_LockVideoMemory(Kernel, Kernel->core, processID, FromUser, Interface)); + break; + + case gcvHAL_UNLOCK_VIDEO_MEMORY: + /* Unlock video memory. */ + gcmkONERROR(gckKERNEL_UnlockVideoMemory(Kernel, processID, Interface)); + break; + + case gcvHAL_EVENT_COMMIT: + /* Commit an event queue. */ +#if gcdMULTI_GPU + if (Interface->u.Event.gpuMode == gcvMULTI_GPU_MODE_INDEPENDENT) + { + gcmkONERROR( + gckEVENT_Commit(Kernel->eventObj, + gcmUINT64_TO_PTR(Interface->u.Event.queue), + Interface->u.Event.chipEnable)); + } + else + { + gcmkONERROR( + gckEVENT_Commit(Kernel->eventObj, + gcmUINT64_TO_PTR(Interface->u.Event.queue), + gcvCORE_3D_ALL_MASK)); + } +#else + gcmkONERROR( + gckEVENT_Commit(Kernel->eventObj, + gcmUINT64_TO_PTR(Interface->u.Event.queue))); +#endif + break; + + case gcvHAL_COMMIT: + /* Commit a command and context buffer. */ +#if gcdMULTI_GPU + if (Interface->u.Commit.gpuMode == gcvMULTI_GPU_MODE_INDEPENDENT) + { + gcmkONERROR( + gckCOMMAND_Commit(Kernel->command, + Interface->u.Commit.context ? + gcmNAME_TO_PTR(Interface->u.Commit.context) : gcvNULL, + gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffer), + gcmUINT64_TO_PTR(Interface->u.Commit.delta), + gcmUINT64_TO_PTR(Interface->u.Commit.queue), + processID, + Interface->u.Commit.chipEnable)); + } + else + { + gcmkONERROR( + gckCOMMAND_Commit(Kernel->command, + Interface->u.Commit.context ? + gcmNAME_TO_PTR(Interface->u.Commit.context) : gcvNULL, + gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffer), + gcmUINT64_TO_PTR(Interface->u.Commit.delta), + gcmUINT64_TO_PTR(Interface->u.Commit.queue), + processID, + gcvCORE_3D_ALL_MASK)); + } +#else + gcmkONERROR( + gckCOMMAND_Commit(Kernel->command, + Interface->u.Commit.context ? + gcmNAME_TO_PTR(Interface->u.Commit.context) : gcvNULL, + gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffer), + gcmUINT64_TO_PTR(Interface->u.Commit.delta), + gcmUINT64_TO_PTR(Interface->u.Commit.queue), + processID)); +#endif + + break; + + case gcvHAL_STALL: + /* Stall the command queue. */ +#if gcdMULTI_GPU + gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE, gcvCORE_3D_ALL_MASK)); +#else + gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE)); +#endif + break; + + case gcvHAL_MAP_USER_MEMORY: + /* Map user memory to DMA. */ + gcmkONERROR( + gckOS_MapUserMemory(Kernel->os, + Kernel->core, + gcmUINT64_TO_PTR(Interface->u.MapUserMemory.memory), + Interface->u.MapUserMemory.physical, + (gctSIZE_T) Interface->u.MapUserMemory.size, + &info, + &Interface->u.MapUserMemory.address)); + + Interface->u.MapUserMemory.info = gcmPTR_TO_NAME(info); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_MAP_USER_MEMORY, + gcmINT2PTR(Interface->u.MapUserMemory.info), + gcmUINT64_TO_PTR(Interface->u.MapUserMemory.memory), + (gctSIZE_T) Interface->u.MapUserMemory.size)); + break; + + case gcvHAL_UNMAP_USER_MEMORY: + address = Interface->u.UnmapUserMemory.address; + info = gcmNAME_TO_PTR(Interface->u.UnmapUserMemory.info); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_MAP_USER_MEMORY, + gcmINT2PTR(Interface->u.UnmapUserMemory.info))); + /* Unmap user memory. */ + gcmkONERROR( + gckOS_UnmapUserMemory(Kernel->os, + Kernel->core, + gcmUINT64_TO_PTR(Interface->u.UnmapUserMemory.memory), + (gctSIZE_T) Interface->u.UnmapUserMemory.size, + info, + address)); + +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Kernel, + cache, + gcmUINT64_TO_PTR(Interface->u.UnmapUserMemory.memory), + (gctSIZE_T) Interface->u.UnmapUserMemory.size)); +#endif + + gcmRELEASE_NAME(Interface->u.UnmapUserMemory.info); + break; + +#if !USE_NEW_LINUX_SIGNAL + case gcvHAL_USER_SIGNAL: + /* Dispatch depends on the user signal subcommands. */ + switch(Interface->u.UserSignal.command) + { + case gcvUSER_SIGNAL_CREATE: + /* Create a signal used in the user space. */ + gcmkONERROR( + gckOS_CreateUserSignal(Kernel->os, + Interface->u.UserSignal.manualReset, + &Interface->u.UserSignal.id)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id), + gcvNULL, + 0)); + break; + + case gcvUSER_SIGNAL_DESTROY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id))); + + /* Destroy the signal. */ + gcmkONERROR( + gckOS_DestroyUserSignal(Kernel->os, + Interface->u.UserSignal.id)); + break; + + case gcvUSER_SIGNAL_SIGNAL: + /* Signal the signal. */ + gcmkONERROR( + gckOS_SignalUserSignal(Kernel->os, + Interface->u.UserSignal.id, + Interface->u.UserSignal.state)); + break; + + case gcvUSER_SIGNAL_WAIT: + /* Wait on the signal. */ + status = gckOS_WaitUserSignal(Kernel->os, + Interface->u.UserSignal.id, + Interface->u.UserSignal.wait); + + break; + + case gcvUSER_SIGNAL_MAP: + gcmkONERROR( + gckOS_MapSignal(Kernel->os, + (gctSIGNAL)(gctUINTPTR_T)Interface->u.UserSignal.id, + (gctHANDLE)(gctUINTPTR_T)processID, + &signal)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id), + gcvNULL, + 0)); + break; + + case gcvUSER_SIGNAL_UNMAP: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id))); + + /* Destroy the signal. */ + gcmkONERROR( + gckOS_DestroyUserSignal(Kernel->os, + Interface->u.UserSignal.id)); + break; + + default: + /* Invalid user signal command. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + break; +#endif + + case gcvHAL_SET_POWER_MANAGEMENT_STATE: + /* Set the power management state. */ + gcmkONERROR( + gckHARDWARE_SetPowerManagementState( + Kernel->hardware, + Interface->u.SetPowerManagement.state)); + break; + + case gcvHAL_QUERY_POWER_MANAGEMENT_STATE: + /* Chip is not idle. */ + Interface->u.QueryPowerManagement.isIdle = gcvFALSE; + + /* Query the power management state. */ + gcmkONERROR(gckHARDWARE_QueryPowerManagementState( + Kernel->hardware, + &Interface->u.QueryPowerManagement.state)); + + /* Query the idle state. */ + gcmkONERROR( + gckHARDWARE_QueryIdle(Kernel->hardware, + &Interface->u.QueryPowerManagement.isIdle)); + break; + + case gcvHAL_READ_REGISTER: +#if gcdREGISTER_ACCESS_FROM_USER + { + gceCHIPPOWERSTATE power; + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); + powerMutexAcquired = gcvTRUE; + gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, + &power)); + if (power == gcvPOWER_ON) + { + /* Read a register. */ + gcmkONERROR(gckOS_ReadRegisterEx( + Kernel->os, + Kernel->core, + Interface->u.ReadRegisterData.address, + &Interface->u.ReadRegisterData.data)); + } + else + { + /* Chip is in power-state. */ + Interface->u.ReadRegisterData.data = 0; + status = gcvSTATUS_CHIP_NOT_READY; + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + powerMutexAcquired = gcvFALSE; + } +#else + /* No access from user land to read registers. */ + Interface->u.ReadRegisterData.data = 0; + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + +#if gcdMULTI_GPU + case gcvHAL_READ_REGISTER_EX: +#if gcdREGISTER_ACCESS_FROM_USER + { + gceCHIPPOWERSTATE power; + gctUINT32 coreId = 0; + gctUINT32 coreSelect = Interface->u.ReadRegisterDataEx.coreSelect; + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); + powerMutexAcquired = gcvTRUE; + gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, + &power)); + if (power == gcvPOWER_ON) + { + for (; coreSelect != 0; coreSelect >>= 1, coreId++) + { + if (coreSelect & 1UL) + { + /* Read a register. */ + gcmkONERROR( + gckOS_ReadRegisterByCoreId( + Kernel->os, + Kernel->core, + coreId, + Interface->u.ReadRegisterDataEx.address, + &Interface->u.ReadRegisterDataEx.data[coreId])); + } + } + } + else + { + for (coreId = 0; coreId < gcdMULTI_GPU; coreId++) + { + /* Chip is in power-state. */ + Interface->u.ReadRegisterDataEx.data[coreId] = 0; + } + status = gcvSTATUS_CHIP_NOT_READY; + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + powerMutexAcquired = gcvFALSE; + } +#else + gctUINT32 coreId; + + /* No access from user land to read registers. */ + for (coreId = 0; coreId < gcdMULTI_GPU; coreId++) + { + Interface->u.ReadRegisterDataEx.data[coreId] = 0; + } + + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + + case gcvHAL_WRITE_REGISTER_EX: +#if gcdREGISTER_ACCESS_FROM_USER + { + gceCHIPPOWERSTATE power; + gctUINT32 coreId = 0; + gctUINT32 coreSelect = Interface->u.WriteRegisterDataEx.coreSelect; + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); + powerMutexAcquired = gcvTRUE; + gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, + &power)); + if (power == gcvPOWER_ON) + { + for (; coreSelect != 0; coreSelect >>= 1, coreId++) + { + if (coreSelect & 1UL) + { + /* Write a register. */ + gcmkONERROR( + gckOS_WriteRegisterByCoreId( + Kernel->os, + Kernel->core, + coreId, + Interface->u.WriteRegisterDataEx.address, + Interface->u.WriteRegisterDataEx.data[coreId])); + } + } + } + else + { + /* Chip is in power-state. */ + for (coreId = 0; coreId < gcdMULTI_GPU; coreId++) + { + Interface->u.WriteRegisterDataEx.data[coreId] = 0; + } + status = gcvSTATUS_CHIP_NOT_READY; + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + powerMutexAcquired = gcvFALSE; + } +#else + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; +#endif + + case gcvHAL_WRITE_REGISTER: +#if gcdREGISTER_ACCESS_FROM_USER + { + gceCHIPPOWERSTATE power; + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); + powerMutexAcquired = gcvTRUE; + gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, + &power)); + if (power == gcvPOWER_ON) + { + /* Write a register. */ + gcmkONERROR( + gckOS_WriteRegisterEx(Kernel->os, + Kernel->core, + Interface->u.WriteRegisterData.address, + Interface->u.WriteRegisterData.data)); + } + else + { + /* Chip is in power-state. */ + Interface->u.WriteRegisterData.data = 0; + status = gcvSTATUS_CHIP_NOT_READY; + } + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + powerMutexAcquired = gcvFALSE; + } +#else + /* No access from user land to write registers. */ + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + + case gcvHAL_READ_ALL_PROFILE_REGISTERS: +#if VIVANTE_PROFILER && VIVANTE_PROFILER_CONTEXT + /* Read profile data according to the context. */ + gcmkONERROR( + gckHARDWARE_QueryContextProfile( + Kernel->hardware, + Kernel->profileCleanRegister, + gcmNAME_TO_PTR(Interface->u.RegisterProfileData.context), + &Interface->u.RegisterProfileData.counters)); +#elif VIVANTE_PROFILER + /* Read all 3D profile registers. */ + gcmkONERROR( + gckHARDWARE_QueryProfileRegisters( + Kernel->hardware, + Kernel->profileCleanRegister, + &Interface->u.RegisterProfileData.counters)); +#else + status = gcvSTATUS_OK; +#endif + break; + + case gcvHAL_PROFILE_REGISTERS_2D: +#if VIVANTE_PROFILER + /* Read all 2D profile registers. */ + gcmkONERROR( + gckHARDWARE_ProfileEngine2D( + Kernel->hardware, + gcmUINT64_TO_PTR(Interface->u.RegisterProfileData2D.hwProfile2D))); +#else + status = gcvSTATUS_OK; +#endif + break; + + case gcvHAL_GET_PROFILE_SETTING: +#if VIVANTE_PROFILER + /* Get profile setting */ + Interface->u.GetProfileSetting.enable = Kernel->profileEnable; +#endif + + status = gcvSTATUS_OK; + break; + + case gcvHAL_SET_PROFILE_SETTING: +#if VIVANTE_PROFILER + /* Set profile setting */ + if(Kernel->hardware->gpuProfiler) + { + Kernel->profileEnable = Interface->u.SetProfileSetting.enable; +#if VIVANTE_PROFILER_NEW + if (Kernel->profileEnable) + gckHARDWARE_InitProfiler(Kernel->hardware); +#endif + } + else + { + status = gcvSTATUS_NOT_SUPPORTED; + break; + } +#endif + + status = gcvSTATUS_OK; + break; + +#if VIVANTE_PROFILER_PERDRAW + case gcvHAL_READ_PROFILER_REGISTER_SETTING: + #if VIVANTE_PROFILER + Kernel->profileCleanRegister = Interface->u.SetProfilerRegisterClear.bclear; + #endif + status = gcvSTATUS_OK; + break; +#endif + + case gcvHAL_QUERY_KERNEL_SETTINGS: + /* Get kernel settings. */ + gcmkONERROR( + gckKERNEL_QuerySettings(Kernel, + &Interface->u.QueryKernelSettings.settings)); + break; + + case gcvHAL_RESET: + /* Reset the hardware. */ + gcmkONERROR( + gckHARDWARE_Reset(Kernel->hardware)); + break; + + case gcvHAL_DEBUG: + /* Set debug level and zones. */ + if (Interface->u.Debug.set) + { + gckOS_SetDebugLevel(Interface->u.Debug.level); + gckOS_SetDebugZones(Interface->u.Debug.zones, + Interface->u.Debug.enable); + } + + if (Interface->u.Debug.message[0] != '\0') + { + /* Print a message to the debugger. */ + if (Interface->u.Debug.type == gcvMESSAGE_TEXT) + { + gckOS_CopyPrint(Interface->u.Debug.message); + } + else + { + gckOS_DumpBuffer(Kernel->os, + Interface->u.Debug.message, + Interface->u.Debug.messageSize, + gceDUMP_BUFFER_FROM_USER, + gcvTRUE); + } + } + status = gcvSTATUS_OK; + break; + + case gcvHAL_DUMP_GPU_STATE: + { + gceCHIPPOWERSTATE power; + + _DumpDriverConfigure(Kernel); + + gcmkONERROR(gckHARDWARE_QueryPowerManagementState( + Kernel->hardware, + &power + )); + + if (power == gcvPOWER_ON) + { + Interface->u.ReadRegisterData.data = 1; + + _DumpState(Kernel); + } + else + { + Interface->u.ReadRegisterData.data = 0; + status = gcvSTATUS_CHIP_NOT_READY; + + gcmkPRINT("[galcore]: Can't dump state if GPU isn't POWER ON."); + } + } + break; + + case gcvHAL_DUMP_EVENT: + break; + + case gcvHAL_CACHE: + + logical = gcmUINT64_TO_PTR(Interface->u.Cache.logical); + + if (Interface->u.Cache.node) + { + gcmkONERROR(gckVIDMEM_HANDLE_Lookup( + Kernel, + processID, + Interface->u.Cache.node, + &nodeObject)); + + if (nodeObject->node->VidMem.memory->object.type == gcvOBJ_VIDMEM + || nodeObject->node->Virtual.contiguous + ) + { + /* If memory is contiguous, get physical address. */ + gcmkONERROR(gckOS_GetPhysicalAddress( + Kernel->os, logical, (gctUINT32*)&paddr)); + } + } + + bytes = (gctSIZE_T) Interface->u.Cache.bytes; + switch(Interface->u.Cache.operation) + { + case gcvCACHE_FLUSH: + /* Clean and invalidate the cache. */ + status = gckOS_CacheFlush(Kernel->os, + processID, + physical, + paddr, + logical, + bytes); + break; + case gcvCACHE_CLEAN: + /* Clean the cache. */ + status = gckOS_CacheClean(Kernel->os, + processID, + physical, + paddr, + logical, + bytes); + break; + case gcvCACHE_INVALIDATE: + /* Invalidate the cache. */ + status = gckOS_CacheInvalidate(Kernel->os, + processID, + physical, + paddr, + logical, + bytes); + break; + + case gcvCACHE_MEMORY_BARRIER: + status = gckOS_MemoryBarrier(Kernel->os, + logical); + break; + default: + status = gcvSTATUS_INVALID_ARGUMENT; + break; + } + break; + + case gcvHAL_TIMESTAMP: + /* Check for invalid timer. */ + if ((Interface->u.TimeStamp.timer >= gcmCOUNTOF(Kernel->timers)) + || (Interface->u.TimeStamp.request != 2)) + { + Interface->u.TimeStamp.timeDelta = 0; + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Return timer results and reset timer. */ + { + gcsTIMER_PTR timer = &(Kernel->timers[Interface->u.TimeStamp.timer]); + gctUINT64 timeDelta = 0; + + if (timer->stopTime < timer->startTime ) + { + Interface->u.TimeStamp.timeDelta = 0; + gcmkONERROR(gcvSTATUS_TIMER_OVERFLOW); + } + + timeDelta = timer->stopTime - timer->startTime; + + /* Check truncation overflow. */ + Interface->u.TimeStamp.timeDelta = (gctINT32) timeDelta; + /*bit0~bit30 is available*/ + if (timeDelta>>31) + { + Interface->u.TimeStamp.timeDelta = 0; + gcmkONERROR(gcvSTATUS_TIMER_OVERFLOW); + } + + status = gcvSTATUS_OK; + } + break; + + case gcvHAL_DATABASE: + gcmkONERROR(gckKERNEL_QueryDatabase(Kernel, processID, Interface)); + break; + + case gcvHAL_VERSION: + Interface->u.Version.major = gcvVERSION_MAJOR; + Interface->u.Version.minor = gcvVERSION_MINOR; + Interface->u.Version.patch = gcvVERSION_PATCH; + Interface->u.Version.build = gcvVERSION_BUILD; +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "KERNEL version %d.%d.%d build %u %s", + gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, + gcvVERSION_BUILD, utsname()->version); +#endif + break; + + case gcvHAL_CHIP_INFO: + /* Only if not support multi-core */ + Interface->u.ChipInfo.count = 1; + Interface->u.ChipInfo.types[0] = Kernel->hardware->type; + break; + +#if (gcdENABLE_3D || gcdENABLE_2D) + case gcvHAL_ATTACH: + /* Attach user process. */ + gcmkONERROR( + gckCOMMAND_Attach(Kernel->command, + &context, + &bytes, + processID)); + + Interface->u.Attach.stateCount = bytes; + Interface->u.Attach.context = gcmPTR_TO_NAME(context); + + if (Interface->u.Attach.map == gcvTRUE) + { + gcmkVERIFY_OK( + gckCONTEXT_MapBuffer(context, + Interface->u.Attach.physicals, + Interface->u.Attach.logicals, + &Interface->u.Attach.bytes)); + } + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_CONTEXT, + gcmINT2PTR(Interface->u.Attach.context), + gcvNULL, + 0)); + break; +#endif + + case gcvHAL_DETACH: + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_CONTEXT, + gcmINT2PTR(Interface->u.Detach.context))); + + /* Detach user process. */ + gcmkONERROR( + gckCOMMAND_Detach(Kernel->command, + gcmNAME_TO_PTR(Interface->u.Detach.context))); + + gcmRELEASE_NAME(Interface->u.Detach.context); + break; + + case gcvHAL_COMPOSE: + Interface->u.Compose.physical = gcmPTR_TO_UINT64(gcmNAME_TO_PTR(Interface->u.Compose.physical)); + /* Start composition. */ + gcmkONERROR( + gckEVENT_Compose(Kernel->eventObj, + &Interface->u.Compose)); + break; + + case gcvHAL_SET_TIMEOUT: + /* set timeOut value from user */ + gckKERNEL_SetTimeOut(Kernel, Interface->u.SetTimeOut.timeOut); + break; + + case gcvHAL_GET_FRAME_INFO: + gcmkONERROR(gckHARDWARE_GetFrameInfo( + Kernel->hardware, + gcmUINT64_TO_PTR(Interface->u.GetFrameInfo.frameInfo))); + break; + + case gcvHAL_SET_FSCALE_VALUE: +#if gcdENABLE_FSCALE_VAL_ADJUST + status = gckHARDWARE_SetFscaleValue(Kernel->hardware, + Interface->u.SetFscaleValue.value); +#else + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + case gcvHAL_GET_FSCALE_VALUE: +#if gcdENABLE_FSCALE_VAL_ADJUST + status = gckHARDWARE_GetFscaleValue(Kernel->hardware, + &Interface->u.GetFscaleValue.value, + &Interface->u.GetFscaleValue.minValue, + &Interface->u.GetFscaleValue.maxValue); +#else + status = gcvSTATUS_NOT_SUPPORTED; +#endif + break; + + case gcvHAL_NAME_VIDEO_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_Name(Kernel, + Interface->u.NameVideoMemory.handle, + &Interface->u.NameVideoMemory.name)); + break; + + case gcvHAL_IMPORT_VIDEO_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_Import(Kernel, + Interface->u.ImportVideoMemory.name, + &Interface->u.ImportVideoMemory.handle)); + + gcmkONERROR( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_VIDEO_MEMORY, + gcmINT2PTR(Interface->u.ImportVideoMemory.handle), + gcvNULL, + 0)); + break; + + case gcvHAL_GET_VIDEO_MEMORY_FD: + gcmkONERROR(gckVIDMEM_NODE_GetFd( + Kernel, + Interface->u.GetVideoMemoryFd.handle, + &Interface->u.GetVideoMemoryFd.fd + )); + + /* No need to add it to processDB because OS will release all fds when + ** process quits. + */ + break; + + case gcvHAL_QUERY_RESET_TIME_STAMP: + Interface->u.QueryResetTimeStamp.timeStamp = Kernel->resetTimeStamp; + break; + + case gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER: + buffer = (gckVIRTUAL_COMMAND_BUFFER_PTR)gcmNAME_TO_PTR(Interface->u.FreeVirtualCommandBuffer.physical); + + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, + gcvDB_COMMAND_BUFFER, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + + gcmkONERROR(gckOS_DestroyUserVirtualMapping( + Kernel->os, + buffer->physical, + (gctSIZE_T)Interface->u.FreeVirtualCommandBuffer.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + + gcmkONERROR(gckKERNEL_DestroyVirtualCommandBuffer( + Kernel, + (gctSIZE_T)Interface->u.FreeVirtualCommandBuffer.bytes, + (gctPHYS_ADDR)buffer, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + + gcmRELEASE_NAME(Interface->u.FreeVirtualCommandBuffer.physical); + break; + +#if gcdANDROID_NATIVE_FENCE_SYNC + case gcvHAL_SYNC_POINT: + { + gctSYNC_POINT syncPoint; + + switch (Interface->u.SyncPoint.command) + { + case gcvSYNC_POINT_CREATE: + gcmkONERROR(gckOS_CreateSyncPoint(Kernel->os, &syncPoint)); + + Interface->u.SyncPoint.syncPoint = gcmPTR_TO_UINT64(syncPoint); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_SYNC_POINT, + syncPoint, + gcvNULL, + 0)); + break; + + case gcvSYNC_POINT_DESTROY: + syncPoint = gcmUINT64_TO_PTR(Interface->u.SyncPoint.syncPoint); + + gcmkONERROR(gckOS_DestroySyncPoint(Kernel->os, syncPoint)); + + gcmkVERIFY_OK( + gckKERNEL_RemoveProcessDB(Kernel, + processID, gcvDB_SYNC_POINT, + syncPoint)); + break; + + default: + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + break; + } + } + break; + + case gcvHAL_CREATE_NATIVE_FENCE: + { + gctINT fenceFD; + gctSYNC_POINT syncPoint = + gcmUINT64_TO_PTR(Interface->u.CreateNativeFence.syncPoint); + + gcmkONERROR( + gckOS_CreateNativeFence(Kernel->os, + Kernel->timeline, + syncPoint, + &fenceFD)); + + Interface->u.CreateNativeFence.fenceFD = fenceFD; + } + break; +#endif + + case gcvHAL_SHBUF: + { + gctSHBUF shBuf; + gctPOINTER uData; + gctUINT32 bytes; + + switch (Interface->u.ShBuf.command) + { + case gcvSHBUF_CREATE: + bytes = Interface->u.ShBuf.bytes; + + /* Create. */ + gcmkONERROR(gckKERNEL_CreateShBuffer(Kernel, bytes, &shBuf)); + + Interface->u.ShBuf.id = gcmPTR_TO_UINT64(shBuf); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf, + gcvNULL, + 0)); + break; + + case gcvSHBUF_DESTROY: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + + /* Check db first to avoid illegal destroy in the process. */ + gcmkONERROR( + gckKERNEL_RemoveProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf)); + + gcmkONERROR(gckKERNEL_DestroyShBuffer(Kernel, shBuf)); + break; + + case gcvSHBUF_MAP: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + + /* Map for current process access. */ + gcmkONERROR(gckKERNEL_MapShBuffer(Kernel, shBuf)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf, + gcvNULL, + 0)); + break; + + case gcvSHBUF_WRITE: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + uData = gcmUINT64_TO_PTR(Interface->u.ShBuf.data); + bytes = Interface->u.ShBuf.bytes; + + /* Write. */ + gcmkONERROR( + gckKERNEL_WriteShBuffer(Kernel, shBuf, uData, bytes)); + break; + + case gcvSHBUF_READ: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + uData = gcmUINT64_TO_PTR(Interface->u.ShBuf.data); + bytes = Interface->u.ShBuf.bytes; + + /* Read. */ + gcmkONERROR( + gckKERNEL_ReadShBuffer(Kernel, + shBuf, + uData, + bytes, + &bytes)); + + /* Return copied size. */ + Interface->u.ShBuf.bytes = bytes; + break; + + default: + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + break; + } + } + break; + + case gcvHAL_CONFIG_POWER_MANAGEMENT: + gcmkONERROR(gckKERNEL_ConfigPowerManagement(Kernel, Interface)); + break; + + default: + /* Invalid command. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +OnError: + /* Save status. */ + Interface->status = status; + +#if QNX_SINGLE_THREADED_DEBUGGING + gckOS_ReleaseMutex(Kernel->os, Kernel->debugMutex); +#endif + + if (powerMutexAcquired == gcvTRUE) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_AttachProcess +** +** Attach or detach a process. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL Attach +** gcvTRUE if a new process gets attached or gcFALSE when a process +** gets detatched. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_AttachProcess( + IN gckKERNEL Kernel, + IN gctBOOL Attach + ) +{ + gceSTATUS status; + gctUINT32 processID; + + gcmkHEADER_ARG("Kernel=0x%x Attach=%d", Kernel, Attach); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Get current process ID. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + + gcmkONERROR(gckKERNEL_AttachProcessEx(Kernel, Attach, processID)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_AttachProcessEx +** +** Attach or detach a process with the given PID. Can be paired with gckKERNEL_AttachProcess +** provided the programmer is aware of the consequences. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL Attach +** gcvTRUE if a new process gets attached or gcFALSE when a process +** gets detatched. +** +** gctUINT32 PID +** PID of the process to attach or detach. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_AttachProcessEx( + IN gckKERNEL Kernel, + IN gctBOOL Attach, + IN gctUINT32 PID + ) +{ + gceSTATUS status; + gctINT32 old; + + gcmkHEADER_ARG("Kernel=0x%x Attach=%d PID=%d", Kernel, Attach, PID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (Attach) + { + /* Increment the number of clients attached. */ + gcmkONERROR( + gckOS_AtomIncrement(Kernel->os, Kernel->atomClients, &old)); + + if (old == 0) + { +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { + gcmkONERROR(gckOS_Broadcast(Kernel->os, + Kernel->hardware, + gcvBROADCAST_FIRST_PROCESS)); + } + } + + if (Kernel->dbCreated) + { + /* Create the process database. */ + gcmkONERROR(gckKERNEL_CreateProcessDB(Kernel, PID)); + } + +#if gcdPROCESS_ADDRESS_SPACE + /* Map kernel command buffer in the process's own MMU. */ + gcmkONERROR(_MapCommandBuffer(Kernel)); +#endif + } + else + { + if (Kernel->dbCreated) + { + /* Clean up the process database. */ + gcmkONERROR(gckKERNEL_DestroyProcessDB(Kernel, PID)); + + /* Save the last know process ID. */ + Kernel->db->lastProcessID = PID; + } + +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { +#if gcdMULTI_GPU + status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE, gcvCORE_3D_ALL_MASK); +#else + status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE); +#endif + + if (status == gcvSTATUS_INTERRUPTED && Kernel->eventObj->submitTimer) + { + gcmkONERROR(gckOS_StartTimer(Kernel->os, + Kernel->eventObj->submitTimer, + 1)); + } + else + { + gcmkONERROR(status); + } + } + + /* Decrement the number of clients attached. */ + gcmkONERROR( + gckOS_AtomDecrement(Kernel->os, Kernel->atomClients, &old)); + + if (old == 1) + { +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { + /* Last client detached, switch to SUSPEND power state. */ + gcmkONERROR(gckOS_Broadcast(Kernel->os, + Kernel->hardware, + gcvBROADCAST_LAST_PROCESS)); + } + + /* Flush the debug cache. */ + gcmkDEBUGFLUSH(~0U); + } + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdSECURE_USER +gceSTATUS +gckKERNEL_MapLogicalToPhysical( + IN gckKERNEL Kernel, + IN gcskSECURE_CACHE_PTR Cache, + IN OUT gctPOINTER * Data + ) +{ + gceSTATUS status; + static gctBOOL baseAddressValid = gcvFALSE; + static gctUINT32 baseAddress; + gctBOOL needBase; + gcskLOGICAL_CACHE_PTR slot; + + gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x *Data=0x%x", + Kernel, Cache, gcmOPT_POINTER(Data)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (!baseAddressValid) + { + /* Get base address. */ + gcmkONERROR(gckHARDWARE_GetBaseAddress(Kernel->hardware, &baseAddress)); + + baseAddressValid = gcvTRUE; + } + + /* Does this state load need a base address? */ + gcmkONERROR(gckHARDWARE_NeedBaseAddress(Kernel->hardware, + ((gctUINT32_PTR) Data)[-1], + &needBase)); + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU + { + gcskLOGICAL_CACHE_PTR next; + gctINT i; + + /* Walk all used cache slots. */ + for (i = 1, slot = Cache->cache[0].next, next = gcvNULL; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = slot->next + ) + { + if (slot->logical == *Data) + { + /* Bail out. */ + next = slot; + break; + } + } + + /* See if we had a miss. */ + if (next == gcvNULL) + { + /* Use the tail of the cache. */ + slot = Cache->cache[0].prev; + + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + } + + /* Move slot to head of list. */ + if (slot != Cache->cache[0].next) + { + /* Unlink. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Move to head of chain. */ + slot->prev = &Cache->cache[0]; + slot->next = Cache->cache[0].next; + slot->prev->next = slot; + slot->next->prev = slot; + } + } +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR + { + gctINT i; + gcskLOGICAL_CACHE_PTR next = gcvNULL; + gcskLOGICAL_CACHE_PTR oldestSlot = gcvNULL; + slot = gcvNULL; + + if (Cache->cacheIndex != gcvNULL) + { + /* Walk the cache forwards. */ + for (i = 1, slot = Cache->cacheIndex; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = slot->next) + { + if (slot->logical == *Data) + { + /* Bail out. */ + next = slot; + break; + } + + /* Determine age of this slot. */ + if ((oldestSlot == gcvNULL) + || (oldestSlot->stamp > slot->stamp) + ) + { + oldestSlot = slot; + } + } + + if (next == gcvNULL) + { + /* Walk the cache backwards. */ + for (slot = Cache->cacheIndex->prev; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = slot->prev) + { + if (slot->logical == *Data) + { + /* Bail out. */ + next = slot; + break; + } + + /* Determine age of this slot. */ + if ((oldestSlot == gcvNULL) + || (oldestSlot->stamp > slot->stamp) + ) + { + oldestSlot = slot; + } + } + } + } + + /* See if we had a miss. */ + if (next == gcvNULL) + { + if (Cache->cacheFree != 0) + { + slot = &Cache->cache[Cache->cacheFree]; + gcmkASSERT(slot->logical == gcvNULL); + + ++ Cache->cacheFree; + if (Cache->cacheFree >= gcmCOUNTOF(Cache->cache)) + { + Cache->cacheFree = 0; + } + } + else + { + /* Use the oldest cache slot. */ + gcmkASSERT(oldestSlot != gcvNULL); + slot = oldestSlot; + + /* Unlink from the chain. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Append to the end. */ + slot->prev = Cache->cache[0].prev; + slot->next = &Cache->cache[0]; + slot->prev->next = slot; + slot->next->prev = slot; + } + + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + } + + /* Save time stamp. */ + slot->stamp = ++ Cache->cacheStamp; + + /* Save current slot for next lookup. */ + Cache->cacheIndex = slot; + } +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + { + gctINT i; + gctUINT32 data = gcmPTR2INT32(*Data); + gctUINT32 key, index; + gcskLOGICAL_CACHE_PTR hash; + + /* Generate a hash key. */ + key = (data >> 24) + (data >> 16) + (data >> 8) + data; + index = key % gcmCOUNTOF(Cache->hash); + + /* Get the hash entry. */ + hash = &Cache->hash[index]; + + for (slot = hash->nextHash, i = 0; + (slot != gcvNULL) && (i < gcdSECURE_CACHE_SLOTS); + slot = slot->nextHash, ++i + ) + { + if (slot->logical == (*Data)) + { + break; + } + } + + if (slot == gcvNULL) + { + /* Grab from the tail of the cache. */ + slot = Cache->cache[0].prev; + + /* Unlink slot from any hash table it is part of. */ + if (slot->prevHash != gcvNULL) + { + slot->prevHash->nextHash = slot->nextHash; + } + if (slot->nextHash != gcvNULL) + { + slot->nextHash->prevHash = slot->prevHash; + } + + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + + if (hash->nextHash != gcvNULL) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "Hash Collision: logical=0x%x key=0x%08x", + *Data, key); + } + + /* Insert the slot at the head of the hash list. */ + slot->nextHash = hash->nextHash; + if (slot->nextHash != gcvNULL) + { + slot->nextHash->prevHash = slot; + } + slot->prevHash = hash; + hash->nextHash = slot; + } + + /* Move slot to head of list. */ + if (slot != Cache->cache[0].next) + { + /* Unlink. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Move to head of chain. */ + slot->prev = &Cache->cache[0]; + slot->next = Cache->cache[0].next; + slot->prev->next = slot; + slot->next->prev = slot; + } + } +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE + { + gctUINT32 index = (gcmPTR2INT32(*Data) % gcdSECURE_CACHE_SLOTS) + 1; + + /* Get cache slot. */ + slot = &Cache->cache[index]; + + /* Check for cache miss. */ + if (slot->logical != *Data) + { + /* Initialize the cache line. */ + slot->logical = *Data; + + /* Map the logical address to a DMA address. */ + gcmkONERROR( + gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); + } + } +#endif + + /* Return DMA address. */ + *Data = gcmINT2PTR(slot->dma + (needBase ? baseAddress : 0)); + + /* Success. */ + gcmkFOOTER_ARG("*Data=0x%08x", *Data); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_FlushTranslationCache( + IN gckKERNEL Kernel, + IN gcskSECURE_CACHE_PTR Cache, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gctINT i; + gcskLOGICAL_CACHE_PTR slot; + gctUINT8_PTR ptr; + + gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x Logical=0x%x Bytes=%lu", + Kernel, Cache, Logical, Bytes); + + /* Do we need to flush the entire cache? */ + if (Logical == gcvNULL) + { + /* Clear all cache slots. */ + for (i = 1; i <= gcdSECURE_CACHE_SLOTS; ++i) + { + Cache->cache[i].logical = gcvNULL; + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + Cache->cache[i].nextHash = gcvNULL; + Cache->cache[i].prevHash = gcvNULL; +#endif +} + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + /* Zero the hash table. */ + for (i = 0; i < gcmCOUNTOF(Cache->hash); ++i) + { + Cache->hash[i].nextHash = gcvNULL; + } +#endif + + /* Reset the cache functionality. */ + Cache->cacheIndex = gcvNULL; + Cache->cacheFree = 1; + Cache->cacheStamp = 0; + } + + else + { + gctUINT8_PTR low = (gctUINT8_PTR) Logical; + gctUINT8_PTR high = low + Bytes; + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU + gcskLOGICAL_CACHE_PTR next; + + /* Walk all used cache slots. */ + for (i = 1, slot = Cache->cache[0].next; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = next + ) + { + /* Save pointer to next slot. */ + next = slot->next; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Unlink slot. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Append slot to tail of cache. */ + slot->prev = Cache->cache[0].prev; + slot->next = &Cache->cache[0]; + slot->prev->next = slot; + slot->next->prev = slot; + + /* Mark slot as empty. */ + slot->logical = gcvNULL; + } + } + +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR + gcskLOGICAL_CACHE_PTR next; + + for (i = 1, slot = Cache->cache[0].next; + (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); + ++i, slot = next) + { + /* Save pointer to next slot. */ + next = slot->next; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Test if this slot is the current slot. */ + if (slot == Cache->cacheIndex) + { + /* Move to next or previous slot. */ + Cache->cacheIndex = (slot->next->logical != gcvNULL) + ? slot->next + : (slot->prev->logical != gcvNULL) + ? slot->prev + : gcvNULL; + } + + /* Unlink slot from cache. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Insert slot to head of cache. */ + slot->prev = &Cache->cache[0]; + slot->next = Cache->cache[0].next; + slot->prev->next = slot; + slot->next->prev = slot; + + /* Mark slot as empty. */ + slot->logical = gcvNULL; + slot->stamp = 0; + } + } + +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + gctINT j; + gcskLOGICAL_CACHE_PTR hash, next; + + /* Walk all hash tables. */ + for (i = 0, hash = Cache->hash; + i < gcmCOUNTOF(Cache->hash); + ++i, ++hash) + { + /* Walk all slots in the hash. */ + for (j = 0, slot = hash->nextHash; + (j < gcdSECURE_CACHE_SLOTS) && (slot != gcvNULL); + ++j, slot = next) + { + /* Save pointer to next slot. */ + next = slot->next; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Unlink slot from hash table. */ + if (slot->prevHash == hash) + { + hash->nextHash = slot->nextHash; + } + else + { + slot->prevHash->nextHash = slot->nextHash; + } + + if (slot->nextHash != gcvNULL) + { + slot->nextHash->prevHash = slot->prevHash; + } + + /* Unlink slot from cache. */ + slot->prev->next = slot->next; + slot->next->prev = slot->prev; + + /* Append slot to tail of cache. */ + slot->prev = Cache->cache[0].prev; + slot->next = &Cache->cache[0]; + slot->prev->next = slot; + slot->next->prev = slot; + + /* Mark slot as empty. */ + slot->logical = gcvNULL; + slot->prevHash = gcvNULL; + slot->nextHash = gcvNULL; + } + } + } + +#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE + gctUINT32 index; + + /* Loop while inside the range. */ + for (i = 1; (low < high) && (i <= gcdSECURE_CACHE_SLOTS); ++i) + { + /* Get index into cache for this range. */ + index = (gcmPTR2INT32(low) % gcdSECURE_CACHE_SLOTS) + 1; + slot = &Cache->cache[index]; + + /* Test if this slot falls within the range to flush. */ + ptr = (gctUINT8_PTR) slot->logical; + if ((ptr >= low) && (ptr < high)) + { + /* Remove entry from cache. */ + slot->logical = gcvNULL; + } + + /* Next block. */ + low += gcdSECURE_CACHE_SLOTS; + } +#endif + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif + +/******************************************************************************* +** +** gckKERNEL_Recovery +** +** Try to recover the GPU from a fatal error. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_Recovery( + IN gckKERNEL Kernel + ) +{ + gceSTATUS status; + gckEVENT eventObj; + gckHARDWARE hardware; +#if gcdSECURE_USER + gctUINT32 processID; + gcskSECURE_CACHE_PTR cache; +#endif + gctUINT32 mask = 0; + gckCOMMAND command; + gckENTRYDATA data; + gctUINT32 i = 0, count = 0; +#if gcdINTERRUPT_STATISTIC + gctINT32 oldValue; +#endif + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Validate the arguemnts. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Grab gckEVENT object. */ + eventObj = Kernel->eventObj; + gcmkVERIFY_OBJECT(eventObj, gcvOBJ_EVENT); + + /* Grab gckHARDWARE object. */ + hardware = Kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + /* Grab gckCOMMAND object. */ + command = Kernel->command; + gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND); + +#if gcdSECURE_USER + /* Flush the secure mapping cache. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache)); + gcmkONERROR(gckKERNEL_FlushTranslationCache(Kernel, cache, gcvNULL, 0)); +#endif + + if (Kernel->stuckDump == gcdSTUCK_DUMP_MINIMAL) + { + gcmkPRINT("[galcore]: GPU[%d] hang, automatic recovery.", Kernel->core); + } + else + { + _DumpDriverConfigure(Kernel); + _DumpState(Kernel); + } + + if (Kernel->recovery == gcvFALSE) + { + gcmkPRINT("[galcore]: Stop driver to keep scene."); + + for (;;) + { + gckOS_Delay(Kernel->os, 10000); + } + } + + /* Clear queue. */ + do + { + status = gckENTRYQUEUE_Dequeue(&command->queue, &data); + } + while (status == gcvSTATUS_OK); + + /* Issuing a soft reset for the GPU. */ + gcmkONERROR(gckHARDWARE_Reset(hardware)); + + mask = Kernel->restoreMask; + + for (i = 0; i < 32; i++) + { + if (mask & (1 << i)) + { + count++; + } + } + + /* Handle all outstanding events now. */ +#if gcdSMP +#if gcdMULTI_GPU + if (Kernel->core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending3D[i], mask)); + } + } + else + { + gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, mask)); + } +#else + gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, mask)); +#endif +#else +#if gcdMULTI_GPU + if (Kernel->core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + eventObj->pending3D[i] = mask; + } + } + else + { + eventObj->pending = mask; + } +#else + eventObj->pending = mask; +#endif +#endif + +#if gcdINTERRUPT_STATISTIC + while (count--) + { + gcmkONERROR(gckOS_AtomDecrement( + Kernel->os, + eventObj->interruptCount, + &oldValue + )); + } + + gckOS_AtomClearMask(Kernel->hardware->pendingEvent, mask); +#endif + + gcmkONERROR(gckEVENT_Notify(eventObj, 1)); + + gcmkVERIFY_OK(gckOS_GetTime(&Kernel->resetTimeStamp)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_OpenUserData +** +** Get access to the user data. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL NeedCopy +** The flag indicating whether or not the data should be copied. +** +** gctPOINTER StaticStorage +** Pointer to the kernel storage where the data is to be copied if +** NeedCopy is gcvTRUE. +** +** gctPOINTER UserPointer +** User pointer to the data. +** +** gctSIZE_T Size +** Size of the data. +** +** OUTPUT: +** +** gctPOINTER * KernelPointer +** Pointer to the kernel pointer that will be pointing to the data. +*/ +gceSTATUS +gckKERNEL_OpenUserData( + IN gckKERNEL Kernel, + IN gctBOOL NeedCopy, + IN gctPOINTER StaticStorage, + IN gctPOINTER UserPointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG( + "Kernel=0x%08X NeedCopy=%d StaticStorage=0x%08X " + "UserPointer=0x%08X Size=%lu KernelPointer=0x%08X", + Kernel, NeedCopy, StaticStorage, UserPointer, Size, KernelPointer + ); + + /* Validate the arguemnts. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(!NeedCopy || (StaticStorage != gcvNULL)); + gcmkVERIFY_ARGUMENT(UserPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + + if (NeedCopy) + { + /* Copy the user data to the static storage. */ + gcmkONERROR(gckOS_CopyFromUserData( + Kernel->os, StaticStorage, UserPointer, Size + )); + + /* Set the kernel pointer. */ + * KernelPointer = StaticStorage; + } + else + { + gctPOINTER pointer = gcvNULL; + + /* Map the user pointer. */ + gcmkONERROR(gckOS_MapUserPointer( + Kernel->os, UserPointer, Size, &pointer + )); + + /* Set the kernel pointer. */ + * KernelPointer = pointer; + } + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_CloseUserData +** +** Release resources associated with the user data connection opened by +** gckKERNEL_OpenUserData. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL NeedCopy +** The flag indicating whether or not the data should be copied. +** +** gctBOOL FlushData +** If gcvTRUE, the data is written back to the user. +** +** gctPOINTER UserPointer +** User pointer to the data. +** +** gctSIZE_T Size +** Size of the data. +** +** OUTPUT: +** +** gctPOINTER * KernelPointer +** Kernel pointer to the data. +*/ +gceSTATUS +gckKERNEL_CloseUserData( + IN gckKERNEL Kernel, + IN gctBOOL NeedCopy, + IN gctBOOL FlushData, + IN gctPOINTER UserPointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctPOINTER pointer; + + gcmkHEADER_ARG( + "Kernel=0x%08X NeedCopy=%d FlushData=%d " + "UserPointer=0x%08X Size=%lu KernelPointer=0x%08X", + Kernel, NeedCopy, FlushData, UserPointer, Size, KernelPointer + ); + + /* Validate the arguemnts. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(UserPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + + /* Get a shortcut to the kernel pointer. */ + pointer = * KernelPointer; + + if (pointer != gcvNULL) + { + if (NeedCopy) + { + if (FlushData) + { + gcmkONERROR(gckOS_CopyToUserData( + Kernel->os, * KernelPointer, UserPointer, Size + )); + } + } + else + { + /* Unmap record from kernel memory. */ + gcmkONERROR(gckOS_UnmapUserPointer( + Kernel->os, + UserPointer, + Size, + * KernelPointer + )); + } + + /* Reset the kernel pointer. */ + * KernelPointer = gcvNULL; + } + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +void +gckKERNEL_SetTimeOut( + IN gckKERNEL Kernel, + IN gctUINT32 timeOut + ) +{ + gcmkHEADER_ARG("Kernel=0x%x timeOut=%d", Kernel, timeOut); +#if gcdGPU_TIMEOUT + Kernel->timeOut = timeOut; +#endif + gcmkFOOTER_NO(); +} + +gceSTATUS +gckKERNEL_AllocateVirtualCommandBuffer( + IN gckKERNEL Kernel, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ) +{ + gckOS os = Kernel->os; + gceSTATUS status; + gctPOINTER logical = gcvNULL; + gctSIZE_T pageCount; + gctSIZE_T bytes = *Bytes; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer = gcvNULL; + gckMMU mmu; + gctUINT32 flag = gcvALLOC_FLAG_NON_CONTIGUOUS; + + gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu", + os, InUserSpace, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes != gcvNULL); + gcmkVERIFY_ARGUMENT(*Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + gcmkONERROR(gckOS_Allocate(os, + sizeof(gckVIRTUAL_COMMAND_BUFFER), + (gctPOINTER)&buffer)); + + gcmkONERROR(gckOS_ZeroMemory(buffer, sizeof(gckVIRTUAL_COMMAND_BUFFER))); + + buffer->bytes = bytes; + + gcmkONERROR(gckOS_AllocatePagedMemoryEx(os, + flag, + bytes, + gcvNULL, + &buffer->physical)); + + if (InUserSpace) + { + gcmkONERROR(gckOS_CreateUserVirtualMapping(os, + buffer->physical, + bytes, + &logical, + &pageCount)); + + *Logical = + buffer->userLogical = logical; + } + else + { + gcmkONERROR(gckOS_CreateKernelVirtualMapping(os, + buffer->physical, + bytes, + &logical, + &pageCount)); + + *Logical = + buffer->kernelLogical = logical; + } + + buffer->pageCount = pageCount; + buffer->kernel = Kernel; + + gcmkONERROR(gckOS_GetProcessID(&buffer->pid)); + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu)); + buffer->mmu = mmu; +#else + mmu = Kernel->mmu; +#endif + + gcmkONERROR(gckMMU_AllocatePages(mmu, + pageCount, + &buffer->pageTable, + &buffer->gpuAddress)); + + + gcmkONERROR(gckOS_MapPagesEx(os, + Kernel->core, + buffer->physical, + pageCount, + buffer->gpuAddress, + buffer->pageTable)); + + gcmkONERROR(gckMMU_Flush(mmu, gcvSURF_INDEX)); + + *Physical = buffer; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, + "gpuAddress = %x pageCount = %d kernelLogical = %x userLogical=%x", + buffer->gpuAddress, buffer->pageCount, + buffer->kernelLogical, buffer->userLogical); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, Kernel->virtualBufferLock, gcvINFINITE)); + + if (Kernel->virtualBufferHead == gcvNULL) + { + Kernel->virtualBufferHead = + Kernel->virtualBufferTail = buffer; + } + else + { + buffer->prev = Kernel->virtualBufferTail; + Kernel->virtualBufferTail->next = buffer; + Kernel->virtualBufferTail = buffer; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Kernel->virtualBufferLock)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (buffer->gpuAddress) + { +#if gcdPROCESS_ADDRESS_SPACE + gcmkVERIFY_OK( + gckMMU_FreePages(mmu, buffer->pageTable, buffer->pageCount)); +#else + gcmkVERIFY_OK( + gckMMU_FreePages(Kernel->mmu, buffer->pageTable, buffer->pageCount)); +#endif + } + + if (buffer->userLogical) + { + gcmkVERIFY_OK( + gckOS_DestroyUserVirtualMapping(os, + buffer->physical, + bytes, + buffer->userLogical)); + } + + if (buffer->kernelLogical) + { + gcmkVERIFY_OK( + gckOS_DestroyKernelVirtualMapping(os, + buffer->physical, + bytes, + buffer->kernelLogical)); + } + + if (buffer->physical) + { + gcmkVERIFY_OK(gckOS_FreePagedMemory(os, buffer->physical, bytes)); + } + + gcmkVERIFY_OK(gckOS_Free(os, buffer)); + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_DestroyVirtualCommandBuffer( + IN gckKERNEL Kernel, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical + ) +{ + gckOS os; + gckKERNEL kernel; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer = (gckVIRTUAL_COMMAND_BUFFER_PTR)Physical; + + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(buffer != gcvNULL); + + kernel = buffer->kernel; + os = kernel->os; + + if (!buffer->userLogical) + { + gcmkVERIFY_OK(gckOS_DestroyKernelVirtualMapping(os, + buffer->physical, + Bytes, + Logical)); + } + +#if !gcdPROCESS_ADDRESS_SPACE + gcmkVERIFY_OK( + gckMMU_FreePages(kernel->mmu, buffer->pageTable, buffer->pageCount)); +#endif + + gcmkVERIFY_OK(gckOS_UnmapPages(os, buffer->pageCount, buffer->gpuAddress)); + + gcmkVERIFY_OK(gckOS_FreePagedMemory(os, buffer->physical, Bytes)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, kernel->virtualBufferLock, gcvINFINITE)); + + if (buffer == kernel->virtualBufferHead) + { + if ((kernel->virtualBufferHead = buffer->next) == gcvNULL) + { + kernel->virtualBufferTail = gcvNULL; + } + } + else + { + buffer->prev->next = buffer->next; + + if (buffer == kernel->virtualBufferTail) + { + kernel->virtualBufferTail = buffer->prev; + } + else + { + buffer->next->prev = buffer->prev; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, kernel->virtualBufferLock)); + + gcmkVERIFY_OK(gckOS_Free(os, buffer)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_GetGPUAddress( + IN gckKERNEL Kernel, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + OUT gctUINT32 * Address + ) +{ + gceSTATUS status; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + gctPOINTER start; + gctUINT32 pid; + + gcmkHEADER_ARG("Logical = %x InUserSpace=%d.", Logical, InUserSpace); + + gcmkVERIFY_OK(gckOS_GetProcessID(&pid)); + + status = gcvSTATUS_INVALID_ADDRESS; + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, Kernel->virtualBufferLock, gcvINFINITE)); + + /* Walk all command buffer. */ + for (buffer = Kernel->virtualBufferHead; buffer != gcvNULL; buffer = buffer->next) + { + if (InUserSpace) + { + start = buffer->userLogical; + } + else + { + start = buffer->kernelLogical; + } + + if (start == gcvNULL) + { + continue; + } + + if (Logical >= start + && (Logical < (gctPOINTER)((gctUINT8_PTR)start + buffer->pageCount * 4096)) + && pid == buffer->pid + ) + { + * Address = buffer->gpuAddress + (gctUINT32)((gctUINT8_PTR)Logical - (gctUINT8_PTR)start); + status = gcvSTATUS_OK; + break; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->virtualBufferLock)); + + gcmkFOOTER_NO(); + return status; +} + +gceSTATUS +gckKERNEL_QueryGPUAddress( + IN gckKERNEL Kernel, + IN gctUINT32 GpuAddress, + OUT gckVIRTUAL_COMMAND_BUFFER_PTR * Buffer + ) +{ + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + gctUINT32 start; + gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, Kernel->virtualBufferLock, gcvINFINITE)); + + /* Walk all command buffers. */ + for (buffer = Kernel->virtualBufferHead; buffer != gcvNULL; buffer = buffer->next) + { + start = (gctUINT32)buffer->gpuAddress; + + if (GpuAddress >= start && GpuAddress < (start + buffer->pageCount * 4096)) + { + /* Find a range matched. */ + *Buffer = buffer; + status = gcvSTATUS_OK; + break; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->virtualBufferLock)); + + return status; +} + +#if gcdLINK_QUEUE_SIZE +static void +gckLINKQUEUE_Dequeue( + IN gckLINKQUEUE LinkQueue + ) +{ + gcmkASSERT(LinkQueue->count == gcdLINK_QUEUE_SIZE); + + LinkQueue->count--; + LinkQueue->front = (LinkQueue->front + 1) % gcdLINK_QUEUE_SIZE; +} + +void +gckLINKQUEUE_Enqueue( + IN gckLINKQUEUE LinkQueue, + IN gctUINT32 start, + IN gctUINT32 end + ) +{ + if (LinkQueue->count == gcdLINK_QUEUE_SIZE) + { + gckLINKQUEUE_Dequeue(LinkQueue); + } + + gcmkASSERT(LinkQueue->count < gcdLINK_QUEUE_SIZE); + + LinkQueue->count++; + + LinkQueue->data[LinkQueue->rear].start = start; + LinkQueue->data[LinkQueue->rear].end = end; + + gcmkVERIFY_OK( + gckOS_GetProcessID(&LinkQueue->data[LinkQueue->rear].pid)); + + LinkQueue->rear = (LinkQueue->rear + 1) % gcdLINK_QUEUE_SIZE; +} + +void +gckLINKQUEUE_GetData( + IN gckLINKQUEUE LinkQueue, + IN gctUINT32 Index, + OUT gckLINKDATA * Data + ) +{ + gcmkASSERT(Index >= 0 && Index < gcdLINK_QUEUE_SIZE); + + *Data = &LinkQueue->data[(Index + LinkQueue->front) % gcdLINK_QUEUE_SIZE]; +} +#endif + +/* +* gckENTRYQUEUE_Enqueue is called with Command->mutexQueue acquired. +*/ +gceSTATUS +gckENTRYQUEUE_Enqueue( + IN gckKERNEL Kernel, + IN gckENTRYQUEUE Queue, + IN gctUINT32 physical, + IN gctUINT32 bytes + ) +{ + gctUINT32 next = (Queue->rear + 1) % gcdENTRY_QUEUE_SIZE; + + if (next == Queue->front) + { + /* Queue is full. */ + return gcvSTATUS_INVALID_REQUEST; + } + + /* Copy data. */ + Queue->data[Queue->rear].physical = physical; + Queue->data[Queue->rear].bytes = bytes; + + gcmkVERIFY_OK(gckOS_MemoryBarrier(Kernel->os, &Queue->rear)); + + /* Update rear. */ + Queue->rear = next; + + return gcvSTATUS_OK; +} + +gceSTATUS +gckENTRYQUEUE_Dequeue( + IN gckENTRYQUEUE Queue, + OUT gckENTRYDATA * Data + ) +{ + if (Queue->front == Queue->rear) + { + /* Queue is empty. */ + return gcvSTATUS_INVALID_REQUEST; + } + + /* Copy data. */ + *Data = &Queue->data[Queue->front]; + + /* Update front. */ + Queue->front = (Queue->front + 1) % gcdENTRY_QUEUE_SIZE; + + return gcvSTATUS_OK; +} + +/******************************************************************************\ +*************************** Pointer - ID translation *************************** +\******************************************************************************/ +#define gcdID_TABLE_LENGTH 1024 +typedef struct _gcsINTEGERDB * gckINTEGERDB; +typedef struct _gcsINTEGERDB +{ + gckOS os; + gctPOINTER* table; + gctPOINTER mutex; + gctUINT32 tableLen; + gctUINT32 currentID; + gctUINT32 unused; +} +gcsINTEGERDB; + +gceSTATUS +gckKERNEL_CreateIntegerDatabase( + IN gckKERNEL Kernel, + OUT gctPOINTER * Database + ) +{ + gceSTATUS status; + gckINTEGERDB database = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%08X Datbase=0x%08X", Kernel, Database); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Database != gcvNULL); + + /* Allocate a database. */ + gcmkONERROR(gckOS_Allocate( + Kernel->os, gcmSIZEOF(gcsINTEGERDB), (gctPOINTER *)&database)); + + gcmkONERROR(gckOS_ZeroMemory(database, gcmSIZEOF(gcsINTEGERDB))); + + /* Allocate a pointer table. */ + gcmkONERROR(gckOS_Allocate( + Kernel->os, gcmSIZEOF(gctPOINTER) * gcdID_TABLE_LENGTH, (gctPOINTER *)&database->table)); + + gcmkONERROR(gckOS_ZeroMemory(database->table, gcmSIZEOF(gctPOINTER) * gcdID_TABLE_LENGTH)); + + /* Allocate a database mutex. */ + gcmkONERROR(gckOS_CreateMutex(Kernel->os, &database->mutex)); + + /* Initialize. */ + database->currentID = 0; + database->unused = gcdID_TABLE_LENGTH; + database->os = Kernel->os; + database->tableLen = gcdID_TABLE_LENGTH; + + *Database = database; + + gcmkFOOTER_ARG("*Database=0x%08X", *Database); + return gcvSTATUS_OK; + +OnError: + /* Rollback. */ + if (database) + { + if (database->table) + { + gcmkOS_SAFE_FREE(Kernel->os, database->table); + } + + gcmkOS_SAFE_FREE(Kernel->os, database); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_DestroyIntegerDatabase( + IN gckKERNEL Kernel, + IN gctPOINTER Database + ) +{ + gckINTEGERDB database = Database; + + gcmkHEADER_ARG("Kernel=0x%08X Datbase=0x%08X", Kernel, Database); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Database != gcvNULL); + + /* Destroy pointer table. */ + gcmkOS_SAFE_FREE(Kernel->os, database->table); + + /* Destroy database mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, database->mutex)); + + /* Destroy database. */ + gcmkOS_SAFE_FREE(Kernel->os, database); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_AllocateIntegerId( + IN gctPOINTER Database, + IN gctPOINTER Pointer, + OUT gctUINT32 * Id + ) +{ + gceSTATUS status; + gckINTEGERDB database = Database; + gctUINT32 i, unused, currentID, tableLen; + gctPOINTER * table; + gckOS os = database->os; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Database=0x%08X Pointer=0x%08X", Database, Pointer); + + gcmkVERIFY_ARGUMENT(Id != gcvNULL); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (database->unused < 1) + { + /* Extend table. */ + gcmkONERROR( + gckOS_Allocate(os, + gcmSIZEOF(gctPOINTER) * (database->tableLen + gcdID_TABLE_LENGTH), + (gctPOINTER *)&table)); + + gcmkONERROR(gckOS_ZeroMemory(table + database->tableLen, + gcmSIZEOF(gctPOINTER) * gcdID_TABLE_LENGTH)); + + /* Copy data from old table. */ + gckOS_MemCopy(table, + database->table, + database->tableLen * gcmSIZEOF(gctPOINTER)); + + gcmkOS_SAFE_FREE(os, database->table); + + /* Update databse with new allocated table. */ + database->table = table; + database->currentID = database->tableLen; + database->tableLen += gcdID_TABLE_LENGTH; + database->unused += gcdID_TABLE_LENGTH; + } + + table = database->table; + currentID = database->currentID; + tableLen = database->tableLen; + unused = database->unused; + + /* Connect id with pointer. */ + table[currentID] = Pointer; + + *Id = currentID + 1; + + /* Update the currentID. */ + if (--unused > 0) + { + for (i = 0; i < tableLen; i++) + { + if (++currentID >= tableLen) + { + /* Wrap to the begin. */ + currentID = 0; + } + + if (table[currentID] == gcvNULL) + { + break; + } + } + } + + database->table = table; + database->currentID = currentID; + database->tableLen = tableLen; + database->unused = unused; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + acquired = gcvFALSE; + + gcmkFOOTER_ARG("*Id=%d", *Id); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_FreeIntegerId( + IN gctPOINTER Database, + IN gctUINT32 Id + ) +{ + gceSTATUS status; + gckINTEGERDB database = Database; + gckOS os = database->os; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Database=0x%08X Id=%d", Database, Id); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (!(Id > 0 && Id <= database->tableLen)) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + Id -= 1; + + database->table[Id] = gcvNULL; + + if (database->unused++ == 0) + { + database->currentID = Id; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_QueryIntegerId( + IN gctPOINTER Database, + IN gctUINT32 Id, + OUT gctPOINTER * Pointer + ) +{ + gceSTATUS status; + gckINTEGERDB database = Database; + gctPOINTER pointer; + gckOS os = database->os; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Database=0x%08X Id=%d", Database, Id); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + gcmkVERIFY_OK(gckOS_AcquireMutex(os, database->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (!(Id > 0 && Id <= database->tableLen)) + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + Id -= 1; + + pointer = database->table[Id]; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + acquired = gcvFALSE; + + if (pointer) + { + *Pointer = pointer; + } + else + { + gcmkONERROR(gcvSTATUS_NOT_FOUND); + } + + gcmkFOOTER_ARG("*Pointer=0x%08X", *Pointer); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, database->mutex)); + } + + gcmkFOOTER(); + return status; +} + + +gctUINT32 +gckKERNEL_AllocateNameFromPointer( + IN gckKERNEL Kernel, + IN gctPOINTER Pointer + ) +{ + gceSTATUS status; + gctUINT32 name; + gctPOINTER database = Kernel->db->pointerDatabase; + + gcmkHEADER_ARG("Kernel=0x%X Pointer=0x%X", Kernel, Pointer); + + gcmkONERROR( + gckKERNEL_AllocateIntegerId(database, Pointer, &name)); + + gcmkFOOTER_ARG("name=%d", name); + return name; + +OnError: + gcmkFOOTER(); + return 0; +} + +gctPOINTER +gckKERNEL_QueryPointerFromName( + IN gckKERNEL Kernel, + IN gctUINT32 Name + ) +{ + gceSTATUS status; + gctPOINTER pointer = gcvNULL; + gctPOINTER database = Kernel->db->pointerDatabase; + + gcmkHEADER_ARG("Kernel=0x%X Name=%d", Kernel, Name); + + /* Lookup in database to get pointer. */ + gcmkONERROR(gckKERNEL_QueryIntegerId(database, Name, &pointer)); + + gcmkFOOTER_ARG("pointer=0x%X", pointer); + return pointer; + +OnError: + gcmkFOOTER(); + return gcvNULL; +} + +gceSTATUS +gckKERNEL_DeleteName( + IN gckKERNEL Kernel, + IN gctUINT32 Name + ) +{ + gctPOINTER database = Kernel->db->pointerDatabase; + + gcmkHEADER_ARG("Kernel=0x%X Name=0x%X", Kernel, Name); + + /* Free name if exists. */ + gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(database, Name)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckKERNEL_SetRecovery( + IN gckKERNEL Kernel, + IN gctBOOL Recovery, + IN gctUINT32 StuckDump + ) +{ + Kernel->recovery = Recovery; + + if (Recovery == gcvFALSE) + { + /* Dump stuck information if Recovery is disabled. */ + Kernel->stuckDump = gcmMAX(StuckDump, gcdSTUCK_DUMP_MIDDLE); + } + + return gcvSTATUS_OK; +} + + +/******************************************************************************* +***** Shared Buffer ************************************************************ +*******************************************************************************/ + +/******************************************************************************* +** +** gckKERNEL_CreateShBuffer +** +** Create shared buffer. +** The shared buffer can be used across processes. Other process needs call +** gckKERNEL_MapShBuffer before use it. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 Size +** Specify the shared buffer size. +** +** OUTPUT: +** +** gctSHBUF * ShBuf +** Pointer to hold return shared buffer handle. +*/ +gceSTATUS +gckKERNEL_CreateShBuffer( + IN gckKERNEL Kernel, + IN gctUINT32 Size, + OUT gctSHBUF * ShBuf + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%X, Size=%u", Kernel, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (Size == 0) + { + /* Invalid size. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + else if (Size > 1024) + { + /* Limite shared buffer size. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Create a shared buffer structure. */ + gcmkONERROR( + gckOS_Allocate(Kernel->os, + sizeof (gcsSHBUF), + (gctPOINTER *)&shBuf)); + + /* Initialize shared buffer. */ + shBuf->id = 0; + shBuf->reference = gcvNULL; + shBuf->size = Size; + shBuf->data = gcvNULL; + + /* Allocate integer id for this shared buffer. */ + gcmkONERROR( + gckKERNEL_AllocateIntegerId(Kernel->db->pointerDatabase, + shBuf, + &shBuf->id)); + + /* Allocate atom. */ + gcmkONERROR(gckOS_AtomConstruct(Kernel->os, &shBuf->reference)); + + /* Set default reference count to 1. */ + gcmkVERIFY_OK(gckOS_AtomSet(Kernel->os, shBuf->reference, 1)); + + /* Return integer id. */ + *ShBuf = (gctSHBUF)(gctUINTPTR_T)shBuf->id; + + gcmkFOOTER_ARG("*ShBuf=%u", shBuf->id); + return gcvSTATUS_OK; + +OnError: + /* Error roll back. */ + if (shBuf != gcvNULL) + { + if (shBuf->id != 0) + { + gcmkVERIFY_OK( + gckKERNEL_FreeIntegerId(Kernel->db->pointerDatabase, + shBuf->id)); + } + + gcmkOS_SAFE_FREE(Kernel->os, shBuf); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_DestroyShBuffer +** +** Destroy shared buffer. +** This will decrease reference of specified shared buffer and do actual +** destroy when no reference on it. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_DestroyShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctINT32 oldValue = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + /* Decrease the reference count. */ + gckOS_AtomDecrement(Kernel->os, shBuf->reference, &oldValue); + + if (oldValue == 1) + { + /* Free integer id. */ + gcmkVERIFY_OK( + gckKERNEL_FreeIntegerId(Kernel->db->pointerDatabase, + shBuf->id)); + + /* Free atom. */ + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, shBuf->reference)); + + if (shBuf->data) + { + gcmkOS_SAFE_FREE(Kernel->os, shBuf->data); + shBuf->data = gcvNULL; + } + + /* Free the shared buffer. */ + gcmkOS_SAFE_FREE(Kernel->os, shBuf); + } + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_MapShBuffer +** +** Map shared buffer into this process so that it can be used in this process. +** This will increase reference count on the specified shared buffer. +** Call gckKERNEL_DestroyShBuffer to dereference. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be mapped. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_MapShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctINT32 oldValue = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + /* Increase the reference count. */ + gckOS_AtomIncrement(Kernel->os, shBuf->reference, &oldValue); + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_WriteShBuffer +** +** Write user data into shared buffer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be written to. +** +** gctPOINTER UserData +** User mode pointer to hold the source data. +** +** gctUINT32 ByteCount +** Specify number of bytes to write. If this is larger than +** shared buffer size, gcvSTATUS_INVALID_ARGUMENT is returned. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_WriteShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf, + IN gctPOINTER UserData, + IN gctUINT32 ByteCount + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u UserData=0x%X ByteCount=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf, UserData, ByteCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + if ((ByteCount > shBuf->size) || + (ByteCount == 0) || + (UserData == gcvNULL)) + { + /* Exceeds buffer max size or invalid. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (shBuf->data == gcvNULL) + { + /* Allocate buffer data when first time write. */ + gcmkONERROR(gckOS_Allocate(Kernel->os, ByteCount, &shBuf->data)); + } + + /* Copy data from user. */ + gcmkONERROR( + gckOS_CopyFromUserData(Kernel->os, + shBuf->data, + UserData, + ByteCount)); + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_ReadShBuffer +** +** Read data from shared buffer and copy to user pointer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSHBUF ShBuf +** Specify the shared buffer to be read from. +** +** gctPOINTER UserData +** User mode pointer to save output data. +** +** gctUINT32 ByteCount +** Specify number of bytes to read. +** If this is larger than shared buffer size, only avaiable bytes are +** copied. If smaller, copy requested size. +** +** OUTPUT: +** +** gctUINT32 * BytesRead +** Pointer to hold how many bytes actually read from shared buffer. +*/ +gceSTATUS +gckKERNEL_ReadShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf, + IN gctPOINTER UserData, + IN gctUINT32 ByteCount, + OUT gctUINT32 * BytesRead + ) +{ + gceSTATUS status; + gcsSHBUF_PTR shBuf; + gctUINT32 bytes; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ShBuf=%u UserData=0x%X ByteCount=%u", + Kernel, (gctUINT32)(gctUINTPTR_T) ShBuf, UserData, ByteCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(ShBuf != gcvNULL); + + /* Acquire mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, + Kernel->db->pointerDatabaseMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Find shared buffer structure. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(Kernel->db->pointerDatabase, + (gctUINT32)(gctUINTPTR_T)ShBuf, + (gctPOINTER)&shBuf)); + + gcmkASSERT(shBuf->id == (gctUINT32)(gctUINTPTR_T)ShBuf); + + if (shBuf->data == gcvNULL) + { + *BytesRead = 0; + + /* No data in shared buffer, skip copy. */ + status = gcvSTATUS_SKIP; + goto OnError; + } + else if (ByteCount == 0) + { + /* Invalid size to read. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Determine bytes to copy. */ + bytes = (ByteCount < shBuf->size) ? ByteCount : shBuf->size; + + /* Copy data to user. */ + gcmkONERROR( + gckOS_CopyToUserData(Kernel->os, + shBuf->data, + UserData, + bytes)); + + /* Return copied size. */ + *BytesRead = bytes; + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_ARG("*BytesRead=%u", bytes); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Kernel->os, Kernel->db->pointerDatabaseMutex)); + } + + gcmkFOOTER(); + return status; +} + + +/******************************************************************************* +***** Test Code **************************************************************** +*******************************************************************************/ + diff --git a/drivers/gpu/galcore/gc_hal_kernel.h b/drivers/gpu/galcore/gc_hal_kernel.h new file mode 100644 index 00000000000000..93764878a42d1b --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel.h @@ -0,0 +1,1489 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_h_ +#define __gc_hal_kernel_h_ + +#include "gc_hal.h" +#include "gc_hal_kernel_hardware.h" +#include "gc_hal_driver.h" + +#if gcdENABLE_VG +#include "gc_hal_kernel_vg.h" +#endif + +#if gcdSECURITY +#include "gc_hal_security_interface.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/******************************************************************************* +***** New MMU Defination *******************************************************/ +#define gcdMMU_MTLB_SHIFT 22 +#define gcdMMU_STLB_4K_SHIFT 12 +#define gcdMMU_STLB_64K_SHIFT 16 + +#define gcdMMU_MTLB_BITS (32 - gcdMMU_MTLB_SHIFT) +#define gcdMMU_PAGE_4K_BITS gcdMMU_STLB_4K_SHIFT +#define gcdMMU_STLB_4K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_PAGE_4K_BITS) +#define gcdMMU_PAGE_64K_BITS gcdMMU_STLB_64K_SHIFT +#define gcdMMU_STLB_64K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_PAGE_64K_BITS) + +#define gcdMMU_MTLB_ENTRY_NUM (1 << gcdMMU_MTLB_BITS) +#define gcdMMU_MTLB_SIZE (gcdMMU_MTLB_ENTRY_NUM << 2) +#define gcdMMU_STLB_4K_ENTRY_NUM (1 << gcdMMU_STLB_4K_BITS) +#define gcdMMU_STLB_4K_SIZE (gcdMMU_STLB_4K_ENTRY_NUM << 2) +#define gcdMMU_PAGE_4K_SIZE (1 << gcdMMU_STLB_4K_SHIFT) +#define gcdMMU_STLB_64K_ENTRY_NUM (1 << gcdMMU_STLB_64K_BITS) +#define gcdMMU_STLB_64K_SIZE (gcdMMU_STLB_64K_ENTRY_NUM << 2) +#define gcdMMU_PAGE_64K_SIZE (1 << gcdMMU_STLB_64K_SHIFT) + +#define gcdMMU_MTLB_MASK (~((1U << gcdMMU_MTLB_SHIFT)-1)) +#define gcdMMU_STLB_4K_MASK ((~0U << gcdMMU_STLB_4K_SHIFT) ^ gcdMMU_MTLB_MASK) +#define gcdMMU_PAGE_4K_MASK (gcdMMU_PAGE_4K_SIZE - 1) +#define gcdMMU_STLB_64K_MASK ((~((1U << gcdMMU_STLB_64K_SHIFT)-1)) ^ gcdMMU_MTLB_MASK) +#define gcdMMU_PAGE_64K_MASK (gcdMMU_PAGE_64K_SIZE - 1) + +/* Page offset definitions. */ +#define gcdMMU_OFFSET_4K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_STLB_4K_BITS) +#define gcdMMU_OFFSET_4K_MASK ((1U << gcdMMU_OFFSET_4K_BITS) - 1) +#define gcdMMU_OFFSET_16K_BITS (32 - gcdMMU_MTLB_BITS - gcdMMU_STLB_16K_BITS) +#define gcdMMU_OFFSET_16K_MASK ((1U << gcdMMU_OFFSET_16K_BITS) - 1) + +#define gcdMMU_MTLB_PRESENT 0x00000001 +#define gcdMMU_MTLB_EXCEPTION 0x00000002 +#define gcdMMU_MTLB_4K_PAGE 0x00000000 + +#define gcdMMU_STLB_PRESENT 0x00000001 +#define gcdMMU_STLB_EXCEPTION 0x00000002 +#define gcdMMU_STLB_4K_PAGE 0x00000000 + +/******************************************************************************* +***** Stuck Dump Level ********************************************************/ + +#define gcdSTUCK_DUMP_MINIMAL 1 +#define gcdSTUCK_DUMP_MIDDLE 2 +#define gcdSTUCK_DUMP_MAXIMAL 3 + +/******************************************************************************* +***** Process Secure Cache ****************************************************/ + +#define gcdSECURE_CACHE_LRU 1 +#define gcdSECURE_CACHE_LINEAR 2 +#define gcdSECURE_CACHE_HASH 3 +#define gcdSECURE_CACHE_TABLE 4 + +#define gcvPAGE_TABLE_DIRTY_BIT_OTHER (1 << 0) +#define gcvPAGE_TABLE_DIRTY_BIT_FE (1 << 1) + +typedef struct _gcskLOGICAL_CACHE * gcskLOGICAL_CACHE_PTR; +typedef struct _gcskLOGICAL_CACHE gcskLOGICAL_CACHE; +struct _gcskLOGICAL_CACHE +{ + /* Logical address. */ + gctPOINTER logical; + + /* DMAable address. */ + gctUINT32 dma; + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + /* Pointer to the previous and next hash tables. */ + gcskLOGICAL_CACHE_PTR nextHash; + gcskLOGICAL_CACHE_PTR prevHash; +#endif + +#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE + /* Pointer to the previous and next slot. */ + gcskLOGICAL_CACHE_PTR next; + gcskLOGICAL_CACHE_PTR prev; +#endif + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR + /* Time stamp. */ + gctUINT64 stamp; +#endif +}; + +typedef struct _gcskSECURE_CACHE * gcskSECURE_CACHE_PTR; +typedef struct _gcskSECURE_CACHE +{ + /* Cache memory. */ + gcskLOGICAL_CACHE cache[1 + gcdSECURE_CACHE_SLOTS]; + + /* Last known index for LINEAR mode. */ + gcskLOGICAL_CACHE_PTR cacheIndex; + + /* Current free slot for LINEAR mode. */ + gctUINT32 cacheFree; + + /* Time stamp for LINEAR mode. */ + gctUINT64 cacheStamp; + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + /* Hash table for HASH mode. */ + gcskLOGICAL_CACHE hash[256]; +#endif +} +gcskSECURE_CACHE; + +/******************************************************************************* +***** Process Database Management *********************************************/ + +typedef enum _gceDATABASE_TYPE +{ + gcvDB_VIDEO_MEMORY = 1, /* Video memory created. */ + gcvDB_COMMAND_BUFFER, /* Command Buffer. */ + gcvDB_NON_PAGED, /* Non paged memory. */ + gcvDB_CONTIGUOUS, /* Contiguous memory. */ + gcvDB_SIGNAL, /* Signal. */ + gcvDB_VIDEO_MEMORY_LOCKED, /* Video memory locked. */ + gcvDB_CONTEXT, /* Context */ + gcvDB_IDLE, /* GPU idle. */ + gcvDB_MAP_MEMORY, /* Map memory */ + gcvDB_MAP_USER_MEMORY, /* Map user memory */ + gcvDB_SYNC_POINT, /* Sync point. */ + gcvDB_SHBUF, /* Shared buffer. */ +} +gceDATABASE_TYPE; + +#define gcdDATABASE_TYPE_MASK 0x000000FF +#define gcdDB_VIDEO_MEMORY_TYPE_MASK 0x0000FF00 +#define gcdDB_VIDEO_MEMORY_TYPE_SHIFT 8 + +#define gcdDB_VIDEO_MEMORY_POOL_MASK 0x00FF0000 +#define gcdDB_VIDEO_MEMORY_POOL_SHIFT 16 + +typedef struct _gcsDATABASE_RECORD * gcsDATABASE_RECORD_PTR; +typedef struct _gcsDATABASE_RECORD +{ + /* Pointer to kernel. */ + gckKERNEL kernel; + + /* Pointer to next database record. */ + gcsDATABASE_RECORD_PTR next; + + /* Type of record. */ + gceDATABASE_TYPE type; + + /* Data for record. */ + gctPOINTER data; + gctPHYS_ADDR physical; + gctSIZE_T bytes; +} +gcsDATABASE_RECORD; + +typedef struct _gcsDATABASE * gcsDATABASE_PTR; +typedef struct _gcsDATABASE +{ + /* Pointer to next entry is hash list. */ + gcsDATABASE_PTR next; + gctSIZE_T slot; + + /* Process ID. */ + gctUINT32 processID; + + /* Sizes to query. */ + gcsDATABASE_COUNTERS vidMem; + gcsDATABASE_COUNTERS nonPaged; + gcsDATABASE_COUNTERS contiguous; + gcsDATABASE_COUNTERS mapUserMemory; + gcsDATABASE_COUNTERS mapMemory; + gcsDATABASE_COUNTERS virtualCommandBuffer; + + gcsDATABASE_COUNTERS vidMemType[gcvSURF_NUM_TYPES]; + /* Counter for each video memory pool. */ + gcsDATABASE_COUNTERS vidMemPool[gcvPOOL_NUMBER_OF_POOLS]; + gctPOINTER counterMutex; + + /* Idle time management. */ + gctUINT64 lastIdle; + gctUINT64 idle; + + /* Pointer to database. */ + gcsDATABASE_RECORD_PTR list[48]; + +#if gcdSECURE_USER + /* Secure cache. */ + gcskSECURE_CACHE cache; +#endif + + gctPOINTER handleDatabase; + gctPOINTER handleDatabaseMutex; + +#if gcdPROCESS_ADDRESS_SPACE + gckMMU mmu; +#endif +} +gcsDATABASE; + +typedef struct _gcsRECORDER * gckRECORDER; + +typedef struct _gcsFDPRIVATE * gcsFDPRIVATE_PTR; +typedef struct _gcsFDPRIVATE +{ + gctINT (* release) (gcsFDPRIVATE_PTR Private); +} +gcsFDPRIVATE; + +/* Create a process database that will contain all its allocations. */ +gceSTATUS +gckKERNEL_CreateProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID + ); + +/* Add a record to the process database. */ +gceSTATUS +gckKERNEL_AddProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Size + ); + +/* Remove a record to the process database. */ +gceSTATUS +gckKERNEL_RemoveProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer + ); + +/* Destroy the process database. */ +gceSTATUS +gckKERNEL_DestroyProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID + ); + +/* Find a record to the process database. */ +gceSTATUS +gckKERNEL_FindProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 ThreadID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer, + OUT gcsDATABASE_RECORD_PTR Record + ); + +/* Query the process database. */ +gceSTATUS +gckKERNEL_QueryProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctBOOL LastProcessID, + IN gceDATABASE_TYPE Type, + OUT gcuDATABASE_INFO * Info + ); + +/* Dump the process database. */ +gceSTATUS +gckKERNEL_DumpProcessDB( + IN gckKERNEL Kernel + ); + +/* Dump the video memory usage for process specified. */ +gceSTATUS +gckKERNEL_DumpVidMemUsage( + IN gckKERNEL Kernel, + IN gctINT32 ProcessID + ); + +gceSTATUS +gckKERNEL_FindDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctBOOL LastProcessID, + OUT gcsDATABASE_PTR * Database + ); + +gceSTATUS +gckKERNEL_FindHandleDatbase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gctPOINTER * HandleDatabase, + OUT gctPOINTER * HandleDatabaseMutex + ); + +gceSTATUS +gckKERNEL_GetProcessMMU( + IN gckKERNEL Kernel, + OUT gckMMU * Mmu + ); + +gceSTATUS +gckKERNEL_SetRecovery( + IN gckKERNEL Kernel, + IN gctBOOL Recovery, + IN gctUINT32 StuckDump + ); + +gceSTATUS +gckMMU_FlatMapping( + IN gckMMU Mmu, + IN gctUINT32 Physical + ); + +gceSTATUS +gckMMU_GetPageEntry( + IN gckMMU Mmu, + IN gctUINT32 Address, + IN gctUINT32_PTR *PageTable + ); + +gceSTATUS +gckMMU_FreePagesEx( + IN gckMMU Mmu, + IN gctUINT32 Address, + IN gctSIZE_T PageCount + ); + +gceSTATUS +gckKERNEL_CreateIntegerDatabase( + IN gckKERNEL Kernel, + OUT gctPOINTER * Database + ); + +gceSTATUS +gckKERNEL_DestroyIntegerDatabase( + IN gckKERNEL Kernel, + IN gctPOINTER Database + ); + +gceSTATUS +gckKERNEL_AllocateIntegerId( + IN gctPOINTER Database, + IN gctPOINTER Pointer, + OUT gctUINT32 * Id + ); + +gceSTATUS +gckKERNEL_FreeIntegerId( + IN gctPOINTER Database, + IN gctUINT32 Id + ); + +gceSTATUS +gckKERNEL_QueryIntegerId( + IN gctPOINTER Database, + IN gctUINT32 Id, + OUT gctPOINTER * Pointer + ); + +/* Pointer rename */ +gctUINT32 +gckKERNEL_AllocateNameFromPointer( + IN gckKERNEL Kernel, + IN gctPOINTER Pointer + ); + +gctPOINTER +gckKERNEL_QueryPointerFromName( + IN gckKERNEL Kernel, + IN gctUINT32 Name + ); + +gceSTATUS +gckKERNEL_DeleteName( + IN gckKERNEL Kernel, + IN gctUINT32 Name + ); + +#if gcdSECURE_USER +/* Get secure cache from the process database. */ +gceSTATUS +gckKERNEL_GetProcessDBCache( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gcskSECURE_CACHE_PTR * Cache + ); +#endif + +/******************************************************************************* +********* Timer Management ****************************************************/ +typedef struct _gcsTIMER * gcsTIMER_PTR; +typedef struct _gcsTIMER +{ + /* Start and Stop time holders. */ + gctUINT64 startTime; + gctUINT64 stopTime; +} +gcsTIMER; + +/******************************************************************************\ +********************************** Structures ********************************** +\******************************************************************************/ + +/* gckDB object. */ +struct _gckDB +{ + /* Database management. */ + gcsDATABASE_PTR db[16]; + gctPOINTER dbMutex; + gcsDATABASE_PTR freeDatabase; + gcsDATABASE_RECORD_PTR freeRecord; + gcsDATABASE_PTR lastDatabase; + gctUINT32 lastProcessID; + gctUINT64 lastIdle; + gctUINT64 idleTime; + gctUINT64 lastSlowdown; + gctUINT64 lastSlowdownIdle; + gctPOINTER nameDatabase; + gctPOINTER nameDatabaseMutex; + + gctPOINTER pointerDatabase; + gctPOINTER pointerDatabaseMutex; +}; + +typedef struct _gckVIRTUAL_COMMAND_BUFFER * gckVIRTUAL_COMMAND_BUFFER_PTR; +typedef struct _gckVIRTUAL_COMMAND_BUFFER +{ + gctPHYS_ADDR physical; + gctPOINTER userLogical; + gctPOINTER kernelLogical; + gctSIZE_T bytes; + gctSIZE_T pageCount; + gctPOINTER pageTable; + gctUINT32 gpuAddress; + gctUINT pid; + gckVIRTUAL_COMMAND_BUFFER_PTR next; + gckVIRTUAL_COMMAND_BUFFER_PTR prev; + gckKERNEL kernel; +#if gcdPROCESS_ADDRESS_SPACE + gckMMU mmu; +#endif +} +gckVIRTUAL_COMMAND_BUFFER; + +/* gckKERNEL object. */ +struct _gckKERNEL +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Core */ + gceCORE core; + + /* Pointer to gckHARDWARE object. */ + gckHARDWARE hardware; + + /* Pointer to gckCOMMAND object. */ + gckCOMMAND command; + + /* Pointer to gckEVENT object. */ + gckEVENT eventObj; + + /* Pointer to context. */ + gctPOINTER context; + + /* Pointer to gckMMU object. */ + gckMMU mmu; + + /* Arom holding number of clients. */ + gctPOINTER atomClients; + +#if VIVANTE_PROFILER + /* Enable profiling */ + gctBOOL profileEnable; + /* Clear profile register or not*/ + gctBOOL profileCleanRegister; +#endif + +#ifdef QNX_SINGLE_THREADED_DEBUGGING + gctPOINTER debugMutex; +#endif + + /* Database management. */ + gckDB db; + gctBOOL dbCreated; + + gctUINT64 resetTimeStamp; + + /* Pointer to gckEVENT object. */ + gcsTIMER timers[8]; + gctUINT32 timeOut; + +#if gcdENABLE_VG + gckVGKERNEL vg; +#endif + + /* Virtual command buffer list. */ + gckVIRTUAL_COMMAND_BUFFER_PTR virtualBufferHead; + gckVIRTUAL_COMMAND_BUFFER_PTR virtualBufferTail; + gctPOINTER virtualBufferLock; + + /* Enable virtual command buffer. */ + gctBOOL virtualCommandBuffer; + +#if gcdDVFS + gckDVFS dvfs; +#endif + +#if gcdANDROID_NATIVE_FENCE_SYNC + gctHANDLE timeline; +#endif + + /* Enable recovery. */ + gctBOOL recovery; + + /* Level of dump information after stuck. */ + gctUINT stuckDump; + +#if gcdSECURITY + gctUINT32 securityChannel; +#endif + + /* Timer to monitor GPU stuck. */ + gctPOINTER monitorTimer; + + /* Flag to quit monitor timer. */ + gctBOOL monitorTimerStop; + + /* Monitor states. */ + gctBOOL monitoring; + gctUINT32 lastCommitStamp; + gctUINT32 timer; + gctUINT32 restoreAddress; + gctUINT32 restoreMask; +}; + +struct _FrequencyHistory +{ + gctUINT32 frequency; + gctUINT32 count; +}; + +/* gckDVFS object. */ +struct _gckDVFS +{ + gckOS os; + gckHARDWARE hardware; + gctPOINTER timer; + gctUINT32 pollingTime; + gctBOOL stop; + gctUINT32 totalConfig; + gctUINT32 loads[8]; + gctUINT8 currentScale; + struct _FrequencyHistory frequencyHistory[16]; +}; + +/* gckCOMMAND object. */ +struct _gckCOMMAND +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to required object. */ + gckKERNEL kernel; + gckOS os; + + /* Number of bytes per page. */ + gctUINT32 pageSize; + + /* Current pipe select. */ + gcePIPE_SELECT pipeSelect; + + /* Command queue running flag. */ + gctBOOL running; + + /* Idle flag and commit stamp. */ + gctBOOL idle; + gctUINT64 commitStamp; + + /* Command queue mutex. */ + gctPOINTER mutexQueue; + + /* Context switching mutex. */ + gctPOINTER mutexContext; + +#if VIVANTE_PROFILER_CONTEXT + /* Context sequence mutex. */ + gctPOINTER mutexContextSeq; +#endif + + /* Command queue power semaphore. */ + gctPOINTER powerSemaphore; + + /* Current command queue. */ + struct _gcskCOMMAND_QUEUE + { + gctSIGNAL signal; + gctPHYS_ADDR physical; + gctPOINTER logical; + gctUINT32 address; + } + queues[gcdCOMMAND_QUEUES]; + + gctPHYS_ADDR physical; + gctPOINTER logical; + gctUINT32 address; + gctUINT32 offset; + gctINT index; +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gctUINT wrapCount; +#endif + + /* The command queue is new. */ + gctBOOL newQueue; + + /* Context management. */ + gckCONTEXT currContext; + + /* Pointer to last WAIT command. */ + gctPHYS_ADDR waitPhysical; + gctPOINTER waitLogical; + gctUINT32 waitSize; + + /* Command buffer alignment. */ + gctUINT32 alignment; + gctUINT32 reservedHead; + gctUINT32 reservedTail; + + /* Commit counter. */ + gctPOINTER atomCommit; + + /* Kernel process ID. */ + gctUINT32 kernelProcessID; + + /* End Event signal. */ + gctSIGNAL endEventSignal; + +#if gcdSECURE_USER + /* Hint array copy buffer. */ + gctBOOL hintArrayAllocated; + gctUINT hintArraySize; + gctUINT32_PTR hintArray; +#endif + +#if gcdPROCESS_ADDRESS_SPACE + gckMMU currentMmu; +#endif + struct _gckENTRYQUEUE queue; +}; + +typedef struct _gcsEVENT * gcsEVENT_PTR; + +/* Structure holding one event to be processed. */ +typedef struct _gcsEVENT +{ + /* Pointer to next event in queue. */ + gcsEVENT_PTR next; + + /* Event information. */ + gcsHAL_INTERFACE info; + + /* Process ID owning the event. */ + gctUINT32 processID; + +#ifdef __QNXNTO__ + /* Kernel. */ + gckKERNEL kernel; +#endif + + gctBOOL fromKernel; +} +gcsEVENT; + +/* Structure holding a list of events to be processed by an interrupt. */ +typedef struct _gcsEVENT_QUEUE * gcsEVENT_QUEUE_PTR; +typedef struct _gcsEVENT_QUEUE +{ + /* Time stamp. */ + gctUINT64 stamp; + + /* Source of the event. */ + gceKERNEL_WHERE source; + +#if gcdMULTI_GPU + /* Which chip(s) of the event */ + gceCORE_3D_MASK chipEnable; +#endif + + /* Pointer to head of event queue. */ + gcsEVENT_PTR head; + + /* Pointer to tail of event queue. */ + gcsEVENT_PTR tail; + + /* Next list of events. */ + gcsEVENT_QUEUE_PTR next; +} +gcsEVENT_QUEUE; + +/* + gcdREPO_LIST_COUNT defines the maximum number of event queues with different + hardware module sources that may coexist at the same time. Only two sources + are supported - gcvKERNEL_COMMAND and gcvKERNEL_PIXEL. gcvKERNEL_COMMAND + source is used only for managing the kernel command queue and is only issued + when the current command queue gets full. Since we commit event queues every + time we commit command buffers, in the worst case we can have up to three + pending event queues: + - gcvKERNEL_PIXEL + - gcvKERNEL_COMMAND (queue overflow) + - gcvKERNEL_PIXEL +*/ +#define gcdREPO_LIST_COUNT 3 + +/* gckEVENT object. */ +struct _gckEVENT +{ + /* The object. */ + gcsOBJECT object; + + /* Pointer to required objects. */ + gckOS os; + gckKERNEL kernel; + + /* Time stamp. */ + gctUINT64 stamp; + gctUINT32 lastCommitStamp; + + /* Queue mutex. */ + gctPOINTER eventQueueMutex; + + /* Array of event queues. */ + gcsEVENT_QUEUE queues[29]; + gctUINT8 lastID; + gctPOINTER freeAtom; + + /* Pending events. */ +#if gcdSMP +#if gcdMULTI_GPU + gctPOINTER pending3D[gcdMULTI_GPU]; + gctPOINTER pending3DMask[gcdMULTI_GPU]; + gctPOINTER pendingMask; +#endif + gctPOINTER pending; +#else +#if gcdMULTI_GPU + volatile gctUINT pending3D[gcdMULTI_GPU]; + volatile gctUINT pending3DMask[gcdMULTI_GPU]; + volatile gctUINT pendingMask; +#endif + volatile gctUINT pending; +#endif +#if gcdMULTI_GPU + gctUINT32 busy; +#endif + + /* List of free event structures and its mutex. */ + gcsEVENT_PTR freeEventList; + gctSIZE_T freeEventCount; + gctPOINTER freeEventMutex; + + /* Event queues. */ + gcsEVENT_QUEUE_PTR queueHead; + gcsEVENT_QUEUE_PTR queueTail; + gcsEVENT_QUEUE_PTR freeList; + gcsEVENT_QUEUE repoList[gcdREPO_LIST_COUNT]; + gctPOINTER eventListMutex; + + gctPOINTER submitTimer; + +#if gcdINTERRUPT_STATISTIC + gctPOINTER interruptCount; +#endif + +#if gcdRECORD_COMMAND + gckRECORDER recorder; +#endif +}; + +/* Free all events belonging to a process. */ +gceSTATUS +gckEVENT_FreeProcess( + IN gckEVENT Event, + IN gctUINT32 ProcessID + ); + +gceSTATUS +gckEVENT_Stop( + IN gckEVENT Event, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Handle, + IN gctPOINTER Logical, + IN gctSIGNAL Signal, + IN OUT gctUINT32 * waitSize + ); + +typedef struct _gcsLOCK_INFO * gcsLOCK_INFO_PTR; +typedef struct _gcsLOCK_INFO +{ + gctUINT32 GPUAddresses[gcdMAX_GPU_COUNT]; + gctPOINTER pageTables[gcdMAX_GPU_COUNT]; + gctUINT32 lockeds[gcdMAX_GPU_COUNT]; + gckKERNEL lockKernels[gcdMAX_GPU_COUNT]; + gckMMU lockMmus[gcdMAX_GPU_COUNT]; +} +gcsLOCK_INFO; + +typedef struct _gcsGPU_MAP * gcsGPU_MAP_PTR; +typedef struct _gcsGPU_MAP +{ + gctINT pid; + gcsLOCK_INFO lockInfo; + gcsGPU_MAP_PTR prev; + gcsGPU_MAP_PTR next; +} +gcsGPU_MAP; + +/* gcuVIDMEM_NODE structure. */ +typedef union _gcuVIDMEM_NODE +{ + /* Allocated from gckVIDMEM. */ + struct _gcsVIDMEM_NODE_VIDMEM + { + /* Owner of this node. */ + gckVIDMEM memory; + + /* Dual-linked list of nodes. */ + gcuVIDMEM_NODE_PTR next; + gcuVIDMEM_NODE_PTR prev; + + /* Dual linked list of free nodes. */ + gcuVIDMEM_NODE_PTR nextFree; + gcuVIDMEM_NODE_PTR prevFree; + + /* Information for this node. */ + gctSIZE_T offset; + gctSIZE_T bytes; + gctUINT32 alignment; + +#ifdef __QNXNTO__ + /* Client virtual address. */ + gctPOINTER logical; +#endif + + /* Locked counter. */ + gctINT32 locked; + + /* Memory pool. */ + gcePOOL pool; + gctUINT32 physical; + + /* Process ID owning this memory. */ + gctUINT32 processID; + +#if gcdENABLE_VG + gctPOINTER kernelVirtual; +#endif + } + VidMem; + + /* Allocated from gckOS. */ + struct _gcsVIDMEM_NODE_VIRTUAL + { + /* Pointer to gckKERNEL object. */ + gckKERNEL kernel; + + /* Information for this node. */ + /* Contiguously allocated? */ + gctBOOL contiguous; + /* mdl record pointer... a kmalloc address. Process agnostic. */ + gctPHYS_ADDR physical; + gctSIZE_T bytes; + /* do_mmap_pgoff address... mapped per-process. */ + gctPOINTER logical; + +#if gcdENABLE_VG + /* Physical address of this node, only meaningful when it is contiguous. */ + gctUINT32 physicalAddress; + + /* Kernel logical of this node. */ + gctPOINTER kernelVirtual; +#endif + + /* Customer private handle */ + gctUINT32 gid; + + /* Page table information. */ + /* Used only when node is not contiguous */ + gctSIZE_T pageCount; + + /* Used only when node is not contiguous */ + gctPOINTER pageTables[gcdMAX_GPU_COUNT]; + /* Pointer to gckKERNEL object who lock this. */ + gckKERNEL lockKernels[gcdMAX_GPU_COUNT]; + /* Actual physical address */ + gctUINT32 addresses[gcdMAX_GPU_COUNT]; + + /* Locked counter. */ + gctINT32 lockeds[gcdMAX_GPU_COUNT]; + + /* Process ID owning this memory. */ + gctUINT32 processID; + + /* Surface type. */ + gceSURF_TYPE type; + } + Virtual; +} +gcuVIDMEM_NODE; + +/* gckVIDMEM object. */ +struct _gckVIDMEM +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Information for this video memory heap. */ + gctUINT32 baseAddress; + gctSIZE_T bytes; + gctSIZE_T freeBytes; + + /* Mapping for each type of surface. */ + gctINT mapping[gcvSURF_NUM_TYPES]; + + /* Sentinel nodes for up to 8 banks. */ + gcuVIDMEM_NODE sentinel[8]; + + /* Allocation threshold. */ + gctSIZE_T threshold; + + /* The heap mutex. */ + gctPOINTER mutex; +}; + +typedef struct _gcsVIDMEM_NODE +{ + /* Pointer to gcuVIDMEM_NODE. */ + gcuVIDMEM_NODE_PTR node; + + /* Mutex to protect node. */ + gctPOINTER mutex; + + /* Reference count. */ + gctPOINTER reference; + + /* Name for client to import. */ + gctUINT32 name; + +#if gcdPROCESS_ADDRESS_SPACE + /* Head of mapping list. */ + gcsGPU_MAP_PTR mapHead; + + /* Tail of mapping list. */ + gcsGPU_MAP_PTR mapTail; + + gctPOINTER mapMutex; +#endif + + /* Surface Type. */ + gceSURF_TYPE type; + + /* Pool from which node is allocated. */ + gcePOOL pool; +} +gcsVIDMEM_NODE; + +typedef struct _gcsVIDMEM_HANDLE * gckVIDMEM_HANDLE; +typedef struct _gcsVIDMEM_HANDLE +{ + /* Pointer to gckVIDMEM_NODE. */ + gckVIDMEM_NODE node; + + /* Handle for current process. */ + gctUINT32 handle; + + /* Reference count for this handle. */ + gctPOINTER reference; +} +gcsVIDMEM_HANDLE; + +typedef struct _gcsSHBUF * gcsSHBUF_PTR; +typedef struct _gcsSHBUF +{ + /* ID. */ + gctUINT32 id; + + /* Reference count. */ + gctPOINTER reference; + + /* Data size. */ + gctUINT32 size; + + /* Data. */ + gctPOINTER data; +} +gcsSHBUF; + +gceSTATUS +gckVIDMEM_HANDLE_Reference( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ); + +gceSTATUS +gckVIDMEM_HANDLE_Dereference( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ); + +gceSTATUS +gckVIDMEM_NODE_Allocate( + IN gckKERNEL Kernel, + IN gcuVIDMEM_NODE_PTR VideoNode, + IN gceSURF_TYPE Type, + IN gcePOOL Pool, + IN gctUINT32 * Handle + ); + +gceSTATUS +gckVIDMEM_Node_Lock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + OUT gctUINT32 *Address + ); + +gceSTATUS +gckVIDMEM_NODE_Unlock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + IN gctUINT32 ProcessID + ); + +gceSTATUS +gckVIDMEM_NODE_Dereference( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node + ); + +gceSTATUS +gckVIDMEM_NODE_Name( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + IN gctUINT32 * Name + ); + +gceSTATUS +gckVIDMEM_NODE_Import( + IN gckKERNEL Kernel, + IN gctUINT32 Name, + IN gctUINT32 * Handle + ); + +gceSTATUS +gckVIDMEM_HANDLE_LookupAndReference( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + OUT gckVIDMEM_NODE * Node + ); + +gceSTATUS +gckVIDMEM_HANDLE_Lookup( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle, + OUT gckVIDMEM_NODE * Node + ); + +gceSTATUS +gckVIDMEM_NODE_GetFd( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + OUT gctINT * Fd + ); + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +gckEVENT_DestroyMmu( + IN gckEVENT Event, + IN gckMMU Mmu, + IN gceKERNEL_WHERE FromWhere + ); +#endif + +/* gckMMU object. */ +struct _gckMMU +{ + /* The object. */ + gcsOBJECT object; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Pointer to gckHARDWARE object. */ + gckHARDWARE hardware; + + /* The page table mutex. */ + gctPOINTER pageTableMutex; + + /* Page table information. */ + gctSIZE_T pageTableSize; + gctPHYS_ADDR pageTablePhysical; + gctUINT32_PTR pageTableLogical; + gctUINT32 pageTableEntries; + + /* Master TLB information. */ + gctSIZE_T mtlbSize; + gctPHYS_ADDR mtlbPhysical; + gctUINT32_PTR mtlbLogical; + gctUINT32 mtlbEntries; + + /* Free entries. */ + gctUINT32 heapList; + gctBOOL freeNodes; + + gctPOINTER staticSTLB; + gctBOOL enabled; + + gctUINT32 dynamicMappingStart; + + gctUINT32_PTR mapLogical; +#if gcdPROCESS_ADDRESS_SPACE + gctPOINTER pageTableDirty[gcdMAX_GPU_COUNT]; + gctPOINTER stlbs; +#endif +}; + +gceSTATUS +gckOS_CreateKernelVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical, + OUT gctSIZE_T * PageCount + ); + +gceSTATUS +gckOS_DestroyKernelVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +gceSTATUS +gckOS_CreateUserVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical, + OUT gctSIZE_T * PageCount + ); + +gceSTATUS +gckOS_DestroyUserVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +gceSTATUS +gckOS_GetFd( + IN gctSTRING Name, + IN gcsFDPRIVATE_PTR Private, + OUT gctINT *Fd + ); + +gceSTATUS +gckKERNEL_AllocateVirtualCommandBuffer( + IN gckKERNEL Kernel, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ); + +gceSTATUS +gckKERNEL_DestroyVirtualCommandBuffer( + IN gckKERNEL Kernel, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical + ); + +gceSTATUS +gckKERNEL_GetGPUAddress( + IN gckKERNEL Kernel, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + OUT gctUINT32 * Address + ); + +gceSTATUS +gckKERNEL_QueryGPUAddress( + IN gckKERNEL Kernel, + IN gctUINT32 GpuAddress, + OUT gckVIRTUAL_COMMAND_BUFFER_PTR * Buffer + ); + +gceSTATUS +gckKERNEL_AttachProcess( + IN gckKERNEL Kernel, + IN gctBOOL Attach + ); + +gceSTATUS +gckKERNEL_AttachProcessEx( + IN gckKERNEL Kernel, + IN gctBOOL Attach, + IN gctUINT32 PID + ); + +#if gcdSECURE_USER +gceSTATUS +gckKERNEL_MapLogicalToPhysical( + IN gckKERNEL Kernel, + IN gcskSECURE_CACHE_PTR Cache, + IN OUT gctPOINTER * Data + ); + +gceSTATUS +gckKERNEL_FlushTranslationCache( + IN gckKERNEL Kernel, + IN gcskSECURE_CACHE_PTR Cache, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); +#endif + +gceSTATUS +gckHARDWARE_QueryIdle( + IN gckHARDWARE Hardware, + OUT gctBOOL_PTR IsIdle + ); + +#if gcdSECURITY +gceSTATUS +gckKERNEL_SecurityOpen( + IN gckKERNEL Kernel, + IN gctUINT32 GPU, + OUT gctUINT32 *Channel + ); + +/* +** Close a security service channel +*/ +gceSTATUS +gckKERNEL_SecurityClose( + IN gctUINT32 Channel + ); + +/* +** Security service interface. +*/ +gceSTATUS +gckKERNEL_SecurityCallService( + IN gctUINT32 Channel, + IN OUT gcsTA_INTERFACE * Interface + ); + +gceSTATUS +gckKERNEL_SecurityStartCommand( + IN gckKERNEL Kernel + ); + +gceSTATUS +gckKERNEL_SecurityAllocateSecurityMemory( + IN gckKERNEL Kernel, + IN gctUINT32 Bytes, + OUT gctUINT32 * Handle + ); + +gceSTATUS +gckKERNEL_SecurityExecute( + IN gckKERNEL Kernel, + IN gctPOINTER Buffer, + IN gctUINT32 Bytes + ); + +gceSTATUS +gckKERNEL_SecurityMapMemory( + IN gckKERNEL Kernel, + IN gctUINT32 *PhysicalArray, + IN gctUINT32 PageCount, + OUT gctUINT32 * GPUAddress + ); + +gceSTATUS +gckKERNEL_SecurityUnmapMemory( + IN gckKERNEL Kernel, + IN gctUINT32 GPUAddress, + IN gctUINT32 PageCount + ); + +#endif + +gceSTATUS +gckKERNEL_CreateShBuffer( + IN gckKERNEL Kernel, + IN gctUINT32 Size, + OUT gctSHBUF * ShBuf + ); + +gceSTATUS +gckKERNEL_DestroyShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf + ); + +gceSTATUS +gckKERNEL_MapShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf + ); + +gceSTATUS +gckKERNEL_WriteShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf, + IN gctPOINTER UserData, + IN gctUINT32 ByteCount + ); + +gceSTATUS +gckKERNEL_ReadShBuffer( + IN gckKERNEL Kernel, + IN gctSHBUF ShBuf, + IN gctPOINTER UserData, + IN gctUINT32 ByteCount, + OUT gctUINT32 * BytesRead + ); + + +/******************************************************************************\ +******************************* gckCONTEXT Object ******************************* +\******************************************************************************/ + +gceSTATUS +gckCONTEXT_Construct( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gctUINT32 ProcessID, + OUT gckCONTEXT * Context + ); + +gceSTATUS +gckCONTEXT_Destroy( + IN gckCONTEXT Context + ); + +gceSTATUS +gckCONTEXT_Update( + IN gckCONTEXT Context, + IN gctUINT32 ProcessID, + IN gcsSTATE_DELTA_PTR StateDelta + ); + +gceSTATUS +gckCONTEXT_MapBuffer( + IN gckCONTEXT Context, + OUT gctUINT32 *Physicals, + OUT gctUINT64 *Logicals, + OUT gctUINT32 *Bytes + ); + +#if gcdLINK_QUEUE_SIZE +void +gckLINKQUEUE_Enqueue( + IN gckLINKQUEUE LinkQueue, + IN gctUINT32 start, + IN gctUINT32 end + ); + +void +gckLINKQUEUE_GetData( + IN gckLINKQUEUE LinkQueue, + IN gctUINT32 Index, + OUT gckLINKDATA * Data + ); +#endif + +gceSTATUS +gckENTRYQUEUE_Enqueue( + IN gckKERNEL Kernel, + IN gckENTRYQUEUE Queue, + IN gctUINT32 physical, + IN gctUINT32 bytes + ); + +gceSTATUS +gckENTRYQUEUE_Dequeue( + IN gckENTRYQUEUE Queue, + OUT gckENTRYDATA * Data + ); + +/******************************************************************************\ +****************************** gckRECORDER Object ****************************** +\******************************************************************************/ +gceSTATUS +gckRECORDER_Construct( + IN gckOS Os, + IN gckHARDWARE Hardware, + OUT gckRECORDER * Recorder + ); + +gceSTATUS +gckRECORDER_Destory( + IN gckOS Os, + IN gckRECORDER Recorder + ); + +void +gckRECORDER_AdvanceIndex( + gckRECORDER Recorder, + gctUINT64 CommitStamp + ); + +void +gckRECORDER_Record( + gckRECORDER Recorder, + gctUINT8_PTR CommandBuffer, + gctUINT32 CommandBytes, + gctUINT8_PTR ContextBuffer, + gctUINT32 ContextBytes + ); + +void +gckRECORDER_Dump( + gckRECORDER Recorder + ); + +gceSTATUS +gckRECORDER_UpdateMirror( + gckRECORDER Recorder, + gctUINT32 State, + gctUINT32 Data + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_kernel_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_allocator.c b/drivers/gpu/galcore/gc_hal_kernel_allocator.c new file mode 100644 index 00000000000000..a7e62a4b5aa274 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_allocator.c @@ -0,0 +1,897 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" +#include "gc_hal_kernel_allocator.h" +#include +#include +#include +#include +#include +#include + +#include "gc_hal_kernel_allocator_array.h" +#include "gc_hal_kernel_platform.h" + +#define _GC_OBJ_ZONE gcvZONE_OS + +typedef struct _gcsDEFAULT_PRIV * gcsDEFAULT_PRIV_PTR; +typedef struct _gcsDEFAULT_PRIV { + gctUINT32 low; + gctUINT32 high; +} +gcsDEFAULT_PRIV; + +/******************************************************************************\ +************************** Default Allocator Debugfs *************************** +\******************************************************************************/ + +int gc_usage_show(struct seq_file* m, void* data) +{ + gcsINFO_NODE *node = m->private; + gckALLOCATOR Allocator = node->device; + gcsDEFAULT_PRIV_PTR priv = Allocator->privateData; + + seq_printf(m, "low: %u bytes\n", priv->low); + seq_printf(m, "high: %u bytes\n", priv->high); + + return 0; +} + +static gcsINFO InfoList[] = +{ + {"lowHighUsage", gc_usage_show}, +}; + +static void +_DefaultAllocatorDebugfsInit( + IN gckALLOCATOR Allocator, + IN gckDEBUGFS_DIR Root + ) +{ + gcmkVERIFY_OK( + gckDEBUGFS_DIR_Init(&Allocator->debugfsDir, Root->root, "default")); + + gcmkVERIFY_OK(gckDEBUGFS_DIR_CreateFiles( + &Allocator->debugfsDir, + InfoList, + gcmCOUNTOF(InfoList), + Allocator + )); +} + +static void +_DefaultAllocatorDebugfsCleanup( + IN gckALLOCATOR Allocator + ) +{ + gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles( + &Allocator->debugfsDir, + InfoList, + gcmCOUNTOF(InfoList) + )); + + gckDEBUGFS_DIR_Deinit(&Allocator->debugfsDir); +} + + +static void +_NonContiguousFree( + IN struct page ** Pages, + IN gctUINT32 NumPages + ) +{ + gctINT i; + + gcmkHEADER_ARG("Pages=0x%X, NumPages=%d", Pages, NumPages); + + gcmkASSERT(Pages != gcvNULL); + + for (i = 0; i < NumPages; i++) + { + __free_page(Pages[i]); + } + + if (is_vmalloc_addr(Pages)) + { + vfree(Pages); + } + else + { + kfree(Pages); + } + + gcmkFOOTER_NO(); +} + +static struct page ** +_NonContiguousAlloc( + IN gctUINT32 NumPages + ) +{ + struct page ** pages; + struct page *p; + gctINT i, size; + + gcmkHEADER_ARG("NumPages=%lu", NumPages); + + if (NumPages > totalram_pages) + { + gcmkFOOTER_NO(); + return gcvNULL; + } + + size = NumPages * sizeof(struct page *); + + pages = kmalloc(size, GFP_KERNEL | gcdNOWARN); + + if (!pages) + { + pages = vmalloc(size); + + if (!pages) + { + gcmkFOOTER_NO(); + return gcvNULL; + } + } + + for (i = 0; i < NumPages; i++) + { + p = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | gcdNOWARN); + + if (!p) + { + _NonContiguousFree(pages, i); + gcmkFOOTER_NO(); + return gcvNULL; + } + + pages[i] = p; + } + + gcmkFOOTER_ARG("pages=0x%X", pages); + return pages; +} + +gctSTRING +_CreateKernelVirtualMapping( + IN PLINUX_MDL Mdl + ) +{ + gctSTRING addr = 0; + gctINT numPages = Mdl->numPages; + +#if gcdNONPAGED_MEMORY_CACHEABLE + if (Mdl->contiguous) + { + addr = page_address(Mdl->u.contiguousPages); + } + else + { + addr = vmap(Mdl->u.nonContiguousPages, + numPages, + 0, + PAGE_KERNEL); + + /* Trigger a page fault. */ + memset(addr, 0, numPages * PAGE_SIZE); + } +#else + struct page ** pages; + gctBOOL free = gcvFALSE; + gctINT i; + + if (Mdl->contiguous) + { + pages = kmalloc(sizeof(struct page *) * numPages, GFP_KERNEL | gcdNOWARN); + + if (!pages) + { + return gcvNULL; + } + + for (i = 0; i < numPages; i++) + { + pages[i] = nth_page(Mdl->u.contiguousPages, i); + } + + free = gcvTRUE; + } + else + { + pages = Mdl->u.nonContiguousPages; + } + + /* ioremap() can't work on system memory since 2.6.38. */ + addr = vmap(pages, numPages, 0, gcmkNONPAGED_MEMROY_PROT(PAGE_KERNEL)); + + if (free) + { + kfree(pages); + } + +#endif + + return addr; +} + +void +_DestoryKernelVirtualMapping( + IN gctSTRING Addr + ) +{ +#if !gcdNONPAGED_MEMORY_CACHEABLE + vunmap(Addr); +#endif +} + +void +_UnmapUserLogical( + IN gctPOINTER Logical, + IN gctUINT32 Size +) +{ + if (unlikely(current->mm == gcvNULL)) + { + /* Do nothing if process is exiting. */ + return; + } + + if (vm_munmap((unsigned long)Logical, Size) < 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): vm_munmap failed", + __FUNCTION__, __LINE__ + ); + } +} + +/***************************************************************************\ +************************ Default Allocator ********************************** +\***************************************************************************/ +#define C_MAX_PAGENUM (50*1024) +static gceSTATUS +_DefaultAlloc( + IN gckALLOCATOR Allocator, + INOUT PLINUX_MDL Mdl, + IN gctSIZE_T NumPages, + IN gctUINT32 Flags + ) +{ + gceSTATUS status; + gctUINT32 order; + gctSIZE_T bytes; + gctPOINTER addr = gcvNULL; + gctUINT32 numPages; + gctUINT i = 0; + gctBOOL contiguous = Flags & gcvALLOC_FLAG_CONTIGUOUS; + struct sysinfo temsysinfo; + gcsDEFAULT_PRIV_PTR priv = (gcsDEFAULT_PRIV_PTR)Allocator->privateData; + + gcmkHEADER_ARG("Mdl=%p NumPages=%d", Mdl, NumPages); + + numPages = NumPages; + bytes = NumPages * PAGE_SIZE; + order = get_order(bytes); + + si_meminfo(&temsysinfo); + + if (Flags & gcvALLOC_FLAG_MEMLIMIT) + { + if ( (temsysinfo.freeram < NumPages) || ((temsysinfo.freeram-NumPages) < C_MAX_PAGENUM) ) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + } + + if (contiguous) + { + if (order >= MAX_ORDER) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + addr = + alloc_pages_exact(bytes, GFP_KERNEL | gcdNOWARN | __GFP_NORETRY); + + Mdl->u.contiguousPages = addr + ? virt_to_page(addr) + : gcvNULL; + + Mdl->exact = gcvTRUE; + + if (Mdl->u.contiguousPages == gcvNULL) + { + Mdl->u.contiguousPages = + alloc_pages(GFP_KERNEL | __GFP_HIGHMEM | gcdNOWARN, order); + + Mdl->exact = gcvFALSE; + } + } + else + { + Mdl->u.nonContiguousPages = _NonContiguousAlloc(numPages); + } + + if (Mdl->u.contiguousPages == gcvNULL && Mdl->u.nonContiguousPages == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + for (i = 0; i < numPages; i++) + { + struct page *page; + + if (contiguous) + { + page = nth_page(Mdl->u.contiguousPages, i); + } + else + { + page = _NonContiguousToPage(Mdl->u.nonContiguousPages, i); + } + + SetPageReserved(page); + + if (!PageHighMem(page) && page_to_phys(page)) + { + gcmkVERIFY_OK( + gckOS_CacheFlush(Allocator->os, _GetProcessID(), gcvNULL, + page_to_phys(page), + page_address(page), + PAGE_SIZE)); + + priv->low += PAGE_SIZE; + } + else + { + flush_dcache_page(page); + +#if !gcdCACHE_FUNCTION_UNIMPLEMENTED && defined(CONFIG_OUTER_CACHE) && gcdENABLE_OUTER_CACHE_PATCH + if (page_to_phys(page)) + { + _HandleOuterCache( + Allocator->os, + page_to_phys(page), + gcvNULL, + PAGE_SIZE, + gcvCACHE_FLUSH + ); + } +#endif + + priv->high += PAGE_SIZE; + } + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +static void +_DefaultFree( + IN gckALLOCATOR Allocator, + IN OUT PLINUX_MDL Mdl + ) +{ + gctINT i; + struct page * page; + gcsDEFAULT_PRIV_PTR priv = (gcsDEFAULT_PRIV_PTR)Allocator->privateData; + + for (i = 0; i < Mdl->numPages; i++) + { + if (Mdl->contiguous) + { + page = nth_page(Mdl->u.contiguousPages, i); + } + else + { + page = _NonContiguousToPage(Mdl->u.nonContiguousPages, i); + } + + ClearPageReserved(page); + + if (PageHighMem(page)) + { + priv->high -= PAGE_SIZE; + } + else + { + priv->low -= PAGE_SIZE; + } + } + + if (Mdl->contiguous) + { + if (Mdl->exact == gcvTRUE) + { + free_pages_exact(page_address(Mdl->u.contiguousPages), Mdl->numPages * PAGE_SIZE); + } + else + { + __free_pages(Mdl->u.contiguousPages, get_order(Mdl->numPages * PAGE_SIZE)); + } + } + else + { + _NonContiguousFree(Mdl->u.nonContiguousPages, Mdl->numPages); + } +} + +gctINT +_DefaultMapUser( + gckALLOCATOR Allocator, + PLINUX_MDL Mdl, + PLINUX_MDL_MAP MdlMap, + gctBOOL Cacheable + ) +{ + + gctSTRING addr; + unsigned long start; + unsigned long pfn; + gctINT i; + gckOS os = Allocator->os; + gcsPLATFORM * platform = os->device->platform; + + PLINUX_MDL mdl = Mdl; + PLINUX_MDL_MAP mdlMap = MdlMap; + + gcmkHEADER_ARG("Allocator=%p Mdl=%p MdlMap=%p gctBOOL=%d", Allocator, Mdl, MdlMap, Cacheable); + + mdlMap->vmaAddr = (gctSTRING)vm_mmap(gcvNULL, + 0L, + mdl->numPages * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + 0); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): vmaAddr->0x%X for phys_addr->0x%X", + __FUNCTION__, __LINE__, + (gctUINT32)(gctUINTPTR_T)mdlMap->vmaAddr, + (gctUINT32)(gctUINTPTR_T)mdl + ); + + if (IS_ERR(mdlMap->vmaAddr)) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): do_mmap_pgoff error", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + down_write(¤t->mm->mmap_sem); + + mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr); + + if (mdlMap->vma == gcvNULL) + { + up_write(¤t->mm->mmap_sem); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): find_vma error", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } + + mdlMap->vma->vm_flags |= gcdVM_FLAGS; + + if (Cacheable == gcvFALSE) + { + /* Make this mapping non-cached. */ + mdlMap->vma->vm_page_prot = gcmkPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot); + } + + if (platform && platform->ops->adjustProt) + { + platform->ops->adjustProt(mdlMap->vma); + } + + addr = mdl->addr; + + /* Now map all the vmalloc pages to this user address. */ + if (mdl->contiguous) + { + /* map kernel memory to user space.. */ + if (remap_pfn_range(mdlMap->vma, + mdlMap->vma->vm_start, + page_to_pfn(mdl->u.contiguousPages), + mdlMap->vma->vm_end - mdlMap->vma->vm_start, + mdlMap->vma->vm_page_prot) < 0) + { + up_write(¤t->mm->mmap_sem); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): unable to mmap ret", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + } + else + { + start = mdlMap->vma->vm_start; + + for (i = 0; i < mdl->numPages; i++) + { + pfn = _NonContiguousToPfn(mdl->u.nonContiguousPages, i); + + if (remap_pfn_range(mdlMap->vma, + start, + pfn, + PAGE_SIZE, + mdlMap->vma->vm_page_prot) < 0) + { + up_write(¤t->mm->mmap_sem); + + mdlMap->vmaAddr = gcvNULL; + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + start += PAGE_SIZE; + addr += PAGE_SIZE; + } + } + + up_write(¤t->mm->mmap_sem); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +void +_DefaultUnmapUser( + IN gckALLOCATOR Allocator, + IN gctPOINTER Logical, + IN gctUINT32 Size + ) +{ + _UnmapUserLogical(Logical, Size); +} + +gceSTATUS +_DefaultMapKernel( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + OUT gctPOINTER *Logical + ) +{ + *Logical = _CreateKernelVirtualMapping(Mdl); + return gcvSTATUS_OK; +} + +gceSTATUS +_DefaultUnmapKernel( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical + ) +{ + _DestoryKernelVirtualMapping(Logical); + return gcvSTATUS_OK; +} + +gceSTATUS +_DefaultLogicalToPhysical( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + OUT gctUINT32_PTR Physical + ) +{ + return _ConvertLogical2Physical( + Allocator->os, Logical, ProcessID, Mdl, Physical); +} + +gceSTATUS +_DefaultCache( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical, + IN gctUINT32 Physical, + IN gctUINT32 Bytes, + IN gceCACHEOPERATION Operation + ) +{ + return gcvSTATUS_OK; +} + +gceSTATUS +_DefaultPhysical( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctUINT32 Offset, + OUT gctUINT32_PTR Physical + ) +{ + gcmkASSERT(Mdl->pagedMem && !Mdl->contiguous); + *Physical = _NonContiguousToPhys(Mdl->u.nonContiguousPages, Offset); + + return gcvSTATUS_OK; +} + +void +_DefaultAllocatorDestructor( + IN void* PrivateData + ) +{ + kfree(PrivateData); +} + +/* Default allocator operations. */ +gcsALLOCATOR_OPERATIONS DefaultAllocatorOperations = { + .Alloc = _DefaultAlloc, + .Free = _DefaultFree, + .MapUser = _DefaultMapUser, + .UnmapUser = _DefaultUnmapUser, + .MapKernel = _DefaultMapKernel, + .UnmapKernel = _DefaultUnmapKernel, + .LogicalToPhysical = _DefaultLogicalToPhysical, + .Cache = _DefaultCache, + .Physical = _DefaultPhysical, +}; + +/* Default allocator entry. */ +gceSTATUS +_DefaultAlloctorInit( + IN gckOS Os, + OUT gckALLOCATOR * Allocator + ) +{ + gceSTATUS status; + gckALLOCATOR allocator; + gcsDEFAULT_PRIV_PTR priv = gcvNULL; + + gcmkONERROR( + gckALLOCATOR_Construct(Os, &DefaultAllocatorOperations, &allocator)); + + priv = kzalloc(gcmSIZEOF(gcsDEFAULT_PRIV), GFP_KERNEL | gcdNOWARN); + + if (!priv) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Register private data. */ + allocator->privateData = priv; + allocator->privateDataDestructor = _DefaultAllocatorDestructor; + + allocator->debugfsInit = _DefaultAllocatorDebugfsInit; + allocator->debugfsCleanup = _DefaultAllocatorDebugfsCleanup; + + *Allocator = allocator; + + return gcvSTATUS_OK; + +OnError: + return status; +} + +/***************************************************************************\ +************************ Allocator helper *********************************** +\***************************************************************************/ + +gceSTATUS +gckALLOCATOR_Construct( + IN gckOS Os, + IN gcsALLOCATOR_OPERATIONS * Operations, + OUT gckALLOCATOR * Allocator + ) +{ + gceSTATUS status; + gckALLOCATOR allocator; + + gcmkHEADER_ARG("Os=%p, Operations=%p, Allocator=%p", + Os, Operations, Allocator); + + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Allocator != gcvNULL); + gcmkVERIFY_ARGUMENT + ( Operations + && Operations->Alloc + && Operations->Free + && Operations->MapUser + && Operations->UnmapUser + && Operations->MapKernel + && Operations->UnmapKernel + && Operations->LogicalToPhysical + && Operations->Cache + && Operations->Physical + ); + + gcmkONERROR( + gckOS_Allocate(Os, gcmSIZEOF(gcsALLOCATOR), (gctPOINTER *)&allocator)); + + gckOS_ZeroMemory(allocator, gcmSIZEOF(gcsALLOCATOR)); + + /* Record os. */ + allocator->os = Os; + + /* Set operations. */ + allocator->ops = Operations; + + allocator->capability = gcvALLOC_FLAG_CONTIGUOUS + | gcvALLOC_FLAG_NON_CONTIGUOUS + | gcvALLOC_FLAG_CACHEABLE + | gcvALLOC_FLAG_MEMLIMIT; + ; + + *Allocator = allocator; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************\ +******************************** Debugfs Support ******************************* +\******************************************************************************/ + +static gceSTATUS +_AllocatorDebugfsInit( + IN gckOS Os + ) +{ + gceSTATUS status; + gckGALDEVICE device = Os->device; + + gckDEBUGFS_DIR dir = &Os->allocatorDebugfsDir; + + gcmkONERROR(gckDEBUGFS_DIR_Init(dir, device->debugfsDir.root, "allocators")); + + return gcvSTATUS_OK; + +OnError: + return status; +} + +static void +_AllocatorDebugfsCleanup( + IN gckOS Os + ) +{ + gckDEBUGFS_DIR dir = &Os->allocatorDebugfsDir; + + gckDEBUGFS_DIR_Deinit(dir); +} + +/***************************************************************************\ +************************ Allocator management ******************************* +\***************************************************************************/ + +gceSTATUS +gckOS_ImportAllocators( + gckOS Os + ) +{ + gceSTATUS status; + gctUINT i; + gckALLOCATOR allocator; + + _AllocatorDebugfsInit(Os); + + INIT_LIST_HEAD(&Os->allocatorList); + + for (i = 0; i < gcmCOUNTOF(allocatorArray); i++) + { + if (allocatorArray[i].construct) + { + /* Construct allocator. */ + status = allocatorArray[i].construct(Os, &allocator); + + if (gcmIS_ERROR(status)) + { + gcmkPRINT("["DEVICE_NAME"]: Can't construct allocator(%s)", + allocatorArray[i].name); + + continue; + } + + allocator->name = allocatorArray[i].name; + + if (allocator->debugfsInit) + { + /* Init allocator's debugfs. */ + allocator->debugfsInit(allocator, &Os->allocatorDebugfsDir); + } + + list_add_tail(&allocator->head, &Os->allocatorList); + } + } + +#if gcdDEBUG + list_for_each_entry(allocator, &Os->allocatorList, head) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d) Allocator: %s", + __FUNCTION__, __LINE__, + allocator->name + ); + } +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_FreeAllocators( + gckOS Os + ) +{ + gckALLOCATOR allocator; + gckALLOCATOR temp; + + list_for_each_entry_safe(allocator, temp, &Os->allocatorList, head) + { + list_del(&allocator->head); + + if (allocator->debugfsCleanup) + { + /* Clean up allocator's debugfs. */ + allocator->debugfsCleanup(allocator); + } + + /* Free private data. */ + if (allocator->privateDataDestructor && allocator->privateData) + { + allocator->privateDataDestructor(allocator->privateData); + } + + gckOS_Free(Os, allocator); + } + + _AllocatorDebugfsCleanup(Os); + + return gcvSTATUS_OK; +} + diff --git a/drivers/gpu/galcore/gc_hal_kernel_allocator.h b/drivers/gpu/galcore/gc_hal_kernel_allocator.h new file mode 100644 index 00000000000000..f36e1b0de86990 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_allocator.h @@ -0,0 +1,400 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_allocator_h_ +#define __gc_hal_kernel_allocator_h_ + +#include "gc_hal_kernel_linux.h" + +typedef struct _gcsALLOCATOR * gckALLOCATOR; + +typedef struct _gcsALLOCATOR_OPERATIONS +{ + /************************************************************************** + ** + ** Alloc + ** + ** Allocte memory, request size is page aligned. + ** + ** INPUT: + ** + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_Mdl + ** Pointer to Mdl whichs stores information + ** about allocated memory. + ** + ** gctSIZE_T NumPages + ** Number of pages need to allocate. + ** + ** gctUINT32 Flag + ** Allocation option. + ** + ** OUTPUT: + ** + ** Nothing. + ** + */ + gceSTATUS + (*Alloc)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctSIZE_T NumPages, + IN gctUINT32 Flag + ); + + /************************************************************************** + ** + ** Free + ** + ** Free memory. + ** + ** INPUT: + ** + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Mdl which stores information. + ** + ** OUTPUT: + ** + ** Nothing. + ** + */ + void + (*Free)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl + ); + + /************************************************************************** + ** + ** MapUser + ** + ** Map memory to user space. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Pointer to a Mdl. + ** + ** PLINUX_MDL_MAP MdlMap + ** Pointer to a MdlMap, mapped address is stored + ** in MdlMap->vmaAddr + ** + ** gctBOOL Cacheable + ** Whether this mapping is cacheable. + ** + ** OUTPUT: + ** + ** Nothing. + ** + */ + gctINT + (*MapUser)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN PLINUX_MDL_MAP MdlMap, + IN gctBOOL Cacheable + ); + + /************************************************************************** + ** + ** UnmapUser + ** + ** Unmap address from user address space. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** gctPOINTER Logical + ** Address to be unmap + ** + ** gctUINT32 Size + ** Size of address space + ** + ** OUTPUT: + ** + ** Nothing. + ** + */ + void + (*UnmapUser)( + IN gckALLOCATOR Allocator, + IN gctPOINTER Logical, + IN gctUINT32 Size + ); + + /************************************************************************** + ** + ** MapKernel + ** + ** Map memory to kernel space. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Pointer to a Mdl object. + ** + ** OUTPUT: + ** gctPOINTER * Logical + ** Mapped kernel address. + */ + gceSTATUS + (*MapKernel)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + OUT gctPOINTER *Logical + ); + + /************************************************************************** + ** + ** UnmapKernel + ** + ** Unmap memory from kernel space. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Pointer to a Mdl object. + ** + ** gctPOINTER Logical + ** Mapped kernel address. + ** + ** OUTPUT: + ** + ** Nothing. + ** + */ + gceSTATUS + (*UnmapKernel)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical + ); + + /************************************************************************** + ** + ** LogicalToPhysical + ** + ** Get physical address from logical address, logical + ** address could be user virtual address or kernel + ** virtual address. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Pointer to a Mdl object. + ** + ** gctPOINTER Logical + ** Mapped kernel address. + ** + ** gctUINT32 ProcessID + ** pid of current process. + ** OUTPUT: + ** + ** gctUINT32_PTR Physical + ** Physical address. + ** + */ + gceSTATUS + (*LogicalToPhysical)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + OUT gctUINT32_PTR Physical + ); + + /************************************************************************** + ** + ** Cache + ** + ** Maintain cache coherency. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Pointer to a Mdl object. + ** + ** gctPOINTER Logical + ** Logical address, could be user address or kernel address + ** + ** gctUINT32_PTR Physical + ** Physical address. + ** + ** gctUINT32 Bytes + ** Size of memory region. + ** + ** gceCACHEOPERATION Opertaion + ** Cache operation. + ** + ** OUTPUT: + ** + ** Nothing. + ** + */ + gceSTATUS (*Cache)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctPOINTER Logical, + IN gctUINT32 Physical, + IN gctUINT32 Bytes, + IN gceCACHEOPERATION Operation + ); + + /************************************************************************** + ** + ** Physical + ** + ** Get physical address from a offset in memory region. + ** + ** INPUT: + ** gckALLOCATOR Allocator + ** Pointer to an gckALLOCATOER object. + ** + ** PLINUX_MDL Mdl + ** Pointer to a Mdl object. + ** + ** gctUINT32 Offset + ** Offset in this memory region. + ** + ** OUTPUT: + ** gctUINT32_PTR Physical + ** Physical address. + ** + */ + gceSTATUS (*Physical)( + IN gckALLOCATOR Allocator, + IN PLINUX_MDL Mdl, + IN gctUINT32 Offset, + OUT gctUINT32_PTR Physical + ); +} +gcsALLOCATOR_OPERATIONS; + +typedef struct _gcsALLOCATOR +{ + /* Pointer to gckOS Object. */ + gckOS os; + + /* Name. */ + gctSTRING name; + + /* Operations. */ + gcsALLOCATOR_OPERATIONS* ops; + + /* Capability of this allocator. */ + gctUINT32 capability; + + struct list_head head; + + /* Debugfs entry of this allocator. */ + gcsDEBUGFS_DIR debugfsDir; + + /* Init allocator debugfs. */ + void (*debugfsInit)(gckALLOCATOR, gckDEBUGFS_DIR); + + /* Cleanup allocator debugfs. */ + void (*debugfsCleanup)(gckALLOCATOR); + + /* Private data used by customer allocator. */ + void * privateData; + + /* Private data destructor. */ + void (*privateDataDestructor)(void *); +} +gcsALLOCATOR; + +typedef struct _gcsALLOCATOR_DESC +{ + /* Name of a allocator. */ + char * name; + + /* Entry function to construct a allocator. */ + gceSTATUS (*construct)(gckOS, gckALLOCATOR *); +} +gcsALLOCATOR_DESC; + +/* +* Helpers +*/ + +/* Fill a gcsALLOCATOR_DESC structure. */ +#define gcmkDEFINE_ALLOCATOR_DESC(Name, Construct) \ + { \ + .name = Name, \ + .construct = Construct, \ + } + +/* Construct a allocator. */ +gceSTATUS +gckALLOCATOR_Construct( + IN gckOS Os, + IN gcsALLOCATOR_OPERATIONS * Operations, + OUT gckALLOCATOR * Allocator + ); + +/* + How to implement customer allocator + + Build in customer alloctor + + It is recommanded that customer allocator is implmented in independent + source file(s) which is specified by CUSOMTER_ALLOCATOR_OBJS in Kbuld. + + Register gcsALLOCATOR + + For each customer specified allocator, a desciption entry must be added + to allocatorArray defined in gc_hal_kernel_allocator_array.h. + + An entry in allocatorArray is a gcsALLOCATOR_DESC structure which describes + name and constructor of a gckALLOCATOR object. + + + Implement gcsALLOCATOR_DESC.init() + + In gcsALLOCATOR_DESC.init(), gckALLOCATOR_Construct should be called + to create a gckALLOCATOR object, customer specified private data can + be put in gcsALLOCATOR.privateData. + + + Implement gcsALLOCATOR_OPERATIONS + + When call gckALLOCATOR_Construct to create a gckALLOCATOR object, a + gcsALLOCATOR_OPERATIONS structure must be provided whose all members + implemented. + +*/ +#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_command.c b/drivers/gpu/galcore/gc_hal_kernel_command.c new file mode 100644 index 00000000000000..72aa966d6dad0f --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_command.c @@ -0,0 +1,3423 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" +#include "gc_hal_kernel_context.h" + +#define _GC_OBJ_ZONE gcvZONE_COMMAND + +/******************************************************************************\ +********************************* Support Code ********************************* +\******************************************************************************/ + +/******************************************************************************* +** +** _NewQueue +** +** Allocate a new command queue. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object. +** +** OUTPUT: +** +** gckCOMMAND Command +** gckCOMMAND object has been updated with a new command queue. +*/ +static gceSTATUS +_NewQueue( + IN OUT gckCOMMAND Command + ) +{ + gceSTATUS status; + gctINT currentIndex, newIndex; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Switch to the next command buffer. */ + currentIndex = Command->index; + newIndex = (currentIndex + 1) % gcdCOMMAND_QUEUES; + + /* Wait for availability. */ +#if gcdDUMP_COMMAND + gcmkPRINT("@[kernel.waitsignal]"); +#endif + + gcmkONERROR(gckOS_WaitSignal( + Command->os, + Command->queues[newIndex].signal, + gcvINFINITE + )); + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + if (newIndex < currentIndex) + { + Command->wrapCount += 1; + + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_COMMAND, + 2 * 4, + "%s(%d): queue array wrapped around.\n", + __FUNCTION__, __LINE__ + ); + } + + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_COMMAND, + 3 * 4, + "%s(%d): total queue wrap arounds %d.\n", + __FUNCTION__, __LINE__, Command->wrapCount + ); + + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_COMMAND, + 3 * 4, + "%s(%d): switched to queue %d.\n", + __FUNCTION__, __LINE__, newIndex + ); +#endif + + /* Update gckCOMMAND object with new command queue. */ + Command->index = newIndex; + Command->newQueue = gcvTRUE; + Command->logical = Command->queues[newIndex].logical; + Command->address = Command->queues[newIndex].address; + Command->offset = 0; + + gcmkONERROR(gckOS_GetPhysicalAddress( + Command->os, + Command->logical, + (gctUINT32 *) &Command->physical + )); + + if (currentIndex != -1) + { + /* Mark the command queue as available. */ + gcmkONERROR(gckEVENT_Signal( + Command->kernel->eventObj, + Command->queues[currentIndex].signal, + gcvKERNEL_COMMAND + )); + } + + /* Success. */ + gcmkFOOTER_ARG("Command->index=%d", Command->index); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +static gceSTATUS +_IncrementCommitAtom( + IN gckCOMMAND Command, + IN gctBOOL Increment + ) +{ + gceSTATUS status; + gckHARDWARE hardware; + gctINT32 atomValue; + gctBOOL powerAcquired = gcvFALSE; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Extract the gckHARDWARE and gckEVENT objects. */ + hardware = Command->kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + /* Grab the power mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + Command->os, hardware->powerMutex, gcvINFINITE + )); + powerAcquired = gcvTRUE; + + /* Increment the commit atom. */ + if (Increment) + { + gcmkONERROR(gckOS_AtomIncrement( + Command->os, Command->atomCommit, &atomValue + )); + } + else + { + gcmkONERROR(gckOS_AtomDecrement( + Command->os, Command->atomCommit, &atomValue + )); + } + + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex( + Command->os, hardware->powerMutex + )); + powerAcquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + if (powerAcquired) + { + /* Release the power mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex( + Command->os, hardware->powerMutex + )); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdSECURE_USER +static gceSTATUS +_ProcessHints( + IN gckCOMMAND Command, + IN gctUINT32 ProcessID, + IN gcoCMDBUF CommandBuffer + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gckKERNEL kernel; + gctBOOL needCopy = gcvFALSE; + gcskSECURE_CACHE_PTR cache; + gctUINT8_PTR commandBufferLogical; + gctUINT8_PTR hintedData; + gctUINT32_PTR hintArray; + gctUINT i, hintCount; + + gcmkHEADER_ARG( + "Command=0x%08X ProcessID=%d CommandBuffer=0x%08X", + Command, ProcessID, CommandBuffer + ); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Reset state array pointer. */ + hintArray = gcvNULL; + + /* Get the kernel object. */ + kernel = Command->kernel; + + /* Get the cache form the database. */ + gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache)); + + /* Determine the start of the command buffer. */ + commandBufferLogical + = (gctUINT8_PTR) CommandBuffer->logical + + CommandBuffer->startOffset; + + /* Determine the number of records in the state array. */ + hintCount = CommandBuffer->hintArrayTail - CommandBuffer->hintArray; + + /* Check wehther we need to copy the structures or not. */ + gcmkONERROR(gckOS_QueryNeedCopy(Command->os, ProcessID, &needCopy)); + + /* Get access to the state array. */ + if (needCopy) + { + gctUINT copySize; + + if (Command->hintArrayAllocated && + (Command->hintArraySize < CommandBuffer->hintArraySize)) + { + gcmkONERROR(gcmkOS_SAFE_FREE(Command->os, gcmUINT64_TO_PTR(Command->hintArray))); + Command->hintArraySize = gcvFALSE; + } + + if (!Command->hintArrayAllocated) + { + gctPOINTER pointer = gcvNULL; + + gcmkONERROR(gckOS_Allocate( + Command->os, + CommandBuffer->hintArraySize, + &pointer + )); + + Command->hintArray = gcmPTR_TO_UINT64(pointer); + Command->hintArrayAllocated = gcvTRUE; + Command->hintArraySize = CommandBuffer->hintArraySize; + } + + hintArray = gcmUINT64_TO_PTR(Command->hintArray); + copySize = hintCount * gcmSIZEOF(gctUINT32); + + gcmkONERROR(gckOS_CopyFromUserData( + Command->os, + hintArray, + gcmUINT64_TO_PTR(CommandBuffer->hintArray), + copySize + )); + } + else + { + gctPOINTER pointer = gcvNULL; + + gcmkONERROR(gckOS_MapUserPointer( + Command->os, + gcmUINT64_TO_PTR(CommandBuffer->hintArray), + CommandBuffer->hintArraySize, + &pointer + )); + + hintArray = pointer; + } + + /* Scan through the buffer. */ + for (i = 0; i < hintCount; i += 1) + { + /* Determine the location of the hinted data. */ + hintedData = commandBufferLogical + hintArray[i]; + + /* Map handle into physical address. */ + gcmkONERROR(gckKERNEL_MapLogicalToPhysical( + kernel, cache, (gctPOINTER) hintedData + )); + } + +OnError: + /* Get access to the state array. */ + if (!needCopy && (hintArray != gcvNULL)) + { + gcmkVERIFY_OK(gckOS_UnmapUserPointer( + Command->os, + gcmUINT64_TO_PTR(CommandBuffer->hintArray), + CommandBuffer->hintArraySize, + hintArray + )); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +static gceSTATUS +_FlushMMU( + IN gckCOMMAND Command + ) +{ +#if gcdSECURITY + return gcvSTATUS_OK; +#else + gceSTATUS status; + gctUINT32 oldValue; + gckHARDWARE hardware = Command->kernel->hardware; + gctBOOL pause = gcvFALSE; + + gctUINT8_PTR pointer; + gctUINT32 eventBytes; + gctUINT32 endBytes; + gctUINT32 bufferSize; + gctUINT32 executeBytes; + gctUINT32 waitLinkBytes; + + gcmkONERROR(gckOS_AtomicExchange(Command->os, + hardware->pageTableDirty, + 0, + &oldValue)); + + if (oldValue) + { + /* Page Table is upated, flush mmu before commit. */ + gcmkONERROR(gckHARDWARE_FlushMMU(hardware)); + + if ((oldValue & gcvPAGE_TABLE_DIRTY_BIT_FE) + && (hardware->endAfterFlushMmuCache) + ) + { + pause = gcvTRUE; + } + } + + if (pause) + { + /* Query size. */ + gcmkONERROR(gckHARDWARE_Event(hardware, gcvNULL, 0, gcvKERNEL_PIXEL, &eventBytes)); + gcmkONERROR(gckHARDWARE_End(hardware, gcvNULL, &endBytes)); + + executeBytes = eventBytes + endBytes; + + gcmkONERROR(gckHARDWARE_WaitLink( + hardware, + gcvNULL, + Command->offset + executeBytes, + &waitLinkBytes, + gcvNULL, + gcvNULL + )); + + /* Reserve space. */ + gcmkONERROR(gckCOMMAND_Reserve( + Command, + executeBytes, + (gctPOINTER *)&pointer, + &bufferSize + )); + + /* Append EVENT(29). */ + gcmkONERROR(gckHARDWARE_Event( + hardware, + pointer, + 29, + gcvKERNEL_PIXEL, + &eventBytes + )); + + /* Append END. */ + pointer += eventBytes; + gcmkONERROR(gckHARDWARE_End(hardware, pointer, &endBytes)); + + /* Store address to queue. */ + gcmkONERROR(gckENTRYQUEUE_Enqueue( + Command->kernel, + &Command->queue, + Command->address + Command->offset + executeBytes, + waitLinkBytes + )); + + gcmkONERROR(gckCOMMAND_Execute(Command, executeBytes)); + } + + return gcvSTATUS_OK; +OnError: + return status; +#endif +} + +static void +_DumpBuffer( + IN gctPOINTER Buffer, + IN gctUINT32 GpuAddress, + IN gctSIZE_T Size + ) +{ + gctSIZE_T i, line, left; + gctUINT32_PTR data = Buffer; + + line = Size / 32; + left = Size % 32; + + for (i = 0; i < line; i++) + { + gcmkPRINT("%X : %08X %08X %08X %08X %08X %08X %08X %08X ", + GpuAddress, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + data += 8; + GpuAddress += 8 * 4; + } + + switch(left) + { + case 28: + gcmkPRINT("%X : %08X %08X %08X %08X %08X %08X %08X ", + GpuAddress, data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + break; + case 24: + gcmkPRINT("%X : %08X %08X %08X %08X %08X %08X ", + GpuAddress, data[0], data[1], data[2], data[3], data[4], data[5]); + break; + case 20: + gcmkPRINT("%X : %08X %08X %08X %08X %08X ", + GpuAddress, data[0], data[1], data[2], data[3], data[4]); + break; + case 16: + gcmkPRINT("%X : %08X %08X %08X %08X ", + GpuAddress, data[0], data[1], data[2], data[3]); + break; + case 12: + gcmkPRINT("%X : %08X %08X %08X ", + GpuAddress, data[0], data[1], data[2]); + break; + case 8: + gcmkPRINT("%X : %08X %08X ", + GpuAddress, data[0], data[1]); + break; + case 4: + gcmkPRINT("%X : %08X ", + GpuAddress, data[0]); + break; + default: + break; + } +} + +static void +_DumpKernelCommandBuffer( + IN gckCOMMAND Command + ) +{ + gctINT i; + gctUINT32 physical = 0; + gctPOINTER entry = gcvNULL; + + for (i = 0; i < gcdCOMMAND_QUEUES; i++) + { + entry = Command->queues[i].logical; + + gckOS_GetPhysicalAddress(Command->os, entry, &physical); + + gcmkPRINT("Kernel command buffer %d\n", i); + + _DumpBuffer(entry, physical, Command->pageSize); + } +} + +/******************************************************************************\ +****************************** gckCOMMAND API Code ****************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckCOMMAND_Construct +** +** Construct a new gckCOMMAND object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** OUTPUT: +** +** gckCOMMAND * Command +** Pointer to a variable that will hold the pointer to the gckCOMMAND +** object. +*/ +gceSTATUS +gckCOMMAND_Construct( + IN gckKERNEL Kernel, + OUT gckCOMMAND * Command + ) +{ + gckOS os; + gckCOMMAND command = gcvNULL; + gceSTATUS status; + gctINT i; + gctPOINTER pointer = gcvNULL; + gctSIZE_T pageSize; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Command != gcvNULL); + + /* Extract the gckOS object. */ + os = Kernel->os; + + /* Allocate the gckCOMMAND structure. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckCOMMAND), &pointer)); + command = pointer; + + /* Reset the entire object. */ + gcmkONERROR(gckOS_ZeroMemory(command, gcmSIZEOF(struct _gckCOMMAND))); + + /* Initialize the gckCOMMAND object.*/ + command->object.type = gcvOBJ_COMMAND; + command->kernel = Kernel; + command->os = os; + + /* Get the command buffer requirements. */ + gcmkONERROR(gckHARDWARE_QueryCommandBuffer( + Kernel->hardware, + &command->alignment, + &command->reservedHead, + &command->reservedTail + )); + + /* Create the command queue mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &command->mutexQueue)); + + /* Create the context switching mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &command->mutexContext)); + +#if VIVANTE_PROFILER_CONTEXT + /* Create the context switching mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &command->mutexContextSeq)); +#endif + + /* Create the power management semaphore. */ + gcmkONERROR(gckOS_CreateSemaphore(os, &command->powerSemaphore)); + + /* Create the commit atom. */ + gcmkONERROR(gckOS_AtomConstruct(os, &command->atomCommit)); + + /* Get the page size from teh OS. */ + gcmkONERROR(gckOS_GetPageSize(os, &pageSize)); + + gcmkSAFECASTSIZET(command->pageSize, pageSize); + + /* Get process ID. */ + gcmkONERROR(gckOS_GetProcessID(&command->kernelProcessID)); + + /* Set hardware to pipe 0. */ + command->pipeSelect = gcvPIPE_INVALID; + + /* Pre-allocate the command queues. */ + for (i = 0; i < gcdCOMMAND_QUEUES; ++i) + { + gcmkONERROR(gckOS_AllocateNonPagedMemory( + os, + gcvFALSE, + &pageSize, + &command->queues[i].physical, + &command->queues[i].logical + )); + + gcmkONERROR(gckHARDWARE_ConvertLogical( + Kernel->hardware, + command->queues[i].logical, + gcvFALSE, + &command->queues[i].address + )); + + gcmkONERROR(gckOS_CreateSignal( + os, gcvFALSE, &command->queues[i].signal + )); + + gcmkONERROR(gckOS_Signal( + os, command->queues[i].signal, gcvTRUE + )); + } + +#if gcdRECORD_COMMAND + gcmkONERROR(gckRECORDER_Construct(os, Kernel->hardware, &command->recorder)); +#endif + + /* No command queue in use yet. */ + command->index = -1; + command->logical = gcvNULL; + command->newQueue = gcvFALSE; + + /* Command is not yet running. */ + command->running = gcvFALSE; + + /* Command queue is idle. */ + command->idle = gcvTRUE; + + /* Commit stamp is zero. */ + command->commitStamp = 0; + + /* END event signal not created. */ + command->endEventSignal = gcvNULL; + + command->queue.front = 0; + command->queue.rear = 0; + command->queue.count = 0; + + /* Return pointer to the gckCOMMAND object. */ + *Command = command; + + /* Success. */ + gcmkFOOTER_ARG("*Command=0x%x", *Command); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (command != gcvNULL) + { + if (command->atomCommit != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, command->atomCommit)); + } + + if (command->powerSemaphore != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DestroySemaphore(os, command->powerSemaphore)); + } + + if (command->mutexContext != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexContext)); + } + +#if VIVANTE_PROFILER_CONTEXT + if (command->mutexContextSeq != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexContextSeq)); + } +#endif + + if (command->mutexQueue != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, command->mutexQueue)); + } + + for (i = 0; i < gcdCOMMAND_QUEUES; ++i) + { + if (command->queues[i].signal != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DestroySignal( + os, command->queues[i].signal + )); + } + + if (command->queues[i].logical != gcvNULL) + { + gcmkVERIFY_OK(gckOS_FreeNonPagedMemory( + os, + command->pageSize, + command->queues[i].physical, + command->queues[i].logical + )); + } + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, command)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Destroy +** +** Destroy an gckCOMMAND object. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_Destroy( + IN gckCOMMAND Command + ) +{ + gctINT i; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Stop the command queue. */ + gcmkVERIFY_OK(gckCOMMAND_Stop(Command, gcvFALSE)); + + for (i = 0; i < gcdCOMMAND_QUEUES; ++i) + { + gcmkASSERT(Command->queues[i].signal != gcvNULL); + gcmkVERIFY_OK(gckOS_DestroySignal( + Command->os, Command->queues[i].signal + )); + + gcmkASSERT(Command->queues[i].logical != gcvNULL); + gcmkVERIFY_OK(gckOS_FreeNonPagedMemory( + Command->os, + Command->pageSize, + Command->queues[i].physical, + Command->queues[i].logical + )); + } + + /* END event signal. */ + if (Command->endEventSignal != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DestroySignal( + Command->os, Command->endEventSignal + )); + } + + /* Delete the context switching mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexContext)); + +#if VIVANTE_PROFILER_CONTEXT + if (Command->mutexContextSeq != gcvNULL) + gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexContextSeq)); +#endif + + /* Delete the command queue mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Command->os, Command->mutexQueue)); + + /* Destroy the power management semaphore. */ + gcmkVERIFY_OK(gckOS_DestroySemaphore(Command->os, Command->powerSemaphore)); + + /* Destroy the commit atom. */ + gcmkVERIFY_OK(gckOS_AtomDestroy(Command->os, Command->atomCommit)); + +#if gcdSECURE_USER + /* Free state array. */ + if (Command->hintArrayAllocated) + { + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, gcmUINT64_TO_PTR(Command->hintArray))); + Command->hintArrayAllocated = gcvFALSE; + } +#endif + +#if gcdRECORD_COMMAND + gckRECORDER_Destory(Command->os, Command->recorder); +#endif + + /* Mark object as unknown. */ + Command->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckCOMMAND object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, Command)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckCOMMAND_EnterCommit +** +** Acquire command queue synchronization objects. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object to destroy. +** +** gctBOOL FromPower +** Determines whether the call originates from inside the power +** management or not. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_EnterCommit( + IN gckCOMMAND Command, + IN gctBOOL FromPower + ) +{ + gceSTATUS status; + gckHARDWARE hardware; + gctBOOL atomIncremented = gcvFALSE; + gctBOOL semaAcquired = gcvFALSE; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Extract the gckHARDWARE and gckEVENT objects. */ + hardware = Command->kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + if (!FromPower) + { + /* Increment COMMIT atom to let power management know that a commit is + ** in progress. */ + gcmkONERROR(_IncrementCommitAtom(Command, gcvTRUE)); + atomIncremented = gcvTRUE; + + /* Notify the system the GPU has a commit. */ + gcmkONERROR(gckOS_Broadcast(Command->os, + hardware, + gcvBROADCAST_GPU_COMMIT)); + + /* Acquire the power management semaphore. */ + gcmkONERROR(gckOS_AcquireSemaphore(Command->os, + Command->powerSemaphore)); + semaAcquired = gcvTRUE; + } + + /* Grab the conmmand queue mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Command->os, + Command->mutexQueue, + gcvINFINITE)); + + /* Success. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + if (semaAcquired) + { + /* Release the power management semaphore. */ + gcmkVERIFY_OK(gckOS_ReleaseSemaphore( + Command->os, Command->powerSemaphore + )); + } + + if (atomIncremented) + { + /* Decrement the commit atom. */ + gcmkVERIFY_OK(_IncrementCommitAtom( + Command, gcvFALSE + )); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_ExitCommit +** +** Release command queue synchronization objects. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object to destroy. +** +** gctBOOL FromPower +** Determines whether the call originates from inside the power +** management or not. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_ExitCommit( + IN gckCOMMAND Command, + IN gctBOOL FromPower + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexQueue)); + + if (!FromPower) + { + /* Release the power management semaphore. */ + gcmkONERROR(gckOS_ReleaseSemaphore(Command->os, + Command->powerSemaphore)); + + /* Decrement the commit atom. */ + gcmkONERROR(_IncrementCommitAtom(Command, gcvFALSE)); + } + + /* Success. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Start +** +** Start up the command queue. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object to start. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_Start( + IN gckCOMMAND Command + ) +{ + gceSTATUS status; + gckHARDWARE hardware; + gctUINT32 waitOffset = 0; + gctUINT32 waitLinkBytes; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->running) + { + /* Command queue already running. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Extract the gckHARDWARE object. */ + hardware = Command->kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + if (Command->logical == gcvNULL) + { + /* Start at beginning of a new queue. */ + gcmkONERROR(_NewQueue(Command)); + } + + /* Start at beginning of page. */ + Command->offset = 0; + + /* Set abvailable number of bytes for WAIT/LINK command sequence. */ + waitLinkBytes = Command->pageSize; + + /* Append WAIT/LINK. */ + gcmkONERROR(gckHARDWARE_WaitLink( + hardware, + Command->logical, + 0, + &waitLinkBytes, + &waitOffset, + &Command->waitSize + )); + + Command->waitLogical = (gctUINT8_PTR) Command->logical + waitOffset; + Command->waitPhysical = (gctUINT8_PTR) Command->physical + waitOffset; + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache for the wait/link. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)Command->physical, + Command->logical, + waitLinkBytes + )); +#endif + + /* Adjust offset. */ + Command->offset = waitLinkBytes; + Command->newQueue = gcvFALSE; + +#if gcdSECURITY + /* Start FE by calling security service. */ + gckKERNEL_SecurityStartCommand( + Command->kernel + ); +#else + /* Enable command processor. */ + gcmkONERROR(gckHARDWARE_Execute( + hardware, + Command->address, + waitLinkBytes + )); +#endif + + /* Command queue is running. */ + Command->running = gcvTRUE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Stop +** +** Stop the command queue. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object to stop. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_Stop( + IN gckCOMMAND Command, + IN gctBOOL FromRecovery + ) +{ + gckHARDWARE hardware; + gceSTATUS status; + gctUINT32 idle; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (!Command->running) + { + /* Command queue is not running. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Extract the gckHARDWARE object. */ + hardware = Command->kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + if (gckHARDWARE_IsFeatureAvailable(hardware, + gcvFEATURE_END_EVENT) == gcvSTATUS_TRUE) + { + /* Allocate the signal. */ + if (Command->endEventSignal == gcvNULL) + { + gcmkONERROR(gckOS_CreateSignal(Command->os, + gcvTRUE, + &Command->endEventSignal)); + } + + /* Append the END EVENT command to trigger the signal. */ + gcmkONERROR(gckEVENT_Stop(Command->kernel->eventObj, + Command->kernelProcessID, + Command->waitPhysical, + Command->waitLogical, + Command->endEventSignal, + &Command->waitSize)); + } + else + { + /* Replace last WAIT with END. */ + gcmkONERROR(gckHARDWARE_End( + hardware, Command->waitLogical, &Command->waitSize + )); + +#if gcdSECURITY + gcmkONERROR(gckKERNEL_SecurityExecute( + Command->kernel, Command->waitLogical, 8 + )); +#endif + + /* Update queue tail pointer. */ + gcmkONERROR(gckHARDWARE_UpdateQueueTail(Command->kernel->hardware, + Command->logical, + Command->offset)); + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache for the END. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)Command->waitPhysical, + Command->waitLogical, + Command->waitSize + )); +#endif + + /* Wait for idle. */ + gcmkONERROR(gckHARDWARE_GetIdle(hardware, !FromRecovery, &idle)); + } + + /* Command queue is no longer running. */ + Command->running = gcvFALSE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Commit +** +** Commit a command buffer to the command queue. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to a gckCOMMAND object. +** +** gckCONTEXT Context +** Pointer to a gckCONTEXT object. +** +** gcoCMDBUF CommandBuffer +** Pointer to a gcoCMDBUF object. +** +** gcsSTATE_DELTA_PTR StateDelta +** Pointer to the state delta. +** +** gctUINT32 ProcessID +** Current process ID. +** +** OUTPUT: +** +** Nothing. +*/ +#if gcdMULTI_GPU +gceSTATUS +gckCOMMAND_Commit( + IN gckCOMMAND Command, + IN gckCONTEXT Context, + IN gcoCMDBUF CommandBuffer, + IN gcsSTATE_DELTA_PTR StateDelta, + IN gcsQUEUE_PTR EventQueue, + IN gctUINT32 ProcessID, + IN gceCORE_3D_MASK ChipEnable + ) +#else +gceSTATUS +gckCOMMAND_Commit( + IN gckCOMMAND Command, + IN gckCONTEXT Context, + IN gcoCMDBUF CommandBuffer, + IN gcsSTATE_DELTA_PTR StateDelta, + IN gcsQUEUE_PTR EventQueue, + IN gctUINT32 ProcessID + ) +#endif +{ + gceSTATUS status; + gctBOOL commitEntered = gcvFALSE; + gctBOOL contextAcquired = gcvFALSE; + gckHARDWARE hardware; + gctBOOL needCopy = gcvFALSE; + gcsQUEUE_PTR eventRecord = gcvNULL; + gcsQUEUE _eventRecord; + gcsQUEUE_PTR nextEventRecord; + gctBOOL commandBufferMapped = gcvFALSE; + gcoCMDBUF commandBufferObject = gcvNULL; + +#if !gcdNULL_DRIVER + gcsCONTEXT_PTR contextBuffer; + struct _gcoCMDBUF _commandBufferObject; + gctPHYS_ADDR commandBufferPhysical; + gctUINT8_PTR commandBufferLogical = gcvNULL; + gctUINT32 commandBufferAddress = 0; + gctUINT8_PTR commandBufferLink = gcvNULL; + gctUINT commandBufferSize; + gctSIZE_T nopBytes; + gctUINT32 pipeBytes; + gctUINT32 linkBytes; + gctSIZE_T bytes; + gctUINT32 offset; +#if gcdNONPAGED_MEMORY_CACHEABLE + gctPHYS_ADDR entryPhysical; +#endif + gctPOINTER entryLogical; + gctUINT32 entryAddress; + gctUINT32 entryBytes; +#if gcdNONPAGED_MEMORY_CACHEABLE + gctPHYS_ADDR exitPhysical; +#endif + gctPOINTER exitLogical; + gctUINT32 exitAddress; + gctUINT32 exitBytes; + gctPHYS_ADDR waitLinkPhysical; + gctPOINTER waitLinkLogical; + gctUINT32 waitLinkAddress; + gctUINT32 waitLinkBytes; + gctPHYS_ADDR waitPhysical; + gctPOINTER waitLogical; + gctUINT32 waitOffset; + gctUINT32 waitSize; + +#ifdef __QNXNTO__ + gctPOINTER userCommandBufferLogical = gcvNULL; + gctBOOL userCommandBufferLogicalMapped = gcvFALSE; + gctPOINTER userCommandBufferLink = gcvNULL; + gctBOOL userCommandBufferLinkMapped = gcvFALSE; +#endif + +#if gcdPROCESS_ADDRESS_SPACE + gctSIZE_T mmuConfigureBytes; + gctPOINTER mmuConfigureLogical = gcvNULL; + gctUINT32 mmuConfigureAddress; + gctPOINTER mmuConfigurePhysical = 0; + gctSIZE_T mmuConfigureWaitLinkOffset; + gckMMU mmu; + gctSIZE_T reservedBytes; + gctUINT32 oldValue; +#endif + +#if gcdDUMP_COMMAND + gctPOINTER contextDumpLogical = gcvNULL; + gctSIZE_T contextDumpBytes = 0; + gctPOINTER bufferDumpLogical = gcvNULL; + gctSIZE_T bufferDumpBytes = 0; +# endif +#endif + +#if VIVANTE_PROFILER_CONTEXT + gctBOOL sequenceAcquired = gcvFALSE; +#endif + + gctPOINTER pointer = gcvNULL; + +#if gcdMULTI_GPU + gctSIZE_T chipEnableBytes; +#endif + + gcmkHEADER_ARG( + "Command=0x%x CommandBuffer=0x%x ProcessID=%d", + Command, CommandBuffer, ProcessID + ); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + if (Command->kernel->hardware->type== gcvHARDWARE_2D) + { + /* There is no context for 2D. */ + Context = gcvNULL; + } + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckKERNEL_GetProcessMMU(Command->kernel, &mmu)); + + gcmkONERROR(gckOS_AtomicExchange(Command->os, + mmu->pageTableDirty[Command->kernel->core], + 0, + &oldValue)); +#else +#endif + +#if VIVANTE_PROFILER_CONTEXT + if((Command->kernel->hardware->gpuProfiler) && (Command->kernel->profileEnable)) + { + /* Acquire the context sequnence mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + Command->os, Command->mutexContextSeq, gcvINFINITE + )); + sequenceAcquired = gcvTRUE; + } +#endif + + /* Acquire the command queue. */ + gcmkONERROR(gckCOMMAND_EnterCommit(Command, gcvFALSE)); + commitEntered = gcvTRUE; + + /* Acquire the context switching mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + Command->os, Command->mutexContext, gcvINFINITE + )); + contextAcquired = gcvTRUE; + + /* Extract the gckHARDWARE and gckEVENT objects. */ + hardware = Command->kernel->hardware; + + /* Check wehther we need to copy the structures or not. */ + gcmkONERROR(gckOS_QueryNeedCopy(Command->os, ProcessID, &needCopy)); + +#if gcdNULL_DRIVER + /* Context switch required? */ + if ((Context != gcvNULL) && (Command->currContext != Context)) + { + /* Yes, merge in the deltas. */ + gckCONTEXT_Update(Context, ProcessID, StateDelta); + + /* Update the current context. */ + Command->currContext = Context; + } +#else + if (needCopy) + { + commandBufferObject = &_commandBufferObject; + + gcmkONERROR(gckOS_CopyFromUserData( + Command->os, + commandBufferObject, + CommandBuffer, + gcmSIZEOF(struct _gcoCMDBUF) + )); + + gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER); + } + else + { + gcmkONERROR(gckOS_MapUserPointer( + Command->os, + CommandBuffer, + gcmSIZEOF(struct _gcoCMDBUF), + &pointer + )); + + commandBufferObject = pointer; + + gcmkVERIFY_OBJECT(commandBufferObject, gcvOBJ_COMMANDBUFFER); + commandBufferMapped = gcvTRUE; + } + + /* Query the size of NOP command. */ + gcmkONERROR(gckHARDWARE_Nop( + hardware, gcvNULL, &nopBytes + )); + + /* Query the size of pipe select command sequence. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + hardware, gcvNULL, gcvPIPE_3D, &pipeBytes + )); + + /* Query the size of LINK command. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, gcvNULL, 0, 0, &linkBytes + )); + +#if gcdMULTI_GPU + /* Query the size of chip enable command sequence. */ + gcmkONERROR(gckHARDWARE_ChipEnable( + hardware, gcvNULL, 0, &chipEnableBytes + )); +#endif + + /* Compute the command buffer entry and the size. */ + commandBufferLogical + = (gctUINT8_PTR) gcmUINT64_TO_PTR(commandBufferObject->logical) + + commandBufferObject->startOffset; + + /* Get the hardware address. */ + if (Command->kernel->virtualCommandBuffer) + { + gcmkONERROR(gckKERNEL_GetGPUAddress( + Command->kernel, + commandBufferLogical, + gcvTRUE, + &commandBufferAddress + )); + } + else + { + gcmkONERROR(gckHARDWARE_ConvertLogical( + hardware, + commandBufferLogical, + gcvTRUE, + &commandBufferAddress + )); + } + + /* Get the physical address. */ + gcmkONERROR(gckOS_UserLogicalToPhysical( + Command->os, + commandBufferLogical, + (gctUINT32_PTR)&commandBufferPhysical + )); + +#ifdef __QNXNTO__ + userCommandBufferLogical = (gctPOINTER) commandBufferLogical; + + gcmkONERROR(gckOS_MapUserPointer( + Command->os, + userCommandBufferLogical, + 0, + &pointer)); + + commandBufferLogical = pointer; + + userCommandBufferLogicalMapped = gcvTRUE; +#endif + + commandBufferSize + = commandBufferObject->offset + + Command->reservedTail + - commandBufferObject->startOffset; + + gcmkONERROR(_FlushMMU(Command)); + + /* Get the current offset. */ + offset = Command->offset; + + /* Compute number of bytes left in current kernel command queue. */ + bytes = Command->pageSize - offset; + +#if gcdMULTI_GPU + if (Command->kernel->core == gcvCORE_MAJOR) + { + commandBufferSize += chipEnableBytes; + + gcmkONERROR(gckHARDWARE_ChipEnable( + hardware, + commandBufferLogical + pipeBytes, + ChipEnable, + &chipEnableBytes + )); + + gcmkONERROR(gckHARDWARE_ChipEnable( + hardware, + commandBufferLogical + commandBufferSize - linkBytes - chipEnableBytes, + gcvCORE_3D_ALL_MASK, + &chipEnableBytes + )); + } + else + { + commandBufferSize += nopBytes; + + gcmkONERROR(gckHARDWARE_Nop( + hardware, + commandBufferLogical + pipeBytes, + &nopBytes + )); + + gcmkONERROR(gckHARDWARE_Nop( + hardware, + commandBufferLogical + commandBufferSize - linkBytes - nopBytes, + &nopBytes + )); + } +#endif + + /* Query the size of WAIT/LINK command sequence. */ + gcmkONERROR(gckHARDWARE_WaitLink( + hardware, + gcvNULL, + offset, + &waitLinkBytes, + gcvNULL, + gcvNULL + )); + + /* Is there enough space in the current command queue? */ + if (bytes < waitLinkBytes) + { + /* No, create a new one. */ + gcmkONERROR(_NewQueue(Command)); + + /* Get the new current offset. */ + offset = Command->offset; + + /* Recompute the number of bytes in the new kernel command queue. */ + bytes = Command->pageSize - offset; + gcmkASSERT(bytes >= waitLinkBytes); + } + + /* Compute the location if WAIT/LINK command sequence. */ + waitLinkPhysical = (gctUINT8_PTR) Command->physical + offset; + waitLinkLogical = (gctUINT8_PTR) Command->logical + offset; + waitLinkAddress = Command->address + offset; + + /* Context switch required? */ + if (Context == gcvNULL) + { + /* See if we have to switch pipes for the command buffer. */ + if (commandBufferObject->entryPipe == Command->pipeSelect) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the entry command buffer pipes + ** are different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Compute the entry. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) commandBufferPhysical + offset; +#endif + entryLogical = commandBufferLogical + offset; + entryAddress = commandBufferAddress + offset; + entryBytes = commandBufferSize - offset; + + Command->currContext = gcvNULL; + } + else if (Command->currContext != Context) + { + /* Temporary disable context length oprimization. */ + Context->dirty = gcvTRUE; + + /* Get the current context buffer. */ + contextBuffer = Context->buffer; + + /* Yes, merge in the deltas. */ + gcmkONERROR(gckCONTEXT_Update(Context, ProcessID, StateDelta)); + + /* Determine context entry and exit points. */ + if (0) + { + /* Reset 2D dirty flag. */ + Context->dirty2D = gcvFALSE; + + if (Context->dirty || commandBufferObject->using3D) + { + /*************************************************************** + ** SWITCHING CONTEXT: 2D and 3D are used. + */ + + /* Reset 3D dirty flag. */ + Context->dirty3D = gcvFALSE; + + /* Compute the entry. */ + if (Command->pipeSelect == gcvPIPE_2D) + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes; + entryAddress = contextBuffer->address + pipeBytes; + entryBytes = Context->bufferSize - pipeBytes; + } + else + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical; + entryAddress = contextBuffer->address; + entryBytes = Context->bufferSize; + } + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_3D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Ensure the NOP between 2D and 3D is in place so that the + execution falls through from 2D to 3D. */ + gcmkONERROR(gckHARDWARE_Nop( + hardware, + contextBuffer->link2D, + &nopBytes + )); + + /* Generate a LINK from the context buffer to + the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link3D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + + /* Mark context as not dirty. */ + Context->dirty = gcvFALSE; + } + else + { + /*************************************************************** + ** SWITCHING CONTEXT: 2D only command buffer. + */ + + /* Mark 3D as dirty. */ + Context->dirty3D = gcvTRUE; + + /* Compute the entry. */ + if (Command->pipeSelect == gcvPIPE_2D) + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes; + entryAddress = contextBuffer->address + pipeBytes; + entryBytes = Context->entryOffset3D - pipeBytes; + } + else + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical; + entryAddress = contextBuffer->address; + entryBytes = Context->entryOffset3D; + } + + /* Store the current context buffer. */ + Context->dirtyBuffer = contextBuffer; + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_2D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* 3D is not used, generate a LINK from the end of 2D part of + the context buffer to the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link2D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + } + } + + /* Not using 2D. */ + else + { + + /* Store the current context buffer. */ + Context->dirtyBuffer = contextBuffer; + + if (Context->dirty || commandBufferObject->using3D) + { + /*************************************************************** + ** SWITCHING CONTEXT: 3D only command buffer. + */ + + /* Reset 3D dirty flag. */ + Context->dirty3D = gcvFALSE; + + /* Determine context buffer entry offset. */ + offset = (Command->pipeSelect == gcvPIPE_3D) + + /* Skip pipe switching sequence. */ + ? Context->entryOffset3D + Context->pipeSelectBytes + + /* Do not skip pipe switching sequence. */ + : Context->entryOffset3D; + + /* Compute the entry. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical + offset; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical + offset; + entryAddress = contextBuffer->address + offset; + entryBytes = Context->bufferSize - offset; + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_3D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Generate a LINK from the context buffer to + the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link3D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + } + else + { + /*************************************************************** + ** SWITCHING CONTEXT: "XD" command buffer - neither 2D nor 3D. + */ + + /* Mark 3D as dirty. */ + Context->dirty3D = gcvTRUE; + + /* Compute the entry. */ + if (Command->pipeSelect == gcvPIPE_3D) + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical + = (gctUINT8_PTR) contextBuffer->physical + + Context->entryOffsetXDFrom3D; +#endif + entryLogical + = (gctUINT8_PTR) contextBuffer->logical + + Context->entryOffsetXDFrom3D; + + entryAddress + = contextBuffer->address + + Context->entryOffsetXDFrom3D; + + entryBytes + = Context->bufferSize + - Context->entryOffsetXDFrom3D; + } + else + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical + = (gctUINT8_PTR) contextBuffer->physical + + Context->entryOffsetXDFrom2D; +#endif + entryLogical + = (gctUINT8_PTR) contextBuffer->logical + + Context->entryOffsetXDFrom2D; + + entryAddress + = contextBuffer->address + + Context->entryOffsetXDFrom2D; + + entryBytes + = Context->totalSize + - Context->entryOffsetXDFrom2D; + } + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_3D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Generate a LINK from the context buffer to + the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link3D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + } + } + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the context buffer cache. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)entryPhysical, + entryLogical, + entryBytes + )); +#endif + + /* Update the current context. */ + Command->currContext = Context; + +#if gcdDUMP_COMMAND + contextDumpLogical = entryLogical; + contextDumpBytes = entryBytes; +#endif + +#if gcdSECURITY + /* Commit context buffer to trust zone. */ + gckKERNEL_SecurityExecute( + Command->kernel, + entryLogical, + entryBytes - 8 + ); +#endif + +#if gcdRECORD_COMMAND + gckRECORDER_Record( + Command->recorder, + gcvNULL, + 0xFFFFFFFF, + entryLogical, + entryBytes - 8 + ); +#endif + } + + /* Same context. */ + else + { + /* Determine context entry and exit points. */ + if (commandBufferObject->using2D && Context->dirty2D) + { + /* Reset 2D dirty flag. */ + Context->dirty2D = gcvFALSE; + + /* Get the "dirty" context buffer. */ + contextBuffer = Context->dirtyBuffer; + + if (commandBufferObject->using3D && Context->dirty3D) + { + /* Reset 3D dirty flag. */ + Context->dirty3D = gcvFALSE; + + /* Compute the entry. */ + if (Command->pipeSelect == gcvPIPE_2D) + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes; + entryAddress = contextBuffer->address + pipeBytes; + entryBytes = Context->bufferSize - pipeBytes; + } + else + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical; + entryAddress = contextBuffer->address; + entryBytes = Context->bufferSize; + } + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_3D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Ensure the NOP between 2D and 3D is in place so that the + execution falls through from 2D to 3D. */ + gcmkONERROR(gckHARDWARE_Nop( + hardware, + contextBuffer->link2D, + &nopBytes + )); + + /* Generate a LINK from the context buffer to + the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link3D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + } + else + { + /* Compute the entry. */ + if (Command->pipeSelect == gcvPIPE_2D) + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical + pipeBytes; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical + pipeBytes; + entryAddress = contextBuffer->address + pipeBytes; + entryBytes = Context->entryOffset3D - pipeBytes; + } + else + { +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical; + entryAddress = contextBuffer->address; + entryBytes = Context->entryOffset3D; + } + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_2D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* 3D is not used, generate a LINK from the end of 2D part of + the context buffer to the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link2D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + } + } + else + { + if (commandBufferObject->using3D && Context->dirty3D) + { + /* Reset 3D dirty flag. */ + Context->dirty3D = gcvFALSE; + + /* Get the "dirty" context buffer. */ + contextBuffer = Context->dirtyBuffer; + + /* Determine context buffer entry offset. */ + offset = (Command->pipeSelect == gcvPIPE_3D) + + /* Skip pipe switching sequence. */ + ? Context->entryOffset3D + pipeBytes + + /* Do not skip pipe switching sequence. */ + : Context->entryOffset3D; + + /* Compute the entry. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) contextBuffer->physical + offset; +#endif + entryLogical = (gctUINT8_PTR) contextBuffer->logical + offset; + entryAddress = contextBuffer->address + offset; + entryBytes = Context->bufferSize - offset; + + /* See if we have to switch pipes between the context + and command buffers. */ + if (commandBufferObject->entryPipe == gcvPIPE_3D) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the initial context pipes are + different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Generate a LINK from the context buffer to + the command buffer. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + contextBuffer->link3D, + commandBufferAddress + offset, + commandBufferSize - offset, + &linkBytes + )); + } + else + { + /* See if we have to switch pipes for the command buffer. */ + if (commandBufferObject->entryPipe == Command->pipeSelect) + { + /* Skip pipe switching sequence. */ + offset = pipeBytes; + } + else + { + /* The current hardware and the entry command buffer pipes + ** are different, switch to the correct pipe. */ + gcmkONERROR(gckHARDWARE_PipeSelect( + Command->kernel->hardware, + commandBufferLogical, + commandBufferObject->entryPipe, + &pipeBytes + )); + + /* Do not skip pipe switching sequence. */ + offset = 0; + } + + /* Compute the entry. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + entryPhysical = (gctUINT8_PTR) commandBufferPhysical + offset; +#endif + entryLogical = commandBufferLogical + offset; + entryAddress = commandBufferAddress + offset; + entryBytes = commandBufferSize - offset; + } + } + } + +#if gcdDUMP_COMMAND + bufferDumpLogical = commandBufferLogical + offset; + bufferDumpBytes = commandBufferSize - offset; +#endif + +#if gcdSECURE_USER + /* Process user hints. */ + gcmkONERROR(_ProcessHints(Command, ProcessID, commandBufferObject)); +#endif + + /* Determine the location to jump to for the command buffer being + ** scheduled. */ + if (Command->newQueue) + { + /* New command queue, jump to the beginning of it. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + exitPhysical = Command->physical; +#endif + + exitLogical = Command->logical; + exitAddress = Command->address; + exitBytes = Command->offset + waitLinkBytes; + } + else + { + /* Still within the preexisting command queue, jump to the new + WAIT/LINK command sequence. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + exitPhysical = waitLinkPhysical; +#endif + exitLogical = waitLinkLogical; + exitAddress = waitLinkAddress; + exitBytes = waitLinkBytes; + } + + /* Add a new WAIT/LINK command sequence. When the command buffer which is + currently being scheduled is fully executed by the GPU, the FE will + jump to this WAIT/LINK sequence. */ + gcmkONERROR(gckHARDWARE_WaitLink( + hardware, + waitLinkLogical, + offset, + &waitLinkBytes, + &waitOffset, + &waitSize + )); + + /* Compute the location if WAIT command. */ + waitPhysical = (gctUINT8_PTR) waitLinkPhysical + waitOffset; + waitLogical = (gctUINT8_PTR) waitLinkLogical + waitOffset; + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the command queue cache. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)exitPhysical, + exitLogical, + exitBytes + )); +#endif + + /* Determine the location of the LINK command in the command buffer. */ + commandBufferLink + = (gctUINT8_PTR) gcmUINT64_TO_PTR(commandBufferObject->logical) + + commandBufferObject->offset; + +#ifdef __QNXNTO__ + userCommandBufferLink = (gctPOINTER) commandBufferLink; + + gcmkONERROR(gckOS_MapUserPointer( + Command->os, + userCommandBufferLink, + 0, + &pointer)); + + commandBufferLink = pointer; + + userCommandBufferLinkMapped = gcvTRUE; +#endif + +#if gcdMULTI_GPU + if (Command->kernel->core == gcvCORE_MAJOR) + { + commandBufferLink += chipEnableBytes; + } + else + { + commandBufferLink += nopBytes; + } +#endif + + /* Generate a LINK from the end of the command buffer being scheduled + back to the kernel command queue. */ +#if !gcdSECURITY + gcmkONERROR(gckHARDWARE_Link( + hardware, + commandBufferLink, + exitAddress, + exitBytes, + &linkBytes + )); +#endif + +#ifdef __QNXNTO__ + gcmkONERROR(gckOS_UnmapUserPointer( + Command->os, + userCommandBufferLink, + 0, + commandBufferLink)); + + userCommandBufferLinkMapped = gcvFALSE; +#endif + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the command buffer cache. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + ProcessID, + gcvNULL, + (gctUINT32)commandBufferPhysical, + commandBufferLogical, + commandBufferSize + )); +#endif + +#if gcdRECORD_COMMAND + gckRECORDER_Record( + Command->recorder, + commandBufferLogical + offset, + commandBufferSize - offset - 8, + gcvNULL, + 0xFFFFFFFF + ); + + gckRECORDER_AdvanceIndex(Command->recorder, Command->commitStamp); + + Command->commitStamp++; +#endif + +#if gcdSECURITY + /* Submit command buffer to trust zone. */ + gckKERNEL_SecurityExecute( + Command->kernel, + commandBufferLogical + offset, + commandBufferSize - offset - 8 + ); +#else + /* Generate a LINK from the previous WAIT/LINK command sequence to the + entry determined above (either the context or the command buffer). + This LINK replaces the WAIT instruction from the previous WAIT/LINK + pair, therefore we use WAIT metrics for generation of this LINK. + This action will execute the entire sequence. */ + gcmkONERROR(gckHARDWARE_Link( + hardware, + Command->waitLogical, + entryAddress, + entryBytes, + &Command->waitSize + )); +#endif + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache for the link. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)Command->waitPhysical, + Command->waitLogical, + Command->waitSize + )); +#endif + + gcmkDUMPCOMMAND( + Command->os, + Command->waitLogical, + Command->waitSize, + gceDUMP_BUFFER_LINK, + gcvFALSE + ); + + gcmkDUMPCOMMAND( + Command->os, + contextDumpLogical, + contextDumpBytes, + gceDUMP_BUFFER_CONTEXT, + gcvFALSE + ); + + gcmkDUMPCOMMAND( + Command->os, + bufferDumpLogical, + bufferDumpBytes, + gceDUMP_BUFFER_USER, + gcvFALSE + ); + + gcmkDUMPCOMMAND( + Command->os, + waitLinkLogical, + waitLinkBytes, + gceDUMP_BUFFER_WAITLINK, + gcvFALSE + ); + + /* Update the current pipe. */ + Command->pipeSelect = commandBufferObject->exitPipe; + + /* Update command queue offset. */ + Command->offset += waitLinkBytes; + Command->newQueue = gcvFALSE; + + /* Update address of last WAIT. */ + Command->waitPhysical = waitPhysical; + Command->waitLogical = waitLogical; + Command->waitSize = waitSize; + + /* Update queue tail pointer. */ + gcmkONERROR(gckHARDWARE_UpdateQueueTail( + hardware, Command->logical, Command->offset + )); + +#if gcdDUMP_COMMAND + gcmkPRINT("@[kernel.commit]"); +#endif +#endif /* gcdNULL_DRIVER */ + + /* Release the context switching mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext)); + contextAcquired = gcvFALSE; + + /* Release the command queue. */ + gcmkONERROR(gckCOMMAND_ExitCommit(Command, gcvFALSE)); + commitEntered = gcvFALSE; + +#if VIVANTE_PROFILER_CONTEXT + if(sequenceAcquired) + { +#if gcdMULTI_GPU + gcmkONERROR(gckCOMMAND_Stall(Command, gcvTRUE, ChipEnable)); +#else + gcmkONERROR(gckCOMMAND_Stall(Command, gcvTRUE)); +#endif + if (Command->currContext) + { + gcmkONERROR(gckHARDWARE_UpdateContextProfile( + hardware, + Command->currContext)); + } + + /* Release the context switching mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContextSeq)); + sequenceAcquired = gcvFALSE; + } +#endif + + /* Loop while there are records in the queue. */ + while (EventQueue != gcvNULL) + { + if (needCopy) + { + /* Point to stack record. */ + eventRecord = &_eventRecord; + + /* Copy the data from the client. */ + gcmkONERROR(gckOS_CopyFromUserData( + Command->os, eventRecord, EventQueue, gcmSIZEOF(gcsQUEUE) + )); + } + else + { + /* Map record into kernel memory. */ + gcmkONERROR(gckOS_MapUserPointer(Command->os, + EventQueue, + gcmSIZEOF(gcsQUEUE), + &pointer)); + + eventRecord = pointer; + } + + /* Append event record to event queue. */ + gcmkONERROR(gckEVENT_AddList( + Command->kernel->eventObj, &eventRecord->iface, gcvKERNEL_PIXEL, gcvTRUE, gcvFALSE + )); + + /* Next record in the queue. */ + nextEventRecord = gcmUINT64_TO_PTR(eventRecord->next); + + if (!needCopy) + { + /* Unmap record from kernel memory. */ + gcmkONERROR(gckOS_UnmapUserPointer( + Command->os, EventQueue, gcmSIZEOF(gcsQUEUE), (gctPOINTER *) eventRecord + )); + + eventRecord = gcvNULL; + } + + EventQueue = nextEventRecord; + } + + if (Command->kernel->eventObj->queueHead == gcvNULL + && Command->kernel->hardware->powerManagement == gcvTRUE + ) + { + /* Commit done event by which work thread knows all jobs done. */ + gcmkVERIFY_OK( + gckEVENT_CommitDone(Command->kernel->eventObj, gcvKERNEL_PIXEL)); + } + + /* Submit events. */ +#if gcdMULTI_GPU + status = gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE, ChipEnable); +#else + status = gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE); +#endif + if (status == gcvSTATUS_INTERRUPTED) + { + gcmkTRACE( + gcvLEVEL_INFO, + "%s(%d): Intterupted in gckEVENT_Submit", + __FUNCTION__, __LINE__ + ); + status = gcvSTATUS_OK; + } + else + { + gcmkONERROR(status); + } + +#ifdef __QNXNTO__ + if (userCommandBufferLogicalMapped) + { + gcmkONERROR(gckOS_UnmapUserPointer( + Command->os, + userCommandBufferLogical, + 0, + commandBufferLogical)); + + userCommandBufferLogicalMapped = gcvFALSE; + } +#endif + + /* Unmap the command buffer pointer. */ + if (commandBufferMapped) + { + gcmkONERROR(gckOS_UnmapUserPointer( + Command->os, + CommandBuffer, + gcmSIZEOF(struct _gcoCMDBUF), + commandBufferObject + )); + + commandBufferMapped = gcvFALSE; + } + + /* Return status. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + if ((eventRecord != gcvNULL) && !needCopy) + { + /* Roll back. */ + gcmkVERIFY_OK(gckOS_UnmapUserPointer( + Command->os, + EventQueue, + gcmSIZEOF(gcsQUEUE), + (gctPOINTER *) eventRecord + )); + } + + if (contextAcquired) + { + /* Release the context switching mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext)); + } + + if (commitEntered) + { + /* Release the command queue mutex. */ + gcmkVERIFY_OK(gckCOMMAND_ExitCommit(Command, gcvFALSE)); + } + +#if VIVANTE_PROFILER_CONTEXT + if (sequenceAcquired) + { + /* Release the context sequence mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContextSeq)); + } +#endif + +#ifdef __QNXNTO__ + if (userCommandBufferLinkMapped) + { + gcmkONERROR(gckOS_UnmapUserPointer( + Command->os, + userCommandBufferLink, + 0, + commandBufferLink)); + } + + if (userCommandBufferLogicalMapped) + { + gcmkVERIFY_OK(gckOS_UnmapUserPointer( + Command->os, + userCommandBufferLogical, + 0, + commandBufferLogical)); + } +#endif + + /* Unmap the command buffer pointer. */ + if (commandBufferMapped) + { + gcmkVERIFY_OK(gckOS_UnmapUserPointer( + Command->os, + CommandBuffer, + gcmSIZEOF(struct _gcoCMDBUF), + commandBufferObject + )); + } + + /* Return status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Reserve +** +** Reserve space in the command queue. Also acquire the command queue mutex. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object. +** +** gctSIZE_T RequestedBytes +** Number of bytes previously reserved. +** +** OUTPUT: +** +** gctPOINTER * Buffer +** Pointer to a variable that will receive the address of the reserved +** space. +** +** gctSIZE_T * BufferSize +** Pointer to a variable that will receive the number of bytes +** available in the command queue. +*/ +gceSTATUS +gckCOMMAND_Reserve( + IN gckCOMMAND Command, + IN gctUINT32 RequestedBytes, + OUT gctPOINTER * Buffer, + OUT gctUINT32 * BufferSize + ) +{ + gceSTATUS status; + gctUINT32 bytes; + gctUINT32 requiredBytes; + gctUINT32 requestedAligned; + + gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu", Command, RequestedBytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Compute aligned number of reuested bytes. */ + requestedAligned = gcmALIGN(RequestedBytes, Command->alignment); + + /* Another WAIT/LINK command sequence will have to be appended after + the requested area being reserved. Compute the number of bytes + required for WAIT/LINK at the location after the reserved area. */ + gcmkONERROR(gckHARDWARE_WaitLink( + Command->kernel->hardware, + gcvNULL, + Command->offset + requestedAligned, + &requiredBytes, + gcvNULL, + gcvNULL + )); + + /* Compute total number of bytes required. */ + requiredBytes += requestedAligned; + + /* Compute number of bytes available in command queue. */ + bytes = Command->pageSize - Command->offset; + + /* Is there enough space in the current command queue? */ + if (bytes < requiredBytes) + { + /* Create a new command queue. */ + gcmkONERROR(_NewQueue(Command)); + + /* Recompute the number of bytes in the new kernel command queue. */ + bytes = Command->pageSize - Command->offset; + + /* Still not enough space? */ + if (bytes < requiredBytes) + { + /* Rare case, not enough room in command queue. */ + gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); + } + } + + /* Return pointer to empty slot command queue. */ + *Buffer = (gctUINT8 *) Command->logical + Command->offset; + + /* Return number of bytes left in command queue. */ + *BufferSize = bytes; + + /* Success. */ + gcmkFOOTER_ARG("*Buffer=0x%x *BufferSize=%lu", *Buffer, *BufferSize); + return gcvSTATUS_OK; + +OnError: + /* Return status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Execute +** +** Execute a previously reserved command queue by appending a WAIT/LINK command +** sequence after it and modifying the last WAIT into a LINK command. The +** command FIFO mutex will be released whether this function succeeds or not. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object. +** +** gctSIZE_T RequestedBytes +** Number of bytes previously reserved. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_Execute( + IN gckCOMMAND Command, + IN gctUINT32 RequestedBytes + ) +{ + gceSTATUS status; + + gctPHYS_ADDR waitLinkPhysical; + gctUINT8_PTR waitLinkLogical; + gctUINT32 waitLinkOffset; + gctUINT32 waitLinkBytes; + + gctPHYS_ADDR waitPhysical; + gctPOINTER waitLogical; + gctUINT32 waitOffset; + gctUINT32 waitBytes; + +#if gcdNONPAGED_MEMORY_CACHEABLE + gctPHYS_ADDR execPhysical; +#endif + gctPOINTER execLogical; + gctUINT32 execAddress; + gctUINT32 execBytes; + + gcmkHEADER_ARG("Command=0x%x RequestedBytes=%lu", Command, RequestedBytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Compute offset for WAIT/LINK. */ + waitLinkOffset = Command->offset + RequestedBytes; + + /* Compute number of bytes left in command queue. */ + waitLinkBytes = Command->pageSize - waitLinkOffset; + + /* Compute the location if WAIT/LINK command sequence. */ + waitLinkPhysical = (gctUINT8_PTR) Command->physical + waitLinkOffset; + waitLinkLogical = (gctUINT8_PTR) Command->logical + waitLinkOffset; + + /* Append WAIT/LINK in command queue. */ + gcmkONERROR(gckHARDWARE_WaitLink( + Command->kernel->hardware, + waitLinkLogical, + waitLinkOffset, + &waitLinkBytes, + &waitOffset, + &waitBytes + )); + + /* Compute the location if WAIT command. */ + waitPhysical = (gctUINT8_PTR) waitLinkPhysical + waitOffset; + waitLogical = waitLinkLogical + waitOffset; + + /* Determine the location to jump to for the command buffer being + ** scheduled. */ + if (Command->newQueue) + { + /* New command queue, jump to the beginning of it. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + execPhysical = Command->physical; +#endif + execLogical = Command->logical; + execAddress = Command->address; + execBytes = waitLinkOffset + waitLinkBytes; + } + else + { + /* Still within the preexisting command queue, jump directly to the + reserved area. */ +#if gcdNONPAGED_MEMORY_CACHEABLE + execPhysical = (gctUINT8 *) Command->physical + Command->offset; +#endif + execLogical = (gctUINT8 *) Command->logical + Command->offset; + execAddress = Command->address + Command->offset; + execBytes = RequestedBytes + waitLinkBytes; + } + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)execPhysical, + execLogical, + execBytes + )); +#endif + + /* Convert the last WAIT into a LINK. */ + gcmkONERROR(gckHARDWARE_Link( + Command->kernel->hardware, + Command->waitLogical, + execAddress, + execBytes, + &Command->waitSize + )); + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache. */ + gcmkONERROR(gckOS_CacheClean( + Command->os, + Command->kernelProcessID, + gcvNULL, + (gctUINT32)Command->waitPhysical, + Command->waitLogical, + Command->waitSize + )); +#endif + + gcmkDUMPCOMMAND( + Command->os, + Command->waitLogical, + Command->waitSize, + gceDUMP_BUFFER_LINK, + gcvFALSE + ); + + gcmkDUMPCOMMAND( + Command->os, + execLogical, + execBytes, + gceDUMP_BUFFER_KERNEL, + gcvFALSE + ); + + /* Update the pointer to the last WAIT. */ + Command->waitPhysical = waitPhysical; + Command->waitLogical = waitLogical; + Command->waitSize = waitBytes; + + /* Update the command queue. */ + Command->offset += RequestedBytes + waitLinkBytes; + Command->newQueue = gcvFALSE; + + /* Update queue tail pointer. */ + gcmkONERROR(gckHARDWARE_UpdateQueueTail( + Command->kernel->hardware, Command->logical, Command->offset + )); + +#if gcdDUMP_COMMAND + gcmkPRINT("@[kernel.execute]"); +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_Stall +** +** The calling thread will be suspended until the command queue has been +** completed. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to an gckCOMMAND object. +** +** gctBOOL FromPower +** Determines whether the call originates from inside the power +** management or not. +** +** OUTPUT: +** +** Nothing. +*/ +#if gcdMULTI_GPU +gceSTATUS +gckCOMMAND_Stall( + IN gckCOMMAND Command, + IN gctBOOL FromPower, + IN gceCORE_3D_MASK ChipEnable + ) +#else +gceSTATUS +gckCOMMAND_Stall( + IN gckCOMMAND Command, + IN gctBOOL FromPower + ) +#endif +{ +#if gcdNULL_DRIVER + /* Do nothing with infinite hardware. */ + return gcvSTATUS_OK; +#else + gckOS os; + gckHARDWARE hardware; + gckEVENT eventObject; + gceSTATUS status; + gctSIGNAL signal = gcvNULL; + gctUINT timer = 0; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Extract the gckOS object pointer. */ + os = Command->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Extract the gckHARDWARE object pointer. */ + hardware = Command->kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + /* Extract the gckEVENT object pointer. */ + eventObject = Command->kernel->eventObj; + gcmkVERIFY_OBJECT(eventObject, gcvOBJ_EVENT); + + /* Allocate the signal. */ + gcmkONERROR(gckOS_CreateSignal(os, gcvTRUE, &signal)); + + /* Append the EVENT command to trigger the signal. */ + gcmkONERROR(gckEVENT_Signal(eventObject, signal, gcvKERNEL_PIXEL)); + + /* Submit the event queue. */ +#if gcdMULTI_GPU + gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower, ChipEnable)); +#else + gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower)); +#endif + +#if gcdDUMP_COMMAND + gcmkPRINT("@[kernel.stall]"); +#endif + + if (status == gcvSTATUS_CHIP_NOT_READY) + { + /* Error. */ + goto OnError; + } + + do + { + /* Wait for the signal. */ + status = gckOS_WaitSignal(os, signal, gcdGPU_ADVANCETIMER); + + if (status == gcvSTATUS_TIMEOUT) + { +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gctUINT32 idle; + + /* Read idle register. */ + gcmkVERIFY_OK(gckHARDWARE_GetIdle( + hardware, gcvFALSE, &idle + )); + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): idle=%08x", + __FUNCTION__, __LINE__, idle + ); + + gcmkVERIFY_OK(gckOS_MemoryBarrier(os, gcvNULL)); +#endif + + /* Advance timer. */ + timer += gcdGPU_ADVANCETIMER; + } + else if (status == gcvSTATUS_INTERRUPTED) + { + gcmkONERROR(gcvSTATUS_INTERRUPTED); + } + + } + while (gcmIS_ERROR(status)); + + /* Bail out on timeout. */ + if (gcmIS_ERROR(status)) + { + /* Broadcast the stuck GPU. */ + gcmkONERROR(gckOS_Broadcast( + os, hardware, gcvBROADCAST_GPU_STUCK + )); + } + + /* Delete the signal. */ + gcmkVERIFY_OK(gckOS_DestroySignal(os, signal)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (signal != gcvNULL) + { + /* Free the signal. */ + gcmkVERIFY_OK(gckOS_DestroySignal(os, signal)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +#endif +} + +/******************************************************************************* +** +** gckCOMMAND_Attach +** +** Attach user process. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to a gckCOMMAND object. +** +** gctUINT32 ProcessID +** Current process ID. +** +** OUTPUT: +** +** gckCONTEXT * Context +** Pointer to a variable that will receive a pointer to a new +** gckCONTEXT object. +** +** gctSIZE_T * StateCount +** Pointer to a variable that will receive the number of states +** in the context buffer. +*/ +#if (gcdENABLE_3D || gcdENABLE_2D) +gceSTATUS +gckCOMMAND_Attach( + IN gckCOMMAND Command, + OUT gckCONTEXT * Context, + OUT gctSIZE_T * StateCount, + IN gctUINT32 ProcessID + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Acquire the context switching mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + Command->os, Command->mutexContext, gcvINFINITE + )); + acquired = gcvTRUE; + + /* Construct a gckCONTEXT object. */ + gcmkONERROR(gckCONTEXT_Construct( + Command->os, + Command->kernel->hardware, + ProcessID, + Context + )); + + /* Return the number of states in the context. */ + * StateCount = (* Context)->stateCount; + + /* Release the context switching mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext)); + acquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER_ARG("*Context=0x%x", *Context); + return gcvSTATUS_OK; + +OnError: + /* Release mutex. */ + if (acquired) + { + /* Release the context switching mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext)); + acquired = gcvFALSE; + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckCOMMAND_Detach +** +** Detach user process. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to a gckCOMMAND object. +** +** gckCONTEXT Context +** Pointer to a gckCONTEXT object to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_Detach( + IN gckCOMMAND Command, + IN gckCONTEXT Context + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Command=0x%x Context=0x%x", Command, Context); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + /* Acquire the context switching mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + Command->os, Command->mutexContext, gcvINFINITE + )); + acquired = gcvTRUE; + + /* Construct a gckCONTEXT object. */ + gcmkONERROR(gckCONTEXT_Destroy(Context)); + + if (Command->currContext == Context) + { + /* Detach from gckCOMMAND object if the destoryed context is current context. */ + Command->currContext = gcvNULL; + } + + /* Release the context switching mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Command->os, Command->mutexContext)); + acquired = gcvFALSE; + + /* Return the status. */ + gcmkFOOTER(); + return gcvSTATUS_OK; + +OnError: + /* Release mutex. */ + if (acquired) + { + /* Release the context switching mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Command->os, Command->mutexContext)); + acquired = gcvFALSE; + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckCOMMAND_DumpExecutingBuffer +** +** Dump the command buffer which GPU is executing. +** +** INPUT: +** +** gckCOMMAND Command +** Pointer to a gckCOMMAND object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckCOMMAND_DumpExecutingBuffer( + IN gckCOMMAND Command + ) +{ + gceSTATUS status; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + gctUINT32 gpuAddress; + gctSIZE_T pageCount; + gctPOINTER entry; + gckOS os = Command->os; + gckKERNEL kernel = Command->kernel; + gctINT pid; + gctUINT32 i, rear; + gctUINT32 start, end; + gctUINT32 dumpFront, dumpRear; + gckLINKQUEUE queue = &kernel->hardware->linkQueue; + gckLINKQUEUE queueMirror; + gctUINT32 bytes; + gckLINKDATA linkData; + + gcmkPRINT("**************************\n"); + gcmkPRINT("**** COMMAND BUF DUMP ****\n"); + gcmkPRINT("**************************\n"); + + gcmkVERIFY_OK(gckOS_ReadRegisterEx(os, kernel->core, 0x664, &gpuAddress)); + + gcmkPRINT("DMA Address 0x%08X", gpuAddress); + + if (Command->kernel->stuckDump > gcdSTUCK_DUMP_MIDDLE) + { + gcmkPRINT("Dump Level is %d", Command->kernel->stuckDump); + + /* Duplicate queue because it will be changed.*/ + gcmkONERROR(gckOS_AllocateMemory(os, + sizeof(struct _gckLINKQUEUE), + (gctPOINTER *)&queueMirror)); + + gckOS_MemCopy(queueMirror, + queue, + sizeof(struct _gckLINKQUEUE)); + + /* If kernel command buffer link to a context buffer, then link to a user command + ** buffer, the second link will be in queue first, so we must fix this. + ** In Queue: C1 U1 U2 C2 U3 U4 U5 C3 + ** Real: C1 X1 U1 C2 U2 U3 U4 C3 U5 + ** Command buffer X1 which is after C1 is out of queue, so C1 is meaningless. + */ + for (i = 0; i < gcdLINK_QUEUE_SIZE; i++) + { + gckLINKQUEUE_GetData(queueMirror, i, &linkData); + + status = gckKERNEL_QueryGPUAddress(kernel, linkData->start, &buffer); + + if (gcmIS_ERROR(status)) + { + /* Can't find it in virtual command buffer list, ignore it. */ + continue; + } + + if (buffer->kernelLogical) + { + /* It is a context buffer. */ + if (i == 0) + { + /* The real command buffer is out, so clear this slot. */ + linkData->start = 0; + linkData->end = 0; + linkData->pid = 0; + } + else + { + /* switch context buffer and command buffer. */ + struct _gckLINKDATA tmp = *linkData; + gckLINKDATA linkDataPrevious; + + gckLINKQUEUE_GetData(queueMirror, i - 1, &linkDataPrevious); + *linkData = *linkDataPrevious; + *linkDataPrevious = tmp; + } + } + } + + /* Clear search result. */ + dumpFront = dumpRear = gcvINFINITE; + + gcmkPRINT("Link Stack:"); + + /* Search stuck address in link queue from rear. */ + rear = gcdLINK_QUEUE_SIZE - 1; + for (i = 0; i < gcdLINK_QUEUE_SIZE; i++) + { + gckLINKQUEUE_GetData(queueMirror, rear, &linkData); + + start = linkData->start; + end = linkData->end; + pid = linkData->pid; + + if (gpuAddress >= start && gpuAddress < end) + { + /* Find latest matched command buffer. */ + gcmkPRINT(" %d, [%08X - %08X]", pid, start, end); + + /* Initiliaze dump information. */ + dumpFront = dumpRear = rear; + } + + /* Advance to previous one. */ + rear--; + + if (dumpFront != gcvINFINITE) + { + break; + } + } + + if (dumpFront == gcvINFINITE) + { + /* Can't find matched record in link queue, dump kernel command buffer. */ + _DumpKernelCommandBuffer(Command); + + /* Free local copy. */ + gcmkOS_SAFE_FREE(os, queueMirror); + return gcvSTATUS_OK; + } + + /* Search the last context buffer linked. */ + while (rear > 0) + { + gckLINKQUEUE_GetData(queueMirror, rear, &linkData); + + gcmkPRINT(" %d, [%08X - %08X]", + linkData->pid, + linkData->start, + linkData->end); + + status = gckKERNEL_QueryGPUAddress(kernel, linkData->start, &buffer); + + if (gcmIS_SUCCESS(status) && buffer->kernelLogical) + { + /* Find a context buffer. */ + dumpFront = rear; + break; + } + + rear--; + } + + if (dumpFront == dumpRear) + { + /* No context buffer is found, dump all we got.*/ + dumpFront = 0; + } + + /* Dump from last context buffer to last command buffer where hang happens. */ + for (i = dumpFront; i <= dumpRear; i++) + { + gckLINKQUEUE_GetData(queueMirror, i, &linkData); + + /* Get gpu address of this command buffer. */ + gpuAddress = linkData->start; + bytes = linkData->end - gpuAddress; + + /* Get the whole buffer. */ + status = gckKERNEL_QueryGPUAddress(kernel, gpuAddress, &buffer); + + if (gcmIS_ERROR(status)) + { + gcmkPRINT("Buffer [%08X - %08X] is lost or not belong to current process", + linkData->start, + linkData->end); + continue; + } + + /* Get kernel logical for dump. */ + if (buffer->kernelLogical) + { + /* Get kernel logical directly if it is a context buffer. */ + entry = buffer->kernelLogical; + gcmkPRINT("Context Buffer:"); + } + else + { + /* Make it accessiable by kernel if it is a user command buffer. */ + gcmkVERIFY_OK( + gckOS_CreateKernelVirtualMapping(os, + buffer->physical, + buffer->bytes, + &entry, + &pageCount)); + gcmkPRINT("User Command Buffer:"); + } + + /* Dump from the entry. */ + _DumpBuffer((gctUINT8_PTR)entry + (gpuAddress - buffer->gpuAddress), gpuAddress, bytes); + + /* Release kernel logical address if neccessary. */ + if (!buffer->kernelLogical) + { + gcmkVERIFY_OK( + gckOS_DestroyKernelVirtualMapping(os, + buffer->physical, + buffer->bytes, + entry)); + } + } + + /* Free local copy. */ + gcmkOS_SAFE_FREE(os, queueMirror); + return gcvSTATUS_OK; + OnError: + return status; + } + else + { + gcmkPRINT("Dump Level is %d, dump memory near the stuck address", + Command->kernel->stuckDump); + + /* Without link queue information, we don't know the entry of last command + ** buffer, just dump the page where GPU stuck. */ + status = gckKERNEL_QueryGPUAddress(kernel, gpuAddress, &buffer); + + if (gcmIS_SUCCESS(status)) + { + gcmkVERIFY_OK( + gckOS_CreateKernelVirtualMapping(os, + buffer->physical, + buffer->bytes, + &entry, + &pageCount)); + + if (entry) + { + gctUINT32 offset = gpuAddress - buffer->gpuAddress; + gctPOINTER entryDump = entry; + + /* Dump one pages. */ + gctUINT32 bytes = 4096; + + /* Align to page. */ + offset &= 0xfffff000; + + /* Kernel address of page where stall point stay. */ + entryDump = (gctUINT8_PTR)entryDump + offset; + + /* Align to page. */ + gpuAddress &= 0xfffff000; + + gcmkPRINT("User Command Buffer:\n"); + _DumpBuffer(entryDump, gpuAddress, bytes); + } + + gcmkVERIFY_OK( + gckOS_DestroyKernelVirtualMapping(os, + buffer->physical, + buffer->bytes, + entry)); + } + else + { + _DumpKernelCommandBuffer(Command); + } + + return gcvSTATUS_OK; + } +} + +gceSTATUS +gckCOMMAND_AddressInKernelCommandBuffer( + IN gckCOMMAND Command, + IN gctUINT32 Address, + OUT gctBOOL *In + ) +{ + gctBOOL in = gcvFALSE; + gctINT i; + + for (i = 0; i < gcdCOMMAND_QUEUES; i++) + { + if ((Address >= Command->queues[i].address) + && (Address < (Command->queues[i].address + Command->pageSize)) + ) + { + in = gcvTRUE; + break; + } + } + + *In = in; + return gcvSTATUS_OK; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_command_vg.c b/drivers/gpu/galcore/gc_hal_kernel_command_vg.c new file mode 100644 index 00000000000000..0eafaf6c0e3881 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_command_vg.c @@ -0,0 +1,3787 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#if gcdENABLE_VG + +#include "gc_hal_kernel_hardware_command_vg.h" + +#define _GC_OBJ_ZONE gcvZONE_COMMAND + +/******************************************************************************\ +*********************************** Debugging ********************************** +\******************************************************************************/ + +#define gcvDISABLE_TIMEOUT 1 +#define gcvDUMP_COMMAND_BUFFER 0 +#define gcvDUMP_COMMAND_LINES 0 + + +#if gcvDEBUG || defined(EMULATOR) || gcvDISABLE_TIMEOUT +# define gcvQUEUE_TIMEOUT ~0 +#else +# define gcvQUEUE_TIMEOUT 10 +#endif + + +/******************************************************************************\ +********************************** Definitions ********************************* +\******************************************************************************/ + +/* Minimum buffer size. */ +#define gcvMINUMUM_BUFFER \ + gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) + \ + gcmSIZEOF(gcsKERNEL_CMDQUEUE) * 2 + +#define gcmDECLARE_INTERRUPT_HANDLER(Block, Number) \ + static gceSTATUS \ + _EventHandler_##Block##_##Number( \ + IN gckVGKERNEL Kernel \ + ) + +#define gcmDEFINE_INTERRUPT_HANDLER(Block, Number) \ + gcmDECLARE_INTERRUPT_HANDLER(Block, Number) \ + { \ + return _EventHandler_Block( \ + Kernel, \ + &Kernel->command->taskTable[gcvBLOCK_##Block], \ + gcvFALSE \ + ); \ + } + +#define gcmDEFINE_INTERRUPT_HANDLER_ENTRY(Block, Number) \ + { gcvBLOCK_##Block, _EventHandler_##Block##_##Number } + +/* Block interrupt handling table entry. */ +typedef struct _gcsBLOCK_INTERRUPT_HANDLER * gcsBLOCK_INTERRUPT_HANDLER_PTR; +typedef struct _gcsBLOCK_INTERRUPT_HANDLER +{ + gceBLOCK block; + gctINTERRUPT_HANDLER handler; +} +gcsBLOCK_INTERRUPT_HANDLER; + +/* Queue control functions. */ +typedef struct _gcsQUEUE_UPDATE_CONTROL * gcsQUEUE_UPDATE_CONTROL_PTR; +typedef struct _gcsQUEUE_UPDATE_CONTROL +{ + gctOBJECT_HANDLER execute; + gctOBJECT_HANDLER update; + gctOBJECT_HANDLER lastExecute; + gctOBJECT_HANDLER lastUpdate; +} +gcsQUEUE_UPDATE_CONTROL; + + +/******************************************************************************\ +********************************* Support Code ********************************* +\******************************************************************************/ +static gceSTATUS +_FlushMMU( + IN gckVGCOMMAND Command + ) +{ + gceSTATUS status; + gctUINT32 oldValue; + gckVGHARDWARE hardware = Command->hardware; + + gcmkONERROR(gckOS_AtomicExchange(Command->os, + hardware->pageTableDirty, + 0, + &oldValue)); + + if (oldValue) + { + /* Page Table is upated, flush mmu before commit. */ + gcmkONERROR(gckVGHARDWARE_FlushMMU(hardware)); + } + + return gcvSTATUS_OK; +OnError: + return status; +} + +static gceSTATUS +_WaitForIdle( + IN gckVGCOMMAND Command, + IN gcsKERNEL_QUEUE_HEADER_PTR Queue + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctUINT32 idle; + gctUINT timeout = 0; + + /* Loop while not idle. */ + while (Queue->pending) + { + /* Did we reach the timeout limit? */ + if (timeout == gcvQUEUE_TIMEOUT) + { + /* Hardware is probably dead... */ + return gcvSTATUS_TIMEOUT; + } + + /* Sleep for 100ms. */ + gcmkERR_BREAK(gckOS_Delay(Command->os, 100)); + + /* Not the first loop? */ + if (timeout > 0) + { + /* Read IDLE register. */ + gcmkVERIFY_OK(gckVGHARDWARE_GetIdle(Command->hardware, &idle)); + + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_COMMAND, + "%s: timeout, IDLE=%08X\n", + __FUNCTION__, idle + ); + } + + /* Increment the timeout counter. */ + timeout += 1; + } + + /* Return status. */ + return status; +} + +static gctINT32 +_GetNextInterrupt( + IN gckVGCOMMAND Command, + IN gceBLOCK Block + ) +{ + gctUINT index; + gcsBLOCK_TASK_ENTRY_PTR entry; + gctINT32 interrupt; + + /* Get the block entry. */ + entry = &Command->taskTable[Block]; + + /* Make sure we have initialized interrupts. */ + gcmkASSERT(entry->interruptCount > 0); + + /* Decrement the interrupt usage semaphore. */ + gcmkVERIFY_OK(gckOS_DecrementSemaphore( + Command->os, entry->interruptSemaphore + )); + + /* Get the value index. */ + index = entry->interruptIndex; + + /* Get the interrupt value. */ + interrupt = entry->interruptArray[index]; + + /* Must be a valid value. */ + gcmkASSERT((interrupt >= 0) && (interrupt <= 31)); + + /* Advance the index to the next value. */ + index += 1; + + /* Set the new index. */ + entry->interruptIndex = (index == entry->interruptCount) + ? 0 + : index; + + /* Return interrupt value. */ + return interrupt; +} + + +/******************************************************************************\ +***************************** Task Storage Management ************************** +\******************************************************************************/ + +/* Minimum task buffer size. */ +#define gcvMIN_TASK_BUFFER \ +( \ + gcmSIZEOF(gcsTASK_CONTAINER) + 128 \ +) + +/* Free list terminator. */ +#define gcvFREE_TASK_TERMINATOR \ +( \ + (gcsTASK_CONTAINER_PTR) gcmINT2PTR(~0) \ +) + + +/*----------------------------------------------------------------------------*/ +/*------------------- Allocated Task Buffer List Management ------------------*/ + +static void +_InsertTaskBuffer( + IN gcsTASK_CONTAINER_PTR AddAfter, + IN gcsTASK_CONTAINER_PTR Buffer + ) +{ + gcsTASK_CONTAINER_PTR addBefore; + + /* Cannot add before the first buffer. */ + gcmkASSERT(AddAfter != gcvNULL); + + /* Create a shortcut to the next buffer. */ + addBefore = AddAfter->allocNext; + + /* Initialize the links. */ + Buffer->allocPrev = AddAfter; + Buffer->allocNext = addBefore; + + /* Link to the previous buffer. */ + AddAfter->allocNext = Buffer; + + /* Link to the next buffer. */ + if (addBefore != gcvNULL) + { + addBefore->allocPrev = Buffer; + } +} + +static void +_RemoveTaskBuffer( + IN gcsTASK_CONTAINER_PTR Buffer + ) +{ + gcsTASK_CONTAINER_PTR prev; + gcsTASK_CONTAINER_PTR next; + + /* Cannot remove the first buffer. */ + gcmkASSERT(Buffer->allocPrev != gcvNULL); + + /* Create shortcuts to the previous and next buffers. */ + prev = Buffer->allocPrev; + next = Buffer->allocNext; + + /* Tail buffer? */ + if (next == gcvNULL) + { + /* Remove from the list. */ + prev->allocNext = gcvNULL; + } + + /* Buffer from the middle. */ + else + { + prev->allocNext = next; + next->allocPrev = prev; + } +} + + +/*----------------------------------------------------------------------------*/ +/*--------------------- Free Task Buffer List Management ---------------------*/ + +static void +_AppendToFreeList( + IN gckVGCOMMAND Command, + IN gcsTASK_CONTAINER_PTR Buffer + ) +{ + /* Cannot be a part of the free list already. */ + gcmkASSERT(Buffer->freePrev == gcvNULL); + gcmkASSERT(Buffer->freeNext == gcvNULL); + + /* First buffer to add? */ + if (Command->taskFreeHead == gcvNULL) + { + /* Terminate the links. */ + Buffer->freePrev = gcvFREE_TASK_TERMINATOR; + Buffer->freeNext = gcvFREE_TASK_TERMINATOR; + + /* Initialize the list pointer. */ + Command->taskFreeHead = Command->taskFreeTail = Buffer; + } + + /* Not the first, add after the tail. */ + else + { + /* Initialize the new tail buffer. */ + Buffer->freePrev = Command->taskFreeTail; + Buffer->freeNext = gcvFREE_TASK_TERMINATOR; + + /* Add after the tail. */ + Command->taskFreeTail->freeNext = Buffer; + Command->taskFreeTail = Buffer; + } +} + +static void +_RemoveFromFreeList( + IN gckVGCOMMAND Command, + IN gcsTASK_CONTAINER_PTR Buffer + ) +{ + /* Has to be a part of the free list. */ + gcmkASSERT(Buffer->freePrev != gcvNULL); + gcmkASSERT(Buffer->freeNext != gcvNULL); + + /* Head buffer? */ + if (Buffer->freePrev == gcvFREE_TASK_TERMINATOR) + { + /* Tail buffer as well? */ + if (Buffer->freeNext == gcvFREE_TASK_TERMINATOR) + { + /* Reset the list pointer. */ + Command->taskFreeHead = Command->taskFreeTail = gcvNULL; + } + + /* No, just the head. */ + else + { + /* Update the head. */ + Command->taskFreeHead = Buffer->freeNext; + + /* Terminate the next buffer. */ + Command->taskFreeHead->freePrev = gcvFREE_TASK_TERMINATOR; + } + } + + /* Not the head. */ + else + { + /* Tail buffer? */ + if (Buffer->freeNext == gcvFREE_TASK_TERMINATOR) + { + /* Update the tail. */ + Command->taskFreeTail = Buffer->freePrev; + + /* Terminate the previous buffer. */ + Command->taskFreeTail->freeNext = gcvFREE_TASK_TERMINATOR; + } + + /* A buffer in the middle. */ + else + { + /* Remove the buffer from the list. */ + Buffer->freePrev->freeNext = Buffer->freeNext; + Buffer->freeNext->freePrev = Buffer->freePrev; + } + } + + /* Reset free list pointers. */ + Buffer->freePrev = gcvNULL; + Buffer->freeNext = gcvNULL; +} + + +/*----------------------------------------------------------------------------*/ +/*-------------------------- Task Buffer Allocation --------------------------*/ + +static void +_SplitTaskBuffer( + IN gckVGCOMMAND Command, + IN gcsTASK_CONTAINER_PTR Buffer, + IN gctUINT Size + ) +{ + /* Determine the size of the new buffer. */ + gctINT splitBufferSize = Buffer->size - Size; + gcmkASSERT(splitBufferSize >= 0); + + /* Is the split buffer big enough to become a separate buffer? */ + if (splitBufferSize >= gcvMIN_TASK_BUFFER) + { + /* Place the new path data. */ + gcsTASK_CONTAINER_PTR splitBuffer = (gcsTASK_CONTAINER_PTR) + ( + (gctUINT8_PTR) Buffer + Size + ); + + /* Set the trimmed buffer size. */ + Buffer->size = Size; + + /* Initialize the split buffer. */ + splitBuffer->referenceCount = 0; + splitBuffer->size = splitBufferSize; + splitBuffer->freePrev = gcvNULL; + splitBuffer->freeNext = gcvNULL; + + /* Link in. */ + _InsertTaskBuffer(Buffer, splitBuffer); + _AppendToFreeList(Command, splitBuffer); + } +} + +static gceSTATUS +_AllocateTaskContainer( + IN gckVGCOMMAND Command, + IN gctUINT Size, + OUT gcsTASK_CONTAINER_PTR * Buffer + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Command=0x%x Size=0x%x, Buffer ==0x%x", Command, Size, Buffer); + + /* Verify arguments. */ + gcmkVERIFY_ARGUMENT(Buffer != gcvNULL); + + do + { + gcsTASK_STORAGE_PTR storage; + gcsTASK_CONTAINER_PTR buffer; + + /* Adjust the size. */ + Size += gcmSIZEOF(gcsTASK_CONTAINER); + + /* Adjust the allocation size if not big enough. */ + if (Size > Command->taskStorageUsable) + { + Command->taskStorageGranularity + = gcmALIGN(Size + gcmSIZEOF(gcsTASK_STORAGE), 1024); + + Command->taskStorageUsable + = Command->taskStorageGranularity - gcmSIZEOF(gcsTASK_STORAGE); + } + + /* Is there a free buffer available? */ + else if (Command->taskFreeHead != gcvNULL) + { + /* Set the initial free buffer. */ + gcsTASK_CONTAINER_PTR buffer = Command->taskFreeHead; + + do + { + /* Is the buffer big enough? */ + if (buffer->size >= Size) + { + /* Remove the buffer from the free list. */ + _RemoveFromFreeList(Command, buffer); + + /* Split the buffer. */ + _SplitTaskBuffer(Command, buffer, Size); + + /* Set the result. */ + * Buffer = buffer; + + gcmkFOOTER_ARG("*Buffer=0x%x",*Buffer); + /* Success. */ + return gcvSTATUS_OK; + } + + /* Get the next free buffer. */ + buffer = buffer->freeNext; + } + while (buffer != gcvFREE_TASK_TERMINATOR); + } + + /* Allocate a container. */ + gcmkERR_BREAK(gckOS_Allocate( + Command->os, + Command->taskStorageGranularity, + (gctPOINTER *) &storage + )); + + /* Link in the storage buffer. */ + storage->next = Command->taskStorage; + Command->taskStorage = storage; + + /* Place the task buffer. */ + buffer = (gcsTASK_CONTAINER_PTR) (storage + 1); + + /* Determine the size of the buffer. */ + buffer->size + = Command->taskStorageGranularity + - gcmSIZEOF(gcsTASK_STORAGE); + + /* Initialize the task buffer. */ + buffer->referenceCount = 0; + buffer->allocPrev = gcvNULL; + buffer->allocNext = gcvNULL; + buffer->freePrev = gcvNULL; + buffer->freeNext = gcvNULL; + + /* Split the buffer. */ + _SplitTaskBuffer(Command, buffer, Size); + + /* Set the result. */ + * Buffer = buffer; + + gcmkFOOTER_ARG("*Buffer=0x%x",*Buffer); + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +static void +_FreeTaskContainer( + IN gckVGCOMMAND Command, + IN gcsTASK_CONTAINER_PTR Buffer + ) +{ + gcsTASK_CONTAINER_PTR prev; + gcsTASK_CONTAINER_PTR next; + gcsTASK_CONTAINER_PTR merged; + + gctUINT32 mergedSize; + + /* Verify arguments. */ + gcmkASSERT(Buffer != gcvNULL); + gcmkASSERT(Buffer->freePrev == gcvNULL); + gcmkASSERT(Buffer->freeNext == gcvNULL); + + /* Get shortcuts to the previous and next path data buffers. */ + prev = Buffer->allocPrev; + next = Buffer->allocNext; + + /* Is the previous path data buffer already free? */ + if (prev && prev->freeNext) + { + /* The previous path data buffer is the one that remains. */ + merged = prev; + + /* Is the next path data buffer already free? */ + if (next && next->freeNext) + { + /* Merge all three path data buffers into the previous. */ + mergedSize = prev->size + Buffer->size + next->size; + + /* Remove the next path data buffer. */ + _RemoveFromFreeList(Command, next); + _RemoveTaskBuffer(next); + } + else + { + /* Merge the current path data buffer into the previous. */ + mergedSize = prev->size + Buffer->size; + } + + /* Delete the current path data buffer. */ + _RemoveTaskBuffer(Buffer); + + /* Set new size. */ + merged->size = mergedSize; + } + else + { + /* The current path data buffer is the one that remains. */ + merged = Buffer; + + /* Is the next buffer already free? */ + if (next && next->freeNext) + { + /* Merge the next into the current. */ + mergedSize = Buffer->size + next->size; + + /* Remove the next buffer. */ + _RemoveFromFreeList(Command, next); + _RemoveTaskBuffer(next); + + /* Set new size. */ + merged->size = mergedSize; + } + + /* Add the current buffer into the free list. */ + _AppendToFreeList(Command, merged); + } +} + +gceSTATUS +_RemoveRecordFromProcesDB( + IN gckVGCOMMAND Command, + IN gcsTASK_HEADER_PTR Task + ) +{ + gceSTATUS status; + gcsTASK_PTR task = (gcsTASK_PTR)((gctUINT8_PTR)Task - sizeof(gcsTASK)); + gcsTASK_FREE_VIDEO_MEMORY_PTR freeVideoMemory; + gcsTASK_UNLOCK_VIDEO_MEMORY_PTR unlockVideoMemory; + gctINT pid; + gctUINT32 size; + gctUINT32 handle; + gckKERNEL kernel = Command->kernel->kernel; + gckVIDMEM_NODE unlockNode = gcvNULL; + gckVIDMEM_NODE nodeObject = gcvNULL; + gceDATABASE_TYPE type; + + /* Get the total size of all tasks. */ + size = task->size; + + gcmkVERIFY_OK(gckOS_GetProcessID((gctUINT32_PTR)&pid)); + + do + { + switch (Task->id) + { + case gcvTASK_FREE_VIDEO_MEMORY: + freeVideoMemory = (gcsTASK_FREE_VIDEO_MEMORY_PTR)Task; + + handle = (gctUINT32)freeVideoMemory->node; + + status = gckVIDMEM_HANDLE_Lookup( + Command->kernel->kernel, + pid, + handle, + &nodeObject); + + if (gcmIS_ERROR(status)) + { + return status; + } + + gckVIDMEM_HANDLE_Dereference(kernel, pid, handle); + freeVideoMemory->node = gcmALL_TO_UINT32(nodeObject); + + type = gcvDB_VIDEO_MEMORY + | (nodeObject->type << gcdDB_VIDEO_MEMORY_TYPE_SHIFT) + | (nodeObject->pool << gcdDB_VIDEO_MEMORY_POOL_SHIFT); + + /* Remove record from process db. */ + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Command->kernel->kernel, + pid, + type, + gcmINT2PTR(handle))); + + /* Advance to next task. */ + size -= sizeof(gcsTASK_FREE_VIDEO_MEMORY); + Task = (gcsTASK_HEADER_PTR)(freeVideoMemory + 1); + + break; + case gcvTASK_UNLOCK_VIDEO_MEMORY: + unlockVideoMemory = (gcsTASK_UNLOCK_VIDEO_MEMORY_PTR)Task; + + /* Remove record from process db. */ + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Command->kernel->kernel, + pid, + gcvDB_VIDEO_MEMORY_LOCKED, + gcmUINT64_TO_PTR(unlockVideoMemory->node))); + + handle = (gctUINT32)unlockVideoMemory->node; + + status = gckVIDMEM_HANDLE_Lookup( + Command->kernel->kernel, + pid, + handle, + &unlockNode); + + if (gcmIS_ERROR(status)) + { + return status; + } + + gckVIDMEM_HANDLE_Dereference(kernel, pid, handle); + unlockVideoMemory->node = gcmPTR_TO_UINT64(unlockNode); + + /* Advance to next task. */ + size -= sizeof(gcsTASK_UNLOCK_VIDEO_MEMORY); + Task = (gcsTASK_HEADER_PTR)(unlockVideoMemory + 1); + + break; + default: + /* Skip the whole task. */ + size = 0; + break; + } + } + while(size); + + return gcvSTATUS_OK; +} + +/******************************************************************************\ +********************************* Task Scheduling ****************************** +\******************************************************************************/ + +static gceSTATUS +_ScheduleTasks( + IN gckVGCOMMAND Command, + IN gcsTASK_MASTER_TABLE_PTR TaskTable, + IN gctUINT8_PTR PreviousEnd + ) +{ + gceSTATUS status; + + do + { + gctINT block; + gcsTASK_CONTAINER_PTR container; + gcsTASK_MASTER_ENTRY_PTR userTaskEntry; + gcsBLOCK_TASK_ENTRY_PTR kernelTaskEntry; + gcsTASK_PTR userTask; + gctUINT8_PTR kernelTask; + gctINT32 interrupt; + gctUINT8_PTR eventCommand; + +#ifdef __QNXNTO__ + gcsTASK_PTR oldUserTask = gcvNULL; + gctPOINTER pointer; +#endif + + /* Nothing to schedule? */ + if (TaskTable->size == 0) + { + status = gcvSTATUS_OK; + break; + } + + /* Acquire the mutex. */ + gcmkERR_BREAK(gckOS_AcquireMutex( + Command->os, + Command->taskMutex, + gcvINFINITE + )); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d)\n", + __FUNCTION__, __LINE__ + ); + + do + { + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " number of tasks scheduled = %d\n" + " size of event data in bytes = %d\n", + TaskTable->count, + TaskTable->size + ); + + /* Allocate task buffer. */ + gcmkERR_BREAK(_AllocateTaskContainer( + Command, + TaskTable->size, + &container + )); + + /* Determine the task data pointer. */ + kernelTask = (gctUINT8_PTR) (container + 1); + + /* Initialize the reference count. */ + container->referenceCount = TaskTable->count; + + /* Process tasks. */ + for (block = gcvBLOCK_COUNT - 1; block >= 0; block -= 1) + { + /* Get the current user table entry. */ + userTaskEntry = &TaskTable->table[block]; + + /* Are there tasks scheduled? */ + if (userTaskEntry->head == gcvNULL) + { + /* No, skip to the next block. */ + continue; + } + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " processing tasks for block %d\n", + block + ); + + /* Get the current kernel table entry. */ + kernelTaskEntry = &Command->taskTable[block]; + + /* Are there tasks for the current block scheduled? */ + if (kernelTaskEntry->container == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " first task container for the block added\n", + block + ); + + /* Nothing yet, set the container buffer pointer. */ + kernelTaskEntry->container = container; + kernelTaskEntry->task = (gcsTASK_HEADER_PTR) kernelTask; + } + + /* Yes, append to the end. */ + else + { + kernelTaskEntry->link->cotainer = container; + kernelTaskEntry->link->task = (gcsTASK_HEADER_PTR) kernelTask; + } + + /* Set initial task. */ + userTask = userTaskEntry->head; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " copying user tasks over to the kernel\n" + ); + + /* Copy tasks. */ + do + { + gcsTASK_HEADER_PTR taskHeader; + +#ifdef __QNXNTO__ + oldUserTask = userTask; + + gcmkERR_BREAK(gckOS_MapUserPointer( + Command->os, + oldUserTask, + 0, + &pointer)); + + userTask = pointer; +#endif + + taskHeader = (gcsTASK_HEADER_PTR) (userTask + 1); + + gcmkVERIFY_OK(_RemoveRecordFromProcesDB(Command, taskHeader)); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " task ID = %d, size = %d\n", + ((gcsTASK_HEADER_PTR) (userTask + 1))->id, + userTask->size + ); + +#ifdef __QNXNTO__ + if (taskHeader->id == gcvTASK_SIGNAL) + { + ((gcsTASK_SIGNAL_PTR)taskHeader)->coid = TaskTable->coid; + ((gcsTASK_SIGNAL_PTR)taskHeader)->rcvid = TaskTable->rcvid; + } +#endif + + /* Copy the task data. */ + gcmkVERIFY_OK(gckOS_MemCopy( + kernelTask, taskHeader, userTask->size + )); + + /* Advance to the next task. */ + kernelTask += userTask->size; + userTask = userTask->next; + +#ifdef __QNXNTO__ + gcmkERR_BREAK(gckOS_UnmapUserPointer( + Command->os, + oldUserTask, + 0, + pointer)); +#endif + } + while (userTask != gcvNULL); + + /* Update link pointer in the header. */ + kernelTaskEntry->link = (gcsTASK_LINK_PTR) kernelTask; + + /* Initialize link task. */ + kernelTaskEntry->link->id = gcvTASK_LINK; + kernelTaskEntry->link->cotainer = gcvNULL; + kernelTaskEntry->link->task = gcvNULL; + + /* Advance the task data pointer. */ + kernelTask += gcmSIZEOF(gcsTASK_LINK); + } + } + while (gcvFALSE); + + /* Release the mutex. */ + gcmkERR_BREAK(gckOS_ReleaseMutex( + Command->os, + Command->taskMutex + )); + + /* Assign interrupts to the blocks. */ + eventCommand = PreviousEnd; + + for (block = gcvBLOCK_COUNT - 1; block >= 0; block -= 1) + { + /* Get the current user table entry. */ + userTaskEntry = &TaskTable->table[block]; + + /* Are there tasks scheduled? */ + if (userTaskEntry->head == gcvNULL) + { + /* No, skip to the next block. */ + continue; + } + + /* Get the interrupt number. */ + interrupt = _GetNextInterrupt(Command, block); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): block = %d interrupt = %d\n", + __FUNCTION__, __LINE__, + block, interrupt + ); + + /* Determine the command position. */ + eventCommand -= Command->info.eventCommandSize; + + /* Append an EVENT command. */ + gcmkERR_BREAK(gckVGCOMMAND_EventCommand( + Command, eventCommand, block, interrupt, gcvNULL + )); + } + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + + +/******************************************************************************\ +******************************** Memory Management ***************************** +\******************************************************************************/ + +static gceSTATUS +_HardwareToKernel( + IN gckOS Os, + IN gcuVIDMEM_NODE_PTR Node, + IN gctUINT32 Address, + OUT gctPOINTER * KernelPointer + ) +{ + gceSTATUS status; + gckVIDMEM memory; + gctUINT32 offset; + gctUINT32 nodePhysical; + gctPOINTER *logical; + gctSIZE_T bytes; + status = gcvSTATUS_OK; + + memory = Node->VidMem.memory; + + if (memory->object.type == gcvOBJ_VIDMEM) + { + nodePhysical = memory->baseAddress + + (gctUINT32)Node->VidMem.offset + + Node->VidMem.alignment; + bytes = Node->VidMem.bytes; + logical = &Node->VidMem.kernelVirtual; + } + else + { + nodePhysical = Node->Virtual.physicalAddress; + bytes = Node->Virtual.bytes; + logical = &Node->Virtual.kernelVirtual; + } + + if (*logical == gcvNULL) + { + status = gckOS_MapPhysical(Os, nodePhysical, bytes, logical); + + if (gcmkIS_ERROR(status)) + { + return status; + } + } + + offset = Address - nodePhysical; + *KernelPointer = (gctPOINTER)((gctUINT8_PTR)(*logical) + offset); + + /* Return status. */ + return status; +} + +static gceSTATUS +_ConvertUserCommandBufferPointer( + IN gckVGCOMMAND Command, + IN gcsCMDBUFFER_PTR UserCommandBuffer, + OUT gcsCMDBUFFER_PTR * KernelCommandBuffer + ) +{ + gceSTATUS status, last; + gcsCMDBUFFER_PTR mappedUserCommandBuffer = gcvNULL; + gckKERNEL kernel = Command->kernel->kernel; + gctUINT32 pid; + gckVIDMEM_NODE node; + + gckOS_GetProcessID(&pid); + + do + { + gctUINT32 headerAddress; + + /* Map the command buffer structure into the kernel space. */ + gcmkERR_BREAK(gckOS_MapUserPointer( + Command->os, + UserCommandBuffer, + gcmSIZEOF(gcsCMDBUFFER), + (gctPOINTER *) &mappedUserCommandBuffer + )); + + /* Determine the address of the header. */ + headerAddress + = mappedUserCommandBuffer->address + - mappedUserCommandBuffer->bufferOffset; + + gcmkERR_BREAK(gckVIDMEM_HANDLE_Lookup( + kernel, + pid, + gcmPTR2INT32(mappedUserCommandBuffer->node), + &node)); + + /* Translate the logical address to the kernel space. */ + gcmkERR_BREAK(_HardwareToKernel( + Command->os, + node->node, + headerAddress, + (gctPOINTER *) KernelCommandBuffer + )); + } + while (gcvFALSE); + + /* Unmap the user command buffer. */ + if (mappedUserCommandBuffer != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_UnmapUserPointer( + Command->os, + UserCommandBuffer, + gcmSIZEOF(gcsCMDBUFFER), + mappedUserCommandBuffer + )); + } + + /* Return status. */ + return status; +} + +static gceSTATUS +_AllocateLinear( + IN gckVGCOMMAND Command, + IN gctUINT Size, + IN gctUINT Alignment, + OUT gcuVIDMEM_NODE_PTR * Node, + OUT gctUINT32 * Address, + OUT gctPOINTER * Logical + ) +{ + gceSTATUS status, last; + gctPOINTER logical; + gctPHYS_ADDR physical; + gctUINT32 address; + gctSIZE_T size = Size; + + do + { + gcmkERR_BREAK(gckOS_AllocateContiguous( + Command->os, + gcvFALSE, + &size, + &physical, + &logical + )); + + gcmkERR_BREAK(gckOS_GetPhysicalAddress(Command->os, logical, &address)); + + /* Set return values. */ + * Node = physical; + * Address = address; + * Logical = logical; + + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Roll back. */ + if (physical != gcvNULL) + { + /* Free the command buffer. */ + gcmkCHECK_STATUS(gckOS_FreeContiguous(Command->os, physical, logical, size)); + } + + /* Return status. */ + return status; +} + +static gceSTATUS +_FreeLinear( + IN gckVGKERNEL Kernel, + IN gcuVIDMEM_NODE_PTR Node, + IN gctPOINTER Logical + ) +{ + gceSTATUS status = gcvSTATUS_OK; + + do + { + gcmkERR_BREAK(gckOS_FreeContiguous(Kernel->os, Node, Logical, 1)); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +gceSTATUS +_AllocateCommandBuffer( + IN gckVGCOMMAND Command, + IN gctSIZE_T Size, + OUT gcsCMDBUFFER_PTR * CommandBuffer + ) +{ + gceSTATUS status, last; + gcuVIDMEM_NODE_PTR node = gcvNULL; + gcsCMDBUFFER_PTR commandBuffer = gcvNULL; + + do + { + gctUINT alignedHeaderSize; + gctUINT requestedSize; + gctUINT allocationSize; + gctUINT32 address = 0; + gctUINT8_PTR endCommand; + + /* Determine the aligned header size. */ + alignedHeaderSize + = (gctUINT32)gcmALIGN(gcmSIZEOF(gcsCMDBUFFER), Command->info.addressAlignment); + + /* Align the requested size. */ + requestedSize + = (gctUINT32)gcmALIGN(Size, Command->info.commandAlignment); + + /* Determine the size of the buffer to allocate. */ + allocationSize + = alignedHeaderSize + + requestedSize + + (gctUINT32)Command->info.staticTailSize; + + /* Allocate the command buffer. */ + gcmkERR_BREAK(_AllocateLinear( + Command, + allocationSize, + Command->info.addressAlignment, + &node, + &address, + (gctPOINTER *) &commandBuffer + )); + + /* Initialize the structure. */ + commandBuffer->completion = gcvVACANT_BUFFER; + commandBuffer->node = node; + commandBuffer->address = address + alignedHeaderSize; + commandBuffer->bufferOffset = alignedHeaderSize; + commandBuffer->size = requestedSize; + commandBuffer->offset = requestedSize; + commandBuffer->nextAllocated = gcvNULL; + commandBuffer->nextSubBuffer = gcvNULL; + + /* Determine the data count. */ + commandBuffer->dataCount + = (requestedSize + Command->info.staticTailSize) + / Command->info.commandAlignment; + + /* Determine the location of the END command. */ + endCommand + = (gctUINT8_PTR) commandBuffer + + alignedHeaderSize + + requestedSize; + + /* Append an END command. */ + gcmkERR_BREAK(gckVGCOMMAND_EndCommand( + Command, + endCommand, + Command->info.feBufferInt, + gcvNULL + )); + + /* Set the return pointer. */ + * CommandBuffer = commandBuffer; + + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Roll back. */ + if (node != gcvNULL) + { + /* Free the command buffer. */ + gcmkCHECK_STATUS(_FreeLinear(Command->kernel, node, commandBuffer)); + } + + /* Return status. */ + return status; +} + +static gceSTATUS +_FreeCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsCMDBUFFER_PTR CommandBuffer + ) +{ + gceSTATUS status; + + /* Free the buffer. */ + status = _FreeLinear(Kernel, CommandBuffer->node, CommandBuffer); + + /* Return status. */ + return status; +} + + +/******************************************************************************\ +****************************** TS Overflow Handler ***************************** +\******************************************************************************/ + +static gceSTATUS +_EventHandler_TSOverflow( + IN gckVGKERNEL Kernel + ) +{ + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): **** TS OVERFLOW ENCOUNTERED ****\n", + __FUNCTION__, __LINE__ + ); + + return gcvSTATUS_OK; +} + + +/******************************************************************************\ +****************************** Bus Error Handler ******************************* +\******************************************************************************/ + +static gceSTATUS +_EventHandler_BusError( + IN gckVGKERNEL Kernel + ) +{ + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): **** BUS ERROR ENCOUNTERED ****\n", + __FUNCTION__, __LINE__ + ); + + return gcvSTATUS_OK; +} + +/******************************************************************************\ +****************************** Power Stall Handler ******************************* +\******************************************************************************/ + +static gceSTATUS +_EventHandler_PowerStall( + IN gckVGKERNEL Kernel + ) +{ + /* Signal. */ + return gckOS_Signal( + Kernel->os, + Kernel->command->powerStallSignal, + gcvTRUE); +} + +/******************************************************************************\ +******************************** Task Routines ********************************* +\******************************************************************************/ + +typedef gceSTATUS (* gctTASKROUTINE) ( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskLink( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskCluster( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskIncrement( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskDecrement( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskSignal( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskLockdown( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskUnlockVideoMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskFreeVideoMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskFreeContiguousMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gceSTATUS +_TaskUnmapUserMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ); + +static gctTASKROUTINE _taskRoutine[] = +{ + _TaskLink, /* gcvTASK_LINK */ + _TaskCluster, /* gcvTASK_CLUSTER */ + _TaskIncrement, /* gcvTASK_INCREMENT */ + _TaskDecrement, /* gcvTASK_DECREMENT */ + _TaskSignal, /* gcvTASK_SIGNAL */ + _TaskLockdown, /* gcvTASK_LOCKDOWN */ + _TaskUnlockVideoMemory, /* gcvTASK_UNLOCK_VIDEO_MEMORY */ + _TaskFreeVideoMemory, /* gcvTASK_FREE_VIDEO_MEMORY */ + _TaskFreeContiguousMemory, /* gcvTASK_FREE_CONTIGUOUS_MEMORY */ + _TaskUnmapUserMemory, /* gcvTASK_UNMAP_USER_MEMORY */ +}; + +static gceSTATUS +_TaskLink( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + /* Cast the task pointer. */ + gcsTASK_LINK_PTR task = (gcsTASK_LINK_PTR) TaskHeader->task; + + /* Save the pointer to the container. */ + gcsTASK_CONTAINER_PTR container = TaskHeader->container; + + /* No more tasks in the list? */ + if (task->task == gcvNULL) + { + /* Reset the entry. */ + TaskHeader->container = gcvNULL; + TaskHeader->task = gcvNULL; + TaskHeader->link = gcvNULL; + } + else + { + /* Update the entry. */ + TaskHeader->container = task->cotainer; + TaskHeader->task = task->task; + } + + /* Decrement the task buffer reference. */ + gcmkASSERT(container->referenceCount >= 0); + if (container->referenceCount == 0) + { + /* Free the container. */ + _FreeTaskContainer(Command, container); + } + + /* Success. */ + return gcvSTATUS_OK; +} + +static gceSTATUS +_TaskCluster( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status = gcvSTATUS_OK; + + /* Cast the task pointer. */ + gcsTASK_CLUSTER_PTR cluster = (gcsTASK_CLUSTER_PTR) TaskHeader->task; + + /* Get the number of tasks. */ + gctUINT taskCount = cluster->taskCount; + + /* Advance to the next task. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (cluster + 1); + + /* Perform all tasks in the cluster. */ + while (taskCount) + { + /* Perform the current task. */ + gcmkERR_BREAK(_taskRoutine[TaskHeader->task->id]( + Command, + TaskHeader + )); + + /* Update the task count. */ + taskCount -= 1; + } + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskIncrement( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + + do + { + /* Cast the task pointer. */ + gcsTASK_INCREMENT_PTR task = (gcsTASK_INCREMENT_PTR) TaskHeader->task; + + /* Convert physical into logical address. */ + gctUINT32_PTR logical; + gcmkERR_BREAK(gckOS_MapPhysical( + Command->os, + task->address, + gcmSIZEOF(gctUINT32), + (gctPOINTER *) &logical + )); + + /* Increment data. */ + (* logical) += 1; + + /* Unmap the physical memory. */ + gcmkERR_BREAK(gckOS_UnmapPhysical( + Command->os, + logical, + gcmSIZEOF(gctUINT32) + )); + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskDecrement( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + + do + { + /* Cast the task pointer. */ + gcsTASK_DECREMENT_PTR task = (gcsTASK_DECREMENT_PTR) TaskHeader->task; + + /* Convert physical into logical address. */ + gctUINT32_PTR logical; + gcmkERR_BREAK(gckOS_MapPhysical( + Command->os, + task->address, + gcmSIZEOF(gctUINT32), + (gctPOINTER *) &logical + )); + + /* Decrement data. */ + (* logical) -= 1; + + /* Unmap the physical memory. */ + gcmkERR_BREAK(gckOS_UnmapPhysical( + Command->os, + logical, + gcmSIZEOF(gctUINT32) + )); + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskSignal( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + + do + { + /* Cast the task pointer. */ + gcsTASK_SIGNAL_PTR task = (gcsTASK_SIGNAL_PTR) TaskHeader->task; + + + /* Map the signal into kernel space. */ +#ifdef __QNXNTO__ + gcmkERR_BREAK(gckOS_UserSignal( + Command->os, task->signal, task->rcvid, task->coid + )); +#else + gcmkERR_BREAK(gckOS_UserSignal( + Command->os, task->signal, task->process + )); +#endif /* __QNXNTO__ */ + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskLockdown( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + gctUINT32_PTR userCounter = gcvNULL; + gctUINT32_PTR kernelCounter = gcvNULL; + gctSIGNAL signal = gcvNULL; + + do + { + /* Cast the task pointer. */ + gcsTASK_LOCKDOWN_PTR task = (gcsTASK_LOCKDOWN_PTR) TaskHeader->task; + + /* Convert physical addresses into logical. */ + gcmkERR_BREAK(gckOS_MapPhysical( + Command->os, + task->userCounter, + gcmSIZEOF(gctUINT32), + (gctPOINTER *) &userCounter + )); + + gcmkERR_BREAK(gckOS_MapPhysical( + Command->os, + task->kernelCounter, + gcmSIZEOF(gctUINT32), + (gctPOINTER *) &kernelCounter + )); + + /* Update the kernel counter. */ + (* kernelCounter) += 1; + + /* Are the counters equal? */ + if ((* userCounter) == (* kernelCounter)) + { + /* Map the signal into kernel space. */ + gcmkERR_BREAK(gckOS_MapSignal( + Command->os, task->signal, task->process, &signal + )); + + if (signal == gcvNULL) + { + /* Signal. */ + gcmkERR_BREAK(gckOS_Signal( + Command->os, task->signal, gcvTRUE + )); + } + else + { + /* Signal. */ + gcmkERR_BREAK(gckOS_Signal( + Command->os, signal, gcvTRUE + )); + } + } + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Destroy the mapped signal. */ + if (signal != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DestroySignal( + Command->os, signal + )); + } + + /* Unmap the physical memory. */ + if (kernelCounter != gcvNULL) + { + gcmkVERIFY_OK(gckOS_UnmapPhysical( + Command->os, + kernelCounter, + gcmSIZEOF(gctUINT32) + )); + } + + if (userCounter != gcvNULL) + { + gcmkVERIFY_OK(gckOS_UnmapPhysical( + Command->os, + userCounter, + gcmSIZEOF(gctUINT32) + )); + } + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskUnlockVideoMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + + do + { + /* Cast the task pointer. */ + gcsTASK_UNLOCK_VIDEO_MEMORY_PTR task + = (gcsTASK_UNLOCK_VIDEO_MEMORY_PTR) TaskHeader->task; + + /* Unlock video memory. */ + gcmkERR_BREAK(gckVIDMEM_Unlock( + Command->kernel->kernel, + (gckVIDMEM_NODE)gcmUINT64_TO_PTR(task->node), + gcvSURF_TYPE_UNKNOWN, + gcvNULL)); + + gcmkERR_BREAK(gckVIDMEM_NODE_Dereference( + Command->kernel->kernel, + gcmUINT64_TO_PTR(task->node))); + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskFreeVideoMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + + do + { + /* Cast the task pointer. */ + gcsTASK_FREE_VIDEO_MEMORY_PTR task + = (gcsTASK_FREE_VIDEO_MEMORY_PTR) TaskHeader->task; + + /* Free video memory. */ + gcmkERR_BREAK(gckVIDMEM_NODE_Dereference( + Command->kernel->kernel, + gcmINT2PTR(task->node))); + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskFreeContiguousMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + + do + { + /* Cast the task pointer. */ + gcsTASK_FREE_CONTIGUOUS_MEMORY_PTR task + = (gcsTASK_FREE_CONTIGUOUS_MEMORY_PTR) TaskHeader->task; + + /* Free contiguous memory. */ + gcmkERR_BREAK(gckOS_FreeContiguous( + Command->os, task->physical, task->logical, task->bytes + )); + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_TaskUnmapUserMemory( + gckVGCOMMAND Command, + gcsBLOCK_TASK_ENTRY_PTR TaskHeader + ) +{ + gceSTATUS status; + gctPOINTER info; + + do + { + /* Cast the task pointer. */ + gcsTASK_UNMAP_USER_MEMORY_PTR task + = (gcsTASK_UNMAP_USER_MEMORY_PTR) TaskHeader->task; + + info = gckKERNEL_QueryPointerFromName( + Command->kernel->kernel, gcmALL_TO_UINT32(task->info)); + + /* Unmap the user memory. */ + gcmkERR_BREAK(gckOS_UnmapUserMemory( + Command->os, gcvCORE_VG, task->memory, task->size, info, task->address + )); + + /* Update the reference counter. */ + TaskHeader->container->referenceCount -= 1; + + /* Update the task pointer. */ + TaskHeader->task = (gcsTASK_HEADER_PTR) (task + 1); + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +/******************************************************************************\ +************ Hardware Block Interrupt Handlers For Scheduled Events ************ +\******************************************************************************/ + +static gceSTATUS +_EventHandler_Block( + IN gckVGKERNEL Kernel, + IN gcsBLOCK_TASK_ENTRY_PTR TaskHeader, + IN gctBOOL ProcessAll + ) +{ + gceSTATUS status = gcvSTATUS_OK, last; + + gcmkHEADER_ARG("Kernel=0x%x TaskHeader=0x%x ProcessAll=0x%x", Kernel, TaskHeader, ProcessAll); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + if (TaskHeader->task == gcvNULL) + { + gcmkFOOTER(); + return gcvSTATUS_OK; + } + + do + { + gckVGCOMMAND command; + + /* Get the command buffer object. */ + command = Kernel->command; + + /* Increment the interrupt usage semaphore. */ + gcmkERR_BREAK(gckOS_IncrementSemaphore( + command->os, TaskHeader->interruptSemaphore + )); + + /* Acquire the mutex. */ + gcmkERR_BREAK(gckOS_AcquireMutex( + command->os, + command->taskMutex, + gcvINFINITE + )); + + /* Verify inputs. */ + gcmkASSERT(TaskHeader != gcvNULL); + gcmkASSERT(TaskHeader->container != gcvNULL); + gcmkASSERT(TaskHeader->task != gcvNULL); + gcmkASSERT(TaskHeader->link != gcvNULL); + + /* Process tasks. */ + do + { + /* Process the current task. */ + gcmkERR_BREAK(_taskRoutine[TaskHeader->task->id]( + command, + TaskHeader + )); + + /* Is the next task is LINK? */ + if (TaskHeader->task->id == gcvTASK_LINK) + { + gcmkERR_BREAK(_taskRoutine[TaskHeader->task->id]( + command, + TaskHeader + )); + + /* Done. */ + break; + } + } + while (ProcessAll); + + /* Release the mutex. */ + gcmkCHECK_STATUS(gckOS_ReleaseMutex( + command->os, + command->taskMutex + )); + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +gcmDECLARE_INTERRUPT_HANDLER(COMMAND, 0) +{ + gceSTATUS status, last; + + gcmkHEADER_ARG("Kernel=0x%x ", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + + do + { + gckVGCOMMAND command; + gcsKERNEL_QUEUE_HEADER_PTR mergeQueue; + gcsKERNEL_QUEUE_HEADER_PTR queueTail; + gcsKERNEL_CMDQUEUE_PTR entry; + gctUINT entryCount; + + /* Get the command buffer object. */ + command = Kernel->command; + + /* Acquire the mutex. */ + gcmkERR_BREAK(gckOS_AcquireMutex( + command->os, + command->queueMutex, + gcvINFINITE + )); + + /* Get the current queue. */ + queueTail = command->queueTail; + + /* Get the current queue entry. */ + entry = queueTail->currentEntry; + + /* Get the number of entries in the queue. */ + entryCount = queueTail->pending; + + /* Process all entries. */ + while (gcvTRUE) + { + /* Call post-execution function. */ + status = entry->handler(Kernel, entry); + + /* Failed? */ + if (gcmkIS_ERROR(status)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, + gcvZONE_COMMAND, + "[%s] line %d: post action failed.\n", + __FUNCTION__, __LINE__ + ); + } + + /* Executed the next buffer? */ + if (status == gcvSTATUS_EXECUTED) + { + /* Update the queue. */ + queueTail->pending = entryCount; + queueTail->currentEntry = entry; + + /* Success. */ + status = gcvSTATUS_OK; + + /* Break out of the loop. */ + break; + } + + /* Advance to the next entry. */ + entry += 1; + entryCount -= 1; + + /* Last entry? */ + if (entryCount == 0) + { + /* Reset the queue to idle. */ + queueTail->pending = 0; + + /* Get a shortcut to the queue to merge with. */ + mergeQueue = command->mergeQueue; + + /* Merge the queues if necessary. */ + if (mergeQueue != queueTail) + { + gcmkASSERT(mergeQueue < queueTail); + gcmkASSERT(mergeQueue->next == queueTail); + + mergeQueue->size + += gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) + + queueTail->size; + + mergeQueue->next = queueTail->next; + } + + /* Advance to the next queue. */ + queueTail = queueTail->next; + + /* Did it wrap around? */ + if (command->queue == queueTail) + { + /* Reset merge queue. */ + command->mergeQueue = queueTail; + } + + /* Set new queue. */ + command->queueTail = queueTail; + + /* Is the next queue scheduled? */ + if (queueTail->pending > 0) + { + gcsCMDBUFFER_PTR commandBuffer; + + /* The first entry must be a command buffer. */ + commandBuffer = queueTail->currentEntry->commandBuffer; + + /* Start the command processor. */ + status = gckVGHARDWARE_Execute( + command->hardware, + commandBuffer->address, + commandBuffer->dataCount + ); + + /* Failed? */ + if (gcmkIS_ERROR(status)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, + gcvZONE_COMMAND, + "[%s] line %d: failed to start the next queue.\n", + __FUNCTION__, __LINE__ + ); + } + } + else + { + status = gckVGHARDWARE_SetPowerManagementState( + Kernel->command->hardware, gcvPOWER_IDLE_BROADCAST + ); + } + + /* Break out of the loop. */ + break; + } + } + + /* Release the mutex. */ + gcmkCHECK_STATUS(gckOS_ReleaseMutex( + command->os, + command->queueMutex + )); + } + while (gcvFALSE); + + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +/* Define standard block interrupt handlers. */ +gcmDEFINE_INTERRUPT_HANDLER(TESSELLATOR, 0) +gcmDEFINE_INTERRUPT_HANDLER(VG, 0) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 0) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 1) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 2) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 3) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 4) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 5) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 6) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 7) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 8) +gcmDEFINE_INTERRUPT_HANDLER(PIXEL, 9) + +/* The entries in the array are arranged by event priority. */ +static gcsBLOCK_INTERRUPT_HANDLER _blockHandlers[] = +{ + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(TESSELLATOR, 0), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(VG, 0), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 0), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 1), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 2), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 3), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 4), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 5), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 6), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 7), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 8), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(PIXEL, 9), + gcmDEFINE_INTERRUPT_HANDLER_ENTRY(COMMAND, 0), +}; + + +/******************************************************************************\ +************************* Static Command Buffer Handlers *********************** +\******************************************************************************/ + +static gceSTATUS +_UpdateStaticCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d)\n", + __FUNCTION__, __LINE__ + ); + + /* Success. */ + return gcvSTATUS_OK; +} + +static gceSTATUS +_ExecuteStaticCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gceSTATUS status; + + do + { + gcsCMDBUFFER_PTR commandBuffer; + + /* Cast the command buffer header. */ + commandBuffer = Entry->commandBuffer; + + /* Set to update the command buffer next time. */ + Entry->handler = _UpdateStaticCommandBuffer; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): executing next buffer @ 0x%08X, data count = %d\n", + __FUNCTION__, __LINE__, + commandBuffer->address, + commandBuffer->dataCount + ); + + /* Start the command processor. */ + gcmkERR_BREAK(gckVGHARDWARE_Execute( + Kernel->hardware, + commandBuffer->address, + commandBuffer->dataCount + )); + + /* Success. */ + return gcvSTATUS_EXECUTED; + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_UpdateLastStaticCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ +#if gcvDEBUG || gcdFORCE_MESSAGES + /* Get the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer; + + /* Validate the command buffer. */ + gcmkASSERT(commandBuffer->completion != gcvNULL); + gcmkASSERT(commandBuffer->completion != gcvVACANT_BUFFER); + +#endif + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): processing all tasks scheduled for FE.\n", + __FUNCTION__, __LINE__ + ); + + /* Perform scheduled tasks. */ + return _EventHandler_Block( + Kernel, + &Kernel->command->taskTable[gcvBLOCK_COMMAND], + gcvTRUE + ); +} + +static gceSTATUS +_ExecuteLastStaticCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gceSTATUS status; + + do + { + /* Cast the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer; + + /* Set to update the command buffer next time. */ + Entry->handler = _UpdateLastStaticCommandBuffer; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): executing next buffer @ 0x%08X, data count = %d\n", + __FUNCTION__, __LINE__, + commandBuffer->address, + commandBuffer->dataCount + ); + + /* Start the command processor. */ + gcmkERR_BREAK(gckVGHARDWARE_Execute( + Kernel->hardware, + commandBuffer->address, + commandBuffer->dataCount + )); + + /* Success. */ + return gcvSTATUS_EXECUTED; + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + + +/******************************************************************************\ +************************* Dynamic Command Buffer Handlers ********************** +\******************************************************************************/ + +static gceSTATUS +_UpdateDynamicCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d)\n", + __FUNCTION__, __LINE__ + ); + + /* Success. */ + return gcvSTATUS_OK; +} + +static gceSTATUS +_ExecuteDynamicCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gceSTATUS status; + + do + { + /* Cast the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer; + + /* Set to update the command buffer next time. */ + Entry->handler = _UpdateDynamicCommandBuffer; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): executing next buffer @ 0x%08X, data count = %d\n", + __FUNCTION__, __LINE__, + commandBuffer->address, + commandBuffer->dataCount + ); + + /* Start the command processor. */ + gcmkERR_BREAK(gckVGHARDWARE_Execute( + Kernel->hardware, + commandBuffer->address, + commandBuffer->dataCount + )); + + /* Success. */ + return gcvSTATUS_EXECUTED; + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_UpdateLastDynamicCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ +#if gcvDEBUG || gcdFORCE_MESSAGES + /* Get the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer; + + /* Validate the command buffer. */ + gcmkASSERT(commandBuffer->completion != gcvNULL); + gcmkASSERT(commandBuffer->completion != gcvVACANT_BUFFER); + +#endif + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): processing all tasks scheduled for FE.\n", + __FUNCTION__, __LINE__ + ); + + /* Perform scheduled tasks. */ + return _EventHandler_Block( + Kernel, + &Kernel->command->taskTable[gcvBLOCK_COMMAND], + gcvTRUE + ); +} + +static gceSTATUS +_ExecuteLastDynamicCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gceSTATUS status; + + do + { + /* Cast the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer = Entry->commandBuffer; + + /* Set to update the command buffer next time. */ + Entry->handler = _UpdateLastDynamicCommandBuffer; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s(%d): executing next buffer @ 0x%08X, data count = %d\n", + __FUNCTION__, __LINE__, + commandBuffer->address, + commandBuffer->dataCount + ); + + /* Start the command processor. */ + gcmkERR_BREAK(gckVGHARDWARE_Execute( + Kernel->hardware, + commandBuffer->address, + commandBuffer->dataCount + )); + + /* Success. */ + return gcvSTATUS_EXECUTED; + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + + +/******************************************************************************\ +********************************* Other Handlers ******************************* +\******************************************************************************/ + +static gceSTATUS +_FreeKernelCommandBuffer( + IN gckVGKERNEL Kernel, + IN gcsKERNEL_CMDQUEUE_PTR Entry + ) +{ + gceSTATUS status; + + /* Free the command buffer. */ + status = _FreeCommandBuffer(Kernel, Entry->commandBuffer); + + /* Return status. */ + return status; +} + + +/******************************************************************************\ +******************************* Queue Management ******************************* +\******************************************************************************/ + +#if gcvDUMP_COMMAND_BUFFER +static void +_DumpCommandQueue( + IN gckVGCOMMAND Command, + IN gcsKERNEL_QUEUE_HEADER_PTR QueueHeader, + IN gctUINT EntryCount + ) +{ + gcsKERNEL_CMDQUEUE_PTR entry; + gctUINT queueIndex; + +#if defined(gcvCOMMAND_BUFFER_NAME) + static gctUINT arrayCount = 0; +#endif + + /* Is dumpinng enabled? */ + if (!Commad->enableDumping) + { + return; + } + +#if !defined(gcvCOMMAND_BUFFER_NAME) + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_COMMAND, + "COMMAND QUEUE DUMP: %d entries\n", EntryCount + ); +#endif + + /* Get the pointer to the first entry. */ + entry = QueueHeader->currentEntry; + + /* Iterate through the queue. */ + for (queueIndex = 0; queueIndex < EntryCount; queueIndex += 1) + { + gcsCMDBUFFER_PTR buffer; + gctUINT bufferCount; + gctUINT bufferIndex; + gctUINT i, count; + gctUINT size; + gctUINT32_PTR data; + +#if gcvDUMP_COMMAND_LINES + gctUINT lineNumber; +#endif + +#if !defined(gcvCOMMAND_BUFFER_NAME) + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_COMMAND, + "ENTRY %d\n", queueIndex + ); +#endif + + /* Reset the count. */ + bufferCount = 0; + + /* Set the initial buffer. */ + buffer = entry->commandBuffer; + + /* Loop through all subbuffers. */ + while (buffer) + { + /* Update the count. */ + bufferCount += 1; + + /* Advance to the next subbuffer. */ + buffer = buffer->nextSubBuffer; + } + +#if !defined(gcvCOMMAND_BUFFER_NAME) + if (bufferCount > 1) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, + gcvZONE_COMMAND, + " COMMAND BUFFER SET: %d buffers.\n", + bufferCount + ); + } +#endif + + /* Reset the buffer index. */ + bufferIndex = 0; + + /* Set the initial buffer. */ + buffer = entry->commandBuffer; + + /* Loop through all subbuffers. */ + while (buffer) + { + /* Determine the size of the buffer. */ + size = buffer->dataCount * Command->info.commandAlignment; + +#if !defined(gcvCOMMAND_BUFFER_NAME) + /* A single buffer? */ + if (bufferCount == 1) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, + gcvZONE_COMMAND, + " COMMAND BUFFER: count=%d (0x%X), size=%d bytes @ %08X.\n", + buffer->dataCount, + buffer->dataCount, + size, + buffer->address + ); + } + else + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, + gcvZONE_COMMAND, + " COMMAND BUFFER %d: count=%d (0x%X), size=%d bytes @ %08X\n", + bufferIndex, + buffer->dataCount, + buffer->dataCount, + size, + buffer->address + ); + } +#endif + + /* Determine the number of double words to print. */ + count = size / 4; + + /* Determine the buffer location. */ + data = (gctUINT32_PTR) + ( + (gctUINT8_PTR) buffer + buffer->bufferOffset + ); + +#if defined(gcvCOMMAND_BUFFER_NAME) + gcmkTRACE_ZONE( + gcvLEVEL_INFO, + gcvZONE_COMMAND, + "unsigned int _" gcvCOMMAND_BUFFER_NAME "_%d[] =\n", + arrayCount + ); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, + gcvZONE_COMMAND, + "{\n" + ); + + arrayCount += 1; +#endif + +#if gcvDUMP_COMMAND_LINES + /* Reset the line number. */ + lineNumber = 0; +#endif + +#if defined(gcvCOMMAND_BUFFER_NAME) + count -= 2; +#endif + + for (i = 0; i < count; i += 1) + { + if ((i % 8) == 0) + { +#if defined(gcvCOMMAND_BUFFER_NAME) + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "\t"); +#else + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, " "); +#endif + } + +#if gcvDUMP_COMMAND_LINES + if (lineNumber == gcvDUMP_COMMAND_LINES) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, " . . . . . . . . .\n"); + break; + } +#endif + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "0x%08X", data[i]); + + if (i + 1 == count) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, "\n"); + +#if gcvDUMP_COMMAND_LINES + lineNumber += 1; +#endif + } + else + { + if (((i + 1) % 8) == 0) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, ",\n"); + +#if gcvDUMP_COMMAND_LINES + lineNumber += 1; +#endif + } + else + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_COMMAND, ", "); + } + } + } + +#if defined(gcvCOMMAND_BUFFER_NAME) + gcmkTRACE_ZONE( + gcvLEVEL_INFO, + gcvZONE_COMMAND, + "};\n\n" + ); +#endif + + /* Advance to the next subbuffer. */ + buffer = buffer->nextSubBuffer; + bufferIndex += 1; + } + + /* Advance to the next entry. */ + entry += 1; + } +} +#endif + +static gceSTATUS +_LockCurrentQueue( + IN gckVGCOMMAND Command, + OUT gcsKERNEL_CMDQUEUE_PTR * Entries, + OUT gctUINT_PTR EntryCount + ) +{ + gceSTATUS status; + + do + { + gcsKERNEL_QUEUE_HEADER_PTR queueHead; + + /* Get a shortcut to the head of the queue. */ + queueHead = Command->queueHead; + + /* Is the head buffer still being worked on? */ + if (queueHead->pending) + { + /* Increment overflow count. */ + Command->queueOverflow += 1; + + /* Wait until the head becomes idle. */ + gcmkERR_BREAK(_WaitForIdle(Command, queueHead)); + } + + /* Acquire the mutex. */ + gcmkERR_BREAK(gckOS_AcquireMutex( + Command->os, + Command->queueMutex, + gcvINFINITE + )); + + /* Determine the first queue entry. */ + queueHead->currentEntry = (gcsKERNEL_CMDQUEUE_PTR) + ( + (gctUINT8_PTR) queueHead + gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) + ); + + /* Set the pointer to the first entry. */ + * Entries = queueHead->currentEntry; + + /* Determine the number of available entries. */ + * EntryCount = queueHead->size / gcmSIZEOF(gcsKERNEL_CMDQUEUE); + + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + +static gceSTATUS +_UnlockCurrentQueue( + IN gckVGCOMMAND Command, + IN gctUINT EntryCount + ) +{ + gceSTATUS status; + + do + { +#if !gcdENABLE_INFINITE_SPEED_HW + gcsKERNEL_QUEUE_HEADER_PTR queueTail; + gcsKERNEL_QUEUE_HEADER_PTR queueHead; + gcsKERNEL_QUEUE_HEADER_PTR queueNext; + gctUINT queueSize; + gctUINT newSize; + gctUINT unusedSize; + + /* Get shortcut to the head and to the tail of the queue. */ + queueTail = Command->queueTail; + queueHead = Command->queueHead; + + /* Dump the command buffer. */ +#if gcvDUMP_COMMAND_BUFFER + _DumpCommandQueue(Command, queueHead, EntryCount); +#endif + + /* Get a shortcut to the current queue size. */ + queueSize = queueHead->size; + + /* Determine the new queue size. */ + newSize = EntryCount * gcmSIZEOF(gcsKERNEL_CMDQUEUE); + gcmkASSERT(newSize <= queueSize); + + /* Determine the size of the unused area. */ + unusedSize = queueSize - newSize; + + /* Is the unused area big enough to become a buffer? */ + if (unusedSize >= gcvMINUMUM_BUFFER) + { + gcsKERNEL_QUEUE_HEADER_PTR nextHead; + + /* Place the new header. */ + nextHead = (gcsKERNEL_QUEUE_HEADER_PTR) + ( + (gctUINT8_PTR) queueHead + + gcmSIZEOF(gcsKERNEL_QUEUE_HEADER) + + newSize + ); + + /* Initialize the buffer. */ + nextHead->size = unusedSize - gcmSIZEOF(gcsKERNEL_QUEUE_HEADER); + nextHead->pending = 0; + + /* Link the buffer in. */ + nextHead->next = queueHead->next; + queueHead->next = nextHead; + queueNext = nextHead; + + /* Update the size of the current buffer. */ + queueHead->size = newSize; + } + + /* Not big enough. */ + else + { + /* Determine the next queue. */ + queueNext = queueHead->next; + } + + /* Mark the buffer as busy. */ + queueHead->pending = EntryCount; + + /* Advance to the next buffer. */ + Command->queueHead = queueNext; + + /* Start the command processor if the queue was empty. */ + if (queueTail == queueHead) + { + gcsCMDBUFFER_PTR commandBuffer; + + /* The first entry must be a command buffer. */ + commandBuffer = queueTail->currentEntry->commandBuffer; + + /* Start the command processor. */ + gcmkERR_BREAK(gckVGHARDWARE_Execute( + Command->hardware, + commandBuffer->address, + commandBuffer->dataCount + )); + } + + /* The queue was not empty. */ + else + { + /* Advance the merge buffer if needed. */ + if (queueHead == Command->mergeQueue) + { + Command->mergeQueue = queueNext; + } + } +#endif + + /* Release the mutex. */ + gcmkERR_BREAK(gckOS_ReleaseMutex( + Command->os, + Command->queueMutex + )); + + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Return status. */ + return status; +} + + + +/******************************************************************************\ +****************************** gckVGCOMMAND API Code ***************************** +\******************************************************************************/ +gceSTATUS +gckVGCOMMAND_Construct( + IN gckVGKERNEL Kernel, + IN gctUINT TaskGranularity, + IN gctUINT QueueSize, + OUT gckVGCOMMAND * Command + ) +{ + gceSTATUS status, last; + gckVGCOMMAND command = gcvNULL; + gcsKERNEL_QUEUE_HEADER_PTR queue; + gctUINT i, j; + + gcmkHEADER_ARG("Kernel=0x%x TaskGranularity=0x%x QueueSize=0x%x Command=0x%x", + Kernel, TaskGranularity, QueueSize, Command); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(QueueSize >= gcvMINUMUM_BUFFER); + gcmkVERIFY_ARGUMENT(Command != gcvNULL); + + do + { + /*********************************************************************** + ** Generic object initialization. + */ + + /* Allocate the gckVGCOMMAND structure. */ + gcmkERR_BREAK(gckOS_Allocate( + Kernel->os, + gcmSIZEOF(struct _gckVGCOMMAND), + (gctPOINTER *) &command + )); + + /* Initialize the object. */ + command->object.type = gcvOBJ_COMMAND; + + /* Set the object pointers. */ + command->kernel = Kernel; + command->os = Kernel->os; + command->hardware = Kernel->hardware; + + /* Reset pointers. */ + command->queue = gcvNULL; + command->queueMutex = gcvNULL; + command->taskMutex = gcvNULL; + command->commitMutex = gcvNULL; + + command->powerStallBuffer = gcvNULL; + command->powerStallSignal = gcvNULL; + command->powerSemaphore = gcvNULL; + + /* Reset context states. */ + command->contextCounter = 0; + command->currentContext = 0; + + /* Enable command buffer dumping. */ + command->enableDumping = gcvTRUE; + + /* Set features. */ + command->fe20 = Kernel->hardware->fe20; + command->vg20 = Kernel->hardware->vg20; + command->vg21 = Kernel->hardware->vg21; + + /* Reset task table .*/ + gcmkVERIFY_OK(gckOS_ZeroMemory( + command->taskTable, gcmSIZEOF(command->taskTable) + )); + + /* Query command buffer attributes. */ + gcmkERR_BREAK(gckVGCOMMAND_InitializeInfo(command)); + + /* Create the control mutexes. */ + gcmkERR_BREAK(gckOS_CreateMutex(Kernel->os, &command->queueMutex)); + gcmkERR_BREAK(gckOS_CreateMutex(Kernel->os, &command->taskMutex)); + gcmkERR_BREAK(gckOS_CreateMutex(Kernel->os, &command->commitMutex)); + + /* Create the power management semaphore. */ + gcmkERR_BREAK(gckOS_CreateSemaphore(Kernel->os, + &command->powerSemaphore)); + + gcmkERR_BREAK(gckOS_CreateSignal(Kernel->os, + gcvFALSE, &command->powerStallSignal)); + + /*********************************************************************** + ** Command queue initialization. + */ + + /* Allocate the command queue. */ + gcmkERR_BREAK(gckOS_Allocate( + Kernel->os, + QueueSize, + (gctPOINTER *) &command->queue + )); + + /* Initialize the command queue. */ + queue = command->queue; + + queue->size = QueueSize - gcmSIZEOF(gcsKERNEL_QUEUE_HEADER); + queue->pending = 0; + queue->next = queue; + + command->queueHead = + command->queueTail = + command->mergeQueue = command->queue; + + command->queueOverflow = 0; + + + /*********************************************************************** + ** Enable TS overflow interrupt. + */ + + command->info.tsOverflowInt = 0; + gcmkERR_BREAK(gckVGINTERRUPT_Enable( + Kernel->interrupt, + &command->info.tsOverflowInt, + _EventHandler_TSOverflow + )); + + /* Mask out the interrupt. */ + Kernel->hardware->eventMask &= ~(1 << command->info.tsOverflowInt); + + + /*********************************************************************** + ** Enable Bus Error interrupt. + */ + + /* Hardwired to bit 31. */ + command->busErrorInt = 31; + + /* Enable the interrupt. */ + gcmkERR_BREAK(gckVGINTERRUPT_Enable( + Kernel->interrupt, + &command->busErrorInt, + _EventHandler_BusError + )); + + + command->powerStallInt = 30; + /* Enable the interrupt. */ + gcmkERR_BREAK(gckVGINTERRUPT_Enable( + Kernel->interrupt, + &command->powerStallInt, + _EventHandler_PowerStall + )); + + /*********************************************************************** + ** Task management initialization. + */ + + command->taskStorage = gcvNULL; + command->taskStorageGranularity = TaskGranularity; + command->taskStorageUsable = TaskGranularity - gcmSIZEOF(gcsTASK_STORAGE); + + command->taskFreeHead = gcvNULL; + command->taskFreeTail = gcvNULL; + + /* Enable block handlers. */ + for (i = 0; i < gcmCOUNTOF(_blockHandlers); i += 1) + { + /* Get the target hardware block. */ + gceBLOCK block = _blockHandlers[i].block; + + /* Get the interrupt array entry. */ + gcsBLOCK_TASK_ENTRY_PTR entry = &command->taskTable[block]; + + /* Determine the interrupt value index. */ + gctUINT index = entry->interruptCount; + + /* Create the block semaphore. */ + if (entry->interruptSemaphore == gcvNULL) + { + gcmkERR_BREAK(gckOS_CreateSemaphoreVG( + command->os, &entry->interruptSemaphore + )); + } + + /* Enable auto-detection. */ + entry->interruptArray[index] = -1; + + /* Enable interrupt for the block. */ + gcmkERR_BREAK(gckVGINTERRUPT_Enable( + Kernel->interrupt, + &entry->interruptArray[index], + _blockHandlers[i].handler + )); + + /* Update the number of registered interrupts. */ + entry->interruptCount += 1; + + /* Inrement the semaphore to allow the usage of the registered + interrupt. */ + gcmkERR_BREAK(gckOS_IncrementSemaphore( + command->os, entry->interruptSemaphore + )); + + } + + /* Error? */ + if (gcmkIS_ERROR(status)) + { + break; + } + + /* Get the FE interrupt. */ + command->info.feBufferInt + = command->taskTable[gcvBLOCK_COMMAND].interruptArray[0]; + + /* Return gckVGCOMMAND object pointer. */ + *Command = command; + + gcmkFOOTER_ARG("*Command=0x%x",*Command); + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Roll back. */ + if (command != gcvNULL) + { + /* Disable block handlers. */ + for (i = 0; i < gcvBLOCK_COUNT; i += 1) + { + /* Get the task table entry. */ + gcsBLOCK_TASK_ENTRY_PTR entry = &command->taskTable[i]; + + /* Destroy the semaphore. */ + if (entry->interruptSemaphore != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_DestroySemaphore( + command->os, entry->interruptSemaphore + )); + } + + /* Disable all enabled interrupts. */ + for (j = 0; j < entry->interruptCount; j += 1) + { + /* Must be a valid value. */ + gcmkASSERT(entry->interruptArray[j] >= 0); + gcmkASSERT(entry->interruptArray[j] <= 31); + + /* Disable the interrupt. */ + gcmkCHECK_STATUS(gckVGINTERRUPT_Disable( + Kernel->interrupt, + entry->interruptArray[j] + )); + } + } + + /* Disable the bus error interrupt. */ + gcmkCHECK_STATUS(gckVGINTERRUPT_Disable( + Kernel->interrupt, + command->busErrorInt + )); + + /* Disable TS overflow interrupt. */ + if (command->info.tsOverflowInt != -1) + { + gcmkCHECK_STATUS(gckVGINTERRUPT_Disable( + Kernel->interrupt, + command->info.tsOverflowInt + )); + } + + /* Delete the commit mutex. */ + if (command->commitMutex != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_DeleteMutex( + Kernel->os, command->commitMutex + )); + } + + /* Delete the command queue mutex. */ + if (command->taskMutex != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_DeleteMutex( + Kernel->os, command->taskMutex + )); + } + + /* Delete the command queue mutex. */ + if (command->queueMutex != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_DeleteMutex( + Kernel->os, command->queueMutex + )); + } + + /* Delete the command queue. */ + if (command->queue != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_Free( + Kernel->os, command->queue + )); + } + + if (command->powerSemaphore != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DestroySemaphore( + Kernel->os, command->powerSemaphore)); + } + + if (command->powerStallSignal != gcvNULL) + { + /* Create the power management semaphore. */ + gcmkVERIFY_OK(gckOS_DestroySignal( + Kernel->os, + command->powerStallSignal)); + } + + /* Free the gckVGCOMMAND structure. */ + gcmkCHECK_STATUS(gckOS_Free( + Kernel->os, command + )); + } + + gcmkFOOTER(); + /* Return the error. */ + return status; +} + +gceSTATUS +gckVGCOMMAND_Destroy( + OUT gckVGCOMMAND Command + ) +{ + gceSTATUS status = gcvSTATUS_OK; + + gcmkHEADER_ARG("Command=0x%x", Command); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + + do + { + gctUINT i; + gcsTASK_STORAGE_PTR nextStorage; + + if (Command->queueHead != gcvNULL) + { + /* Wait until the head becomes idle. */ + gcmkERR_BREAK(_WaitForIdle(Command, Command->queueHead)); + } + + /* Disable block handlers. */ + for (i = 0; i < gcvBLOCK_COUNT; i += 1) + { + /* Get the interrupt array entry. */ + gcsBLOCK_TASK_ENTRY_PTR entry = &Command->taskTable[i]; + + /* Determine the index of the last interrupt in the array. */ + gctINT index = entry->interruptCount - 1; + + /* Destroy the semaphore. */ + if (entry->interruptSemaphore != gcvNULL) + { + gcmkERR_BREAK(gckOS_DestroySemaphore( + Command->os, entry->interruptSemaphore + )); + } + + /* Disable all enabled interrupts. */ + while (index >= 0) + { + /* Must be a valid value. */ + gcmkASSERT(entry->interruptArray[index] >= 0); + gcmkASSERT(entry->interruptArray[index] <= 31); + + /* Disable the interrupt. */ + gcmkERR_BREAK(gckVGINTERRUPT_Disable( + Command->kernel->interrupt, + entry->interruptArray[index] + )); + + /* Update to the next interrupt. */ + index -= 1; + entry->interruptCount -= 1; + } + + /* Error? */ + if (gcmkIS_ERROR(status)) + { + break; + } + } + + /* Error? */ + if (gcmkIS_ERROR(status)) + { + break; + } + + /* Disable the bus error interrupt. */ + gcmkERR_BREAK(gckVGINTERRUPT_Disable( + Command->kernel->interrupt, + Command->busErrorInt + )); + + /* Disable TS overflow interrupt. */ + if (Command->info.tsOverflowInt != -1) + { + gcmkERR_BREAK(gckVGINTERRUPT_Disable( + Command->kernel->interrupt, + Command->info.tsOverflowInt + )); + + Command->info.tsOverflowInt = -1; + } + + /* Delete the commit mutex. */ + if (Command->commitMutex != gcvNULL) + { + gcmkERR_BREAK(gckOS_DeleteMutex( + Command->os, Command->commitMutex + )); + + Command->commitMutex = gcvNULL; + } + + /* Delete the command queue mutex. */ + if (Command->taskMutex != gcvNULL) + { + gcmkERR_BREAK(gckOS_DeleteMutex( + Command->os, Command->taskMutex + )); + + Command->taskMutex = gcvNULL; + } + + /* Delete the command queue mutex. */ + if (Command->queueMutex != gcvNULL) + { + gcmkERR_BREAK(gckOS_DeleteMutex( + Command->os, Command->queueMutex + )); + + Command->queueMutex = gcvNULL; + } + + if (Command->powerSemaphore != gcvNULL) + { + /* Destroy the power management semaphore. */ + gcmkERR_BREAK(gckOS_DestroySemaphore( + Command->os, Command->powerSemaphore)); + } + + if (Command->powerStallSignal != gcvNULL) + { + /* Create the power management semaphore. */ + gcmkERR_BREAK(gckOS_DestroySignal( + Command->os, + Command->powerStallSignal)); + } + + if (Command->queue != gcvNULL) + { + /* Delete the command queue. */ + gcmkERR_BREAK(gckOS_Free( + Command->os, Command->queue + )); + } + + /* Destroy all allocated buffers. */ + while (Command->taskStorage) + { + /* Copy the buffer pointer. */ + nextStorage = Command->taskStorage->next; + + /* Free the current container. */ + gcmkERR_BREAK(gckOS_Free( + Command->os, Command->taskStorage + )); + + /* Advance to the next one. */ + Command->taskStorage = nextStorage; + } + + /* Error? */ + if (gcmkIS_ERROR(status)) + { + break; + } + + /* Mark the object as unknown. */ + Command->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckVGCOMMAND structure. */ + gcmkERR_BREAK(gckOS_Free(Command->os, Command)); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Restore the object type if failed. */ + Command->object.type = gcvOBJ_COMMAND; + + gcmkFOOTER(); + /* Return the error. */ + return status; +} + +gceSTATUS +gckVGCOMMAND_QueryCommandBuffer( + IN gckVGCOMMAND Command, + OUT gcsCOMMAND_BUFFER_INFO_PTR Information + ) +{ + gcmkHEADER_ARG("Command=0x%x Information=0x%x", Command, Information); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + gcmkVERIFY_ARGUMENT(Information != gcvNULL); + + /* Copy the information. */ + gcmkVERIFY_OK(gckOS_MemCopy( + Information, &Command->info, sizeof(gcsCOMMAND_BUFFER_INFO) + )); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +gceSTATUS +gckVGCOMMAND_Allocate( + IN gckVGCOMMAND Command, + IN gctSIZE_T Size, + OUT gcsCMDBUFFER_PTR * CommandBuffer, + OUT gctPOINTER * Data + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Command=0x%x Size=0x%x CommandBuffer=0x%x Data=0x%x", + Command, Size, CommandBuffer, Data); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + gcmkVERIFY_ARGUMENT(Data != gcvNULL); + + do + { + /* Allocate the buffer. */ + gcmkERR_BREAK(_AllocateCommandBuffer(Command, Size, CommandBuffer)); + + /* Determine the data pointer. */ + * Data = (gctUINT8_PTR) (*CommandBuffer) + (* CommandBuffer)->bufferOffset; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +gceSTATUS +gckVGCOMMAND_Free( + IN gckVGCOMMAND Command, + IN gcsCMDBUFFER_PTR CommandBuffer + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Command=0x%x CommandBuffer=0x%x", + Command, CommandBuffer); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + gcmkVERIFY_ARGUMENT(CommandBuffer != gcvNULL); + + /* Free command buffer. */ + status = _FreeCommandBuffer(Command->kernel, CommandBuffer); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +gceSTATUS +gckVGCOMMAND_Execute( + IN gckVGCOMMAND Command, + IN gcsCMDBUFFER_PTR CommandBuffer + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Command=0x%x CommandBuffer=0x%x", + Command, CommandBuffer); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + gcmkVERIFY_ARGUMENT(CommandBuffer != gcvNULL); + + do + { + gctUINT queueLength; + gcsKERNEL_CMDQUEUE_PTR kernelEntry; + + /* Lock the current queue. */ + gcmkERR_BREAK(_LockCurrentQueue( + Command, &kernelEntry, &queueLength + )); + + /* Set the buffer. */ + kernelEntry->commandBuffer = CommandBuffer; + kernelEntry->handler = _FreeKernelCommandBuffer; + + /* Lock the current queue. */ + gcmkERR_BREAK(_UnlockCurrentQueue( + Command, 1 + )); + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +gceSTATUS +gckVGCOMMAND_Commit( + IN gckVGCOMMAND Command, + IN gcsVGCONTEXT_PTR Context, + IN gcsVGCMDQUEUE_PTR Queue, + IN gctUINT EntryCount, + IN gcsTASK_MASTER_TABLE_PTR TaskTable + ) +{ + /* + The first buffer is executed through a direct gckVGHARDWARE_Execute call, + therefore only an update is needed after the execution is over. All + consequent buffers need to be executed upon the first update call from + the FE interrupt handler. + */ + + static gcsQUEUE_UPDATE_CONTROL _dynamicBuffer[] = + { + { + _UpdateDynamicCommandBuffer, + _UpdateDynamicCommandBuffer, + _UpdateLastDynamicCommandBuffer, + _UpdateLastDynamicCommandBuffer + }, + { + _ExecuteDynamicCommandBuffer, + _UpdateDynamicCommandBuffer, + _ExecuteLastDynamicCommandBuffer, + _UpdateLastDynamicCommandBuffer + } + }; + + static gcsQUEUE_UPDATE_CONTROL _staticBuffer[] = + { + { + _UpdateStaticCommandBuffer, + _UpdateStaticCommandBuffer, + _UpdateLastStaticCommandBuffer, + _UpdateLastStaticCommandBuffer + }, + { + _ExecuteStaticCommandBuffer, + _UpdateStaticCommandBuffer, + _ExecuteLastStaticCommandBuffer, + _UpdateLastStaticCommandBuffer + } + }; + + gceSTATUS status, last; + +#ifdef __QNXNTO__ + gcsVGCONTEXT_PTR userContext = gcvNULL; + gctBOOL userContextMapped = gcvFALSE; + gcsTASK_MASTER_TABLE_PTR userTaskTable = gcvNULL; + gctBOOL userTaskTableMapped = gcvFALSE; + gctPOINTER pointer = gcvNULL; +#endif + + gcmkHEADER_ARG("Command=0x%x Context=0x%x Queue=0x%x EntryCount=0x%x TaskTable=0x%x", + Command, Context, Queue, EntryCount, TaskTable); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); + gcmkVERIFY_ARGUMENT(Context != gcvNULL); + gcmkVERIFY_ARGUMENT(Queue != gcvNULL); + gcmkVERIFY_ARGUMENT(EntryCount > 1); + + do + { + gctBOOL haveFETasks; + gctUINT queueSize; + gcsVGCMDQUEUE_PTR mappedQueue; + gcsVGCMDQUEUE_PTR userEntry; + gcsKERNEL_CMDQUEUE_PTR kernelEntry; + gcsQUEUE_UPDATE_CONTROL_PTR queueControl; + gctUINT currentLength; + gctUINT queueLength; + gctUINT entriesQueued; + gctUINT8_PTR previousEnd; + gctBOOL previousDynamic; + gctBOOL previousExecuted; + gctUINT controlIndex; + +#ifdef __QNXNTO__ + /* Map the context into the kernel space. */ + userContext = Context; + + gcmkERR_BREAK(gckOS_MapUserPointer( + Command->os, + userContext, + gcmSIZEOF(*userContext), + &pointer)); + + Context = pointer; + + userContextMapped = gcvTRUE; + + /* Map the taskTable into the kernel space. */ + userTaskTable = TaskTable; + + gcmkERR_BREAK(gckOS_MapUserPointer( + Command->os, + userTaskTable, + gcmSIZEOF(*userTaskTable), + &pointer)); + + TaskTable = pointer; + + userTaskTableMapped = gcvTRUE; + + /* Update the signal info. */ + TaskTable->coid = Context->coid; + TaskTable->rcvid = Context->rcvid; +#endif + + gcmkERR_BREAK(gckVGHARDWARE_SetPowerManagementState( + Command->hardware, gcvPOWER_ON_AUTO + )); + + /* Acquire the power semaphore. */ + gcmkERR_BREAK(gckOS_AcquireSemaphore( + Command->os, Command->powerSemaphore + )); + + /* Acquire the mutex. */ + status = gckOS_AcquireMutex( + Command->os, + Command->commitMutex, + gcvINFINITE + ); + + if (gcmIS_ERROR(status)) + { + gcmkVERIFY_OK(gckOS_ReleaseSemaphore( + Command->os, Command->powerSemaphore)); + break; + } + + do + { + gcmkERR_BREAK(_FlushMMU(Command)); + + /* Assign a context ID if not yet assigned. */ + if (Context->id == 0) + { + /* Assign the next context number. */ + Context->id = ++ Command->contextCounter; + + /* See if we overflowed. */ + if (Command->contextCounter == 0) + { + /* We actually did overflow, wow... */ + status = gcvSTATUS_OUT_OF_RESOURCES; + break; + } + } + + /* The first entry in the queue is always the context buffer. + Verify whether the user context is the same as the current + context and if that's the case, skip the first entry. */ + if (Context->id == Command->currentContext) + { + /* Same context as before, skip the first entry. */ + EntryCount -= 1; + Queue += 1; + + /* Set the signal to avoid user waiting. */ +#ifdef __QNXNTO__ + gcmkERR_BREAK(gckOS_UserSignal( + Command->os, Context->signal, Context->rcvid, Context->coid + )); +#else + gcmkERR_BREAK(gckOS_UserSignal( + Command->os, Context->signal, Context->process + )); + +#endif /* __QNXNTO__ */ + + } + else + { + /* Different user context - keep the first entry. + Set the user context as the current one. */ + Command->currentContext = Context->id; + } + + /* Reset pointers. */ + queueControl = gcvNULL; + previousEnd = gcvNULL; + + /* Determine whether there are FE tasks to be performed. */ + haveFETasks = (TaskTable->table[gcvBLOCK_COMMAND].head != gcvNULL); + + /* Determine the size of the queue. */ + queueSize = EntryCount * gcmSIZEOF(gcsVGCMDQUEUE); + + /* Map the command queue into the kernel space. */ + gcmkERR_BREAK(gckOS_MapUserPointer( + Command->os, + Queue, + queueSize, + (gctPOINTER *) &mappedQueue + )); + + /* Set the first entry. */ + userEntry = mappedQueue; + + /* Process the command queue. */ + while (EntryCount) + { + /* Lock the current queue. */ + gcmkERR_BREAK(_LockCurrentQueue( + Command, &kernelEntry, &queueLength + )); + + /* Determine the number of entries to process. */ + currentLength = (queueLength < EntryCount) + ? queueLength + : EntryCount; + + /* Update the number of the entries left to process. */ + EntryCount -= currentLength; + + /* Reset previous flags. */ + previousDynamic = gcvFALSE; + previousExecuted = gcvFALSE; + + /* Set the initial control index. */ + controlIndex = 0; + + /* Process entries. */ + for (entriesQueued = 0; entriesQueued < currentLength; entriesQueued += 1) + { + /* Get the kernel pointer to the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer = gcvNULL; + gcmkERR_BREAK(_ConvertUserCommandBufferPointer( + Command, + userEntry->commandBuffer, + &commandBuffer + )); + + /* Is it a dynamic command buffer? */ + if (userEntry->dynamic) + { + /* Select dynamic buffer control functions. */ + queueControl = &_dynamicBuffer[controlIndex]; + } + + /* No, a static command buffer. */ + else + { + /* Select static buffer control functions. */ + queueControl = &_staticBuffer[controlIndex]; + } + + /* Set the command buffer pointer to the entry. */ + kernelEntry->commandBuffer = commandBuffer; + + /* If the previous entry was a dynamic command buffer, + link it to the current. */ + if (previousDynamic) + { + gcmkERR_BREAK(gckVGCOMMAND_FetchCommand( + Command, + previousEnd, + commandBuffer->address, + commandBuffer->dataCount, + gcvNULL + )); + + /* The buffer will be auto-executed, only need to + update it after it has been executed. */ + kernelEntry->handler = queueControl->update; + + /* The buffer is only being updated. */ + previousExecuted = gcvFALSE; + } + else + { + /* Set the buffer up for execution. */ + kernelEntry->handler = queueControl->execute; + + /* The buffer is being updated. */ + previousExecuted = gcvTRUE; + } + + /* The current buffer's END command becomes the last END. */ + previousEnd + = ((gctUINT8_PTR) commandBuffer) + + commandBuffer->bufferOffset + + commandBuffer->dataCount * Command->info.commandAlignment + - Command->info.staticTailSize; + + /* Update the last entry info. */ + previousDynamic = userEntry->dynamic; + + /* Advance entries. */ + userEntry += 1; + kernelEntry += 1; + + /* Update the control index. */ + controlIndex = 1; + } + + /* If the previous entry was a dynamic command buffer, + terminate it with an END. */ + if (previousDynamic) + { + gcmkERR_BREAK(gckVGCOMMAND_EndCommand( + Command, + previousEnd, + Command->info.feBufferInt, + gcvNULL + )); + } + + /* Last buffer? */ + if (EntryCount == 0) + { + /* Modify the last command buffer's routines to handle + tasks if any.*/ + if (haveFETasks) + { + if (previousExecuted) + { + kernelEntry[-1].handler = queueControl->lastExecute; + } + else + { + kernelEntry[-1].handler = queueControl->lastUpdate; + } + } + + /* Release the mutex. */ + gcmkERR_BREAK(gckOS_ReleaseMutex( + Command->os, + Command->queueMutex + )); + /* Schedule tasks. */ + gcmkERR_BREAK(_ScheduleTasks(Command, TaskTable, previousEnd)); + + /* Acquire the mutex. */ + gcmkERR_BREAK(gckOS_AcquireMutex( + Command->os, + Command->queueMutex, + gcvINFINITE + )); + } + + /* Unkock and schedule the current queue for execution. */ + gcmkERR_BREAK(_UnlockCurrentQueue( + Command, currentLength + )); + } + + + /* Unmap the user command buffer. */ + gcmkERR_BREAK(gckOS_UnmapUserPointer( + Command->os, + Queue, + queueSize, + mappedQueue + )); + } + while (gcvFALSE); + + /* Release the mutex. */ + gcmkCHECK_STATUS(gckOS_ReleaseMutex( + Command->os, + Command->commitMutex + )); + + gcmkVERIFY_OK(gckOS_ReleaseSemaphore( + Command->os, Command->powerSemaphore)); + } + while (gcvFALSE); + +#ifdef __QNXNTO__ + if (userContextMapped) + { + /* Unmap the user context. */ + gcmkVERIFY_OK(gckOS_UnmapUserPointer( + Command->os, + userContext, + gcmSIZEOF(*userContext), + Context)); + } + + if (userTaskTableMapped) + { + /* Unmap the user taskTable. */ + gcmkVERIFY_OK(gckOS_UnmapUserPointer( + Command->os, + userTaskTable, + gcmSIZEOF(*userTaskTable), + TaskTable)); + } +#endif + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +#endif /* gcdENABLE_VG */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_db.c b/drivers/gpu/galcore/gc_hal_kernel_db.c new file mode 100644 index 00000000000000..021f6338d1ef6e --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_db.c @@ -0,0 +1,1861 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_DATABASE + +/******************************************************************************* +***** Private fuctions ********************************************************/ + +#define _GetSlot(database, x) \ + (gctUINT32)(gcmPTR_TO_UINT64(x) % gcmCOUNTOF(database->list)) + +/******************************************************************************* +** gckKERNEL_NewDatabase +** +** Create a new database structure and insert it to the head of the hash list. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID that identifies the database. +** +** OUTPUT: +** +** gcsDATABASE_PTR * Database +** Pointer to a variable receiving the database structure pointer on +** success. +*/ +static gceSTATUS +gckKERNEL_NewDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gcsDATABASE_PTR * Database + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gctBOOL acquired = gcvFALSE; + gctSIZE_T slot; + gcsDATABASE_PTR existingDatabase; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Acquire the database mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Compute the hash for the database. */ + slot = ProcessID % gcmCOUNTOF(Kernel->db->db); + + /* Walk the hash list. */ + for (existingDatabase = Kernel->db->db[slot]; + existingDatabase != gcvNULL; + existingDatabase = existingDatabase->next) + { + if (existingDatabase->processID == ProcessID) + { + /* One process can't be added twice. */ + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + } + + if (Kernel->db->freeDatabase != gcvNULL) + { + /* Allocate a database from the free list. */ + database = Kernel->db->freeDatabase; + Kernel->db->freeDatabase = database->next; + } + else + { + gctPOINTER pointer = gcvNULL; + + /* Allocate a new database from the heap. */ + gcmkONERROR(gckOS_Allocate(Kernel->os, + gcmSIZEOF(gcsDATABASE), + &pointer)); + + gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsDATABASE)); + + database = pointer; + + gcmkONERROR(gckOS_CreateMutex(Kernel->os, &database->counterMutex)); + } + + /* Insert the database into the hash. */ + database->next = Kernel->db->db[slot]; + Kernel->db->db[slot] = database; + + /* Save the hash slot. */ + database->slot = slot; + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Return the database. */ + *Database = database; + + /* Success. */ + gcmkFOOTER_ARG("*Database=0x%x", *Database); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_FindDatabase +** +** Find a database identified by a process ID and move it to the head of the +** hash list. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** ProcessID that identifies the database. +** +** gctBOOL LastProcessID +** gcvTRUE if searching for the last known process ID. gcvFALSE if +** we need to search for the process ID specified by the ProcessID +** argument. +** +** OUTPUT: +** +** gcsDATABASE_PTR * Database +** Pointer to a variable receiving the database structure pointer on +** success. +*/ +gceSTATUS +gckKERNEL_FindDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctBOOL LastProcessID, + OUT gcsDATABASE_PTR * Database + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database, previous; + gctSIZE_T slot; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d LastProcessID=%d", + Kernel, ProcessID, LastProcessID); + + /* Compute the hash for the database. */ + slot = ProcessID % gcmCOUNTOF(Kernel->db->db); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Check whether we are getting the last known database. */ + if (LastProcessID) + { + /* Use last database. */ + database = Kernel->db->lastDatabase; + + if (database == gcvNULL) + { + /* Database not found. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + } + else + { + /* Walk the hash list. */ + for (previous = gcvNULL, database = Kernel->db->db[slot]; + database != gcvNULL; + database = database->next) + { + if (database->processID == ProcessID) + { + /* Found it! */ + break; + } + + previous = database; + } + + if (database == gcvNULL) + { + /* Database not found. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + if (previous != gcvNULL) + { + /* Move database to the head of the hash list. */ + previous->next = database->next; + database->next = Kernel->db->db[slot]; + Kernel->db->db[slot] = database; + } + } + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Return the database. */ + *Database = database; + + /* Success. */ + gcmkFOOTER_ARG("*Database=0x%x", *Database); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_DeleteDatabase +** +** Remove a database from the hash list and delete its structure. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to the database structure to remove. +** +** OUTPUT: +** +** Nothing. +*/ +static gceSTATUS +gckKERNEL_DeleteDatabase( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x", Kernel, Database); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Check slot value. */ + gcmkVERIFY_ARGUMENT(Database->slot < gcmCOUNTOF(Kernel->db->db)); + + if (Database->slot < gcmCOUNTOF(Kernel->db->db)) + { + /* Check if database if the head of the hash list. */ + if (Kernel->db->db[Database->slot] == Database) + { + /* Remove the database from the hash list. */ + Kernel->db->db[Database->slot] = Database->next; + } + else + { + /* Walk the has list to find the database. */ + for (database = Kernel->db->db[Database->slot]; + database != gcvNULL; + database = database->next + ) + { + /* Check if the next list entry is this database. */ + if (database->next == Database) + { + /* Remove the database from the hash list. */ + database->next = Database->next; + break; + } + } + + if (database == gcvNULL) + { + /* Ouch! Something got corrupted. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + } + } + + if (Kernel->db->lastDatabase != gcvNULL) + { + /* Insert database to the free list. */ + Kernel->db->lastDatabase->next = Kernel->db->freeDatabase; + Kernel->db->freeDatabase = Kernel->db->lastDatabase; + } + + /* Keep database as the last database. */ + Kernel->db->lastDatabase = Database; + + /* Destory handle db. */ + gcmkVERIFY_OK(gckKERNEL_DestroyIntegerDatabase(Kernel, Database->handleDatabase)); + Database->handleDatabase = gcvNULL; + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Database->handleDatabaseMutex)); + Database->handleDatabaseMutex = gcvNULL; + +#if gcdPROCESS_ADDRESS_SPACE + /* Destory process MMU. */ + gcmkVERIFY_OK(gckEVENT_DestroyMmu(Kernel->eventObj, Database->mmu, gcvKERNEL_PIXEL)); + Database->mmu = gcvNULL; +#endif + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_NewRecord +** +** Create a new database record structure and insert it to the head of the +** database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to a database structure. +** +** OUTPUT: +** +** gcsDATABASE_RECORD_PTR * Record +** Pointer to a variable receiving the database record structure +** pointer on success. +*/ +static gceSTATUS +gckKERNEL_NewRecord( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database, + IN gctUINT32 Slot, + OUT gcsDATABASE_RECORD_PTR * Record + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_RECORD_PTR record = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x", Kernel, Database); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + if (Kernel->db->freeRecord != gcvNULL) + { + /* Allocate the record from the free list. */ + record = Kernel->db->freeRecord; + Kernel->db->freeRecord = record->next; + } + else + { + gctPOINTER pointer = gcvNULL; + + /* Allocate the record from the heap. */ + gcmkONERROR(gckOS_Allocate(Kernel->os, + gcmSIZEOF(gcsDATABASE_RECORD), + &pointer)); + + record = pointer; + } + + /* Insert the record in the database. */ + record->next = Database->list[Slot]; + Database->list[Slot] = record; + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Return the record. */ + *Record = record; + + /* Success. */ + gcmkFOOTER_ARG("*Record=0x%x", *Record); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + if (record != gcvNULL) + { + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, record)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_DeleteRecord +** +** Remove a database record from the database and delete its structure. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to a database structure. +** +** gceDATABASE_TYPE Type +** Type of the record to remove. +** +** gctPOINTER Data +** Data of the record to remove. +** +** OUTPUT: +** +** gctSIZE_T_PTR Bytes +** Pointer to a variable that receives the size of the record deleted. +** Can be gcvNULL if the size is not required. +*/ +static gceSTATUS +gckKERNEL_DeleteRecord( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Data, + OUT gctSIZE_T_PTR Bytes OPTIONAL + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_RECORD_PTR record, previous; + gctUINT32 slot = _GetSlot(Database, Data); + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x", + Kernel, Database, Type, Data); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Scan the database for this record. */ + for (record = Database->list[slot], previous = gcvNULL; + record != gcvNULL; + record = record->next + ) + { + if ((record->type == Type) + && (record->data == Data) + ) + { + /* Found it! */ + break; + } + + previous = record; + } + + if (record == gcvNULL) + { + /* Ouch! This record is not found? */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + if (Bytes != gcvNULL) + { + /* Return size of record. */ + *Bytes = record->bytes; + } + + /* Remove record from database. */ + if (previous == gcvNULL) + { + Database->list[slot] = record->next; + } + else + { + previous->next = record->next; + } + + /* Insert record in free list. */ + record->next = Kernel->db->freeRecord; + Kernel->db->freeRecord = record; + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_FindRecord +** +** Find a database record from the database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gcsDATABASE_PTR Database +** Pointer to a database structure. +** +** gceDATABASE_TYPE Type +** Type of the record to remove. +** +** gctPOINTER Data +** Data of the record to remove. +** +** OUTPUT: +** +** gctSIZE_T_PTR Bytes +** Pointer to a variable that receives the size of the record deleted. +** Can be gcvNULL if the size is not required. +*/ +static gceSTATUS +gckKERNEL_FindRecord( + IN gckKERNEL Kernel, + IN gcsDATABASE_PTR Database, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Data, + OUT gcsDATABASE_RECORD_PTR Record + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsDATABASE_RECORD_PTR record; + gctUINT32 slot = _GetSlot(Database, Data); + + gcmkHEADER_ARG("Kernel=0x%x Database=0x%x Type=%d Data=0x%x", + Kernel, Database, Type, Data); + + /* Acquire the database mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Scan the database for this record. */ + for (record = Database->list[slot]; + record != gcvNULL; + record = record->next + ) + { + if ((record->type == Type) + && (record->data == Data) + ) + { + /* Found it! */ + break; + } + } + + if (record == gcvNULL) + { + /* Ouch! This record is not found? */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + if (Record != gcvNULL) + { + /* Return information of record. */ + gcmkONERROR( + gckOS_MemCopy(Record, record, sizeof(gcsDATABASE_RECORD))); + } + + /* Release the database mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_ARG("Record=0x%x", Record); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +***** Public API **************************************************************/ + +/******************************************************************************* +** gckKERNEL_CreateProcessDB +** +** Create a new process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_CreateProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database = gcvNULL; + gctUINT32 i; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Create a new database. */ + gcmkONERROR(gckKERNEL_NewDatabase(Kernel, ProcessID, &database)); + + /* Initialize the database. */ + database->processID = ProcessID; + database->vidMem.bytes = 0; + database->vidMem.maxBytes = 0; + database->vidMem.totalBytes = 0; + database->nonPaged.bytes = 0; + database->nonPaged.maxBytes = 0; + database->nonPaged.totalBytes = 0; + database->contiguous.bytes = 0; + database->contiguous.maxBytes = 0; + database->contiguous.totalBytes = 0; + database->mapMemory.bytes = 0; + database->mapMemory.maxBytes = 0; + database->mapMemory.totalBytes = 0; + database->mapUserMemory.bytes = 0; + database->mapUserMemory.maxBytes = 0; + database->mapUserMemory.totalBytes = 0; + database->virtualCommandBuffer.bytes = 0; + database->virtualCommandBuffer.maxBytes = 0; + database->virtualCommandBuffer.totalBytes = 0; + + for (i = 0; i < gcmCOUNTOF(database->list); i++) + { + database->list[i] = gcvNULL; + } + + for (i = 0; i < gcvSURF_NUM_TYPES; i++) + { + database->vidMemType[i].bytes = 0; + database->vidMemType[i].maxBytes = 0; + database->vidMemType[i].totalBytes = 0; + } + + for (i = 0; i < gcvPOOL_NUMBER_OF_POOLS; i++) + { + database->vidMemPool[i].bytes = 0; + database->vidMemPool[i].maxBytes = 0; + database->vidMemPool[i].totalBytes = 0; + } + + gcmkASSERT(database->handleDatabase == gcvNULL); + gcmkONERROR( + gckKERNEL_CreateIntegerDatabase(Kernel, &database->handleDatabase)); + + gcmkASSERT(database->handleDatabaseMutex == gcvNULL); + gcmkONERROR( + gckOS_CreateMutex(Kernel->os, &database->handleDatabaseMutex)); + +#if gcdPROCESS_ADDRESS_SPACE + gcmkASSERT(database->mmu == gcvNULL); + gcmkONERROR( + gckMMU_Construct(Kernel, gcdMMU_SIZE, &database->mmu)); +#endif + +#if gcdSECURE_USER + { + gctINT slot; + gcskSECURE_CACHE * cache = &database->cache; + + /* Setup the linked list of cache nodes. */ + for (slot = 1; slot <= gcdSECURE_CACHE_SLOTS; ++slot) + { + cache->cache[slot].logical = gcvNULL; + +#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE + cache->cache[slot].prev = &cache->cache[slot - 1]; + cache->cache[slot].next = &cache->cache[slot + 1]; +# endif +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + cache->cache[slot].nextHash = gcvNULL; + cache->cache[slot].prevHash = gcvNULL; +# endif + } + +#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE + /* Setup the head and tail of the cache. */ + cache->cache[0].next = &cache->cache[1]; + cache->cache[0].prev = &cache->cache[gcdSECURE_CACHE_SLOTS]; + cache->cache[0].logical = gcvNULL; + + /* Fix up the head and tail pointers. */ + cache->cache[0].next->prev = &cache->cache[0]; + cache->cache[0].prev->next = &cache->cache[0]; +# endif + +#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH + /* Zero out the hash table. */ + for (slot = 0; slot < gcmCOUNTOF(cache->hash); ++slot) + { + cache->hash[slot].logical = gcvNULL; + cache->hash[slot].nextHash = gcvNULL; + } +# endif + + /* Initialize cache index. */ + cache->cacheIndex = gcvNULL; + cache->cacheFree = 1; + cache->cacheStamp = 0; + } +#endif + + /* Reset idle timer. */ + Kernel->db->lastIdle = 0; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_AddProcessDB +** +** Add a record to a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gceDATABASE_TYPE TYPE +** Type of the record to add. +** +** gctPOINTER Pointer +** Data of the record to add. +** +** gctPHYS_ADDR Physical +** Physical address of the record to add. +** +** gctSIZE_T Size +** Size of the record to add. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_AddProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Size + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcsDATABASE_RECORD_PTR record = gcvNULL; + gcsDATABASE_COUNTERS * count; + gctUINT32 vidMemType; + gcePOOL vidMemPool; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x " + "Physical=0x%x Size=%lu", + Kernel, ProcessID, Type, Pointer, Physical, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Decode type. */ + vidMemType = (Type & gcdDB_VIDEO_MEMORY_TYPE_MASK) >> gcdDB_VIDEO_MEMORY_TYPE_SHIFT; + vidMemPool = (Type & gcdDB_VIDEO_MEMORY_POOL_MASK) >> gcdDB_VIDEO_MEMORY_POOL_SHIFT; + + Type &= gcdDATABASE_TYPE_MASK; + + /* Special case the idle record. */ + if (Type == gcvDB_IDLE) + { + gctUINT64 time; + + /* Get the current profile time. */ + gcmkONERROR(gckOS_GetProfileTick(&time)); + + if ((ProcessID == 0) && (Kernel->db->lastIdle != 0)) + { + /* Out of idle, adjust time it was idle. */ + Kernel->db->idleTime += time - Kernel->db->lastIdle; + Kernel->db->lastIdle = 0; + } + else if (ProcessID == 1) + { + /* Save current idle time. */ + Kernel->db->lastIdle = time; + } + +#if gcdDYNAMIC_SPEED + { + /* Test for first call. */ + if (Kernel->db->lastSlowdown == 0) + { + /* Save milliseconds. */ + Kernel->db->lastSlowdown = time; + Kernel->db->lastSlowdownIdle = Kernel->db->idleTime; + } + else + { + /* Compute ellapsed time in milliseconds. */ + gctUINT delta = gckOS_ProfileToMS(time - Kernel->db->lastSlowdown); + + /* Test for end of period. */ + if (delta >= gcdDYNAMIC_SPEED) + { + /* Compute number of idle milliseconds. */ + gctUINT idle = gckOS_ProfileToMS( + Kernel->db->idleTime - Kernel->db->lastSlowdownIdle); + + /* Broadcast to slow down the GPU. */ + gcmkONERROR(gckOS_BroadcastCalibrateSpeed(Kernel->os, + Kernel->hardware, + idle, + delta)); + + /* Save current time. */ + Kernel->db->lastSlowdown = time; + Kernel->db->lastSlowdownIdle = Kernel->db->idleTime; + } + } + } +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Create a new record in the database. */ + gcmkONERROR(gckKERNEL_NewRecord(Kernel, database, _GetSlot(database, Pointer), &record)); + + /* Initialize the record. */ + record->kernel = Kernel; + record->type = Type; + record->data = Pointer; + record->physical = Physical; + record->bytes = Size; + + /* Get pointer to counters. */ + switch (Type) + { + case gcvDB_VIDEO_MEMORY: + count = &database->vidMem; + break; + + case gcvDB_NON_PAGED: + count = &database->nonPaged; + break; + + case gcvDB_CONTIGUOUS: + count = &database->contiguous; + break; + + case gcvDB_MAP_MEMORY: + count = &database->mapMemory; + break; + + case gcvDB_MAP_USER_MEMORY: + count = &database->mapUserMemory; + break; + + case gcvDB_COMMAND_BUFFER: + count = &database->virtualCommandBuffer; + break; + + default: + count = gcvNULL; + break; + } + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, database->counterMutex, gcvINFINITE)); + + if (count != gcvNULL) + { + /* Adjust counters. */ + count->totalBytes += Size; + count->bytes += Size; + + if (count->bytes > count->maxBytes) + { + count->maxBytes = count->bytes; + } + } + + if (Type == gcvDB_VIDEO_MEMORY) + { + count = &database->vidMemType[vidMemType]; + + /* Adjust counters. */ + count->totalBytes += Size; + count->bytes += Size; + + if (count->bytes > count->maxBytes) + { + count->maxBytes = count->bytes; + } + + count = &database->vidMemPool[vidMemPool]; + + /* Adjust counters. */ + count->totalBytes += Size; + count->bytes += Size; + + if (count->bytes > count->maxBytes) + { + count->maxBytes = count->bytes; + } + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, database->counterMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_RemoveProcessDB +** +** Remove a record from a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gceDATABASE_TYPE TYPE +** Type of the record to remove. +** +** gctPOINTER Pointer +** Data of the record to remove. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_RemoveProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gctSIZE_T bytes = 0; + gctUINT32 vidMemType; + gcePOOL vidMempool; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x", + Kernel, ProcessID, Type, Pointer); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + /* Decode type. */ + vidMemType = (Type & gcdDB_VIDEO_MEMORY_TYPE_MASK) >> gcdDB_VIDEO_MEMORY_TYPE_SHIFT; + vidMempool = (Type & gcdDB_VIDEO_MEMORY_POOL_MASK) >> gcdDB_VIDEO_MEMORY_POOL_SHIFT; + + Type &= gcdDATABASE_TYPE_MASK; + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Delete the record. */ + gcmkONERROR( + gckKERNEL_DeleteRecord(Kernel, database, Type, Pointer, &bytes)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, database->counterMutex, gcvINFINITE)); + + /* Update counters. */ + switch (Type) + { + case gcvDB_VIDEO_MEMORY: + database->vidMem.bytes -= bytes; + database->vidMemType[vidMemType].bytes -= bytes; + database->vidMemPool[vidMempool].bytes -= bytes; + break; + + case gcvDB_NON_PAGED: + database->nonPaged.bytes -= bytes; + break; + + case gcvDB_CONTIGUOUS: + database->contiguous.bytes -= bytes; + break; + + case gcvDB_MAP_MEMORY: + database->mapMemory.bytes -= bytes; + break; + + case gcvDB_MAP_USER_MEMORY: + database->mapUserMemory.bytes -= bytes; + break; + + case gcvDB_COMMAND_BUFFER: + database->virtualCommandBuffer.bytes -= bytes; + break; + + default: + break; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, database->counterMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_FindProcessDB +** +** Find a record from a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gceDATABASE_TYPE TYPE +** Type of the record to remove. +** +** gctPOINTER Pointer +** Data of the record to remove. +** +** OUTPUT: +** +** gcsDATABASE_RECORD_PTR Record +** Copy of record. +*/ +gceSTATUS +gckKERNEL_FindProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 ThreadID, + IN gceDATABASE_TYPE Type, + IN gctPOINTER Pointer, + OUT gcsDATABASE_RECORD_PTR Record + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Pointer=0x%x", + Kernel, ProcessID, ThreadID, Type, Pointer); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Find the record. */ + gcmkONERROR( + gckKERNEL_FindRecord(Kernel, database, Type, Pointer, Record)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_DestroyProcessDB +** +** Destroy a process database. If the database contains any records, the data +** inside those records will be deleted as well. This aids in the cleanup if +** a process has died unexpectedly or has memory leaks. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_DestroyProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcsDATABASE_RECORD_PTR record, next; + gctBOOL asynchronous = gcvTRUE; + gckVIDMEM_NODE nodeObject; + gctPHYS_ADDR physical; + gckKERNEL kernel = Kernel; + gctUINT32 handle; + gctUINT32 i; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): VidMem: total=%lu max=%lu", + ProcessID, database->vidMem.totalBytes, + database->vidMem.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): NonPaged: total=%lu max=%lu", + ProcessID, database->nonPaged.totalBytes, + database->nonPaged.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Contiguous: total=%lu max=%lu", + ProcessID, database->contiguous.totalBytes, + database->contiguous.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Idle time=%llu", + ProcessID, Kernel->db->idleTime); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Map: total=%lu max=%lu", + ProcessID, database->mapMemory.totalBytes, + database->mapMemory.maxBytes); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DATABASE, + "DB(%d): Map: total=%lu max=%lu", + ProcessID, database->mapUserMemory.totalBytes, + database->mapUserMemory.maxBytes); + + if (database->list != gcvNULL) + { + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "Process %d has entries in its database:", + ProcessID); + } + + for(i = 0; i < gcmCOUNTOF(database->list); i++) + { + + /* Walk all records. */ + for (record = database->list[i]; record != gcvNULL; record = next) + { + /* Next next record. */ + next = record->next; + + /* Dispatch on record type. */ + switch (record->type) + { + case gcvDB_VIDEO_MEMORY: + gcmkERR_BREAK(gckVIDMEM_HANDLE_Lookup(record->kernel, + ProcessID, + gcmPTR2INT32(record->data), + &nodeObject)); + + /* Free the video memory. */ + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(record->kernel, + ProcessID, + gcmPTR2INT32(record->data))); + + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(record->kernel, + nodeObject)); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: VIDEO_MEMORY 0x%x (status=%d)", + record->data, status); + break; + + case gcvDB_NON_PAGED: + physical = gcmNAME_TO_PTR(record->physical); + /* Unmap user logical memory first. */ + status = gckOS_UnmapUserLogical(Kernel->os, + physical, + record->bytes, + record->data); + + /* Free the non paged memory. */ + status = gckEVENT_FreeNonPagedMemory(Kernel->eventObj, + record->bytes, + physical, + record->data, + gcvKERNEL_PIXEL); + gcmRELEASE_NAME(record->physical); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: NON_PAGED 0x%x, bytes=%lu (status=%d)", + record->data, record->bytes, status); + break; + + case gcvDB_COMMAND_BUFFER: + /* Free the command buffer. */ + status = gckEVENT_DestroyVirtualCommandBuffer(record->kernel->eventObj, + record->bytes, + gcmNAME_TO_PTR(record->physical), + record->data, + gcvKERNEL_PIXEL); + gcmRELEASE_NAME(record->physical); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: COMMAND_BUFFER 0x%x, bytes=%lu (status=%d)", + record->data, record->bytes, status); + break; + + case gcvDB_CONTIGUOUS: + physical = gcmNAME_TO_PTR(record->physical); + /* Unmap user logical memory first. */ + status = gckOS_UnmapUserLogical(Kernel->os, + physical, + record->bytes, + record->data); + + /* Free the contiguous memory. */ + status = gckEVENT_FreeContiguousMemory(Kernel->eventObj, + record->bytes, + physical, + record->data, + gcvKERNEL_PIXEL); + gcmRELEASE_NAME(record->physical); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: CONTIGUOUS 0x%x bytes=%lu (status=%d)", + record->data, record->bytes, status); + break; + + case gcvDB_SIGNAL: +#if USE_NEW_LINUX_SIGNAL + status = gcvSTATUS_NOT_SUPPORTED; +#else + /* Free the user signal. */ + status = gckOS_DestroyUserSignal(Kernel->os, + gcmPTR2INT32(record->data)); +#endif /* USE_NEW_LINUX_SIGNAL */ + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: SIGNAL %d (status=%d)", + (gctINT)(gctUINTPTR_T)record->data, status); + break; + + case gcvDB_VIDEO_MEMORY_LOCKED: + handle = gcmPTR2INT32(record->data); + + gcmkERR_BREAK(gckVIDMEM_HANDLE_Lookup(record->kernel, + ProcessID, + handle, + &nodeObject)); + + /* Unlock what we still locked */ + status = gckVIDMEM_Unlock(record->kernel, + nodeObject, + nodeObject->type, + &asynchronous); + +#if gcdENABLE_VG + if (record->kernel->core == gcvCORE_VG) + { + if (gcmIS_SUCCESS(status) && (gcvTRUE == asynchronous)) + { + /* TODO: we maybe need to schedule a event here */ + status = gckVIDMEM_Unlock(record->kernel, + nodeObject, + nodeObject->type, + gcvNULL); + } + + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(record->kernel, + ProcessID, + handle)); + + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(record->kernel, + nodeObject)); + } + else +#endif + { + gcmkVERIFY_OK(gckVIDMEM_HANDLE_Dereference(record->kernel, + ProcessID, + handle)); + + if (gcmIS_SUCCESS(status) && (gcvTRUE == asynchronous)) + { + status = gckEVENT_Unlock(record->kernel->eventObj, + gcvKERNEL_PIXEL, + nodeObject, + nodeObject->type); + } + else + { + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(record->kernel, + nodeObject)); + } + } + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: VIDEO_MEMORY_LOCKED 0x%x (status=%d)", + record->data, status); + break; + + case gcvDB_CONTEXT: + /* TODO: Free the context */ + status = gckCOMMAND_Detach(Kernel->command, gcmNAME_TO_PTR(record->data)); + gcmRELEASE_NAME(record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: CONTEXT 0x%x (status=%d)", + record->data, status); + break; + + case gcvDB_MAP_MEMORY: + /* Unmap memory. */ + status = gckKERNEL_UnmapMemory(Kernel, + record->physical, + record->bytes, + record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: MAP MEMORY %d (status=%d)", + gcmPTR2INT32(record->data), status); + break; + + case gcvDB_MAP_USER_MEMORY: + /* TODO: Unmap user memory. */ + status = gckOS_UnmapUserMemory(Kernel->os, + Kernel->core, + record->physical, + record->bytes, + gcmNAME_TO_PTR(record->data), + 0); + gcmRELEASE_NAME(record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: MAP USER MEMORY %d (status=%d)", + gcmPTR2INT32(record->data), status); + break; + +#if gcdANDROID_NATIVE_FENCE_SYNC + case gcvDB_SYNC_POINT: + /* Free the user signal. */ + status = gckOS_DestroySyncPoint(Kernel->os, + (gctSYNC_POINT) record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: SYNC POINT %d (status=%d)", + (gctINT)(gctUINTPTR_T)record->data, status); + break; +#endif + + case gcvDB_SHBUF: + /* Free shared buffer. */ + status = gckKERNEL_DestroyShBuffer(Kernel, + (gctSHBUF) record->data); + + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, + "DB: SHBUF %u (status=%d)", + (gctUINT32)(gctUINTPTR_T) record->data, status); + break; + + default: + gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DATABASE, + "DB: Correcupted record=0x%08x type=%d", + record, record->type); + break; + } + + /* Delete the record. */ + gcmkONERROR(gckKERNEL_DeleteRecord(Kernel, + database, + record->type, + record->data, + gcvNULL)); + } + + } + + /* Delete the database. */ + gcmkONERROR(gckKERNEL_DeleteDatabase(Kernel, database)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckKERNEL_QueryProcessDB +** +** Query a process database for the current usage of a particular record type. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** gctBOOL LastProcessID +** gcvTRUE if searching for the last known process ID. gcvFALSE if +** we need to search for the process ID specified by the ProcessID +** argument. +** +** gceDATABASE_TYPE Type +** Type of the record to query. +** +** OUTPUT: +** +** gcuDATABASE_INFO * Info +** Pointer to a variable that receives the requested information. +*/ +gceSTATUS +gckKERNEL_QueryProcessDB( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctBOOL LastProcessID, + IN gceDATABASE_TYPE Type, + OUT gcuDATABASE_INFO * Info + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcePOOL vidMemPool; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d Type=%d Info=0x%x", + Kernel, ProcessID, Type, Info); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Info != gcvNULL); + + /* Deocde pool. */ + vidMemPool = (Type & gcdDB_VIDEO_MEMORY_POOL_MASK) >> gcdDB_VIDEO_MEMORY_POOL_SHIFT; + + Type &= gcdDATABASE_TYPE_MASK; + + /* Find the database. */ + if(Type != gcvDB_IDLE) + { + gcmkONERROR( + gckKERNEL_FindDatabase(Kernel, ProcessID, LastProcessID, &database)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, database->counterMutex, gcvINFINITE)); + + /* Get pointer to counters. */ + switch (Type) + { + case gcvDB_VIDEO_MEMORY: + if (vidMemPool != gcvPOOL_UNKNOWN) + { + gckOS_MemCopy(&Info->counters, + &database->vidMemPool[vidMemPool], + gcmSIZEOF(database->vidMemPool[vidMemPool])); + } + else + { + gckOS_MemCopy(&Info->counters, + &database->vidMem, + gcmSIZEOF(database->vidMem)); + } + break; + + case gcvDB_NON_PAGED: + gckOS_MemCopy(&Info->counters, + &database->nonPaged, + gcmSIZEOF(database->vidMem)); + break; + + case gcvDB_CONTIGUOUS: + gckOS_MemCopy(&Info->counters, + &database->contiguous, + gcmSIZEOF(database->vidMem)); + break; + + case gcvDB_MAP_MEMORY: + gckOS_MemCopy(&Info->counters, + &database->mapMemory, + gcmSIZEOF(database->mapMemory)); + break; + + case gcvDB_MAP_USER_MEMORY: + gckOS_MemCopy(&Info->counters, + &database->mapUserMemory, + gcmSIZEOF(database->mapUserMemory)); + break; + + case gcvDB_COMMAND_BUFFER: + gckOS_MemCopy(&Info->counters, + &database->virtualCommandBuffer, + gcmSIZEOF(database->virtualCommandBuffer)); + break; + + default: + break; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, database->counterMutex)); + } + else + { + Info->time = Kernel->db->idleTime; + Kernel->db->idleTime = 0; + } + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_FindHandleDatbase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gctPOINTER * HandleDatabase, + OUT gctPOINTER * HandleDatabaseMutex + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", + Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + *HandleDatabase = database->handleDatabase; + *HandleDatabaseMutex = database->handleDatabaseMutex; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +gckKERNEL_GetProcessMMU( + IN gckKERNEL Kernel, + OUT gckMMU * Mmu + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gctUINT32 processID; + + gcmkONERROR(gckOS_GetProcessID(&processID)); + + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, processID, gcvFALSE, &database)); + + *Mmu = database->mmu; + + return gcvSTATUS_OK; + +OnError: + return status; +} +#endif + +#if gcdSECURE_USER +/******************************************************************************* +** gckKERNEL_GetProcessDBCache +** +** Get teh secure cache from a process database. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to a gckKERNEL object. +** +** gctUINT32 ProcessID +** Process ID used to identify the database. +** +** OUTPUT: +** +** gcskSECURE_CACHE_PTR * Cache +** Pointer to a variable that receives the secure cache pointer. +*/ +gceSTATUS +gckKERNEL_GetProcessDBCache( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + OUT gcskSECURE_CACHE_PTR * Cache + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Cache != gcvNULL); + + /* Find the database. */ + gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + /* Return the pointer to the cache. */ + *Cache = &database->cache; + + /* Success. */ + gcmkFOOTER_ARG("*Cache=0x%x", *Cache); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +gceSTATUS +gckKERNEL_DumpProcessDB( + IN gckKERNEL Kernel + ) +{ + gcsDATABASE_PTR database; + gctINT i, pid; + gctUINT8 name[24]; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Acquire the database mutex. */ + gcmkVERIFY_OK( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + + gcmkPRINT("**************************\n"); + gcmkPRINT("*** PROCESS DB DUMP ***\n"); + gcmkPRINT("**************************\n"); + + gcmkPRINT_N(8, "%-8s%s\n", "PID", "NAME"); + /* Walk the databases. */ + for (i = 0; i < gcmCOUNTOF(Kernel->db->db); ++i) + { + for (database = Kernel->db->db[i]; + database != gcvNULL; + database = database->next) + { + pid = database->processID; + + gcmkVERIFY_OK(gckOS_ZeroMemory(name, gcmSIZEOF(name))); + + gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name)); + + gcmkPRINT_N(8, "%-8d%s\n", pid, name); + } + } + + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +void +_DumpCounter( + IN gcsDATABASE_COUNTERS * Counter, + IN gctCONST_STRING Name + ) +{ + gcmkPRINT("%s:", Name); + gcmkPRINT(" Currently allocated : %10lld", Counter->bytes); + gcmkPRINT(" Maximum allocated : %10lld", Counter->maxBytes); + gcmkPRINT(" Total allocated : %10lld", Counter->totalBytes); +} + +gceSTATUS +gckKERNEL_DumpVidMemUsage( + IN gckKERNEL Kernel, + IN gctINT32 ProcessID + ) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gcsDATABASE_COUNTERS * counter; + gctUINT32 i = 0; + + static gctCONST_STRING surfaceTypes[] = { + "UNKNOWN", + "INDEX", + "VERTEX", + "TEXTURE", + "RENDER_TARGET", + "DEPTH", + "BITMAP", + "TILE_STATUS", + "IMAGE", + "MASK", + "SCISSOR", + "HIERARCHICAL_DEPTH", + }; + + gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", + Kernel, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Find the database. */ + gcmkONERROR( + gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); + + gcmkPRINT("VidMem Usage (Process %d):", ProcessID); + + /* Get pointer to counters. */ + counter = &database->vidMem; + + _DumpCounter(counter, "Total Video Memory"); + + for (i = 0; i < gcvSURF_NUM_TYPES; i++) + { + counter = &database->vidMemType[i]; + + _DumpCounter(counter, surfaceTypes[i]); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_debug.c b/drivers/gpu/galcore/gc_hal_kernel_debug.c new file mode 100644 index 00000000000000..97b8275e326f13 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_debug.c @@ -0,0 +1,2785 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" +#include + +/******************************************************************************\ +******************************** Debug Variables ******************************* +\******************************************************************************/ + +static gceSTATUS _lastError = gcvSTATUS_OK; +static gctUINT32 _debugLevel = gcvLEVEL_INFO; +/* +_debugZones config value +Please Reference define in gc_hal_base.h +*/ +static gctUINT32 _debugZones = gcvZONE_ALL; + +/******************************************************************************\ +********************************* Debug Switches ******************************* +\******************************************************************************/ + +/* + gcdBUFFERED_OUTPUT + + When set to non-zero, all output is collected into a buffer with the + specified size. Once the buffer gets full, the debug buffer will be + printed to the console. gcdBUFFERED_SIZE determines the size of the buffer. +*/ +#define gcdBUFFERED_OUTPUT 0 + +/* + gcdBUFFERED_SIZE + + When set to non-zero, all output is collected into a buffer with the + specified size. Once the buffer gets full, the debug buffer will be + printed to the console. +*/ +#define gcdBUFFERED_SIZE (1024 * 1024 * 2) + +/* + gcdDMA_BUFFER_COUNT + + If greater then zero, the debugger will attempt to find the command buffer + where DMA is currently executing and then print this buffer and + (gcdDMA_BUFFER_COUNT - 1) buffers before the current one. If set to zero + or the current buffer is not found, all buffers are printed. +*/ +#define gcdDMA_BUFFER_COUNT 0 + +/* + gcdTHREAD_BUFFERS + + When greater then one, will accumulate messages from the specified number + of threads in separate output buffers. +*/ +#define gcdTHREAD_BUFFERS 1 + +/* + gcdENABLE_OVERFLOW + + When set to non-zero, and the output buffer gets full, instead of being + printed, it will be allowed to overflow removing the oldest messages. +*/ +#define gcdENABLE_OVERFLOW 1 + +/* + gcdSHOW_LINE_NUMBER + + When enabledm each print statement will be preceeded with the current + line number. +*/ +#define gcdSHOW_LINE_NUMBER 0 + +/* + gcdSHOW_PROCESS_ID + + When enabledm each print statement will be preceeded with the current + process ID. +*/ +#define gcdSHOW_PROCESS_ID 0 + +/* + gcdSHOW_THREAD_ID + + When enabledm each print statement will be preceeded with the current + thread ID. +*/ +#define gcdSHOW_THREAD_ID 0 + +/* + gcdSHOW_TIME + + When enabled each print statement will be preceeded with the current + high-resolution time. +*/ +#define gcdSHOW_TIME 0 + + +/******************************************************************************\ +****************************** Miscellaneous Macros **************************** +\******************************************************************************/ + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +# define gcmDBGASSERT(Expression, Format, Value) \ + if (!(Expression)) \ + { \ + _DirectPrint( \ + "*** gcmDBGASSERT ***************************\n" \ + " function : %s\n" \ + " line : %d\n" \ + " expression : " #Expression "\n" \ + " actual value : " Format "\n", \ + __FUNCTION__, __LINE__, Value \ + ); \ + } +#else +# define gcmDBGASSERT(Expression, Format, Value) +#endif + +#define gcmPTRALIGNMENT(Pointer, Alignemnt) \ +( \ + gcmALIGN(gcmPTR2INT32(Pointer), Alignemnt) - gcmPTR2INT32(Pointer) \ +) + +#if gcdALIGNBYSIZE +# define gcmISALIGNED(Offset, Alignment) \ + (((Offset) & ((Alignment) - 1)) == 0) + +# define gcmkALIGNPTR(Type, Pointer, Alignment) \ + Pointer = (Type) gcmINT2PTR(gcmALIGN(gcmPTR2INT32(Pointer), Alignment)) +#else +# define gcmISALIGNED(Offset, Alignment) \ + gcvTRUE + +# define gcmkALIGNPTR(Type, Pointer, Alignment) +#endif + +#define gcmALIGNSIZE(Offset, Size) \ + ((Size - Offset) + Size) + +#define gcdHAVEPREFIX \ +( \ + gcdSHOW_TIME \ + || gcdSHOW_LINE_NUMBER \ + || gcdSHOW_PROCESS_ID \ + || gcdSHOW_THREAD_ID \ +) + +#if gcdHAVEPREFIX + +# define gcdOFFSET 0 + +#if gcdSHOW_TIME +#if gcmISALIGNED(gcdOFFSET, 8) +# define gcdTIMESIZE gcmSIZEOF(gctUINT64) +# elif gcdOFFSET == 4 +# define gcdTIMESIZE gcmALIGNSIZE(4, gcmSIZEOF(gctUINT64)) +# else +# error "Unexpected offset value." +# endif +# undef gcdOFFSET +# define gcdOFFSET 8 +#if !defined(gcdPREFIX_LEADER) +# define gcdPREFIX_LEADER gcmSIZEOF(gctUINT64) +# define gcdTIMEFORMAT "0x%016llX" +# else +# define gcdTIMEFORMAT ", 0x%016llX" +# endif +# else +# define gcdTIMESIZE 0 +# define gcdTIMEFORMAT +# endif + +#if gcdSHOW_LINE_NUMBER +#if gcmISALIGNED(gcdOFFSET, 8) +# define gcdNUMSIZE gcmSIZEOF(gctUINT64) +# elif gcdOFFSET == 4 +# define gcdNUMSIZE gcmALIGNSIZE(4, gcmSIZEOF(gctUINT64)) +# else +# error "Unexpected offset value." +# endif +# undef gcdOFFSET +# define gcdOFFSET 8 +#if !defined(gcdPREFIX_LEADER) +# define gcdPREFIX_LEADER gcmSIZEOF(gctUINT64) +# define gcdNUMFORMAT "%8llu" +# else +# define gcdNUMFORMAT ", %8llu" +# endif +# else +# define gcdNUMSIZE 0 +# define gcdNUMFORMAT +# endif + +#if gcdSHOW_PROCESS_ID +#if gcmISALIGNED(gcdOFFSET, 4) +# define gcdPIDSIZE gcmSIZEOF(gctUINT32) +# else +# error "Unexpected offset value." +# endif +# undef gcdOFFSET +# define gcdOFFSET 4 +#if !defined(gcdPREFIX_LEADER) +# define gcdPREFIX_LEADER gcmSIZEOF(gctUINT32) +# define gcdPIDFORMAT "pid=%5d" +# else +# define gcdPIDFORMAT ", pid=%5d" +# endif +# else +# define gcdPIDSIZE 0 +# define gcdPIDFORMAT +# endif + +#if gcdSHOW_THREAD_ID +#if gcmISALIGNED(gcdOFFSET, 4) +# define gcdTIDSIZE gcmSIZEOF(gctUINT32) +# else +# error "Unexpected offset value." +# endif +# undef gcdOFFSET +# define gcdOFFSET 4 +#if !defined(gcdPREFIX_LEADER) +# define gcdPREFIX_LEADER gcmSIZEOF(gctUINT32) +# define gcdTIDFORMAT "tid=%5d" +# else +# define gcdTIDFORMAT ", tid=%5d" +# endif +# else +# define gcdTIDSIZE 0 +# define gcdTIDFORMAT +# endif + +# define gcdPREFIX_SIZE \ + ( \ + gcdTIMESIZE \ + + gcdNUMSIZE \ + + gcdPIDSIZE \ + + gcdTIDSIZE \ + ) + + static const char * _prefixFormat = + "[" + gcdTIMEFORMAT + gcdNUMFORMAT + gcdPIDFORMAT + gcdTIDFORMAT + "] "; + +#else + +# define gcdPREFIX_LEADER gcmSIZEOF(gctUINT32) +# define gcdPREFIX_SIZE 0 + +#endif + +/* Assumed largest variable argument leader size. */ +#define gcdVARARG_LEADER gcmSIZEOF(gctUINT64) + +/* Alignnments. */ +#if gcdALIGNBYSIZE +# define gcdPREFIX_ALIGNMENT gcdPREFIX_LEADER +# define gcdVARARG_ALIGNMENT gcdVARARG_LEADER +#else +# define gcdPREFIX_ALIGNMENT 0 +# define gcdVARARG_ALIGNMENT 0 +#endif + +#if gcdBUFFERED_OUTPUT +# define gcdOUTPUTPREFIX _AppendPrefix +# define gcdOUTPUTSTRING _AppendString +# define gcdOUTPUTCOPY _AppendCopy +# define gcdOUTPUTBUFFER _AppendBuffer +#else +# define gcdOUTPUTPREFIX _PrintPrefix +# define gcdOUTPUTSTRING _PrintString +# define gcdOUTPUTCOPY _PrintString +# define gcdOUTPUTBUFFER _PrintBuffer +#endif + +/******************************************************************************\ +****************************** Private Structures ****************************** +\******************************************************************************/ + +typedef enum _gceBUFITEM +{ + gceBUFITEM_NONE, + gcvBUFITEM_PREFIX, + gcvBUFITEM_STRING, + gcvBUFITEM_COPY, + gcvBUFITEM_BUFFER +} +gceBUFITEM; + +/* Common item head/buffer terminator. */ +typedef struct _gcsBUFITEM_HEAD * gcsBUFITEM_HEAD_PTR; +typedef struct _gcsBUFITEM_HEAD +{ + gceBUFITEM type; +} +gcsBUFITEM_HEAD; + +/* String prefix (for ex. [ 1,tid=0x019A]) */ +typedef struct _gcsBUFITEM_PREFIX * gcsBUFITEM_PREFIX_PTR; +typedef struct _gcsBUFITEM_PREFIX +{ + gceBUFITEM type; +#if gcdHAVEPREFIX + gctPOINTER prefixData; +#endif +} +gcsBUFITEM_PREFIX; + +/* Buffered string. */ +typedef struct _gcsBUFITEM_STRING * gcsBUFITEM_STRING_PTR; +typedef struct _gcsBUFITEM_STRING +{ + gceBUFITEM type; + gctINT indent; + gctCONST_STRING message; + gctPOINTER messageData; + gctUINT messageDataSize; +} +gcsBUFITEM_STRING; + +/* Buffered string (copy of the string is included with the record). */ +typedef struct _gcsBUFITEM_COPY * gcsBUFITEM_COPY_PTR; +typedef struct _gcsBUFITEM_COPY +{ + gceBUFITEM type; + gctINT indent; + gctPOINTER messageData; + gctUINT messageDataSize; +} +gcsBUFITEM_COPY; + +/* Memory buffer. */ +typedef struct _gcsBUFITEM_BUFFER * gcsBUFITEM_BUFFER_PTR; +typedef struct _gcsBUFITEM_BUFFER +{ + gceBUFITEM type; + gctINT indent; + gceDUMP_BUFFER bufferType; + +#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1) + gctUINT32 dmaAddress; +#endif + + gctUINT dataSize; + gctUINT32 address; +#if gcdHAVEPREFIX + gctPOINTER prefixData; +#endif +} +gcsBUFITEM_BUFFER; + +typedef struct _gcsBUFFERED_OUTPUT * gcsBUFFERED_OUTPUT_PTR; +typedef struct _gcsBUFFERED_OUTPUT +{ +#if gcdTHREAD_BUFFERS > 1 + gctUINT32 threadID; +#endif + +#if gcdSHOW_LINE_NUMBER + gctUINT64 lineNumber; +#endif + + gctINT indent; + +#if gcdBUFFERED_OUTPUT + gctINT start; + gctINT index; + gctINT count; + gctUINT8 buffer[gcdBUFFERED_SIZE]; +#endif + + gcsBUFFERED_OUTPUT_PTR prev; + gcsBUFFERED_OUTPUT_PTR next; +} +gcsBUFFERED_OUTPUT; + +typedef gctUINT (* gcfPRINTSTRING) ( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gcsBUFITEM_HEAD_PTR Item + ); + +typedef gctINT (* gcfGETITEMSIZE) ( + IN gcsBUFITEM_HEAD_PTR Item + ); + +/******************************************************************************\ +******************************* Private Variables ****************************** +\******************************************************************************/ + +static gcsBUFFERED_OUTPUT _outputBuffer[gcdTHREAD_BUFFERS]; +static gcsBUFFERED_OUTPUT_PTR _outputBufferHead = gcvNULL; +static gcsBUFFERED_OUTPUT_PTR _outputBufferTail = gcvNULL; + +/******************************************************************************\ +****************************** Item Size Functions ***************************** +\******************************************************************************/ + +#if gcdBUFFERED_OUTPUT +static gctINT +_GetTerminatorItemSize( + IN gcsBUFITEM_HEAD_PTR Item + ) +{ + return gcmSIZEOF(gcsBUFITEM_HEAD); +} + +static gctINT +_GetPrefixItemSize( + IN gcsBUFITEM_HEAD_PTR Item + ) +{ +#if gcdHAVEPREFIX + gcsBUFITEM_PREFIX_PTR item = (gcsBUFITEM_PREFIX_PTR) Item; + gctUINT vlen = ((gctUINT8_PTR) item->prefixData) - ((gctUINT8_PTR) item); + return vlen + gcdPREFIX_SIZE; +#else + return gcmSIZEOF(gcsBUFITEM_PREFIX); +#endif +} + +static gctINT +_GetStringItemSize( + IN gcsBUFITEM_HEAD_PTR Item + ) +{ + gcsBUFITEM_STRING_PTR item = (gcsBUFITEM_STRING_PTR) Item; + gctUINT vlen = ((gctUINT8_PTR) item->messageData) - ((gctUINT8_PTR) item); + return vlen + item->messageDataSize; +} + +static gctINT +_GetCopyItemSize( + IN gcsBUFITEM_HEAD_PTR Item + ) +{ + gcsBUFITEM_COPY_PTR item = (gcsBUFITEM_COPY_PTR) Item; + gctUINT vlen = ((gctUINT8_PTR) item->messageData) - ((gctUINT8_PTR) item); + return vlen + item->messageDataSize; +} + +static gctINT +_GetBufferItemSize( + IN gcsBUFITEM_HEAD_PTR Item + ) +{ +#if gcdHAVEPREFIX + gcsBUFITEM_BUFFER_PTR item = (gcsBUFITEM_BUFFER_PTR) Item; + gctUINT vlen = ((gctUINT8_PTR) item->prefixData) - ((gctUINT8_PTR) item); + return vlen + gcdPREFIX_SIZE + item->dataSize; +#else + gcsBUFITEM_BUFFER_PTR item = (gcsBUFITEM_BUFFER_PTR) Item; + return gcmSIZEOF(gcsBUFITEM_BUFFER) + item->dataSize; +#endif +} + +static gcfGETITEMSIZE _itemSize[] = +{ + _GetTerminatorItemSize, + _GetPrefixItemSize, + _GetStringItemSize, + _GetCopyItemSize, + _GetBufferItemSize +}; +#endif + +/******************************************************************************\ +******************************* Printing Functions ***************************** +\******************************************************************************/ + +#if gcdDEBUG || gcdBUFFERED_OUTPUT +static void +_DirectPrint( + gctCONST_STRING Message, + ... + ) +{ + gctINT len; + char buffer[768]; + gctARGUMENTS arguments; + + gcmkARGUMENTS_START(arguments, Message); + len = gcmkVSPRINTF(buffer, gcmSIZEOF(buffer), Message, &arguments); + gcmkARGUMENTS_END(arguments); + + buffer[len] = '\0'; + gcmkOUTPUT_STRING(buffer); +} +#endif + +static int +_AppendIndent( + IN gctINT Indent, + IN char * Buffer, + IN int BufferSize + ) +{ + gctINT i; + + gctINT len = 0; + gctINT indent = Indent % 40; + + for (i = 0; i < indent; i += 1) + { + Buffer[len++] = ' '; + } + + if (indent != Indent) + { + len += gcmkSPRINTF( + Buffer + len, BufferSize - len, " <%d> ", Indent + ); + + Buffer[len] = '\0'; + } + + return len; +} + +#if gcdHAVEPREFIX +static void +_PrintPrefix( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctPOINTER Data + ) +{ + char buffer[768]; + gctINT len; + + /* Format the string. */ + len = gcmkVSPRINTF(buffer, gcmSIZEOF(buffer), _prefixFormat, Data); + buffer[len] = '\0'; + + /* Print the string. */ + gcmkOUTPUT_STRING(buffer); +} +#endif + +static void +_PrintString( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctINT Indent, + IN gctCONST_STRING Message, + IN gctUINT ArgumentSize, + IN gctPOINTER Data + ) +{ + char buffer[768]; + gctINT len; + + /* Append the indent string. */ + len = _AppendIndent(Indent, buffer, gcmSIZEOF(buffer)); + + /* Format the string. */ + len += gcmkVSPRINTF(buffer + len, gcmSIZEOF(buffer) - len, Message, Data); + buffer[len] = '\0'; + + /* Add end-of-line if missing. */ + if (buffer[len - 1] != '\n') + { + buffer[len++] = '\n'; + buffer[len] = '\0'; + } + + /* Print the string. */ + gcmkOUTPUT_STRING(buffer); +} + +static void +_PrintBuffer( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctINT Indent, + IN gctPOINTER PrefixData, + IN gctPOINTER Data, + IN gctUINT Address, + IN gctUINT DataSize, + IN gceDUMP_BUFFER Type, + IN gctUINT32 DmaAddress + ) +{ + static gctCONST_STRING _titleString[] = + { + "CONTEXT BUFFER", + "USER COMMAND BUFFER", + "KERNEL COMMAND BUFFER", + "LINK BUFFER", + "WAIT LINK BUFFER", + "" + }; + + static const gctINT COLUMN_COUNT = 8; + + gctUINT i, count, column, address; + gctUINT32_PTR data; + gctCHAR buffer[768]; + gctUINT indent, len; + gctBOOL command; + + /* Append space for the prefix. */ +#if gcdHAVEPREFIX + indent = gcmkVSPRINTF(buffer, gcmSIZEOF(buffer), _prefixFormat, PrefixData); + buffer[indent] = '\0'; +#else + indent = 0; +#endif + + /* Append the indent string. */ + indent += _AppendIndent( + Indent, buffer + indent, gcmSIZEOF(buffer) - indent + ); + + switch (Type) + { + case gceDUMP_BUFFER_CONTEXT: + case gceDUMP_BUFFER_USER: + case gceDUMP_BUFFER_KERNEL: + case gceDUMP_BUFFER_LINK: + case gceDUMP_BUFFER_WAITLINK: + /* Form and print the title string. */ + gcmkSPRINTF2( + buffer + indent, gcmSIZEOF(buffer) - indent, + "%s%s\n", _titleString[Type], + ((DmaAddress >= Address) && (DmaAddress < Address + DataSize)) + ? " (CURRENT)" : "" + ); + + gcmkOUTPUT_STRING(buffer); + + /* Terminate the string. */ + buffer[indent] = '\0'; + + /* This is a command buffer. */ + command = gcvTRUE; + break; + + case gceDUMP_BUFFER_FROM_USER: + /* This is not a command buffer. */ + command = gcvFALSE; + + /* No title. */ + break; + + default: + gcmDBGASSERT(gcvFALSE, "%s", "invalid buffer type"); + + /* This is not a command buffer. */ + command = gcvFALSE; + } + + /* Overwrite the prefix with spaces. */ + for (i = 0; i < indent; i += 1) + { + buffer[i] = ' '; + } + + /* Form and print the opening string. */ + if (command) + { + gcmkSPRINTF2( + buffer + indent, gcmSIZEOF(buffer) - indent, + "@[kernel.command %08X %08X\n", Address, DataSize + ); + + gcmkOUTPUT_STRING(buffer); + + /* Terminate the string. */ + buffer[indent] = '\0'; + } + + /* Get initial address. */ + address = Address; + + /* Cast the data pointer. */ + data = (gctUINT32_PTR) Data; + + /* Compute the number of double words. */ + count = DataSize / gcmSIZEOF(gctUINT32); + + /* Print the buffer. */ + for (i = 0, len = indent, column = 0; i < count; i += 1) + { + /* Append the address. */ + if (column == 0) + { + len += gcmkSPRINTF( + buffer + len, gcmSIZEOF(buffer) - len, "0x%08X:", address + ); + } + + /* Append the data value. */ + len += gcmkSPRINTF2( + buffer + len, gcmSIZEOF(buffer) - len, "%c%08X", + (address == DmaAddress)? '>' : ' ', data[i] + ); + + buffer[len] = '\0'; + + /* Update the address. */ + address += gcmSIZEOF(gctUINT32); + + /* Advance column count. */ + column += 1; + + /* End of line? */ + if ((column % COLUMN_COUNT) == 0) + { + /* Append EOL. */ + gcmkSTRCAT(buffer + len, gcmSIZEOF(buffer) - len, "\n"); + + /* Print the string. */ + gcmkOUTPUT_STRING(buffer); + + /* Reset. */ + len = indent; + column = 0; + } + } + + /* Print the last partial string. */ + if (column != 0) + { + /* Append EOL. */ + gcmkSTRCAT(buffer + len, gcmSIZEOF(buffer) - len, "\n"); + + /* Print the string. */ + gcmkOUTPUT_STRING(buffer); + } + + /* Form and print the opening string. */ + if (command) + { + buffer[indent] = '\0'; + gcmkSTRCAT(buffer, gcmSIZEOF(buffer), "] -- command\n"); + gcmkOUTPUT_STRING(buffer); + } +} + +#if gcdBUFFERED_OUTPUT +static gctUINT +_PrintNone( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gcsBUFITEM_HEAD_PTR Item + ) +{ + /* Return the size of the node. */ + return gcmSIZEOF(gcsBUFITEM_HEAD); +} + +static gctUINT +_PrintPrefixWrapper( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gcsBUFITEM_HEAD_PTR Item + ) +{ +#if gcdHAVEPREFIX + gcsBUFITEM_PREFIX_PTR item; + gctUINT vlen; + + /* Get access to the data. */ + item = (gcsBUFITEM_PREFIX_PTR) Item; + + /* Print the message. */ + _PrintPrefix(OutputBuffer, item->prefixData); + + /* Compute the size of the variable portion of the structure. */ + vlen = ((gctUINT8_PTR) item->prefixData) - ((gctUINT8_PTR) item); + + /* Return the size of the node. */ + return vlen + gcdPREFIX_SIZE; +#else + return gcmSIZEOF(gcsBUFITEM_PREFIX); +#endif +} + +static gctUINT +_PrintStringWrapper( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gcsBUFITEM_HEAD_PTR Item + ) +{ + gcsBUFITEM_STRING_PTR item; + gctUINT vlen; + + /* Get access to the data. */ + item = (gcsBUFITEM_STRING_PTR) Item; + + /* Print the message. */ + _PrintString( + OutputBuffer, + item->indent, item->message, item->messageDataSize, item->messageData + ); + + /* Compute the size of the variable portion of the structure. */ + vlen = ((gctUINT8_PTR) item->messageData) - ((gctUINT8_PTR) item); + + /* Return the size of the node. */ + return vlen + item->messageDataSize; +} + +static gctUINT +_PrintCopyWrapper( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gcsBUFITEM_HEAD_PTR Item + ) +{ + gcsBUFITEM_COPY_PTR item; + gctCONST_STRING message; + gctUINT vlen; + + /* Get access to the data. */ + item = (gcsBUFITEM_COPY_PTR) Item; + + /* Determine the string pointer. */ + message = (gctCONST_STRING) (item + 1); + + /* Print the message. */ + _PrintString( + OutputBuffer, + item->indent, message, item->messageDataSize, item->messageData + ); + + /* Compute the size of the variable portion of the structure. */ + vlen = ((gctUINT8_PTR) item->messageData) - ((gctUINT8_PTR) item); + + /* Return the size of the node. */ + return vlen + item->messageDataSize; +} + +static gctUINT +_PrintBufferWrapper( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gcsBUFITEM_HEAD_PTR Item + ) +{ +#if gcdHAVEPREFIX + gctUINT32 dmaAddress; + gcsBUFITEM_BUFFER_PTR item; + gctPOINTER data; + gctUINT vlen; + + /* Get access to the data. */ + item = (gcsBUFITEM_BUFFER_PTR) Item; + +#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1) + dmaAddress = item->dmaAddress; +#else + dmaAddress = 0xFFFFFFFF; +#endif + + if (dmaAddress != 0) + { + /* Compute the data address. */ + data = ((gctUINT8_PTR) item->prefixData) + gcdPREFIX_SIZE; + + /* Print buffer. */ + _PrintBuffer( + OutputBuffer, + item->indent, item->prefixData, + data, item->address, item->dataSize, + item->bufferType, dmaAddress + ); + } + + /* Compute the size of the variable portion of the structure. */ + vlen = ((gctUINT8_PTR) item->prefixData) - ((gctUINT8_PTR) item); + + /* Return the size of the node. */ + return vlen + gcdPREFIX_SIZE + item->dataSize; +#else + gctUINT32 dmaAddress; + gcsBUFITEM_BUFFER_PTR item; + + /* Get access to the data. */ + item = (gcsBUFITEM_BUFFER_PTR) Item; + +#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1) + dmaAddress = item->dmaAddress; +#else + dmaAddress = 0xFFFFFFFF; +#endif + + if (dmaAddress != 0) + { + /* Print buffer. */ + _PrintBuffer( + OutputBuffer, + item->indent, gcvNULL, + item + 1, item->address, item->dataSize, + item->bufferType, dmaAddress + ); + } + + /* Return the size of the node. */ + return gcmSIZEOF(gcsBUFITEM_BUFFER) + item->dataSize; +#endif +} + +static gcfPRINTSTRING _printArray[] = +{ + _PrintNone, + _PrintPrefixWrapper, + _PrintStringWrapper, + _PrintCopyWrapper, + _PrintBufferWrapper +}; +#endif + +/******************************************************************************\ +******************************* Private Functions ****************************** +\******************************************************************************/ + +#if gcdBUFFERED_OUTPUT + +#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1) +static gcsBUFITEM_BUFFER_PTR +_FindCurrentDMABuffer( + gctUINT32 DmaAddress + ) +{ + gctINT i, skip; + gcsBUFITEM_HEAD_PTR item; + gcsBUFITEM_BUFFER_PTR dmaCurrent; + + /* Reset the current buffer. */ + dmaCurrent = gcvNULL; + + /* Get the first stored item. */ + item = (gcsBUFITEM_HEAD_PTR) &_outputBufferHead->buffer[_outputBufferHead->start]; + + /* Run through all items. */ + for (i = 0; i < _outputBufferHead->count; i += 1) + { + /* Buffer item? */ + if (item->type == gcvBUFITEM_BUFFER) + { + gcsBUFITEM_BUFFER_PTR buffer = (gcsBUFITEM_BUFFER_PTR) item; + + if ((DmaAddress >= buffer->address) && + (DmaAddress < buffer->address + buffer->dataSize)) + { + dmaCurrent = buffer; + } + } + + /* Get the item size and skip it. */ + skip = (* _itemSize[item->type]) (item); + item = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + skip); + + /* End of the buffer? Wrap around. */ + if (item->type == gceBUFITEM_NONE) + { + item = (gcsBUFITEM_HEAD_PTR) _outputBufferHead->buffer; + } + } + + /* Return result. */ + return dmaCurrent; +} + +static void +_EnableAllDMABuffers( + void + ) +{ + gctINT i, skip; + gcsBUFITEM_HEAD_PTR item; + + /* Get the first stored item. */ + item = (gcsBUFITEM_HEAD_PTR) &_outputBufferHead->buffer[_outputBufferHead->start]; + + /* Run through all items. */ + for (i = 0; i < _outputBufferHead->count; i += 1) + { + /* Buffer item? */ + if (item->type == gcvBUFITEM_BUFFER) + { + gcsBUFITEM_BUFFER_PTR buffer = (gcsBUFITEM_BUFFER_PTR) item; + + /* Enable the buffer. */ + buffer->dmaAddress = ~0U; + } + + /* Get the item size and skip it. */ + skip = (* _itemSize[item->type]) (item); + item = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + skip); + + /* End of the buffer? Wrap around. */ + if (item->type == gceBUFITEM_NONE) + { + item = (gcsBUFITEM_HEAD_PTR) _outputBufferHead->buffer; + } + } +} + +static void +_EnableDMABuffers( + gctUINT32 DmaAddress, + gcsBUFITEM_BUFFER_PTR CurrentDMABuffer + ) +{ + gctINT i, skip, index; + gcsBUFITEM_HEAD_PTR item; + gcsBUFITEM_BUFFER_PTR buffers[gcdDMA_BUFFER_COUNT]; + + /* Reset buffer pointers. */ + gckOS_ZeroMemory(buffers, gcmSIZEOF(buffers)); + + /* Set the current buffer index. */ + index = -1; + + /* Get the first stored item. */ + item = (gcsBUFITEM_HEAD_PTR) &_outputBufferHead->buffer[_outputBufferHead->start]; + + /* Run through all items until the current DMA buffer is found. */ + for (i = 0; i < _outputBufferHead->count; i += 1) + { + /* Buffer item? */ + if (item->type == gcvBUFITEM_BUFFER) + { + /* Advance the index. */ + index = (index + 1) % gcdDMA_BUFFER_COUNT; + + /* Add to the buffer array. */ + buffers[index] = (gcsBUFITEM_BUFFER_PTR) item; + + /* Stop if this is the current DMA buffer. */ + if ((gcsBUFITEM_BUFFER_PTR) item == CurrentDMABuffer) + { + break; + } + } + + /* Get the item size and skip it. */ + skip = (* _itemSize[item->type]) (item); + item = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + skip); + + /* End of the buffer? Wrap around. */ + if (item->type == gceBUFITEM_NONE) + { + item = (gcsBUFITEM_HEAD_PTR) _outputBufferHead->buffer; + } + } + + /* Enable the found buffers. */ + gcmDBGASSERT(index != -1, "%d", index); + + for (i = 0; i < gcdDMA_BUFFER_COUNT; i += 1) + { + if (buffers[index] == gcvNULL) + { + break; + } + + buffers[index]->dmaAddress = DmaAddress; + + index -= 1; + + if (index == -1) + { + index = gcdDMA_BUFFER_COUNT - 1; + } + } +} +#endif + +static void +_Flush( + gctUINT32 DmaAddress + ) +{ + gctINT i, skip; + gcsBUFITEM_HEAD_PTR item; + + gcsBUFFERED_OUTPUT_PTR outputBuffer = _outputBufferHead; + +#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1) + if ((outputBuffer != gcvNULL) && (outputBuffer->count != 0)) + { + /* Find the current DMA buffer. */ + gcsBUFITEM_BUFFER_PTR dmaCurrent = _FindCurrentDMABuffer(DmaAddress); + + /* Was the current buffer found? */ + if (dmaCurrent == gcvNULL) + { + /* No, print all buffers. */ + _EnableAllDMABuffers(); + } + else + { + /* Yes, enable only specified number of buffers. */ + _EnableDMABuffers(DmaAddress, dmaCurrent); + } + } +#endif + + while (outputBuffer != gcvNULL) + { + if (outputBuffer->count != 0) + { + _DirectPrint("********************************************************************************\n"); + _DirectPrint("FLUSHING DEBUG OUTPUT BUFFER (%d elements).\n", outputBuffer->count); + _DirectPrint("********************************************************************************\n"); + + item = (gcsBUFITEM_HEAD_PTR) &outputBuffer->buffer[outputBuffer->start]; + + for (i = 0; i < outputBuffer->count; i += 1) + { + skip = (* _printArray[item->type]) (outputBuffer, item); + + item = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + skip); + + if (item->type == gceBUFITEM_NONE) + { + item = (gcsBUFITEM_HEAD_PTR) outputBuffer->buffer; + } + } + + outputBuffer->start = 0; + outputBuffer->index = 0; + outputBuffer->count = 0; + } + + outputBuffer = outputBuffer->next; + } +} + +static gcsBUFITEM_HEAD_PTR +_AllocateItem( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctINT Size + ) +{ + gctINT skip; + gcsBUFITEM_HEAD_PTR item, next; + +#if gcdENABLE_OVERFLOW + if ( + (OutputBuffer->index + Size >= gcdBUFFERED_SIZE - gcmSIZEOF(gcsBUFITEM_HEAD)) + || + ( + (OutputBuffer->index < OutputBuffer->start) && + (OutputBuffer->index + Size >= OutputBuffer->start) + ) + ) + { + if (OutputBuffer->index + Size >= gcdBUFFERED_SIZE - gcmSIZEOF(gcsBUFITEM_HEAD)) + { + if (OutputBuffer->index < OutputBuffer->start) + { + item = (gcsBUFITEM_HEAD_PTR) &OutputBuffer->buffer[OutputBuffer->start]; + + while (item->type != gceBUFITEM_NONE) + { + skip = (* _itemSize[item->type]) (item); + + OutputBuffer->start += skip; + OutputBuffer->count -= 1; + + item->type = gceBUFITEM_NONE; + item = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + skip); + } + + OutputBuffer->start = 0; + } + + OutputBuffer->index = 0; + } + + item = (gcsBUFITEM_HEAD_PTR) &OutputBuffer->buffer[OutputBuffer->start]; + + while (OutputBuffer->start - OutputBuffer->index <= Size) + { + skip = (* _itemSize[item->type]) (item); + + OutputBuffer->start += skip; + OutputBuffer->count -= 1; + + item->type = gceBUFITEM_NONE; + item = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + skip); + + if (item->type == gceBUFITEM_NONE) + { + OutputBuffer->start = 0; + break; + } + } + } +#else + if (OutputBuffer->index + Size > gcdBUFFERED_SIZE - gcmSIZEOF(gcsBUFITEM_HEAD)) + { + _DirectPrint("\nMessage buffer full; forcing message flush.\n\n"); + _Flush(~0U); + } +#endif + + item = (gcsBUFITEM_HEAD_PTR) &OutputBuffer->buffer[OutputBuffer->index]; + + OutputBuffer->index += Size; + OutputBuffer->count += 1; + + next = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) item + Size); + next->type = gceBUFITEM_NONE; + + return item; +} + +#if gcdALIGNBYSIZE +static void +_FreeExtraSpace( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctPOINTER Item, + IN gctINT ItemSize, + IN gctINT FreeSize + ) +{ + gcsBUFITEM_HEAD_PTR next; + + OutputBuffer->index -= FreeSize; + + next = (gcsBUFITEM_HEAD_PTR) ((gctUINT8_PTR) Item + ItemSize); + next->type = gceBUFITEM_NONE; +} +#endif + +#if gcdHAVEPREFIX +static void +_AppendPrefix( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctPOINTER Data + ) +{ + gctUINT8_PTR prefixData; + gcsBUFITEM_PREFIX_PTR item; + gctINT allocSize; + +#if gcdALIGNBYSIZE + gctUINT alignment; + gctINT size, freeSize; +#endif + + gcmDBGASSERT(Data != gcvNULL, "%p", Data); + + /* Determine the maximum item size. */ + allocSize + = gcmSIZEOF(gcsBUFITEM_PREFIX) + + gcdPREFIX_SIZE + + gcdPREFIX_ALIGNMENT; + + /* Allocate prefix item. */ + item = (gcsBUFITEM_PREFIX_PTR) _AllocateItem(OutputBuffer, allocSize); + + /* Compute the initial prefix data pointer. */ + prefixData = (gctUINT8_PTR) (item + 1); + + /* Align the data pointer as necessary. */ +#if gcdALIGNBYSIZE + alignment = gcmPTRALIGNMENT(prefixData, gcdPREFIX_ALIGNMENT); + prefixData += alignment; +#endif + + /* Set item data. */ + item->type = gcvBUFITEM_PREFIX; + item->prefixData = prefixData; + + /* Copy argument value. */ + memcpy(prefixData, Data, gcdPREFIX_SIZE); + +#if gcdALIGNBYSIZE + /* Compute the actual node size. */ + size = gcmSIZEOF(gcsBUFITEM_PREFIX) + gcdPREFIX_SIZE + alignment; + + /* Free extra memory if any. */ + freeSize = allocSize - size; + if (freeSize != 0) + { + _FreeExtraSpace(OutputBuffer, item, size, freeSize); + } +#endif +} +#endif + +static void +_AppendString( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctINT Indent, + IN gctCONST_STRING Message, + IN gctUINT ArgumentSize, + IN gctPOINTER Data + ) +{ + gctUINT8_PTR messageData; + gcsBUFITEM_STRING_PTR item; + gctINT allocSize; + +#if gcdALIGNBYSIZE + gctUINT alignment; + gctINT size, freeSize; +#endif + + /* Determine the maximum item size. */ + allocSize + = gcmSIZEOF(gcsBUFITEM_STRING) + + ArgumentSize + + gcdVARARG_ALIGNMENT; + + /* Allocate prefix item. */ + item = (gcsBUFITEM_STRING_PTR) _AllocateItem(OutputBuffer, allocSize); + + /* Compute the initial message data pointer. */ + messageData = (gctUINT8_PTR) (item + 1); + + /* Align the data pointer as necessary. */ +#if gcdALIGNBYSIZE + alignment = gcmPTRALIGNMENT(messageData, gcdVARARG_ALIGNMENT); + messageData += alignment; +#endif + + /* Set item data. */ + item->type = gcvBUFITEM_STRING; + item->indent = Indent; + item->message = Message; + item->messageData = messageData; + item->messageDataSize = ArgumentSize; + + /* Copy argument value. */ + if (ArgumentSize != 0) + { + memcpy(messageData, Data, ArgumentSize); + } + +#if gcdALIGNBYSIZE + /* Compute the actual node size. */ + size = gcmSIZEOF(gcsBUFITEM_STRING) + ArgumentSize + alignment; + + /* Free extra memory if any. */ + freeSize = allocSize - size; + if (freeSize != 0) + { + _FreeExtraSpace(OutputBuffer, item, size, freeSize); + } +#endif +} + +static void +_AppendCopy( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctINT Indent, + IN gctCONST_STRING Message, + IN gctUINT ArgumentSize, + IN gctPOINTER Data + ) +{ + gctUINT8_PTR messageData; + gcsBUFITEM_COPY_PTR item; + gctINT allocSize; + gctINT messageLength; + gctCONST_STRING message; + +#if gcdALIGNBYSIZE + gctUINT alignment; + gctINT size, freeSize; +#endif + + /* Get the length of the string. */ + messageLength = strlen(Message) + 1; + + /* Determine the maximum item size. */ + allocSize + = gcmSIZEOF(gcsBUFITEM_COPY) + + messageLength + + ArgumentSize + + gcdVARARG_ALIGNMENT; + + /* Allocate prefix item. */ + item = (gcsBUFITEM_COPY_PTR) _AllocateItem(OutputBuffer, allocSize); + + /* Determine the message placement. */ + message = (gctCONST_STRING) (item + 1); + + /* Compute the initial message data pointer. */ + messageData = (gctUINT8_PTR) message + messageLength; + + /* Align the data pointer as necessary. */ +#if gcdALIGNBYSIZE + if (ArgumentSize == 0) + { + alignment = 0; + } + else + { + alignment = gcmPTRALIGNMENT(messageData, gcdVARARG_ALIGNMENT); + messageData += alignment; + } +#endif + + /* Set item data. */ + item->type = gcvBUFITEM_COPY; + item->indent = Indent; + item->messageData = messageData; + item->messageDataSize = ArgumentSize; + + /* Copy the message. */ + memcpy((gctPOINTER) message, Message, messageLength); + + /* Copy argument value. */ + if (ArgumentSize != 0) + { + memcpy(messageData, Data, ArgumentSize); + } + +#if gcdALIGNBYSIZE + /* Compute the actual node size. */ + size + = gcmSIZEOF(gcsBUFITEM_COPY) + + messageLength + + ArgumentSize + + alignment; + + /* Free extra memory if any. */ + freeSize = allocSize - size; + if (freeSize != 0) + { + _FreeExtraSpace(OutputBuffer, item, size, freeSize); + } +#endif +} + +static void +_AppendBuffer( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctINT Indent, + IN gctPOINTER PrefixData, + IN gctPOINTER Data, + IN gctUINT Address, + IN gctUINT DataSize, + IN gceDUMP_BUFFER Type, + IN gctUINT32 DmaAddress + ) +{ +#if gcdHAVEPREFIX + gctUINT8_PTR prefixData; + gcsBUFITEM_BUFFER_PTR item; + gctINT allocSize; + gctPOINTER data; + +#if gcdALIGNBYSIZE + gctUINT alignment; + gctINT size, freeSize; +#endif + + gcmDBGASSERT(DataSize != 0, "%d", DataSize); + gcmDBGASSERT(Data != gcvNULL, "%p", Data); + + /* Determine the maximum item size. */ + allocSize + = gcmSIZEOF(gcsBUFITEM_BUFFER) + + gcdPREFIX_SIZE + + gcdPREFIX_ALIGNMENT + + DataSize; + + /* Allocate prefix item. */ + item = (gcsBUFITEM_BUFFER_PTR) _AllocateItem(OutputBuffer, allocSize); + + /* Compute the initial prefix data pointer. */ + prefixData = (gctUINT8_PTR) (item + 1); + +#if gcdALIGNBYSIZE + /* Align the data pointer as necessary. */ + alignment = gcmPTRALIGNMENT(prefixData, gcdPREFIX_ALIGNMENT); + prefixData += alignment; +#endif + + /* Set item data. */ + item->type = gcvBUFITEM_BUFFER; + item->indent = Indent; + item->bufferType = Type; + item->dataSize = DataSize; + item->address = Address; + item->prefixData = prefixData; + +#if gcdDMA_BUFFER_COUNT && (gcdTHREAD_BUFFERS == 1) + item->dmaAddress = DmaAddress; +#endif + + /* Copy prefix data. */ + memcpy(prefixData, PrefixData, gcdPREFIX_SIZE); + + /* Compute the data pointer. */ + data = prefixData + gcdPREFIX_SIZE; + + /* Copy argument value. */ + memcpy(data, Data, DataSize); + +#if gcdALIGNBYSIZE + /* Compute the actual node size. */ + size + = gcmSIZEOF(gcsBUFITEM_BUFFER) + + gcdPREFIX_SIZE + + alignment + + DataSize; + + /* Free extra memory if any. */ + freeSize = allocSize - size; + if (freeSize != 0) + { + _FreeExtraSpace(OutputBuffer, item, size, freeSize); + } +#endif +#else + gcsBUFITEM_BUFFER_PTR item; + gctINT size; + + gcmDBGASSERT(DataSize != 0, "%d", DataSize); + gcmDBGASSERT(Data != gcvNULL, "%p", Data); + + /* Determine the maximum item size. */ + size = gcmSIZEOF(gcsBUFITEM_BUFFER) + DataSize; + + /* Allocate prefix item. */ + item = (gcsBUFITEM_BUFFER_PTR) _AllocateItem(OutputBuffer, size); + + /* Set item data. */ + item->type = gcvBUFITEM_BUFFER; + item->indent = Indent; + item->dataSize = DataSize; + item->address = Address; + + /* Copy argument value. */ + memcpy(item + 1, Data, DataSize); +#endif +} +#endif + +static gcmINLINE void +_InitBuffers( + void + ) +{ + int i; + + if (_outputBufferHead == gcvNULL) + { + for (i = 0; i < gcdTHREAD_BUFFERS; i += 1) + { + if (_outputBufferTail == gcvNULL) + { + _outputBufferHead = &_outputBuffer[i]; + } + else + { + _outputBufferTail->next = &_outputBuffer[i]; + } + +#if gcdTHREAD_BUFFERS > 1 + _outputBuffer[i].threadID = ~0U; +#endif + + _outputBuffer[i].prev = _outputBufferTail; + _outputBuffer[i].next = gcvNULL; + + _outputBufferTail = &_outputBuffer[i]; + } + } +} + +static gcmINLINE gcsBUFFERED_OUTPUT_PTR +_GetOutputBuffer( + void + ) +{ + gcsBUFFERED_OUTPUT_PTR outputBuffer; + +#if gcdTHREAD_BUFFERS > 1 + /* Get the current thread ID. */ + gctUINT32 ThreadID = gcmkGETTHREADID(); + + /* Locate the output buffer for the thread. */ + outputBuffer = _outputBufferHead; + + while (outputBuffer != gcvNULL) + { + if (outputBuffer->threadID == ThreadID) + { + break; + } + + outputBuffer = outputBuffer->next; + } + + /* No matching buffer found? */ + if (outputBuffer == gcvNULL) + { + /* Get the tail for the buffer. */ + outputBuffer = _outputBufferTail; + + /* Move it to the head. */ + _outputBufferTail = _outputBufferTail->prev; + _outputBufferTail->next = gcvNULL; + + outputBuffer->prev = gcvNULL; + outputBuffer->next = _outputBufferHead; + + _outputBufferHead->prev = outputBuffer; + _outputBufferHead = outputBuffer; + + /* Reset the buffer. */ + outputBuffer->threadID = ThreadID; +#if gcdBUFFERED_OUTPUT + outputBuffer->start = 0; + outputBuffer->index = 0; + outputBuffer->count = 0; +#endif +#if gcdSHOW_LINE_NUMBER + outputBuffer->lineNumber = 0; +#endif + } +#else + outputBuffer = _outputBufferHead; +#endif + + return outputBuffer; +} + +static gcmINLINE int _GetArgumentSize( + IN gctCONST_STRING Message + ) +{ + int i, count; + + gcmDBGASSERT(Message != gcvNULL, "%p", Message); + + for (i = 0, count = 0; Message[i]; i += 1) + { + if (Message[i] == '%') + { + count += 1; + } + } + + return count * gcmSIZEOF(gctUINT32); +} + +#if gcdHAVEPREFIX +static void +_InitPrefixData( + IN gcsBUFFERED_OUTPUT_PTR OutputBuffer, + IN gctPOINTER Data + ) +{ + gctUINT8_PTR data = (gctUINT8_PTR) Data; + +#if gcdSHOW_TIME + { + gctUINT64 time; + gckOS_GetProfileTick(&time); + gcmkALIGNPTR(gctUINT8_PTR, data, gcmSIZEOF(gctUINT64)); + * ((gctUINT64_PTR) data) = time; + data += gcmSIZEOF(gctUINT64); + } +#endif + +#if gcdSHOW_LINE_NUMBER + { + gcmkALIGNPTR(gctUINT8_PTR, data, gcmSIZEOF(gctUINT64)); + * ((gctUINT64_PTR) data) = OutputBuffer->lineNumber; + data += gcmSIZEOF(gctUINT64); + } +#endif + +#if gcdSHOW_PROCESS_ID + { + gcmkALIGNPTR(gctUINT8_PTR, data, gcmSIZEOF(gctUINT32)); + * ((gctUINT32_PTR) data) = gcmkGETPROCESSID(); + data += gcmSIZEOF(gctUINT32); + } +#endif + +#if gcdSHOW_THREAD_ID + { + gcmkALIGNPTR(gctUINT8_PTR, data, gcmSIZEOF(gctUINT32)); + * ((gctUINT32_PTR) data) = gcmkGETTHREADID(); + } +#endif +} +#endif + +static void +_Print( + IN gctUINT ArgumentSize, + IN gctBOOL CopyMessage, + IN gctCONST_STRING Message, + IN gctARGUMENTS * Arguments + ) +{ + gcsBUFFERED_OUTPUT_PTR outputBuffer; + gcmkDECLARE_LOCK(lockHandle); + + gcmkLOCKSECTION(lockHandle); + + /* Initialize output buffer list. */ + _InitBuffers(); + + /* Locate the proper output buffer. */ + outputBuffer = _GetOutputBuffer(); + + /* Update the line number. */ +#if gcdSHOW_LINE_NUMBER + outputBuffer->lineNumber += 1; +#endif + + /* Print prefix. */ +#if gcdHAVEPREFIX + { + gctUINT8_PTR alignedPrefixData; + gctUINT8 prefixData[gcdPREFIX_SIZE + gcdPREFIX_ALIGNMENT]; + + /* Compute aligned pointer. */ + alignedPrefixData = prefixData; + gcmkALIGNPTR(gctUINT8_PTR, alignedPrefixData, gcdPREFIX_ALIGNMENT); + + /* Initialize the prefix data. */ + _InitPrefixData(outputBuffer, alignedPrefixData); + + /* Print the prefix. */ + gcdOUTPUTPREFIX(outputBuffer, alignedPrefixData); + } +#endif + + /* Form the indent string. */ + if (strncmp(Message, "--", 2) == 0) + { + outputBuffer->indent -= 2; + } + + /* Print the message. */ + if (CopyMessage) + { + gcdOUTPUTCOPY( + outputBuffer, outputBuffer->indent, + Message, ArgumentSize, (gctPOINTER) Arguments + ); + } + else + { + gcdOUTPUTSTRING( + outputBuffer, outputBuffer->indent, + Message, ArgumentSize, ((gctPOINTER) Arguments) + ); + } + + /* Check increasing indent. */ + if (strncmp(Message, "++", 2) == 0) + { + outputBuffer->indent += 2; + } + + gcmkUNLOCKSECTION(lockHandle); +} + + +/******************************************************************************\ +********************************* Debug Macros ********************************* +\******************************************************************************/ + +#ifdef __QNXNTO__ + +extern volatile unsigned g_nQnxInIsrs; + +#define gcmDEBUGPRINT(ArgumentSize, CopyMessage, Message) \ +{ \ + if (atomic_add_value(&g_nQnxInIsrs, 1) == 0) \ + { \ + gctARGUMENTS __arguments__; \ + gcmkARGUMENTS_START(__arguments__, Message); \ + _Print(ArgumentSize, CopyMessage, Message, &__arguments__); \ + gcmkARGUMENTS_END(__arguments__); \ + } \ + atomic_sub(&g_nQnxInIsrs, 1); \ +} + +#else + +#define gcmDEBUGPRINT(ArgumentSize, CopyMessage, Message) \ +{ \ + gctARGUMENTS __arguments__; \ + gcmkARGUMENTS_START(__arguments__, Message); \ + _Print(ArgumentSize, CopyMessage, Message, &__arguments__); \ + gcmkARGUMENTS_END(__arguments__); \ +} + +#endif + +/******************************************************************************\ +********************************** Debug Code ********************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckOS_Print +** +** Send a message to the debugger. +** +** INPUT: +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_Print( + IN gctCONST_STRING Message, + ... + ) +{ + gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message); +} + +/******************************************************************************* +** +** gckOS_PrintN +** +** Send a message to the debugger. +** +** INPUT: +** +** gctUINT ArgumentSize +** The size of the optional arguments in bytes. +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_PrintN( + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ) +{ + gcmDEBUGPRINT(ArgumentSize, gcvFALSE, Message); +} + +/******************************************************************************* +** +** gckOS_CopyPrint +** +** Send a message to the debugger. If in buffered output mode, the entire +** message will be copied into the buffer instead of using the pointer to +** the string. +** +** INPUT: +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_CopyPrint( + IN gctCONST_STRING Message, + ... + ) +{ + gcmDEBUGPRINT(_GetArgumentSize(Message), gcvTRUE, Message); +} + +/******************************************************************************* +** +** gckOS_DumpBuffer +** +** Print the contents of the specified buffer. +** +** INPUT: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctPOINTER Buffer +** Pointer to the buffer to print. +** +** gctUINT Size +** Size of the buffer. +** +** gceDUMP_BUFFER Type +** Buffer type. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_DumpBuffer( + IN gckOS Os, + IN gctPOINTER Buffer, + IN gctUINT Size, + IN gceDUMP_BUFFER Type, + IN gctBOOL CopyMessage + ) +{ + gctUINT32 address = 0; + gcsBUFFERED_OUTPUT_PTR outputBuffer = gcvNULL; + static gctBOOL userLocked; + gctCHAR *buffer = (gctCHAR*)Buffer; + + gcmkDECLARE_LOCK(lockHandle); + + /* Request lock when not coming from user, + or coming from user and not yet locked + and message is starting with @[. */ + if (Type == gceDUMP_BUFFER_FROM_USER) + { + if ((Size > 2) + && (buffer[0] == '@') + && (buffer[1] == '[')) + { + /* Beginning of a user dump. */ + gcmkLOCKSECTION(lockHandle); + userLocked = gcvTRUE; + } + /* Else, let it pass through. */ + } + else + { + gcmkLOCKSECTION(lockHandle); + userLocked = gcvFALSE; + } + + if (Buffer != gcvNULL) + { + /* Initialize output buffer list. */ + _InitBuffers(); + + /* Locate the proper output buffer. */ + outputBuffer = _GetOutputBuffer(); + + /* Update the line number. */ +#if gcdSHOW_LINE_NUMBER + outputBuffer->lineNumber += 1; +#endif + + /* Get the physical address of the buffer. */ + if (Type != gceDUMP_BUFFER_FROM_USER) + { + gcmkVERIFY_OK(gckOS_GetPhysicalAddress(Os, Buffer, &address)); + } + else + { + address = 0; + } + +#if gcdHAVEPREFIX + { + gctUINT8_PTR alignedPrefixData; + gctUINT8 prefixData[gcdPREFIX_SIZE + gcdPREFIX_ALIGNMENT]; + + /* Compute aligned pointer. */ + alignedPrefixData = prefixData; + gcmkALIGNPTR(gctUINT8_PTR, alignedPrefixData, gcdPREFIX_ALIGNMENT); + + /* Initialize the prefix data. */ + _InitPrefixData(outputBuffer, alignedPrefixData); + + /* Print/schedule the buffer. */ + gcdOUTPUTBUFFER( + outputBuffer, outputBuffer->indent, + alignedPrefixData, Buffer, address, Size, Type, 0 + ); + } +#else + /* Print/schedule the buffer. */ + if (Type == gceDUMP_BUFFER_FROM_USER) + { + gcdOUTPUTSTRING( + outputBuffer, outputBuffer->indent, + Buffer, 0, gcvNULL + ); + } + else + { + gcdOUTPUTBUFFER( + outputBuffer, outputBuffer->indent, + gcvNULL, Buffer, address, Size, Type, 0 + ); + } +#endif + } + + /* Unlock when not coming from user, + or coming from user and not yet locked. */ + if (userLocked) + { + if ((Size > 4) + && (buffer[0] == ']') + && (buffer[1] == ' ') + && (buffer[2] == '-') + && (buffer[3] == '-')) + { + /* End of a user dump. */ + gcmkUNLOCKSECTION(lockHandle); + userLocked = gcvFALSE; + } + /* Else, let it pass through, don't unlock. */ + } + else + { + gcmkUNLOCKSECTION(lockHandle); + } +} + +/******************************************************************************* +** +** gckOS_DebugTrace +** +** Send a leveled message to the debugger. +** +** INPUT: +** +** gctUINT32 Level +** Debug level of message. +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_DebugTrace( + IN gctUINT32 Level, + IN gctCONST_STRING Message, + ... + ) +{ + if (Level > _debugLevel) + { + return; + } + + gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message); +} + +/******************************************************************************* +** +** gckOS_DebugTraceN +** +** Send a leveled message to the debugger. +** +** INPUT: +** +** gctUINT32 Level +** Debug level of message. +** +** gctUINT ArgumentSize +** The size of the optional arguments in bytes. +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_DebugTraceN( + IN gctUINT32 Level, + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ) +{ + if (Level > _debugLevel) + { + return; + } + + gcmDEBUGPRINT(ArgumentSize, gcvFALSE, Message); +} + +/******************************************************************************* +** +** gckOS_DebugTraceZone +** +** Send a leveled and zoned message to the debugger. +** +** INPUT: +** +** gctUINT32 Level +** Debug level for message. +** +** gctUINT32 Zone +** Debug zone for message. +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_DebugTraceZone( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctCONST_STRING Message, + ... + ) +{ + if ((Level > _debugLevel) || !(Zone & _debugZones)) + { + return; + } + + gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message); +} + +/******************************************************************************* +** +** gckOS_DebugTraceZoneN +** +** Send a leveled and zoned message to the debugger. +** +** INPUT: +** +** gctUINT32 Level +** Debug level for message. +** +** gctUINT32 Zone +** Debug zone for message. +** +** gctUINT ArgumentSize +** The size of the optional arguments in bytes. +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_DebugTraceZoneN( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ) +{ + if ((Level > _debugLevel) || !(Zone & _debugZones)) + { + return; + } + + gcmDEBUGPRINT(ArgumentSize, gcvFALSE, Message); +} + +/******************************************************************************* +** +** gckOS_DebugBreak +** +** Break into the debugger. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** Nothing. +*/ +void +gckOS_DebugBreak( + void + ) +{ + gckOS_DebugTrace(gcvLEVEL_ERROR, "%s(%d)", __FUNCTION__, __LINE__); +} + +/******************************************************************************* +** +** gckOS_DebugFatal +** +** Send a message to the debugger and break into the debugger. +** +** INPUT: +** +** gctCONST_STRING Message +** Pointer to message. +** +** ... +** Optional arguments. +** +** OUTPUT: +** +** Nothing. +*/ +void +gckOS_DebugFatal( + IN gctCONST_STRING Message, + ... + ) +{ + gcmkPRINT_VERSION(); + gcmDEBUGPRINT(_GetArgumentSize(Message), gcvFALSE, Message); + + /* Break into the debugger. */ + gckOS_DebugBreak(); +} + +/******************************************************************************* +** +** gckOS_SetDebugLevel +** +** Set the debug level. +** +** INPUT: +** +** gctUINT32 Level +** New debug level. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_SetDebugLevel( + IN gctUINT32 Level + ) +{ + _debugLevel = Level; +} + +/******************************************************************************* +** +** gckOS_SetDebugZone +** +** Set the debug zone. +** +** INPUT: +** +** gctUINT32 Zone +** New debug zone. +** +** OUTPUT: +** +** Nothing. +*/ +void +gckOS_SetDebugZone( + IN gctUINT32 Zone + ) +{ + _debugZones = Zone; +} + +/******************************************************************************* +** +** gckOS_SetDebugLevelZone +** +** Set the debug level and zone. +** +** INPUT: +** +** gctUINT32 Level +** New debug level. +** +** gctUINT32 Zone +** New debug zone. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_SetDebugLevelZone( + IN gctUINT32 Level, + IN gctUINT32 Zone + ) +{ + _debugLevel = Level; + _debugZones = Zone; +} + +/******************************************************************************* +** +** gckOS_SetDebugZones +** +** Enable or disable debug zones. +** +** INPUT: +** +** gctUINT32 Zones +** Debug zones to enable or disable. +** +** gctBOOL Enable +** Set to gcvTRUE to enable the zones (or the Zones with the current +** zones) or gcvFALSE to disable the specified Zones. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_SetDebugZones( + IN gctUINT32 Zones, + IN gctBOOL Enable + ) +{ + if (Enable) + { + /* Enable the zones. */ + _debugZones |= Zones; + } + else + { + /* Disable the zones. */ + _debugZones &= ~Zones; + } +} + +/******************************************************************************* +** +** gckOS_Verify +** +** Called to verify the result of a function call. +** +** INPUT: +** +** gceSTATUS Status +** Function call result. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_Verify( + IN gceSTATUS status + ) +{ + _lastError = status; +} + +/******************************************************************************* +** +** gckOS_DebugFlush +** +** Force messages to be flushed out. +** +** INPUT: +** +** gctCONST_STRING CallerName +** Name of the caller function. +** +** gctUINT LineNumber +** Line number of the caller. +** +** gctUINT32 DmaAddress +** The current DMA address or ~0U to ignore. +** +** OUTPUT: +** +** Nothing. +*/ + +void +gckOS_DebugFlush( + gctCONST_STRING CallerName, + gctUINT LineNumber, + gctUINT32 DmaAddress + ) +{ +#if gcdBUFFERED_OUTPUT + _DirectPrint("\nFlush requested by %s(%d).\n\n", CallerName, LineNumber); + _Flush(DmaAddress); +#endif +} +gctCONST_STRING +gckOS_DebugStatus2Name( + gceSTATUS status + ) +{ + switch (status) + { + case gcvSTATUS_OK: + return "gcvSTATUS_OK"; + case gcvSTATUS_TRUE: + return "gcvSTATUS_TRUE"; + case gcvSTATUS_NO_MORE_DATA: + return "gcvSTATUS_NO_MORE_DATA"; + case gcvSTATUS_CACHED: + return "gcvSTATUS_CACHED"; + case gcvSTATUS_MIPMAP_TOO_LARGE: + return "gcvSTATUS_MIPMAP_TOO_LARGE"; + case gcvSTATUS_NAME_NOT_FOUND: + return "gcvSTATUS_NAME_NOT_FOUND"; + case gcvSTATUS_NOT_OUR_INTERRUPT: + return "gcvSTATUS_NOT_OUR_INTERRUPT"; + case gcvSTATUS_MISMATCH: + return "gcvSTATUS_MISMATCH"; + case gcvSTATUS_MIPMAP_TOO_SMALL: + return "gcvSTATUS_MIPMAP_TOO_SMALL"; + case gcvSTATUS_LARGER: + return "gcvSTATUS_LARGER"; + case gcvSTATUS_SMALLER: + return "gcvSTATUS_SMALLER"; + case gcvSTATUS_CHIP_NOT_READY: + return "gcvSTATUS_CHIP_NOT_READY"; + case gcvSTATUS_NEED_CONVERSION: + return "gcvSTATUS_NEED_CONVERSION"; + case gcvSTATUS_SKIP: + return "gcvSTATUS_SKIP"; + case gcvSTATUS_DATA_TOO_LARGE: + return "gcvSTATUS_DATA_TOO_LARGE"; + case gcvSTATUS_INVALID_CONFIG: + return "gcvSTATUS_INVALID_CONFIG"; + case gcvSTATUS_CHANGED: + return "gcvSTATUS_CHANGED"; + case gcvSTATUS_NOT_SUPPORT_DITHER: + return "gcvSTATUS_NOT_SUPPORT_DITHER"; + + case gcvSTATUS_INVALID_ARGUMENT: + return "gcvSTATUS_INVALID_ARGUMENT"; + case gcvSTATUS_INVALID_OBJECT: + return "gcvSTATUS_INVALID_OBJECT"; + case gcvSTATUS_OUT_OF_MEMORY: + return "gcvSTATUS_OUT_OF_MEMORY"; + case gcvSTATUS_MEMORY_LOCKED: + return "gcvSTATUS_MEMORY_LOCKED"; + case gcvSTATUS_MEMORY_UNLOCKED: + return "gcvSTATUS_MEMORY_UNLOCKED"; + case gcvSTATUS_HEAP_CORRUPTED: + return "gcvSTATUS_HEAP_CORRUPTED"; + case gcvSTATUS_GENERIC_IO: + return "gcvSTATUS_GENERIC_IO"; + case gcvSTATUS_INVALID_ADDRESS: + return "gcvSTATUS_INVALID_ADDRESS"; + case gcvSTATUS_CONTEXT_LOSSED: + return "gcvSTATUS_CONTEXT_LOSSED"; + case gcvSTATUS_TOO_COMPLEX: + return "gcvSTATUS_TOO_COMPLEX"; + case gcvSTATUS_BUFFER_TOO_SMALL: + return "gcvSTATUS_BUFFER_TOO_SMALL"; + case gcvSTATUS_INTERFACE_ERROR: + return "gcvSTATUS_INTERFACE_ERROR"; + case gcvSTATUS_NOT_SUPPORTED: + return "gcvSTATUS_NOT_SUPPORTED"; + case gcvSTATUS_MORE_DATA: + return "gcvSTATUS_MORE_DATA"; + case gcvSTATUS_TIMEOUT: + return "gcvSTATUS_TIMEOUT"; + case gcvSTATUS_OUT_OF_RESOURCES: + return "gcvSTATUS_OUT_OF_RESOURCES"; + case gcvSTATUS_INVALID_DATA: + return "gcvSTATUS_INVALID_DATA"; + case gcvSTATUS_INVALID_MIPMAP: + return "gcvSTATUS_INVALID_MIPMAP"; + case gcvSTATUS_NOT_FOUND: + return "gcvSTATUS_NOT_FOUND"; + case gcvSTATUS_NOT_ALIGNED: + return "gcvSTATUS_NOT_ALIGNED"; + case gcvSTATUS_INVALID_REQUEST: + return "gcvSTATUS_INVALID_REQUEST"; + case gcvSTATUS_GPU_NOT_RESPONDING: + return "gcvSTATUS_GPU_NOT_RESPONDING"; + case gcvSTATUS_TIMER_OVERFLOW: + return "gcvSTATUS_TIMER_OVERFLOW"; + case gcvSTATUS_VERSION_MISMATCH: + return "gcvSTATUS_VERSION_MISMATCH"; + case gcvSTATUS_LOCKED: + return "gcvSTATUS_LOCKED"; + case gcvSTATUS_INTERRUPTED: + return "gcvSTATUS_INTERRUPTED"; + case gcvSTATUS_DEVICE: + return "gcvSTATUS_DEVICE"; + case gcvSTATUS_NOT_MULTI_PIPE_ALIGNED: + return "gcvSTATUS_NOT_MULTI_PIPE_ALIGNED"; + + /* Linker errors. */ + case gcvSTATUS_GLOBAL_TYPE_MISMATCH: + return "gcvSTATUS_GLOBAL_TYPE_MISMATCH"; + case gcvSTATUS_TOO_MANY_ATTRIBUTES: + return "gcvSTATUS_TOO_MANY_ATTRIBUTES"; + case gcvSTATUS_TOO_MANY_UNIFORMS: + return "gcvSTATUS_TOO_MANY_UNIFORMS"; + case gcvSTATUS_TOO_MANY_SAMPLER: + return "gcvSTATUS_TOO_MANY_SAMPLER"; + case gcvSTATUS_TOO_MANY_VARYINGS: + return "gcvSTATUS_TOO_MANY_VARYINGS"; + case gcvSTATUS_UNDECLARED_VARYING: + return "gcvSTATUS_UNDECLARED_VARYING"; + case gcvSTATUS_VARYING_TYPE_MISMATCH: + return "gcvSTATUS_VARYING_TYPE_MISMATCH"; + case gcvSTATUS_MISSING_MAIN: + return "gcvSTATUS_MISSING_MAIN"; + case gcvSTATUS_NAME_MISMATCH: + return "gcvSTATUS_NAME_MISMATCH"; + case gcvSTATUS_INVALID_INDEX: + return "gcvSTATUS_INVALID_INDEX"; + case gcvSTATUS_UNIFORM_MISMATCH: + return "gcvSTATUS_UNIFORM_MISMATCH"; + case gcvSTATUS_UNSAT_LIB_SYMBOL: + return "gcvSTATUS_UNSAT_LIB_SYMBOL"; + case gcvSTATUS_TOO_MANY_SHADERS: + return "gcvSTATUS_TOO_MANY_SHADERS"; + case gcvSTATUS_LINK_INVALID_SHADERS: + return "gcvSTATUS_LINK_INVALID_SHADERS"; + case gcvSTATUS_CS_NO_WORKGROUP_SIZE: + return "gcvSTATUS_CS_NO_WORKGROUP_SIZE"; + case gcvSTATUS_LINK_LIB_ERROR: + return "gcvSTATUS_LINK_LIB_ERROR"; + case gcvSTATUS_SHADER_VERSION_MISMATCH: + return "gcvSTATUS_SHADER_VERSION_MISMATCH"; + case gcvSTATUS_TOO_MANY_INSTRUCTION: + return "gcvSTATUS_TOO_MANY_INSTRUCTION"; + case gcvSTATUS_SSBO_MISMATCH: + return "gcvSTATUS_SSBO_MISMATCH"; + case gcvSTATUS_TOO_MANY_OUTPUT: + return "gcvSTATUS_TOO_MANY_OUTPUT"; + case gcvSTATUS_TOO_MANY_INPUT: + return "gcvSTATUS_TOO_MANY_INPUT"; + case gcvSTATUS_NOT_SUPPORT_CL: + return "gcvSTATUS_NOT_SUPPORT_CL"; + case gcvSTATUS_NOT_SUPPORT_INTEGER: + return "gcvSTATUS_NOT_SUPPORT_INTEGER"; + + /* Compiler errors. */ + case gcvSTATUS_COMPILER_FE_PREPROCESSOR_ERROR: + return "gcvSTATUS_COMPILER_FE_PREPROCESSOR_ERROR"; + case gcvSTATUS_COMPILER_FE_PARSER_ERROR: + return "gcvSTATUS_COMPILER_FE_PARSER_ERROR"; + + default: + return "nil"; + } +} + +/******************************************************************************* +***** Binary Trace ************************************************************* +*******************************************************************************/ + +/******************************************************************************* +** _VerifyMessage +** +** Verify a binary trace message, decode it to human readable string and print +** it. +** +** ARGUMENTS: +** +** gctCONST_STRING Buffer +** Pointer to buffer to store. +** +** gctSIZE_T Bytes +** Buffer length. +*/ +void +_VerifyMessage( + IN gctCONST_STRING Buffer, + IN gctSIZE_T Bytes + ) +{ + char arguments[150] = {0}; + char format[100] = {0}; + + gctSTRING function; + gctPOINTER args; + gctUINT32 numArguments; + int i = 0; + gctUINT32 functionBytes; + + gcsBINARY_TRACE_MESSAGE_PTR message = (gcsBINARY_TRACE_MESSAGE_PTR)Buffer; + + /* Check signature. */ + if (message->signature != 0x7FFFFFFF) + { + gcmkPRINT("Signature error"); + return; + } + + /* Get function name. */ + function = (gctSTRING)&message->payload; + functionBytes = (gctUINT32)strlen(function) + 1; + + /* Get arguments number. */ + numArguments = message->numArguments; + + /* Get arguments . */ + args = function + functionBytes; + + /* Prepare format string. */ + while (numArguments--) + { + format[i++] = '%'; + format[i++] = 'x'; + format[i++] = ' '; + } + + format[i] = '\0'; + + if (numArguments) + { + gcmkVSPRINTF(arguments, 150, format, (gctARGUMENTS *) &args); + } + + gcmkPRINT("[%d](%d): %s(%d) %s", + message->pid, + message->tid, + function, + message->line, + arguments); +} + + +/******************************************************************************* +** gckOS_WriteToRingBuffer +** +** Store a buffer to ring buffer. +** +** ARGUMENTS: +** +** gctCONST_STRING Buffer +** Pointer to buffer to store. +** +** gctSIZE_T Bytes +** Buffer length. +*/ +void +gckOS_WriteToRingBuffer( + IN gctCONST_STRING Buffer, + IN gctSIZE_T Bytes + ) +{ + +} + +/******************************************************************************* +** gckOS_BinaryTrace +** +** Output a binary trace message. +** +** ARGUMENTS: +** +** gctCONST_STRING Function +** Pointer to function name. +** +** gctINT Line +** Line number. +** +** gctCONST_STRING Text OPTIONAL +** Optional pointer to a descriptive text. +** +** ... +** Optional arguments to the descriptive text. +*/ +void +gckOS_BinaryTrace( + IN gctCONST_STRING Function, + IN gctINT Line, + IN gctCONST_STRING Text OPTIONAL, + ... + ) +{ + static gctUINT32 messageSignature = 0x7FFFFFFF; + char buffer[gcdBINARY_TRACE_MESSAGE_SIZE]; + gctUINT32 numArguments = 0; + gctUINT32 functionBytes; + gctUINT32 i = 0; + gctSTRING payload; + gcsBINARY_TRACE_MESSAGE_PTR message = (gcsBINARY_TRACE_MESSAGE_PTR)buffer; + + /* Calculate arguments number. */ + if (Text) + { + while (Text[i] != '\0') + { + if (Text[i] == '%') + { + numArguments++; + } + i++; + } + } + + message->signature = messageSignature; + message->pid = gcmkGETPROCESSID(); + message->tid = gcmkGETTHREADID(); + message->line = Line; + message->numArguments = numArguments; + + payload = (gctSTRING)&message->payload; + + /* Function name. */ + functionBytes = (gctUINT32)gcmkSTRLEN(Function) + 1; + gcmkMEMCPY(payload, Function, functionBytes); + + /* Advance to next payload. */ + payload += functionBytes; + + /* Arguments value. */ + if (numArguments) + { + gctARGUMENTS p; + gcmkARGUMENTS_START(p, Text); + + for (i = 0; i < numArguments; ++i) + { + gctPOINTER value = gcmkARGUMENTS_ARG(p, gctPOINTER); + gcmkMEMCPY(payload, &value, gcmSIZEOF(gctPOINTER)); + payload += gcmSIZEOF(gctPOINTER); + } + + gcmkARGUMENTS_END(p); + } + + gcmkASSERT(payload - buffer <= gcdBINARY_TRACE_MESSAGE_SIZE); + + + /* Send buffer to ring buffer. */ + gckOS_WriteToRingBuffer(buffer, (gctUINT32)(payload - buffer)); +} + diff --git a/drivers/gpu/galcore/gc_hal_kernel_debug.h b/drivers/gpu/galcore/gc_hal_kernel_debug.h new file mode 100644 index 00000000000000..26bc55c0254731 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_debug.h @@ -0,0 +1,103 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_debug_h_ +#define __gc_hal_kernel_debug_h_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +****************************** OS-dependent Macros ***************************** +\******************************************************************************/ + +typedef va_list gctARGUMENTS; + +#define gcmkARGUMENTS_START(Arguments, Pointer) \ + va_start(Arguments, Pointer) + +#define gcmkARGUMENTS_END(Arguments) \ + va_end(Arguments) + +#define gcmkARGUMENTS_ARG(Arguments, Type) \ + va_arg(Arguments, Type) + +#define gcmkDECLARE_LOCK(__spinLock__) \ + static DEFINE_SPINLOCK(__spinLock__); \ + unsigned long __spinLock__##flags = 0; + +#define gcmkLOCKSECTION(__spinLock__) \ + spin_lock_irqsave(&__spinLock__, __spinLock__##flags) + +#define gcmkUNLOCKSECTION(__spinLock__) \ + spin_unlock_irqrestore(&__spinLock__, __spinLock__##flags) + +# define gcmkGETPROCESSID() \ + task_tgid_vnr(current) + +# define gcmkGETTHREADID() \ + task_pid_vnr(current) + +#define gcmkOUTPUT_STRING(String) \ + if(gckDEBUGFS_IsEnabled()) {\ + while(-ERESTARTSYS == gckDEBUGFS_Print(String));\ + }else{\ + printk(String); \ + }\ + touch_softlockup_watchdog() + + +#define gcmkSPRINTF(Destination, Size, Message, Value) \ + snprintf(Destination, Size, Message, Value) + +#define gcmkSPRINTF2(Destination, Size, Message, Value1, Value2) \ + snprintf(Destination, Size, Message, Value1, Value2) + +#define gcmkSPRINTF3(Destination, Size, Message, Value1, Value2, Value3) \ + snprintf(Destination, Size, Message, Value1, Value2, Value3) + +#define gcmkVSPRINTF(Destination, Size, Message, Arguments) \ + vsnprintf(Destination, Size, Message, *((va_list*)Arguments)) + +#define gcmkSTRCAT(Destination, Size, String) \ + strncat(Destination, String, Size) + +#define gcmkMEMCPY(Destination, Source, Size) \ + memcpy(Destination, Source, Size) + +#define gcmkSTRLEN(String) \ + strlen(String) + +/* If not zero, forces data alignment in the variable argument list + by its individual size. */ +#define gcdALIGNBYSIZE 1 + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_kernel_debug_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_debugfs.c b/drivers/gpu/galcore/gc_hal_kernel_debugfs.c new file mode 100644 index 00000000000000..de7b96ccb0910f --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_debugfs.c @@ -0,0 +1,1166 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifdef MODULE +#include +#endif +#include +#include +#include +#ifdef MODVERSIONS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gc_hal_kernel_linux.h" +#include "gc_hal_kernel.h" + +/* + Prequsite: + + 1) Debugfs feature must be enabled in the kernel. + 1.a) You can enable this, in the compilation of the uImage, all you have to do is, In the "make menuconfig" part, + you have to enable the debugfs in the kernel hacking part of the menu. + + HOW TO USE: + 1) insert the driver with the following option logFileSize, Ex: insmod galcore.ko ...... logFileSize=10240 + This gives a circular buffer of 10 MB + + 2)Usually after inserting the driver, the debug file system is mounted under /sys/kernel/debug/ + + 2.a)If the debugfs is not mounted, you must do "mount -t debugfs none /sys/kernel/debug" + + 3) To read what is being printed in the debugfs file system: + Ex : cat /sys/kernel/debug/gc/galcore_trace + + 4)To write into the debug file system from user side : + Ex: echo "hello" > cat /sys/kernel/debug/gc/galcore_trace + + 5)To write into debugfs from kernel side, Use the function called gckDEBUGFS_Print + + How to Get Video Memory Usage: + 1) Select a process whose video memory usage can be dump, no need to reset it until is needed to be change. + echo > /sys/kernel/debug/gc/vidmem + + 2) Get video memory usage. + cat /sys/kernel/debug/gc/vidmem + + USECASE Kernel Dump: + + 1) Go to /hal/inc/gc_hal_options.h, and enable the following flags: + - # define gcdDUMP 1 + - # define gcdDUMP_IN_KERNEL 1 + - # define gcdDUMP_COMMAND 1 + + 2) Go to /hal/kernel/gc_hal_kernel_command.c and disable the following flag + -#define gcdSIMPLE_COMMAND_DUMP 0 + + 3) Compile the driver + 4) insmod it with the logFileSize option + 5) Run an application + 6) You can get the dump by cat /sys/kernel/debug/gpu/galcore_trace + + */ + +/**/ +typedef va_list gctDBGARGS ; +#define gcmkARGS_START(argument, pointer) va_start(argument, pointer) +#define gcmkARGS_END(argument) va_end(argument) + +#define gcmkDEBUGFS_PRINT(ArgumentSize, Message) \ + { \ + gctDBGARGS __arguments__; \ + gcmkARGS_START(__arguments__, Message); \ + _debugfs_res = _DebugFSPrint(ArgumentSize, Message, &__arguments__);\ + gcmkARGS_END(__arguments__); \ + } + +/* Debug File System Node Struct. */ +struct _gcsDEBUGFS_Node +{ + /*wait queues for read and write operations*/ +#if defined(DECLARE_WAIT_QUEUE_HEAD) + wait_queue_head_t read_q , write_q ; +#else + struct wait_queue *read_q , *write_q ; +#endif + struct dentry *parent ; /*parent directory*/ + struct dentry *filen ; /*filename*/ + struct dentry *vidmem; + struct semaphore sem ; /* mutual exclusion semaphore */ + char *data ; /* The circular buffer data */ + int size ; /* Size of the buffer pointed to by 'data' */ + int refcount ; /* Files that have this buffer open */ + int read_point ; /* Offset in circ. buffer of oldest data */ + int write_point ; /* Offset in circ. buffer of newest data */ + int offset ; /* Byte number of read_point in the stream */ + struct _gcsDEBUGFS_Node *next ; +}; + +/* amount of data in the queue */ +#define gcmkNODE_QLEN(node) ( (node)->write_point >= (node)->read_point ? \ + (node)->write_point - (node)->read_point : \ + (node)->size - (node)->read_point + (node)->write_point) + +/* byte number of the last byte in the queue */ +#define gcmkNODE_FIRST_EMPTY_BYTE(node) ((node)->offset + gcmkNODE_QLEN(node)) + +/*Synchronization primitives*/ +#define gcmkNODE_READQ(node) (&((node)->read_q)) +#define gcmkNODE_WRITEQ(node) (&((node)->write_q)) +#define gcmkNODE_SEM(node) (&((node)->sem)) + +/*Utilities*/ +#define gcmkMIN(x, y) ((x) < (y) ? (x) : y) + +/*Debug File System Struct*/ +typedef struct _gcsDEBUGFS_ +{ + gcsDEBUGFS_Node* linkedlist ; + gcsDEBUGFS_Node* currentNode ; + int isInited ; +} gcsDEBUGFS_ ; + +/*debug file system*/ +static gcsDEBUGFS_ gc_dbgfs ; + +static int gc_debugfs_open(struct inode *inode, struct file *file) +{ + gcsINFO_NODE *node = inode->i_private; + + return single_open(file, node->info->show, node); +} + +static const struct file_operations gc_debugfs_operations = { + .owner = THIS_MODULE, + .open = gc_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +gceSTATUS +gckDEBUGFS_DIR_Init( + IN gckDEBUGFS_DIR Dir, + IN struct dentry *root, + IN gctCONST_STRING Name + ) +{ + Dir->root = debugfs_create_dir(Name, root); + + if (!Dir->root) + { + return gcvSTATUS_NOT_SUPPORTED; + } + + INIT_LIST_HEAD(&Dir->nodeList); + + return gcvSTATUS_OK; +} + +gceSTATUS +gckDEBUGFS_DIR_CreateFiles( + IN gckDEBUGFS_DIR Dir, + IN gcsINFO * List, + IN int count, + IN gctPOINTER Data + ) +{ + int i; + gcsINFO_NODE * node; + gceSTATUS status; + + for (i = 0; i < count; i++) + { + /* Create a node. */ + node = (gcsINFO_NODE *)kzalloc(sizeof(gcsINFO_NODE), GFP_KERNEL); + + node->info = &List[i]; + node->device = Data; + + /* Bind to a file. TODO: clean up when fail. */ + node->entry = debugfs_create_file( + List[i].name, S_IRUGO|S_IWUSR, Dir->root, node, &gc_debugfs_operations); + + if (!node->entry) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + list_add(&(node->head), &(Dir->nodeList)); + } + + return gcvSTATUS_OK; + +OnError: + gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles(Dir, List, count)); + return status; +} + +gceSTATUS +gckDEBUGFS_DIR_RemoveFiles( + IN gckDEBUGFS_DIR Dir, + IN gcsINFO * List, + IN int count + ) +{ + int i; + gcsINFO_NODE * node; + gcsINFO_NODE * temp; + + for (i = 0; i < count; i++) + { + list_for_each_entry_safe(node, temp, &Dir->nodeList, head) + { + if (node->info == &List[i]) + { + debugfs_remove(node->entry); + list_del(&node->head); + kfree(node); + } + } + } + + return gcvSTATUS_OK; +} + +void +gckDEBUGFS_DIR_Deinit( + IN gckDEBUGFS_DIR Dir + ) +{ + if (Dir->root != NULL) + { + debugfs_remove(Dir->root); + Dir->root = NULL; + } +} + +/******************************************************************************* + ** + ** READ & WRITE FUNCTIONS (START) + ** + *******************************************************************************/ + +/******************************************************************************* + ** + ** _ReadFromNode + ** + ** 1) reading bytes out of a circular buffer with wraparound. + ** 2)returns caddr_t, pointer to data read, which the caller must free. + ** 3) length is (a pointer to) the number of bytes to be read, which will be set by this function to + ** be the number of bytes actually returned + ** + *******************************************************************************/ +static caddr_t +_ReadFromNode ( + gcsDEBUGFS_Node* Node , + size_t *Length , + loff_t *Offset + ) +{ + caddr_t retval ; + int bytes_copied = 0 , n , start_point , remaining ; + + /* is the user trying to read data that has already scrolled off? */ + if ( *Offset < Node->offset ) + { + *Offset = Node->offset ; + } + + /* is the user trying to read past EOF? */ + if ( *Offset >= gcmkNODE_FIRST_EMPTY_BYTE ( Node ) ) + { + return NULL ; + } + + /* find the smaller of the total bytes we have available and what + * the user is asking for */ + + *Length = gcmkMIN ( *Length , gcmkNODE_FIRST_EMPTY_BYTE ( Node ) - *Offset ) ; + + remaining = * Length ; + + /* figure out where to start based on user's Offset */ + start_point = Node->read_point + ( *Offset - Node->offset ) ; + + start_point = start_point % Node->size ; + + /* allocate memory to return */ + if ( ( retval = kmalloc ( sizeof (char ) * remaining , GFP_KERNEL ) ) == NULL ) + return NULL ; + + /* copy the (possibly noncontiguous) data to our buffer */ + while ( remaining ) + { + n = gcmkMIN ( remaining , Node->size - start_point ) ; + memcpy ( retval + bytes_copied , Node->data + start_point , n ) ; + bytes_copied += n ; + remaining -= n ; + start_point = ( start_point + n ) % Node->size ; + } + + /* advance user's file pointer */ + *Offset += * Length ; + + return retval ; +} + +/******************************************************************************* + ** + ** _WriteToNode + ** + ** 1) writes to a circular buffer with wraparound. + ** 2)in case of an overflow, it overwrites the oldest unread data. + ** + *********************************************************************************/ +static void +_WriteToNode ( + gcsDEBUGFS_Node* Node , + caddr_t Buf , + int Length + ) +{ + int bytes_copied = 0 ; + int overflow = 0 ; + int n ; + + if ( Length + gcmkNODE_QLEN ( Node ) >= ( Node->size - 1 ) ) + { + overflow = 1 ; + + /* in case of overflow, figure out where the new buffer will + * begin. we start by figuring out where the current buffer ENDS: + * node->parent->offset + gcmkNODE_QLEN. we then advance the end-offset + * by the Length of the current write, and work backwards to + * figure out what the oldest unoverwritten data will be (i.e., + * size of the buffer). */ + Node->offset = Node->offset + gcmkNODE_QLEN ( Node ) + Length + - Node->size + 1 ; + } + + while ( Length ) + { + /* how many contiguous bytes are available from the write point to + * the end of the circular buffer? */ + n = gcmkMIN ( Length , Node->size - Node->write_point ) ; + memcpy ( Node->data + Node->write_point , Buf + bytes_copied , n ) ; + bytes_copied += n ; + Length -= n ; + Node->write_point = ( Node->write_point + n ) % Node->size ; + } + + /* if there is an overflow, reset the read point to read whatever is + * the oldest data that we have, that has not yet been + * overwritten. */ + if ( overflow ) + { + Node->read_point = ( Node->write_point + 1 ) % Node->size ; + } +} + +/******************************************************************************* + ** + ** PRINTING UTILITY (START) + ** + *******************************************************************************/ + +/******************************************************************************* + ** + ** _GetArgumentSize + ** + ** + *******************************************************************************/ +static gctINT +_GetArgumentSize ( + IN gctCONST_STRING Message + ) +{ + gctINT i , count ; + + for ( i = 0 , count = 0 ; Message[i] ; i += 1 ) + { + if ( Message[i] == '%' ) + { + count += 1 ; + } + } + return count * sizeof (unsigned int ) ; +} + +/******************************************************************************* + ** + ** _AppendString + ** + ** + *******************************************************************************/ +static ssize_t +_AppendString ( + IN gcsDEBUGFS_Node* Node , + IN gctCONST_STRING String , + IN int Length + ) +{ + caddr_t message = NULL ; + int n ; + + /* if the message is longer than the buffer, just take the beginning + * of it, in hopes that the reader (if any) will have time to read + * before we wrap around and obliterate it */ + n = gcmkMIN ( Length , Node->size - 1 ) ; + + /* make sure we have the memory for it */ + if ( ( message = kmalloc ( n , GFP_KERNEL ) ) == NULL ) + return - ENOMEM ; + + /* copy into our temp buffer */ + memcpy ( message , String , n ) ; + + /* now copy it into the circular buffer and free our temp copy */ + _WriteToNode ( Node , message , n ) ; + kfree ( message ) ; + return n ; +} + +/******************************************************************************* + ** + ** _DebugFSPrint + ** + ** + *******************************************************************************/ +static ssize_t +_DebugFSPrint ( + IN unsigned int ArgumentSize , + IN const char* Message , + IN gctDBGARGS * Arguments + + ) +{ + char buffer[MAX_LINE_SIZE] ; + int len ; + ssize_t res=0; + + if(in_interrupt()) + { + return - ERESTARTSYS ; + } + + if(down_interruptible( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ) + { + return - ERESTARTSYS ; + } + len = vsnprintf ( buffer , sizeof (buffer ) , Message , *( va_list * ) Arguments ) ; + buffer[len] = '\0' ; + + /* Add end-of-line if missing. */ + if ( buffer[len - 1] != '\n' ) + { + buffer[len ++] = '\n' ; + buffer[len] = '\0' ; + } + res = _AppendString ( gc_dbgfs.currentNode , buffer , len ) ; + up ( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ; + wake_up_interruptible ( gcmkNODE_READQ ( gc_dbgfs.currentNode ) ) ; /* blocked in read*/ + return res; +} + +/******************************************************************************* + ** + ** LINUX SYSTEM FUNCTIONS (START) + ** + *******************************************************************************/ + +/******************************************************************************* + ** + ** find the vivlog structure associated with an inode. + ** returns a pointer to the structure if found, NULL if not found + ** + *******************************************************************************/ +static gcsDEBUGFS_Node* +_GetNodeInfo ( + IN struct inode *Inode + ) +{ + gcsDEBUGFS_Node* node ; + + if ( Inode == NULL ) + return NULL ; + + for ( node = gc_dbgfs.linkedlist ; node != NULL ; node = node->next ) + if ( node->filen->d_inode->i_ino == Inode->i_ino ) + return node ; + + return NULL ; +} + +/******************************************************************************* + ** + ** _DebugFSRead + ** + *******************************************************************************/ +static ssize_t +_DebugFSRead ( + struct file *file , + char __user * buffer , + size_t length , + loff_t * offset + ) +{ + int retval ; + caddr_t data_to_return ; + gcsDEBUGFS_Node* node ; + /* get the metadata about this emlog */ + if ( ( node = _GetNodeInfo ( file->f_path.dentry->d_inode ) ) == NULL ) + { + printk ( "debugfs_read: record not found\n" ) ; + return - EIO ; + } + + if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) + { + return - ERESTARTSYS ; + } + + /* wait until there's data available (unless we do nonblocking reads) */ + while ( *offset >= gcmkNODE_FIRST_EMPTY_BYTE ( node ) ) + { + up ( gcmkNODE_SEM ( node ) ) ; + if ( file->f_flags & O_NONBLOCK ) + { + return - EAGAIN ; + } + if ( wait_event_interruptible ( ( *( gcmkNODE_READQ ( node ) ) ) , ( *offset < gcmkNODE_FIRST_EMPTY_BYTE ( node ) ) ) ) + { + return - ERESTARTSYS ; /* signal: tell the fs layer to handle it */ + } + /* otherwise loop, but first reacquire the lock */ + if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) + { + return - ERESTARTSYS ; + } + } + data_to_return = _ReadFromNode ( node , &length , offset ) ; + if ( data_to_return == NULL ) + { + retval = 0 ; + goto unlock ; + } + if ( copy_to_user ( buffer , data_to_return , length ) > 0 ) + { + retval = - EFAULT ; + } + else + { + retval = length ; + } + kfree ( data_to_return ) ; +unlock: + up ( gcmkNODE_SEM ( node ) ) ; + wake_up_interruptible ( gcmkNODE_WRITEQ ( node ) ) ; + return retval ; +} + +/******************************************************************************* + ** + **_DebugFSWrite + ** + *******************************************************************************/ +static ssize_t +_DebugFSWrite ( + struct file *file , + const char __user * buffer , + size_t length , + loff_t * offset + ) +{ + caddr_t message = NULL ; + int n ; + gcsDEBUGFS_Node*node ; + + /* get the metadata about this log */ + if ( ( node = _GetNodeInfo ( file->f_path.dentry->d_inode ) ) == NULL ) + { + return - EIO ; + } + + if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) + { + return - ERESTARTSYS ; + } + + /* if the message is longer than the buffer, just take the beginning + * of it, in hopes that the reader (if any) will have time to read + * before we wrap around and obliterate it */ + n = gcmkMIN ( length , node->size - 1 ) ; + + /* make sure we have the memory for it */ + if ( ( message = kmalloc ( n , GFP_KERNEL ) ) == NULL ) + { + up ( gcmkNODE_SEM ( node ) ) ; + return - ENOMEM ; + } + + + /* copy into our temp buffer */ + if ( copy_from_user ( message , buffer , n ) > 0 ) + { + up ( gcmkNODE_SEM ( node ) ) ; + kfree ( message ) ; + return - EFAULT ; + } + + /* now copy it into the circular buffer and free our temp copy */ + _WriteToNode ( node , message , n ) ; + + kfree ( message ) ; + up ( gcmkNODE_SEM ( node ) ) ; + + /* wake up any readers that might be waiting for the data. we call + * schedule in the vague hope that a reader will run before the + * writer's next write, to avoid losing data. */ + wake_up_interruptible ( gcmkNODE_READQ ( node ) ) ; + + return n ; +} + +int dumpProcess = 0; + +void +_PrintCounter( + struct seq_file *file, + gcsDATABASE_COUNTERS * counter, + gctCONST_STRING Name + ) +{ + seq_printf(file,"Counter: %s\n", Name); + + seq_printf(file,"%-9s%10s","", "All"); + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Current"); + + seq_printf(file,"%10lld", counter->bytes); + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Maximum"); + + seq_printf(file,"%10lld", counter->maxBytes); + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Total"); + + seq_printf(file,"%10lld", counter->totalBytes); + + seq_printf(file, "\n"); +} + +void +_ShowCounters( + struct seq_file *file, + gcsDATABASE_PTR database + ) +{ + gctUINT i = 0; + gcsDATABASE_COUNTERS * counter; + gcsDATABASE_COUNTERS * nonPaged; + + static gctCONST_STRING surfaceTypes[] = { + "UNKNOWN", + "Index", + "Vertex", + "Texture", + "RT", + "Depth", + "Bitmap", + "TS", + "Image", + "Mask", + "Scissor", + "HZDepth", + }; + + /* Get pointer to counters. */ + counter = &database->vidMem; + + nonPaged = &database->nonPaged; + + seq_printf(file,"Counter: vidMem (for each surface type)\n"); + + seq_printf(file,"%-9s%10s","", "All"); + + for (i = 1; i < gcvSURF_NUM_TYPES; i++) + { + counter = &database->vidMemType[i]; + + seq_printf(file, "%10s",surfaceTypes[i]); + } + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Current"); + + seq_printf(file,"%10lld", database->vidMem.bytes); + + for (i = 1; i < gcvSURF_NUM_TYPES; i++) + { + counter = &database->vidMemType[i]; + + seq_printf(file,"%10lld", counter->bytes); + } + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Maximum"); + + seq_printf(file,"%10lld", database->vidMem.maxBytes); + + for (i = 1; i < gcvSURF_NUM_TYPES; i++) + { + counter = &database->vidMemType[i]; + + seq_printf(file,"%10lld", counter->maxBytes); + } + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Total"); + + seq_printf(file,"%10lld", database->vidMem.totalBytes); + + for (i = 1; i < gcvSURF_NUM_TYPES; i++) + { + counter = &database->vidMemType[i]; + + seq_printf(file,"%10lld", counter->totalBytes); + } + + seq_printf(file, "\n"); + + seq_printf(file,"Counter: vidMem (for each pool)\n"); + + seq_printf(file,"%-9s%10s","", "All"); + + for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++) + { + seq_printf(file, "%10d", i); + } + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Current"); + + seq_printf(file,"%10lld", database->vidMem.bytes); + + for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++) + { + counter = &database->vidMemPool[i]; + + seq_printf(file,"%10lld", counter->bytes); + } + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Maximum"); + + seq_printf(file,"%10lld", database->vidMem.maxBytes); + + for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++) + { + counter = &database->vidMemPool[i]; + + seq_printf(file,"%10lld", counter->maxBytes); + } + + seq_printf(file, "\n"); + + seq_printf(file,"%-9s","Total"); + + seq_printf(file,"%10lld", database->vidMem.totalBytes); + + for (i = 1; i < gcvPOOL_NUMBER_OF_POOLS; i++) + { + counter = &database->vidMemPool[i]; + + seq_printf(file,"%10lld", counter->totalBytes); + } + + seq_printf(file, "\n"); + + /* Print nonPaged. */ + _PrintCounter(file, &database->nonPaged, "nonPaged"); + _PrintCounter(file, &database->contiguous, "contiguous"); + _PrintCounter(file, &database->mapUserMemory, "mapUserMemory"); + _PrintCounter(file, &database->mapMemory, "mapMemory"); +} + +gckKERNEL +_GetValidKernel( + gckGALDEVICE Device +); +static int vidmem_show(struct seq_file *file, void *unused) +{ + gceSTATUS status; + gcsDATABASE_PTR database; + gckGALDEVICE device = file->private; + + gckKERNEL kernel = _GetValidKernel(device); + if(kernel == gcvNULL) + { + return 0; + } + + /* Find the database. */ + gcmkONERROR( + gckKERNEL_FindDatabase(kernel, dumpProcess, gcvFALSE, &database)); + + seq_printf(file, "VidMem Usage (Process %d):\n", dumpProcess); + + _ShowCounters(file, database); + + return 0; + +OnError: + return 0; +} + +static int +vidmem_open( + struct inode *inode, + struct file *file + ) +{ + return single_open(file, vidmem_show, inode->i_private); +} + +static ssize_t +vidmem_write( + struct file *file, + const char __user *buf, + size_t count, + loff_t *pos + ) +{ + dumpProcess = simple_strtol(buf, NULL, 0); + return count; +} + +/******************************************************************************* + ** + ** File Operations Table + ** + *******************************************************************************/ +static const struct file_operations debugfs_operations = { + .owner = THIS_MODULE , + .read = _DebugFSRead , + .write = _DebugFSWrite , +} ; + +static const struct file_operations vidmem_operations = { + .owner = THIS_MODULE , + .open = vidmem_open, + .read = seq_read, + .write = vidmem_write, + .llseek = seq_lseek, +} ; + +/******************************************************************************* + ** + ** INTERFACE FUNCTIONS (START) + ** + *******************************************************************************/ + +/******************************************************************************* + ** + ** gckDEBUGFS_IsEnabled + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ + + +gctINT +gckDEBUGFS_IsEnabled ( void ) +{ + return gc_dbgfs.isInited ; +} +/******************************************************************************* + ** + ** gckDEBUGFS_Initialize + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ + +gctINT +gckDEBUGFS_Initialize ( void ) +{ + if ( ! gc_dbgfs.isInited ) + { + gc_dbgfs.linkedlist = gcvNULL ; + gc_dbgfs.currentNode = gcvNULL ; + gc_dbgfs.isInited = 1 ; + } + return gc_dbgfs.isInited ; +} +/******************************************************************************* + ** + ** gckDEBUGFS_Terminate + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ + +gctINT +gckDEBUGFS_Terminate ( void ) +{ + gcsDEBUGFS_Node * next = gcvNULL ; + gcsDEBUGFS_Node * temp = gcvNULL ; + if ( gc_dbgfs.isInited ) + { + temp = gc_dbgfs.linkedlist ; + while ( temp != gcvNULL ) + { + next = temp->next ; + gckDEBUGFS_FreeNode ( temp ) ; + kfree ( temp ) ; + temp = next ; + } + gc_dbgfs.isInited = 0 ; + } + return 0 ; +} + + +/******************************************************************************* + ** + ** gckDEBUGFS_CreateNode + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + ** gckDEBUGFS_FreeNode * Device + ** Pointer to a variable receiving the gcsDEBUGFS_Node object pointer on + ** success. + *********************************************************************************/ + +gctINT +gckDEBUGFS_CreateNode ( + IN gctPOINTER Device, + IN gctINT SizeInKB , + IN struct dentry * Root , + IN gctCONST_STRING NodeName , + OUT gcsDEBUGFS_Node **Node + ) +{ + gcsDEBUGFS_Node*node ; + /* allocate space for our metadata and initialize it */ + if ( ( node = kmalloc ( sizeof (gcsDEBUGFS_Node ) , GFP_KERNEL ) ) == NULL ) + goto struct_malloc_failed ; + + /*Zero it out*/ + memset ( node , 0 , sizeof (gcsDEBUGFS_Node ) ) ; + + /*Init the sync primitives*/ +#if defined(DECLARE_WAIT_QUEUE_HEAD) + init_waitqueue_head ( gcmkNODE_READQ ( node ) ) ; +#else + init_waitqueue ( gcmkNODE_READQ ( node ) ) ; +#endif + +#if defined(DECLARE_WAIT_QUEUE_HEAD) + init_waitqueue_head ( gcmkNODE_WRITEQ ( node ) ) ; +#else + init_waitqueue ( gcmkNODE_WRITEQ ( node ) ) ; +#endif + sema_init ( gcmkNODE_SEM ( node ) , 1 ) ; + /*End the sync primitives*/ + + /*creating the debug file system*/ + node->parent = Root; + + if (SizeInKB) + { + /* figure out how much of a buffer this should be and allocate the buffer */ + node->size = 1024 * SizeInKB ; + if ( ( node->data = ( char * ) vmalloc ( sizeof (char ) * node->size ) ) == NULL ) + goto data_malloc_failed ; + + /*creating the file*/ + node->filen = debugfs_create_file(NodeName, S_IRUGO|S_IWUSR, node->parent, NULL, + &debugfs_operations); + } + + node->vidmem + = debugfs_create_file("vidmem", S_IRUGO|S_IWUSR, node->parent, Device, &vidmem_operations); + + /* add it to our linked list */ + node->next = gc_dbgfs.linkedlist ; + gc_dbgfs.linkedlist = node ; + + + /* pass the struct back */ + *Node = node ; + return 0 ; + + +data_malloc_failed: + kfree ( node ) ; +struct_malloc_failed: + return - ENOMEM ; +} + +/******************************************************************************* + ** + ** gckDEBUGFS_FreeNode + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ +void +gckDEBUGFS_FreeNode ( + IN gcsDEBUGFS_Node * Node + ) +{ + + gcsDEBUGFS_Node **ptr ; + + if ( Node == NULL ) + { + printk ( "null passed to free_vinfo\n" ) ; + return ; + } + + down ( gcmkNODE_SEM ( Node ) ) ; + /*free data*/ + vfree ( Node->data ) ; + + /*Close Debug fs*/ + if (Node->vidmem) + { + debugfs_remove(Node->vidmem); + } + + if ( Node->filen ) + { + debugfs_remove ( Node->filen ) ; + } + + /* now delete the node from the linked list */ + ptr = & ( gc_dbgfs.linkedlist ) ; + while ( *ptr != Node ) + { + if ( ! *ptr ) + { + printk ( "corrupt info list!\n" ) ; + break ; + } + else + ptr = & ( ( **ptr ).next ) ; + } + *ptr = Node->next ; + up ( gcmkNODE_SEM ( Node ) ) ; +} + +/******************************************************************************* + ** + ** gckDEBUGFS_SetCurrentNode + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ +void +gckDEBUGFS_SetCurrentNode ( + IN gcsDEBUGFS_Node * Node + ) +{ + gc_dbgfs.currentNode = Node ; +} + +/******************************************************************************* + ** + ** gckDEBUGFS_GetCurrentNode + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ +void +gckDEBUGFS_GetCurrentNode ( + OUT gcsDEBUGFS_Node ** Node + ) +{ + *Node = gc_dbgfs.currentNode ; +} + +/******************************************************************************* + ** + ** gckDEBUGFS_Print + ** + ** + ** INPUT: + ** + ** OUTPUT: + ** + *******************************************************************************/ +ssize_t +gckDEBUGFS_Print ( + IN gctCONST_STRING Message , + ... + ) +{ + ssize_t _debugfs_res; + gcmkDEBUGFS_PRINT ( _GetArgumentSize ( Message ) , Message ) ; + return _debugfs_res; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_debugfs.h b/drivers/gpu/galcore/gc_hal_kernel_debugfs.h new file mode 100644 index 00000000000000..9d17e879410d99 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_debugfs.h @@ -0,0 +1,135 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include + +#ifndef __gc_hal_kernel_debugfs_h_ +#define __gc_hal_kernel_debugfs_h_ + + #define MAX_LINE_SIZE 768 /* Max bytes for a line of debug info */ + + + typedef struct _gcsDEBUGFS_Node gcsDEBUGFS_Node; + +typedef struct _gcsDEBUGFS_DIR *gckDEBUGFS_DIR; +typedef struct _gcsDEBUGFS_DIR +{ + struct dentry * root; + struct list_head nodeList; +} +gcsDEBUGFS_DIR; + +typedef struct _gcsINFO +{ + const char * name; + int (*show)(struct seq_file*, void*); +} +gcsINFO; + +typedef struct _gcsINFO_NODE +{ + gcsINFO * info; + gctPOINTER device; + struct dentry * entry; + struct list_head head; +} +gcsINFO_NODE; + +gceSTATUS +gckDEBUGFS_DIR_Init( + IN gckDEBUGFS_DIR Dir, + IN struct dentry *root, + IN gctCONST_STRING Name + ); + +gceSTATUS +gckDEBUGFS_DIR_CreateFiles( + IN gckDEBUGFS_DIR Dir, + IN gcsINFO * List, + IN int count, + IN gctPOINTER Data + ); + +gceSTATUS +gckDEBUGFS_DIR_RemoveFiles( + IN gckDEBUGFS_DIR Dir, + IN gcsINFO * List, + IN int count + ); + +void +gckDEBUGFS_DIR_Deinit( + IN gckDEBUGFS_DIR Dir + ); + +/******************************************************************************* + ** + ** System Related + ** + *******************************************************************************/ + +gctINT gckDEBUGFS_IsEnabled(void); + +gctINT gckDEBUGFS_Initialize(void); + +gctINT gckDEBUGFS_Terminate(void); + + +/******************************************************************************* + ** + ** Node Related + ** + *******************************************************************************/ + +gctINT +gckDEBUGFS_CreateNode( + IN gctPOINTER Device, + IN gctINT SizeInKB, + IN struct dentry * Root, + IN gctCONST_STRING NodeName, + OUT gcsDEBUGFS_Node **Node + ); + +void gckDEBUGFS_FreeNode( + IN gcsDEBUGFS_Node * Node + ); + + + +void gckDEBUGFS_SetCurrentNode( + IN gcsDEBUGFS_Node * Node + ); + + + +void gckDEBUGFS_GetCurrentNode( + OUT gcsDEBUGFS_Node ** Node + ); + + +ssize_t gckDEBUGFS_Print( + IN gctCONST_STRING Message, + ... + ); + +#endif + + diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c new file mode 100644 index 00000000000000..e64db71e9dead8 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -0,0 +1,2706 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" +#include +#include +#include +#include + +#define _GC_OBJ_ZONE gcvZONE_DEVICE + +#define DEBUG_FILE "galcore_trace" +#define PARENT_FILE "gpu" + +gckKERNEL +_GetValidKernel( + gckGALDEVICE Device + ) +{ + if (Device->kernels[gcvCORE_MAJOR]) + { + return Device->kernels[gcvCORE_MAJOR]; + } + else + if (Device->kernels[gcvCORE_2D]) + { + return Device->kernels[gcvCORE_2D]; + } + else + if (Device->kernels[gcvCORE_VG]) + { + return Device->kernels[gcvCORE_VG]; + } + else + { + return gcvNULL; + } +} + +/******************************************************************************\ +******************************** Debugfs Support ******************************* +\******************************************************************************/ + +/******************************************************************************\ +***************************** DEBUG SHOW FUNCTIONS ***************************** +\******************************************************************************/ + +int gc_info_show(struct seq_file* m, void* data) +{ + gcsINFO_NODE *node = m->private; + gckGALDEVICE device = node->device; + int i = 0; + gceCHIPMODEL chipModel; + gctUINT32 chipRevision; + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->irqLines[i] != -1) + { +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + chipModel = device->kernels[i]->vg->hardware->chipModel; + chipRevision = device->kernels[i]->vg->hardware->chipRevision; + } + else +#endif + { + chipModel = device->kernels[i]->hardware->identity.chipModel; + chipRevision = device->kernels[i]->hardware->identity.chipRevision; + } + + seq_printf(m, "gpu : %d\n", i); + seq_printf(m, "model : %4x\n", chipModel); + seq_printf(m, "revision : %4x\n", chipRevision); + seq_printf(m, "\n"); + } + } + + return 0; +} + +int gc_clients_show(struct seq_file* m, void* data) +{ + gcsINFO_NODE *node = m->private; + gckGALDEVICE device = node->device; + + gckKERNEL kernel = _GetValidKernel(device); + + gcsDATABASE_PTR database; + gctINT i, pid; + gctUINT8 name[24]; + + seq_printf(m, "%-8s%s\n", "PID", "NAME"); + seq_printf(m, "------------------------\n"); + + /* Acquire the database mutex. */ + gcmkVERIFY_OK( + gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE)); + + /* Walk the databases. */ + for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i) + { + for (database = kernel->db->db[i]; + database != gcvNULL; + database = database->next) + { + pid = database->processID; + + gcmkVERIFY_OK(gckOS_ZeroMemory(name, gcmSIZEOF(name))); + + gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name)); + + seq_printf(m, "%-8d%s\n", pid, name); + } + } + + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex)); + + /* Success. */ + return 0; +} + +static void +_CounterAdd( + gcsDATABASE_COUNTERS * Dest, + gcsDATABASE_COUNTERS * Src + ) +{ + Dest->bytes += Src->bytes; + Dest->maxBytes += Src->maxBytes; + Dest->totalBytes += Src->totalBytes; +} + +static void +_CounterPrint( + gcsDATABASE_COUNTERS * Counter, + gctCONST_STRING Name, + struct seq_file* m + ) +{ + seq_printf(m, " %s:\n", Name); + seq_printf(m, " Used : %10llu B\n", Counter->bytes); +} + +int gc_meminfo_show(struct seq_file* m, void* data) +{ + gcsINFO_NODE *node = m->private; + gckGALDEVICE device = node->device; + gckKERNEL kernel = _GetValidKernel(device); + gckVIDMEM memory; + gceSTATUS status; + gcsDATABASE_PTR database; + gctUINT32 i; + + gctUINT32 free = 0, used = 0, total = 0; + + gcsDATABASE_COUNTERS contiguousCounter = {0, 0, 0}; + gcsDATABASE_COUNTERS virtualCounter = {0, 0, 0}; + gcsDATABASE_COUNTERS nonPagedCounter = {0, 0, 0}; + + status = gckKERNEL_GetVideoMemoryPool(kernel, gcvPOOL_SYSTEM, &memory); + + if (gcmIS_SUCCESS(status)) + { + gcmkVERIFY_OK( + gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE)); + + free = memory->freeBytes; + used = memory->bytes - memory->freeBytes; + total = memory->bytes; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex)); + } + + seq_printf(m, "VIDEO MEMORY:\n"); + seq_printf(m, " gcvPOOL_SYSTEM:\n"); + seq_printf(m, " Free : %10u B\n", free); + seq_printf(m, " Used : %10u B\n", used); + seq_printf(m, " Total : %10u B\n", total); + + /* Acquire the database mutex. */ + gcmkVERIFY_OK( + gckOS_AcquireMutex(kernel->os, kernel->db->dbMutex, gcvINFINITE)); + + /* Walk the databases. */ + for (i = 0; i < gcmCOUNTOF(kernel->db->db); ++i) + { + for (database = kernel->db->db[i]; + database != gcvNULL; + database = database->next) + { + gcsDATABASE_COUNTERS * counter = &database->vidMemPool[gcvPOOL_CONTIGUOUS]; + _CounterAdd(&contiguousCounter, counter); + + counter = &database->vidMemPool[gcvPOOL_VIRTUAL]; + _CounterAdd(&virtualCounter, counter); + + + counter = &database->nonPaged; + _CounterAdd(&nonPagedCounter, counter); + } + } + + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(kernel->os, kernel->db->dbMutex)); + + _CounterPrint(&contiguousCounter, "gcvPOOL_CONTIGUOUS", m); + _CounterPrint(&virtualCounter, "gcvPOOL_VIRTUAL", m); + + seq_printf(m, "\n"); + + seq_printf(m, "NON PAGED MEMORY:\n"); + seq_printf(m, " Used : %10llu B\n", nonPagedCounter.bytes); + + return 0; +} + +static int +_ShowRecord( + IN struct seq_file *file, + IN gcsDATABASE_RECORD_PTR record + ) +{ + seq_printf(file, "%4d%8d%16p%16p%16zu\n", + record->type, + record->kernel->core, + record->data, + record->physical, + record->bytes + ); + + return 0; +} + +static int +_ShowRecords( + IN struct seq_file *File, + IN gcsDATABASE_PTR Database + ) +{ + gctUINT i; + + seq_printf(File, "Records:\n"); + + seq_printf(File, "%s%8s%16s%16s%16s\n", + "Type", "GPU", "Data", "Physical", "Bytes"); + + for (i = 0; i < gcmCOUNTOF(Database->list); i++) + { + gcsDATABASE_RECORD_PTR record = Database->list[i]; + + while (record != NULL) + { + _ShowRecord(File, record); + record = record->next; + } + } + + return 0; +} + +void +_ShowCounters( + struct seq_file *File, + gcsDATABASE_PTR Database + ); + +static void +_ShowProcess( + IN struct seq_file *File, + IN gcsDATABASE_PTR Database + ) +{ + gctINT pid; + gctUINT8 name[24]; + + /* Process ID and name */ + pid = Database->processID; + gcmkVERIFY_OK(gckOS_ZeroMemory(name, gcmSIZEOF(name))); + gcmkVERIFY_OK(gckOS_GetProcessNameByPid(pid, gcmSIZEOF(name), name)); + + seq_printf(File, "--------------------------------------------------------------------------------\n"); + seq_printf(File, "Process: %-8d %s\n", pid, name); + + /* Detailed records */ + _ShowRecords(File, Database); + + seq_printf(File, "Counters:\n"); + + _ShowCounters(File, Database); +} + +static void +_ShowProcesses( + IN struct seq_file * file, + IN gckKERNEL Kernel + ) +{ + gcsDATABASE_PTR database; + gctINT i; + + /* Acquire the database mutex. */ + gcmkVERIFY_OK( + gckOS_AcquireMutex(Kernel->os, Kernel->db->dbMutex, gcvINFINITE)); + + /* Idle time since last call */ + seq_printf(file, "GPU Idle: %llu ns\n", Kernel->db->idleTime); + Kernel->db->idleTime = 0; + + /* Walk the databases. */ + for (i = 0; i < gcmCOUNTOF(Kernel->db->db); ++i) + { + for (database = Kernel->db->db[i]; + database != gcvNULL; + database = database->next) + { + _ShowProcess(file, database); + } + } + + /* Release the database mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->db->dbMutex)); +} + +static int +gc_db_show(struct seq_file *m, void *data) +{ + gcsINFO_NODE *node = m->private; + gckGALDEVICE device = node->device; + gckKERNEL kernel = _GetValidKernel(device); + _ShowProcesses(m, kernel); + return 0 ; +} + +static int +gc_version_show(struct seq_file *m, void *data) +{ + seq_printf(m, "%s\n", gcvVERSION_STRING); + + return 0 ; +} + +int gc_idle_show(struct seq_file* m, void* data) +{ + gcsINFO_NODE *node = m->private; + gckGALDEVICE device = node->device; + gckKERNEL kernel = _GetValidKernel(device); + gcuDATABASE_INFO info; + + gckKERNEL_QueryProcessDB(kernel, 0, gcvFALSE, gcvDB_IDLE, &info); + + seq_printf(m, "GPU idle time since last query: %llu ns\n", info.time); + + return 0; +} + +static gcsINFO InfoList[] = +{ + {"info", gc_info_show}, + {"clients", gc_clients_show}, + {"meminfo", gc_meminfo_show}, + {"idle", gc_idle_show}, + {"database", gc_db_show}, + {"version", gc_version_show}, +}; + +static gceSTATUS +_DebugfsInit( + IN gckGALDEVICE Device + ) +{ + gceSTATUS status; + + gckDEBUGFS_DIR dir = &Device->debugfsDir; + + gcmkONERROR(gckDEBUGFS_DIR_Init(dir, gcvNULL, "gc")); + + gcmkONERROR(gckDEBUGFS_DIR_CreateFiles(dir, InfoList, gcmCOUNTOF(InfoList), Device)); + + return gcvSTATUS_OK; + +OnError: + return status; +} + +static void +_DebugfsCleanup( + IN gckGALDEVICE Device + ) +{ + gckDEBUGFS_DIR dir = &Device->debugfsDir; + + if (Device->debugfsDir.root) + { + gcmkVERIFY_OK(gckDEBUGFS_DIR_RemoveFiles(dir, InfoList, gcmCOUNTOF(InfoList))); + + gckDEBUGFS_DIR_Deinit(dir); + } +} + + +/******************************************************************************\ +*************************** Memory Allocation Wrappers ************************* +\******************************************************************************/ + +static gceSTATUS +_AllocateMemory( + IN gckGALDEVICE Device, + IN gctSIZE_T Bytes, + OUT gctPOINTER *Logical, + OUT gctPHYS_ADDR *Physical, + OUT gctUINT32 *PhysAddr + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Device=0x%x Bytes=%lu", Device, Bytes); + + gcmkVERIFY_ARGUMENT(Device != NULL); + gcmkVERIFY_ARGUMENT(Logical != NULL); + gcmkVERIFY_ARGUMENT(Physical != NULL); + gcmkVERIFY_ARGUMENT(PhysAddr != NULL); + + gcmkONERROR(gckOS_AllocateContiguous( + Device->os, gcvFALSE, &Bytes, Physical, Logical + )); + + *PhysAddr = ((PLINUX_MDL)*Physical)->dmaHandle; + + /* Success. */ + gcmkFOOTER_ARG( + "*Logical=0x%x *Physical=0x%x *PhysAddr=0x%08x", + *Logical, *Physical, *PhysAddr + ); + + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +static gceSTATUS +_FreeMemory( + IN gckGALDEVICE Device, + IN gctPOINTER Logical, + IN gctPHYS_ADDR Physical) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Device=0x%x Logical=0x%x Physical=0x%x", + Device, Logical, Physical); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + status = gckOS_FreeContiguous( + Device->os, Physical, Logical, + ((PLINUX_MDL) Physical)->numPages * PAGE_SIZE + ); + + gcmkFOOTER(); + return status; +} + + + +/******************************************************************************\ +******************************* Interrupt Handler ****************************** +\******************************************************************************/ +#if gcdMULTI_GPU +static irqreturn_t isrRoutine3D0(int irq, void *ctxt) +{ + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Call kernel interrupt notification. */ + status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], + gcvCORE_3D_0_ID, + gcvNOTIFY_INTERRUPT, + gcvTRUE); + + if (gcmIS_SUCCESS(status)) + { + /* Wake up the threadRoutine to process events. */ + device->dataReady3D[gcvCORE_3D_0_ID] = gcvTRUE; + wake_up_interruptible(&device->intrWaitQueue3D[gcvCORE_3D_0_ID]); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int threadRoutine3D0(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + /* Sleep until being awaken by the interrupt handler. */ + wait_event_interruptible(device->intrWaitQueue3D[gcvCORE_3D_0_ID], + device->dataReady3D[gcvCORE_3D_0_ID] == gcvTRUE); + device->dataReady3D[gcvCORE_3D_0_ID] = gcvFALSE; + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + + gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], + gcvCORE_3D_0_ID, + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} + +#if gcdMULTI_GPU > 1 +static irqreturn_t isrRoutine3D1(int irq, void *ctxt) +{ + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Call kernel interrupt notification. */ + status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], + gcvCORE_3D_1_ID, + gcvNOTIFY_INTERRUPT, + gcvTRUE); + + if (gcmIS_SUCCESS(status)) + { + /* Wake up the worker thread to process events. */ + device->dataReady3D[gcvCORE_3D_1_ID] = gcvTRUE; + wake_up_interruptible(&device->intrWaitQueue3D[gcvCORE_3D_1_ID]); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int threadRoutine3D1(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + /* Sleep until being awaken by the interrupt handler. */ + wait_event_interruptible(device->intrWaitQueue3D[gcvCORE_3D_1_ID], + device->dataReady3D[gcvCORE_3D_1_ID] == gcvTRUE); + device->dataReady3D[gcvCORE_3D_1_ID] = gcvFALSE; + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + + gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], + gcvCORE_3D_1_ID, + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} +#endif +#elif gcdMULTI_GPU_AFFINITY +static irqreturn_t isrRoutine3D0(int irq, void *ctxt) +{ + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Call kernel interrupt notification. */ + status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvTRUE); + + if (gcmIS_SUCCESS(status)) + { + up(&device->semas[gcvCORE_MAJOR]); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int threadRoutine3D0(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + static int down; + + down = down_interruptible(&device->semas[gcvCORE_MAJOR]); + if (down); /*To make gcc 4.6 happye*/ + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + + gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} + +static irqreturn_t isrRoutine3D1(int irq, void *ctxt) +{ + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Call kernel interrupt notification. */ + status = gckKERNEL_Notify(device->kernels[gcvCORE_OCL], gcvNOTIFY_INTERRUPT, gcvTRUE); + + if (gcmIS_SUCCESS(status)) + { + up(&device->semas[gcvCORE_OCL]); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int threadRoutine3D1(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + static int down; + + down = down_interruptible(&device->semas[gcvCORE_OCL]); + if (down); /*To make gcc 4.6 happye*/ + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + + gckKERNEL_Notify(device->kernels[gcvCORE_OCL], + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} +#else +static irqreturn_t isrRoutine(int irq, void *ctxt) +{ + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Call kernel interrupt notification. */ + status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvTRUE); + + if (gcmIS_SUCCESS(status)) + { + up(&device->semas[gcvCORE_MAJOR]); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int threadRoutine(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + static int down; + + down = down_interruptible(&device->semas[gcvCORE_MAJOR]); + if (down); /*To make gcc 4.6 happye*/ + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + + gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} +#endif + +static irqreturn_t isrRoutine2D(int irq, void *ctxt) +{ + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Call kernel interrupt notification. */ + status = gckKERNEL_Notify(device->kernels[gcvCORE_2D], +#if gcdMULTI_GPU + 0, +#endif + gcvNOTIFY_INTERRUPT, + gcvTRUE); + if (gcmIS_SUCCESS(status)) + { + up(&device->semas[gcvCORE_2D]); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int threadRoutine2D(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + static int down; + + down = down_interruptible(&device->semas[gcvCORE_2D]); + if (down); /*To make gcc 4.6 happye*/ + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + gckKERNEL_Notify(device->kernels[gcvCORE_2D], +#if gcdMULTI_GPU + 0, +#endif + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} + +static irqreturn_t isrRoutineVG(int irq, void *ctxt) +{ +#if gcdENABLE_VG + gceSTATUS status; + gckGALDEVICE device; + + device = (gckGALDEVICE) ctxt; + + /* Serve the interrupt. */ + status = gckVGINTERRUPT_Enque(device->kernels[gcvCORE_VG]->vg->interrupt); + + /* Determine the return value. */ + return (status == gcvSTATUS_NOT_OUR_INTERRUPT) + ? IRQ_RETVAL(0) + : IRQ_RETVAL(1); +#else + return IRQ_NONE; +#endif +} + +static int threadRoutineVG(void *ctxt) +{ + gckGALDEVICE device = (gckGALDEVICE) ctxt; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, + "Starting isr Thread with extension=%p", + device); + + for (;;) + { + static int down; + + down = down_interruptible(&device->semas[gcvCORE_VG]); + if (down); /*To make gcc 4.6 happye*/ + + if (device->killThread == gcvTRUE) + { + /* The daemon exits. */ + while (!kthread_should_stop()) + { + gckOS_Delay(device->os, 1); + } + + return 0; + } + gckKERNEL_Notify(device->kernels[gcvCORE_VG], +#if gcdMULTI_GPU + 0, +#endif + gcvNOTIFY_INTERRUPT, + gcvFALSE); + } +} + +/******************************************************************************\ +******************************* gckGALDEVICE Code ****************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckGALDEVICE_Construct +** +** Constructor. +** +** INPUT: +** +** OUTPUT: +** +** gckGALDEVICE * Device +** Pointer to a variable receiving the gckGALDEVICE object pointer on +** success. +*/ +gceSTATUS +gckGALDEVICE_Construct( +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + IN gctINT IrqLine3D0, + IN gctUINT32 RegisterMemBase3D0, + IN gctSIZE_T RegisterMemSize3D0, + IN gctINT IrqLine3D1, + IN gctUINT32 RegisterMemBase3D1, + IN gctSIZE_T RegisterMemSize3D1, +#else + IN gctINT IrqLine, + IN gctUINT32 RegisterMemBase, + IN gctSIZE_T RegisterMemSize, +#endif + IN gctINT IrqLine2D, + IN gctUINT32 RegisterMemBase2D, + IN gctSIZE_T RegisterMemSize2D, + IN gctINT IrqLineVG, + IN gctUINT32 RegisterMemBaseVG, + IN gctSIZE_T RegisterMemSizeVG, + IN gctUINT32 ContiguousBase, + IN gctSIZE_T ContiguousSize, + IN gctSIZE_T BankSize, + IN gctINT FastClear, + IN gctINT Compression, + IN gctUINT32 PhysBaseAddr, + IN gctUINT32 PhysSize, + IN gctINT Signal, + IN gctUINT LogFileSize, + IN gctINT PowerManagement, + IN gctINT GpuProfiler, + IN gcsDEVICE_CONSTRUCT_ARGS * Args, + OUT gckGALDEVICE *Device + ) +{ + gctUINT32 internalBaseAddress = 0, internalAlignment = 0; + gctUINT32 externalBaseAddress = 0, externalAlignment = 0; + gctUINT32 horizontalTileSize, verticalTileSize; + struct resource* mem_region; + gctUINT32 physAddr; + gctUINT32 physical; + gckGALDEVICE device; + gceSTATUS status; + gctINT32 i; +#if gcdMULTI_GPU + gctINT32 j; +#endif + gceHARDWARE_TYPE type; + gckDB sharedDB = gcvNULL; + gckKERNEL kernel = gcvNULL; + +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + gcmkHEADER_ARG("IrqLine3D0=%d RegisterMemBase3D0=0x%08x RegisterMemSize3D0=%u " + "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u " + "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u " + "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu " + "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d", + IrqLine3D0, RegisterMemBase3D0, RegisterMemSize3D0, + IrqLine2D, RegisterMemBase2D, RegisterMemSize2D, + IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG, + ContiguousBase, ContiguousSize, BankSize, FastClear, Compression, + PhysBaseAddr, PhysSize, Signal); +#else + gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u " + "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u " + "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u " + "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu " + "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d", + IrqLine, RegisterMemBase, RegisterMemSize, + IrqLine2D, RegisterMemBase2D, RegisterMemSize2D, + IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG, + ContiguousBase, ContiguousSize, BankSize, FastClear, Compression, + PhysBaseAddr, PhysSize, Signal); +#endif + +#if gcdDISABLE_CORES_2D3D + IrqLine = -1; + IrqLine2D = -1; +#endif + + /* Allocate device structure. */ + device = kmalloc(sizeof(struct _gckGALDEVICE), GFP_KERNEL | __GFP_NOWARN); + + if (!device) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + memset(device, 0, sizeof(struct _gckGALDEVICE)); + + device->dbgNode = gcvNULL; + + device->platform = Args->platform; + + gcmkONERROR(_DebugfsInit(device)); + + if (gckDEBUGFS_CreateNode( + device, LogFileSize, device->debugfsDir.root ,DEBUG_FILE, &(device->dbgNode))) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to create the debug file system %s/%s \n", + __FUNCTION__, __LINE__, + PARENT_FILE, DEBUG_FILE + ); + } + else if (LogFileSize) + { + gckDEBUGFS_SetCurrentNode(device->dbgNode); + } + +#if gcdMULTI_GPU + if (IrqLine3D0 != -1) + { + device->requestedRegisterMemBase3D[gcvCORE_3D_0_ID] = RegisterMemBase3D0; + device->requestedRegisterMemSize3D[gcvCORE_3D_0_ID] = RegisterMemSize3D0; + } + + if (IrqLine3D1 != -1) + { + device->requestedRegisterMemBase3D[gcvCORE_3D_1_ID] = RegisterMemBase3D1; + device->requestedRegisterMemSize3D[gcvCORE_3D_1_ID] = RegisterMemSize3D1; + } +#elif gcdMULTI_GPU_AFFINITY + if (IrqLine3D0 != -1) + { + device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase3D0; + device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize3D0; + } + + if (IrqLine3D1 != -1) + { + device->requestedRegisterMemBases[gcvCORE_OCL] = RegisterMemBase3D1; + device->requestedRegisterMemSizes[gcvCORE_OCL] = RegisterMemSize3D1; + } +#else + if (IrqLine != -1) + { + device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase; + device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize; + } +#endif + + if (IrqLine2D != -1) + { + device->requestedRegisterMemBases[gcvCORE_2D] = RegisterMemBase2D; + device->requestedRegisterMemSizes[gcvCORE_2D] = RegisterMemSize2D; + } + + if (IrqLineVG != -1) + { + device->requestedRegisterMemBases[gcvCORE_VG] = RegisterMemBaseVG; + device->requestedRegisterMemSizes[gcvCORE_VG] = RegisterMemSizeVG; + } + + device->requestedContiguousBase = 0; + device->requestedContiguousSize = 0; + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { +#if gcdMULTI_GPU + if (i == gcvCORE_MAJOR) + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + physical = device->requestedRegisterMemBase3D[j]; + + /* Set up register memory region. */ + if (physical != 0) + { + mem_region = request_mem_region(physical, + device->requestedRegisterMemSize3D[j], + "galcore register region"); + + if (mem_region == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to claim %lu bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + physical, device->requestedRegisterMemSize3D[j] + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + device->registerBase3D[j] = (gctPOINTER) ioremap_nocache( + physical, device->requestedRegisterMemSize3D[j]); + + if (device->registerBase3D[j] == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Unable to map %ld bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + physical, device->requestedRegisterMemSize3D[j] + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + physical += device->requestedRegisterMemSize3D[j]; + } + else + { + device->registerBase3D[j] = gcvNULL; + } + } + } + else +#endif + { + physical = device->requestedRegisterMemBases[i]; + + /* Set up register memory region. */ + if (physical != 0) + { + mem_region = request_mem_region(physical, + device->requestedRegisterMemSizes[i], + "galcore register region"); + + if (mem_region == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to claim %lu bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + physical, device->requestedRegisterMemSizes[i] + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + device->registerBases[i] = (gctPOINTER) ioremap_nocache( + physical, device->requestedRegisterMemSizes[i]); + + if (device->registerBases[i] == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Unable to map %ld bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + physical, device->requestedRegisterMemSizes[i] + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + physical += device->requestedRegisterMemSizes[i]; + } + } + } + + /* Set the base address */ + device->baseAddress = device->physBase = PhysBaseAddr; + device->physSize = PhysSize; + device->mmu = Args->mmu; + + /* Construct the gckOS object. */ + gcmkONERROR(gckOS_Construct(device, &device->os)); + +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + if (IrqLine3D0 != -1) +#else + if (IrqLine != -1) +#endif + { + /* Construct the gckKERNEL object. */ + gcmkONERROR(gckKERNEL_Construct( + device->os, gcvCORE_MAJOR, device, + gcvNULL, &device->kernels[gcvCORE_MAJOR])); + + sharedDB = device->kernels[gcvCORE_MAJOR]->db; + + /* Initialize core mapping */ + for (i = 0; i < 8; i++) + { + device->coreMapping[i] = gcvCORE_MAJOR; + } + + /* Setup the ISR manager. */ + gcmkONERROR(gckHARDWARE_SetIsrManager( + device->kernels[gcvCORE_MAJOR]->hardware, + (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR, + (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR, + device + )); + + gcmkONERROR(gckHARDWARE_SetFastClear( + device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression + )); + + if(PowerManagement != -1) + { + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE + )); + gcmkONERROR(gckHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement + )); + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE + )); + } + else + { + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE + )); + gcmkONERROR(gckHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE + )); + } + +#if gcdENABLE_FSCALE_VAL_ADJUST + gcmkONERROR(gckHARDWARE_SetMinFscaleValue( + device->kernels[gcvCORE_MAJOR]->hardware, Args->gpu3DMinClock + )); +#endif + + gcmkONERROR(gckHARDWARE_SetGpuProfiler( + device->kernels[gcvCORE_MAJOR]->hardware, GpuProfiler + )); + + gcmkVERIFY_OK(gckKERNEL_SetRecovery( + device->kernels[gcvCORE_MAJOR], Args->recovery, Args->stuckDump + )); + +#if COMMAND_PROCESSOR_VERSION == 1 + /* Start the command queue. */ + gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command)); +#endif + } + else + { + device->kernels[gcvCORE_MAJOR] = gcvNULL; + } + +#if gcdMULTI_GPU_AFFINITY + if (IrqLine3D1 != -1) + { + /* Construct the gckKERNEL object. */ + gcmkONERROR(gckKERNEL_Construct( + device->os, gcvCORE_OCL, device, + gcvNULL, &device->kernels[gcvCORE_OCL])); + + if (sharedDB == gcvNULL) sharedDB = device->kernels[gcvCORE_OCL]->db; + + /* Initialize core mapping */ + if (device->kernels[gcvCORE_MAJOR] == gcvNULL) + { + for (i = 0; i < 8; i++) + { + device->coreMapping[i] = gcvCORE_OCL; + } + } + else + { + device->coreMapping[gcvHARDWARE_OCL] = gcvCORE_OCL; + } + + /* Setup the ISR manager. */ + gcmkONERROR(gckHARDWARE_SetIsrManager( + device->kernels[gcvCORE_OCL]->hardware, + (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR, + (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR, + device + )); + + gcmkONERROR(gckHARDWARE_SetFastClear( + device->kernels[gcvCORE_OCL]->hardware, FastClear, Compression + )); + +#if gcdENABLE_FSCALE_VAL_ADJUST + gcmkONERROR(gckHARDWARE_SetMinFscaleValue( + device->kernels[gcvCORE_OCL]->hardware, Args->gpu3DMinClock + )); +#endif + if(PowerManagement != -1) + { + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_OCL]->hardware, gcvFALSE + )); + gcmkONERROR(gckHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_OCL]->hardware, PowerManagement + )); + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_OCL]->hardware, gcvTRUE + )); + } + else + { + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_OCL]->hardware, gcvFALSE + )); + gcmkONERROR(gckHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_OCL]->hardware, gcvTRUE + )); + } + +#if COMMAND_PROCESSOR_VERSION == 1 + /* Start the command queue. */ + gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_OCL]->command)); +#endif + } + else + { + device->kernels[gcvCORE_OCL] = gcvNULL; + } +#endif + + if (IrqLine2D != -1) + { + gcmkONERROR(gckKERNEL_Construct( + device->os, gcvCORE_2D, device, + sharedDB, &device->kernels[gcvCORE_2D])); + + if (sharedDB == gcvNULL) sharedDB = device->kernels[gcvCORE_2D]->db; + + /* Verify the hardware type */ + gcmkONERROR(gckHARDWARE_GetType(device->kernels[gcvCORE_2D]->hardware, &type)); + + if (type != gcvHARDWARE_2D) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Unexpected hardware type: %d\n", + __FUNCTION__, __LINE__, + type + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Initialize core mapping */ + if (device->kernels[gcvCORE_MAJOR] == gcvNULL +#if gcdMULTI_GPU_AFFINITY + && device->kernels[gcvCORE_OCL] == gcvNULL +#endif + ) + { + for (i = 0; i < 8; i++) + { + device->coreMapping[i] = gcvCORE_2D; + } + } + else + { + device->coreMapping[gcvHARDWARE_2D] = gcvCORE_2D; + } + + /* Setup the ISR manager. */ + gcmkONERROR(gckHARDWARE_SetIsrManager( + device->kernels[gcvCORE_2D]->hardware, + (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR_2D, + (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR_2D, + device + )); + + if(PowerManagement != -1) + { + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_2D]->hardware, gcvFALSE + )); + gcmkONERROR(gckHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_2D]->hardware, PowerManagement + )); + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_2D]->hardware, gcvTRUE + )); + } + else + { + gcmkONERROR(gckHARDWARE_SetPowerManagementLock( + device->kernels[gcvCORE_2D]->hardware, gcvFALSE + )); + gcmkONERROR(gckHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_2D]->hardware, gcvTRUE + )); + } + +#if gcdENABLE_FSCALE_VAL_ADJUST + gcmkONERROR(gckHARDWARE_SetMinFscaleValue( + device->kernels[gcvCORE_2D]->hardware, 1 + )); +#endif + + gcmkVERIFY_OK(gckKERNEL_SetRecovery( + device->kernels[gcvCORE_2D], Args->recovery, Args->stuckDump + )); + +#if COMMAND_PROCESSOR_VERSION == 1 + /* Start the command queue. */ + gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_2D]->command)); +#endif + } + else + { + device->kernels[gcvCORE_2D] = gcvNULL; + } + + if (IrqLineVG != -1) + { +#if gcdENABLE_VG + gcmkONERROR(gckKERNEL_Construct( + device->os, gcvCORE_VG, device, + sharedDB, &device->kernels[gcvCORE_VG])); + /* Initialize core mapping */ + if (device->kernels[gcvCORE_MAJOR] == gcvNULL + && device->kernels[gcvCORE_2D] == gcvNULL +#if gcdMULTI_GPU_AFFINITY + && device->kernels[gcvCORE_OCL] == gcvNULL +#endif + ) + { + for (i = 0; i < 8; i++) + { + device->coreMapping[i] = gcvCORE_VG; + } + } + else + { + device->coreMapping[gcvHARDWARE_VG] = gcvCORE_VG; + } + + if(PowerManagement != -1) + { + gcmkONERROR(gckVGHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_VG]->vg->hardware, + PowerManagement + )); + } + else + { + gcmkONERROR(gckVGHARDWARE_SetPowerManagement( + device->kernels[gcvCORE_VG]->vg->hardware, + gcvTRUE + )); + } + + +#endif + } + else + { + device->kernels[gcvCORE_VG] = gcvNULL; + } + + /* Initialize the ISR. */ +#if gcdMULTI_GPU + device->irqLine3D[gcvCORE_3D_0_ID] = IrqLine3D0; +#if gcdMULTI_GPU > 1 + device->irqLine3D[gcvCORE_3D_1_ID] = IrqLine3D1; +#endif +#elif gcdMULTI_GPU_AFFINITY + device->irqLines[gcvCORE_MAJOR] = IrqLine3D0; + device->irqLines[gcvCORE_OCL] = IrqLine3D1; +#else + device->irqLines[gcvCORE_MAJOR] = IrqLine; +#endif + device->irqLines[gcvCORE_2D] = IrqLine2D; + device->irqLines[gcvCORE_VG] = IrqLineVG; + + /* Initialize the kernel thread semaphores. */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { +#if gcdMULTI_GPU + if (i == gcvCORE_MAJOR) + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + if (device->irqLine3D[j] != -1) init_waitqueue_head(&device->intrWaitQueue3D[j]); + } + } + else +#endif + { + if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0); + } + } + + device->signal = Signal; + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) break; + } + + if (i == gcdMAX_GPU_COUNT) + { + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + /* Query the ceiling of the system memory. */ + gcmkONERROR(gckVGHARDWARE_QuerySystemMemory( + device->kernels[i]->vg->hardware, + &device->systemMemorySize, + &device->systemMemoryBaseAddress + )); + /* query the amount of video memory */ + gcmkONERROR(gckVGHARDWARE_QueryMemory( + device->kernels[i]->vg->hardware, + &device->internalSize, &internalBaseAddress, &internalAlignment, + &device->externalSize, &externalBaseAddress, &externalAlignment, + &horizontalTileSize, &verticalTileSize + )); + } + else +#endif + { + /* Query the ceiling of the system memory. */ + gcmkONERROR(gckHARDWARE_QuerySystemMemory( + device->kernels[i]->hardware, + &device->systemMemorySize, + &device->systemMemoryBaseAddress + )); + + /* query the amount of video memory */ + gcmkONERROR(gckHARDWARE_QueryMemory( + device->kernels[i]->hardware, + &device->internalSize, &internalBaseAddress, &internalAlignment, + &device->externalSize, &externalBaseAddress, &externalAlignment, + &horizontalTileSize, &verticalTileSize + )); + } + + + /* Grab the first availiable kernel */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { +#if gcdMULTI_GPU + if (i == gcvCORE_MAJOR) + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + if (device->irqLine3D[j] != -1) + { + kernel = device->kernels[i]; + break; + } + } + } + else +#endif + { + if (device->irqLines[i] != -1) + { + kernel = device->kernels[i]; + break; + } + } + } + + /* Set up the internal memory region. */ + if (device->internalSize > 0) + { + status = gckVIDMEM_Construct( + device->os, + internalBaseAddress, device->internalSize, internalAlignment, + 0, &device->internalVidMem + ); + + if (gcmIS_ERROR(status)) + { + /* Error, disable internal heap. */ + device->internalSize = 0; + } + else + { + /* Map internal memory. */ + device->internalLogical + = (gctPOINTER) ioremap_nocache(physical, device->internalSize); + + if (device->internalLogical == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + device->internalPhysical = (gctPHYS_ADDR)(gctUINTPTR_T) physical; + device->internalPhysicalName = gcmPTR_TO_NAME(device->internalPhysical); + physical += device->internalSize; + } + } + + if (device->externalSize > 0) + { + /* create the external memory heap */ + status = gckVIDMEM_Construct( + device->os, + externalBaseAddress, device->externalSize, externalAlignment, + 0, &device->externalVidMem + ); + + if (gcmIS_ERROR(status)) + { + /* Error, disable internal heap. */ + device->externalSize = 0; + } + else + { + /* Map external memory. */ + device->externalLogical + = (gctPOINTER) ioremap_nocache(physical, device->externalSize); + + if (device->externalLogical == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + device->externalPhysical = (gctPHYS_ADDR)(gctUINTPTR_T) physical; + device->externalPhysicalName = gcmPTR_TO_NAME(device->externalPhysical); + physical += device->externalSize; + } + } + + /* set up the contiguous memory */ + device->contiguousSize = ContiguousSize; + + if (ContiguousSize > 0) + { + if (ContiguousBase == 0) + { + while (device->contiguousSize > 0) + { + /* Allocate contiguous memory. */ + status = _AllocateMemory( + device, + device->contiguousSize, + &device->contiguousBase, + &device->contiguousPhysical, + &physAddr + ); + + if (gcmIS_SUCCESS(status)) + { + device->contiguousPhysicalName = gcmPTR_TO_NAME(device->contiguousPhysical); + status = gckVIDMEM_Construct( + device->os, + physAddr | device->systemMemoryBaseAddress, + device->contiguousSize, + 64, + BankSize, + &device->contiguousVidMem + ); + + if (gcmIS_SUCCESS(status)) + { + break; + } + + gcmkONERROR(_FreeMemory( + device, + device->contiguousBase, + device->contiguousPhysical + )); + + gcmRELEASE_NAME(device->contiguousPhysicalName); + device->contiguousBase = gcvNULL; + device->contiguousPhysical = gcvNULL; + } + + if (device->contiguousSize <= (4 << 20)) + { + device->contiguousSize = 0; + } + else + { + device->contiguousSize -= (4 << 20); + } + } + } + else + { + /* Create the contiguous memory heap. */ + status = gckVIDMEM_Construct( + device->os, + ContiguousBase | device->systemMemoryBaseAddress, + ContiguousSize, + 64, BankSize, + &device->contiguousVidMem + ); + + if (gcmIS_ERROR(status)) + { + /* Error, disable contiguous memory pool. */ + device->contiguousVidMem = gcvNULL; + device->contiguousSize = 0; + } + else + { + if (Args->contiguousRequested == gcvFALSE) + { + mem_region = request_mem_region( + ContiguousBase, ContiguousSize, "galcore managed memory" + ); + + if (mem_region == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to claim %ld bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + ContiguousSize, ContiguousBase + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + + device->requestedContiguousBase = ContiguousBase; + device->requestedContiguousSize = ContiguousSize; + device->contiguousRequested = Args->contiguousRequested; + + device->contiguousPhysical = gcvNULL; + device->contiguousPhysicalName = 0; + device->contiguousSize = ContiguousSize; + device->contiguousMapped = gcvTRUE; + } + } + } + + /* Return pointer to the device. */ + *Device = device; + + gcmkFOOTER_ARG("*Device=0x%x", * Device); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + gcmkVERIFY_OK(gckGALDEVICE_Destroy(device)); + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckGALDEVICE_Destroy +** +** Class destructor. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** Nothing. +*/ +gceSTATUS +gckGALDEVICE_Destroy( + gckGALDEVICE Device) +{ + gctINT i; +#if gcdMULTI_GPU + gctINT j; +#endif + gckKERNEL kernel = gcvNULL; + + gcmkHEADER_ARG("Device=0x%x", Device); + + if (Device != gcvNULL) + { + /* Grab the first availiable kernel */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { +#if gcdMULTI_GPU + if (i == gcvCORE_MAJOR) + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + if (Device->irqLine3D[j] != -1) + { + kernel = Device->kernels[i]; + break; + } + } + } + else +#endif + { + if (Device->irqLines[i] != -1) + { + kernel = Device->kernels[i]; + break; + } + } + } + + if (Device->internalPhysicalName != 0) + { + gcmRELEASE_NAME(Device->internalPhysicalName); + Device->internalPhysicalName = 0; + } + if (Device->externalPhysicalName != 0) + { + gcmRELEASE_NAME(Device->externalPhysicalName); + Device->externalPhysicalName = 0; + } + if (Device->contiguousPhysicalName != 0) + { + gcmRELEASE_NAME(Device->contiguousPhysicalName); + Device->contiguousPhysicalName = 0; + } + + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (Device->kernels[i] != gcvNULL) + { + /* Destroy the gckKERNEL object. */ + gcmkVERIFY_OK(gckKERNEL_Destroy(Device->kernels[i])); + Device->kernels[i] = gcvNULL; + } + } + + if (Device->internalLogical != gcvNULL) + { + /* Unmap the internal memory. */ + iounmap(Device->internalLogical); + Device->internalLogical = gcvNULL; + } + + if (Device->internalVidMem != gcvNULL) + { + /* Destroy the internal heap. */ + gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->internalVidMem)); + Device->internalVidMem = gcvNULL; + } + + if (Device->externalLogical != gcvNULL) + { + /* Unmap the external memory. */ + iounmap(Device->externalLogical); + Device->externalLogical = gcvNULL; + } + + if (Device->externalVidMem != gcvNULL) + { + /* destroy the external heap */ + gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->externalVidMem)); + Device->externalVidMem = gcvNULL; + } + + if (Device->contiguousBase != gcvNULL) + { + if (Device->contiguousMapped == gcvFALSE) + { + gcmkVERIFY_OK(_FreeMemory( + Device, + Device->contiguousBase, + Device->contiguousPhysical + )); + } + + Device->contiguousBase = gcvNULL; + Device->contiguousPhysical = gcvNULL; + } + + if (Device->requestedContiguousBase != 0 + && Device->contiguousRequested == gcvFALSE + ) + { + release_mem_region(Device->requestedContiguousBase, Device->requestedContiguousSize); + Device->requestedContiguousBase = 0; + Device->requestedContiguousSize = 0; + } + + if (Device->contiguousVidMem != gcvNULL) + { + /* Destroy the contiguous heap. */ + gcmkVERIFY_OK(gckVIDMEM_Destroy(Device->contiguousVidMem)); + Device->contiguousVidMem = gcvNULL; + } + + if (Device->dbgNode) + { + gckDEBUGFS_FreeNode(Device->dbgNode); + + if(Device->dbgNode != gcvNULL) + { + kfree(Device->dbgNode); + Device->dbgNode = gcvNULL; + } + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { +#if gcdMULTI_GPU + if (i == gcvCORE_MAJOR) + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + if (Device->registerBase3D[j] != gcvNULL) + { + /* Unmap register memory. */ + iounmap(Device->registerBase3D[j]); + if (Device->requestedRegisterMemBase3D[j] != 0) + { + release_mem_region(Device->requestedRegisterMemBase3D[j], + Device->requestedRegisterMemSize3D[j]); + } + + Device->registerBase3D[j] = gcvNULL; + Device->requestedRegisterMemBase3D[j] = 0; + Device->requestedRegisterMemSize3D[j] = 0; + } + } + } + else +#endif + { + if (Device->registerBases[i] != gcvNULL) + { + /* Unmap register memory. */ + iounmap(Device->registerBases[i]); + if (Device->requestedRegisterMemBases[i] != 0) + { + release_mem_region(Device->requestedRegisterMemBases[i], + Device->requestedRegisterMemSizes[i]); + } + + Device->registerBases[i] = gcvNULL; + Device->requestedRegisterMemBases[i] = 0; + Device->requestedRegisterMemSizes[i] = 0; + } + } + } + + /* Destroy the gckOS object. */ + if (Device->os != gcvNULL) + { + gcmkVERIFY_OK(gckOS_Destroy(Device->os)); + Device->os = gcvNULL; + } + + _DebugfsCleanup(Device); + + /* Free the device. */ + kfree(Device); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckGALDEVICE_Setup_ISR +** +** Start the ISR routine. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** gcvSTATUS_OK +** Setup successfully. +** gcvSTATUS_GENERIC_IO +** Setup failed. +*/ +gceSTATUS +gckGALDEVICE_Setup_ISR( + IN gckGALDEVICE Device + ) +{ + gceSTATUS status; + gctINT ret = 0; + + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + if (Device->irqLines[gcvCORE_MAJOR] < 0) + { + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Hook up the isr based on the irq line. */ +#if gcdMULTI_GPU + ret = request_irq( + Device->irqLine3D[gcvCORE_3D_0_ID], isrRoutine3D0, 0, + "galcore_3d_0", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLine3D[gcvCORE_3D_0_ID], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitialized3D[gcvCORE_3D_0_ID] = gcvTRUE; + +#if gcdMULTI_GPU > 1 + ret = request_irq( + Device->irqLine3D[gcvCORE_3D_1_ID], isrRoutine3D1, 0, + "galcore_3d_1", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLine3D[gcvCORE_3D_1_ID], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitialized3D[gcvCORE_3D_1_ID] = gcvTRUE; +#endif +#elif gcdMULTI_GPU_AFFINITY + ret = request_irq( + Device->irqLines[gcvCORE_MAJOR], isrRoutine3D0, 0, + "galcore_3d_0", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLines[gcvCORE_MAJOR], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE; + + ret = request_irq( + Device->irqLines[gcvCORE_OCL], isrRoutine3D1, 0, + "galcore_3d_1", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLines[gcvCORE_OCL], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitializeds[gcvCORE_OCL] = gcvTRUE; +#else + ret = request_irq( + Device->irqLines[gcvCORE_MAJOR], isrRoutine, 0, + "galcore interrupt service", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLines[gcvCORE_MAJOR], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE; +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckGALDEVICE_Setup_ISR_2D( + IN gckGALDEVICE Device + ) +{ + gceSTATUS status; + gctINT ret; + + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + if (Device->irqLines[gcvCORE_2D] < 0) + { + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Hook up the isr based on the irq line. */ + ret = request_irq( + Device->irqLines[gcvCORE_2D], isrRoutine2D, 0, + "galcore interrupt service for 2D", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLines[gcvCORE_2D], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitializeds[gcvCORE_2D] = gcvTRUE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckGALDEVICE_Setup_ISR_VG( + IN gckGALDEVICE Device + ) +{ + gceSTATUS status; + gctINT ret; + + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + if (Device->irqLines[gcvCORE_VG] < 0) + { + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Hook up the isr based on the irq line. */ + ret = request_irq( + Device->irqLines[gcvCORE_VG], isrRoutineVG, 0, + "galcore interrupt service for 2D", Device + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not register irq line %d (error=%d)\n", + __FUNCTION__, __LINE__, + Device->irqLines[gcvCORE_VG], ret + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + /* Mark ISR as initialized. */ + Device->isrInitializeds[gcvCORE_VG] = gcvTRUE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckGALDEVICE_Release_ISR +** +** Release the irq line. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** Nothing. +*/ +gceSTATUS +gckGALDEVICE_Release_ISR( + IN gckGALDEVICE Device + ) +{ + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + +#if gcdMULTI_GPU + /* release the irq */ + if (Device->isrInitialized3D[gcvCORE_3D_0_ID]) + { + free_irq(Device->irqLine3D[gcvCORE_3D_0_ID], Device); + Device->isrInitialized3D[gcvCORE_3D_0_ID] = gcvFALSE; + } +#if gcdMULTI_GPU > 1 + /* release the irq */ + if (Device->isrInitialized3D[gcvCORE_3D_1_ID]) + { + free_irq(Device->irqLine3D[gcvCORE_3D_1_ID], Device); + Device->isrInitialized3D[gcvCORE_3D_1_ID] = gcvFALSE; + } +#endif +#else + /* release the irq */ + if (Device->isrInitializeds[gcvCORE_MAJOR]) + { + free_irq(Device->irqLines[gcvCORE_MAJOR], Device); + Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE; + } +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckGALDEVICE_Release_ISR_2D( + IN gckGALDEVICE Device + ) +{ + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + /* release the irq */ + if (Device->isrInitializeds[gcvCORE_2D]) + { + free_irq(Device->irqLines[gcvCORE_2D], Device); + + Device->isrInitializeds[gcvCORE_2D] = gcvFALSE; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckGALDEVICE_Release_ISR_VG( + IN gckGALDEVICE Device + ) +{ + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + /* release the irq */ + if (Device->isrInitializeds[gcvCORE_VG]) + { + free_irq(Device->irqLines[gcvCORE_VG], Device); + Device->isrInitializeds[gcvCORE_VG] = gcvFALSE; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckGALDEVICE_Start_Threads +** +** Start the daemon threads. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** gcvSTATUS_OK +** Start successfully. +** gcvSTATUS_GENERIC_IO +** Start failed. +*/ +gceSTATUS +gckGALDEVICE_Start_Threads( + IN gckGALDEVICE Device + ) +{ + gceSTATUS status; + struct task_struct * task; + + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + +#if gcdMULTI_GPU + if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) + { + /* Start the kernel thread. */ + task = kthread_run(threadRoutine3D0, Device, "galcore_3d_0"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxt3D[gcvCORE_3D_0_ID] = task; + Device->threadInitialized3D[gcvCORE_3D_0_ID] = gcvTRUE; + +#if gcdMULTI_GPU > 1 + /* Start the kernel thread. */ + task = kthread_run(threadRoutine3D1, Device, "galcore_3d_1"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxt3D[gcvCORE_3D_1_ID] = task; + Device->threadInitialized3D[gcvCORE_3D_1_ID] = gcvTRUE; +#endif + } +#elif gcdMULTI_GPU_AFFINITY + if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) + { + /* Start the kernel thread. */ + task = kthread_run(threadRoutine3D0, Device, "galcore_3d_0"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxts[gcvCORE_MAJOR] = task; + Device->threadInitializeds[gcvCORE_MAJOR] = gcvTRUE; + } + + if (Device->kernels[gcvCORE_OCL] != gcvNULL) + { + /* Start the kernel thread. */ + task = kthread_run(threadRoutine3D1, Device, "galcore_3d_1"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxts[gcvCORE_OCL] = task; + Device->threadInitializeds[gcvCORE_OCL] = gcvTRUE; + } +#else + if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) + { + /* Start the kernel thread. */ + task = kthread_run(threadRoutine, Device, "galcore daemon thread"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxts[gcvCORE_MAJOR] = task; + Device->threadInitializeds[gcvCORE_MAJOR] = gcvTRUE; + } +#endif + + if (Device->kernels[gcvCORE_2D] != gcvNULL) + { + /* Start the kernel thread. */ + task = kthread_run(threadRoutine2D, Device, "galcore daemon thread for 2D"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxts[gcvCORE_2D] = task; + Device->threadInitializeds[gcvCORE_2D] = gcvTRUE; + } + else + { + Device->threadInitializeds[gcvCORE_2D] = gcvFALSE; + } + + if (Device->kernels[gcvCORE_VG] != gcvNULL) + { + /* Start the kernel thread. */ + task = kthread_run(threadRoutineVG, Device, "galcore daemon thread for VG"); + + if (IS_ERR(task)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not start the kernel thread.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + Device->threadCtxts[gcvCORE_VG] = task; + Device->threadInitializeds[gcvCORE_VG] = gcvTRUE; + } + else + { + Device->threadInitializeds[gcvCORE_VG] = gcvFALSE; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckGALDEVICE_Stop_Threads +** +** Stop the gal device, including the following actions: stop the daemon +** thread, release the irq. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** Nothing. +*/ +gceSTATUS +gckGALDEVICE_Stop_Threads( + gckGALDEVICE Device + ) +{ + gctINT i; +#if gcdMULTI_GPU + gctINT j; +#endif + + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { +#if gcdMULTI_GPU + if (i == gcvCORE_MAJOR) + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + /* Stop the kernel threads. */ + if (Device->threadInitialized3D[j]) + { + Device->killThread = gcvTRUE; + Device->dataReady3D[j] = gcvTRUE; + wake_up_interruptible(&Device->intrWaitQueue3D[j]); + + kthread_stop(Device->threadCtxt3D[j]); + Device->threadCtxt3D[j] = gcvNULL; + Device->threadInitialized3D[j] = gcvFALSE; + } + } + } + else +#endif + { + /* Stop the kernel threads. */ + if (Device->threadInitializeds[i]) + { + Device->killThread = gcvTRUE; + up(&Device->semas[i]); + + kthread_stop(Device->threadCtxts[i]); + Device->threadCtxts[i] = gcvNULL; + Device->threadInitializeds[i] = gcvFALSE; + } + } + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckGALDEVICE_Start +** +** Start the gal device, including the following actions: setup the isr routine +** and start the daemoni thread. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** gcvSTATUS_OK +** Start successfully. +*/ +gceSTATUS +gckGALDEVICE_Start( + IN gckGALDEVICE Device + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Device=0x%x", Device); + + /* Start the kernel thread. */ + gcmkONERROR(gckGALDEVICE_Start_Threads(Device)); + + if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) + { + /* Setup the ISR routine. */ + gcmkONERROR(gckGALDEVICE_Setup_ISR(Device)); + + /* Switch to SUSPEND power state. */ + gcmkONERROR(gckHARDWARE_SetPowerManagementState( + Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF_BROADCAST + )); + } + + if (Device->kernels[gcvCORE_2D] != gcvNULL) + { + /* Setup the ISR routine. */ + gcmkONERROR(gckGALDEVICE_Setup_ISR_2D(Device)); + + /* Switch to SUSPEND power state. */ + gcmkONERROR(gckHARDWARE_SetPowerManagementState( + Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF_BROADCAST + )); + } + + if (Device->kernels[gcvCORE_VG] != gcvNULL) + { + /* Setup the ISR routine. */ + gcmkONERROR(gckGALDEVICE_Setup_ISR_VG(Device)); + +#if gcdENABLE_VG + /* Switch to SUSPEND power state. */ + gcmkONERROR(gckVGHARDWARE_SetPowerManagementState( + Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF_BROADCAST + )); +#endif + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckGALDEVICE_Stop +** +** Stop the gal device, including the following actions: stop the daemon +** thread, release the irq. +** +** INPUT: +** +** gckGALDEVICE Device +** Pointer to an gckGALDEVICE object. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** Nothing. +*/ +gceSTATUS +gckGALDEVICE_Stop( + gckGALDEVICE Device + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Device=0x%x", Device); + + gcmkVERIFY_ARGUMENT(Device != NULL); + + if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) + { + /* Switch to OFF power state. */ + gcmkONERROR(gckHARDWARE_SetPowerManagementState( + Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF + )); + + /* Remove the ISR routine. */ + gcmkONERROR(gckGALDEVICE_Release_ISR(Device)); + } + + if (Device->kernels[gcvCORE_2D] != gcvNULL) + { + /* Setup the ISR routine. */ + gcmkONERROR(gckGALDEVICE_Release_ISR_2D(Device)); + + /* Switch to OFF power state. */ + gcmkONERROR(gckHARDWARE_SetPowerManagementState( + Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF + )); + } + + if (Device->kernels[gcvCORE_VG] != gcvNULL) + { + /* Setup the ISR routine. */ + gcmkONERROR(gckGALDEVICE_Release_ISR_VG(Device)); + +#if gcdENABLE_VG + /* Switch to OFF power state. */ + gcmkONERROR(gckVGHARDWARE_SetPowerManagementState( + Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF + )); +#endif + } + + /* Stop the kernel thread. */ + gcmkONERROR(gckGALDEVICE_Stop_Threads(Device)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.h b/drivers/gpu/galcore/gc_hal_kernel_device.h new file mode 100644 index 00000000000000..08900158497704 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_device.h @@ -0,0 +1,215 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_device_h_ +#define __gc_hal_kernel_device_h_ + +#include "gc_hal_kernel_debugfs.h" + +/******************************************************************************\ +******************************* gckGALDEVICE Structure ******************************* +\******************************************************************************/ + +typedef struct _gckGALDEVICE +{ + /* Objects. */ + gckOS os; + gckKERNEL kernels[gcdMAX_GPU_COUNT]; + + gcsPLATFORM* platform; + + /* Attributes. */ + gctSIZE_T internalSize; + gctPHYS_ADDR internalPhysical; + gctUINT32 internalPhysicalName; + gctPOINTER internalLogical; + gckVIDMEM internalVidMem; + gctSIZE_T externalSize; + gctPHYS_ADDR externalPhysical; + gctUINT32 externalPhysicalName; + gctPOINTER externalLogical; + gckVIDMEM externalVidMem; + gckVIDMEM contiguousVidMem; + gctPOINTER contiguousBase; + gctPHYS_ADDR contiguousPhysical; + gctUINT32 contiguousPhysicalName; + gctSIZE_T contiguousSize; + gctBOOL contiguousMapped; + gctPOINTER contiguousMappedUser; + gctBOOL contiguousRequested; + gctSIZE_T systemMemorySize; + gctUINT32 systemMemoryBaseAddress; +#if gcdMULTI_GPU + gctPOINTER registerBase3D[gcdMULTI_GPU]; + gctSIZE_T registerSize3D[gcdMULTI_GPU]; +#endif + gctPOINTER registerBases[gcdMAX_GPU_COUNT]; + gctSIZE_T registerSizes[gcdMAX_GPU_COUNT]; + gctUINT32 baseAddress; + gctUINT32 physBase; + gctUINT32 physSize; + gctBOOL mmu; +#if gcdMULTI_GPU + gctUINT32 requestedRegisterMemBase3D[gcdMULTI_GPU]; + gctSIZE_T requestedRegisterMemSize3D[gcdMULTI_GPU]; +#endif + gctUINT32 requestedRegisterMemBases[gcdMAX_GPU_COUNT]; + gctSIZE_T requestedRegisterMemSizes[gcdMAX_GPU_COUNT]; + gctUINT32 requestedContiguousBase; + gctSIZE_T requestedContiguousSize; + + /* IRQ management. */ +#if gcdMULTI_GPU + gctINT irqLine3D[gcdMULTI_GPU]; + gctBOOL isrInitialized3D[gcdMULTI_GPU]; + gctBOOL dataReady3D[gcdMULTI_GPU]; +#endif + gctINT irqLines[gcdMAX_GPU_COUNT]; + gctBOOL isrInitializeds[gcdMAX_GPU_COUNT]; + + /* Thread management. */ +#if gcdMULTI_GPU + struct task_struct *threadCtxt3D[gcdMULTI_GPU]; + wait_queue_head_t intrWaitQueue3D[gcdMULTI_GPU]; + gctBOOL threadInitialized3D[gcdMULTI_GPU]; +#endif + struct task_struct *threadCtxts[gcdMAX_GPU_COUNT]; + struct semaphore semas[gcdMAX_GPU_COUNT]; + gctBOOL threadInitializeds[gcdMAX_GPU_COUNT]; + gctBOOL killThread; + + /* Signal management. */ + gctINT signal; + + /* Core mapping */ + gceCORE coreMapping[8]; + + /* States before suspend. */ + gceCHIPPOWERSTATE statesStored[gcdMAX_GPU_COUNT]; + + /* Device Debug File System Entry in kernel. */ + struct _gcsDEBUGFS_Node * dbgNode; + + gcsDEBUGFS_DIR debugfsDir; +} +* gckGALDEVICE; + +typedef struct _gcsHAL_PRIVATE_DATA +{ + gckGALDEVICE device; + gctPOINTER mappedMemory; + gctPOINTER contiguousLogical; + /* The process opening the device may not be the same as the one that closes it. */ + gctUINT32 pidOpen; +} +gcsHAL_PRIVATE_DATA, * gcsHAL_PRIVATE_DATA_PTR; + +typedef struct _gcsDEVICE_CONSTRUCT_ARGS +{ + gctBOOL recovery; + gctUINT stuckDump; + gctUINT gpu3DMinClock; + + gctBOOL contiguousRequested; + gcsPLATFORM* platform; + gctBOOL mmu; +} +gcsDEVICE_CONSTRUCT_ARGS; + +gceSTATUS gckGALDEVICE_Setup_ISR( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Setup_ISR_2D( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Setup_ISR_VG( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Release_ISR( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Release_ISR_2D( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Release_ISR_VG( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Start_Threads( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Stop_Threads( + gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Start( + IN gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Stop( + gckGALDEVICE Device + ); + +gceSTATUS gckGALDEVICE_Construct( +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + IN gctINT IrqLine3D0, + IN gctUINT32 RegisterMemBase3D0, + IN gctSIZE_T RegisterMemSize3D0, + IN gctINT IrqLine3D1, + IN gctUINT32 RegisterMemBase3D1, + IN gctSIZE_T RegisterMemSize3D1, +#else + IN gctINT IrqLine, + IN gctUINT32 RegisterMemBase, + IN gctSIZE_T RegisterMemSize, +#endif + IN gctINT IrqLine2D, + IN gctUINT32 RegisterMemBase2D, + IN gctSIZE_T RegisterMemSize2D, + IN gctINT IrqLineVG, + IN gctUINT32 RegisterMemBaseVG, + IN gctSIZE_T RegisterMemSizeVG, + IN gctUINT32 ContiguousBase, + IN gctSIZE_T ContiguousSize, + IN gctSIZE_T BankSize, + IN gctINT FastClear, + IN gctINT Compression, + IN gctUINT32 PhysBaseAddr, + IN gctUINT32 PhysSize, + IN gctINT Signal, + IN gctUINT LogFileSize, + IN gctINT PowerManagement, + IN gctINT GpuProfiler, + IN gcsDEVICE_CONSTRUCT_ARGS * Args, + OUT gckGALDEVICE *Device + ); + +gceSTATUS gckGALDEVICE_Destroy( + IN gckGALDEVICE Device + ); + +#endif /* __gc_hal_kernel_device_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c new file mode 100644 index 00000000000000..f160ae9c7e729e --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -0,0 +1,3459 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" +#include "gc_hal_kernel_buffer.h" + +#ifdef __QNXNTO__ +#include +#include "gc_hal_kernel_qnx.h" +#endif + +#define _GC_OBJ_ZONE gcvZONE_EVENT + +#define gcdEVENT_ALLOCATION_COUNT (4096 / gcmSIZEOF(gcsHAL_INTERFACE)) +#define gcdEVENT_MIN_THRESHOLD 4 + +/******************************************************************************\ +********************************* Support Code ********************************* +\******************************************************************************/ + +static gceSTATUS +gckEVENT_AllocateQueue( + IN gckEVENT Event, + OUT gcsEVENT_QUEUE_PTR * Queue + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Event=0x%x", Event); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Queue != gcvNULL); + + /* Do we have free queues? */ + if (Event->freeList == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Move one free queue from the free list. */ + * Queue = Event->freeList; + Event->freeList = Event->freeList->next; + + /* Success. */ + gcmkFOOTER_ARG("*Queue=0x%x", gcmOPT_POINTER(Queue)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +static gceSTATUS +gckEVENT_FreeQueue( + IN gckEVENT Event, + OUT gcsEVENT_QUEUE_PTR Queue + ) +{ + gceSTATUS status = gcvSTATUS_OK; + + gcmkHEADER_ARG("Event=0x%x", Event); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Queue != gcvNULL); + + /* Move one free queue from the free list. */ + Queue->next = Event->freeList; + Event->freeList = Queue; + + /* Success. */ + gcmkFOOTER(); + return status; +} + +static gceSTATUS +gckEVENT_FreeRecord( + IN gckEVENT Event, + IN gcsEVENT_PTR Record + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Record != gcvNULL); + + /* Acquire the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, + Event->freeEventMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Push the record on the free list. */ + Record->next = Event->freeEventList; + Event->freeEventList = Record; + Event->freeEventCount += 1; + + /* Release the mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return gcvSTATUS_OK; +} + +static gceSTATUS +gckEVENT_IsEmpty( + IN gckEVENT Event, + OUT gctBOOL_PTR IsEmpty + ) +{ + gceSTATUS status; + gctSIZE_T i; + + gcmkHEADER_ARG("Event=0x%x", Event); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(IsEmpty != gcvNULL); + + /* Assume the event queue is empty. */ + *IsEmpty = gcvTRUE; + + /* Walk the event queue. */ + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + /* Check whether this event is in use. */ + if (Event->queues[i].head != gcvNULL) + { + /* The event is in use, hence the queue is not empty. */ + *IsEmpty = gcvFALSE; + break; + } + } + + /* Try acquiring the mutex. */ + status = gckOS_AcquireMutex(Event->os, Event->eventQueueMutex, 0); + if (status == gcvSTATUS_TIMEOUT) + { + /* Timeout - queue is no longer empty. */ + *IsEmpty = gcvFALSE; + } + else + { + /* Bail out on error. */ + gcmkONERROR(status); + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + } + + /* Success. */ + gcmkFOOTER_ARG("*IsEmpty=%d", gcmOPT_VALUE(IsEmpty)); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +static gceSTATUS +_TryToIdleGPU( + IN gckEVENT Event +) +{ + gceSTATUS status; + gctBOOL empty = gcvFALSE, idle = gcvFALSE; + gctBOOL powerLocked = gcvFALSE; + gckHARDWARE hardware; + + gcmkHEADER_ARG("Event=0x%x", Event); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + /* Grab gckHARDWARE object. */ + hardware = Event->kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + /* Check whether the event queue is empty. */ + gcmkONERROR(gckEVENT_IsEmpty(Event, &empty)); + + if (empty) + { + status = gckOS_AcquireMutex(hardware->os, hardware->powerMutex, 0); + if (status == gcvSTATUS_TIMEOUT) + { + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + powerLocked = gcvTRUE; + + /* Query whether the hardware is idle. */ + gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle)); + + gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex)); + powerLocked = gcvFALSE; + + if (idle) + { + /* Inform the system of idle GPU. */ + gcmkONERROR(gckOS_Broadcast(Event->os, + Event->kernel->hardware, + gcvBROADCAST_GPU_IDLE)); + } + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (powerLocked) + { + gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex)); + } + + gcmkFOOTER(); + return status; +} + +static gceSTATUS +__RemoveRecordFromProcessDB( + IN gckEVENT Event, + IN gcsEVENT_PTR Record + ) +{ + gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record); + gcmkVERIFY_ARGUMENT(Record != gcvNULL); + + while (Record != gcvNULL) + { + if (Record->info.command == gcvHAL_SIGNAL) + { + /* TODO: Find a better place to bind signal to hardware.*/ + gcmkVERIFY_OK(gckOS_SignalSetHardware(Event->os, + gcmUINT64_TO_PTR(Record->info.u.Signal.signal), + Event->kernel->hardware)); + } + + if (Record->fromKernel) + { + /* No need to check db if event is from kernel. */ + Record = Record->next; + continue; + } + + switch (Record->info.command) + { + case gcvHAL_FREE_NON_PAGED_MEMORY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Event->kernel, + Record->processID, + gcvDB_NON_PAGED, + gcmUINT64_TO_PTR(Record->info.u.FreeNonPagedMemory.logical))); + break; + + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Event->kernel, + Record->processID, + gcvDB_CONTIGUOUS, + gcmUINT64_TO_PTR(Record->info.u.FreeContiguousMemory.logical))); + break; + + case gcvHAL_UNLOCK_VIDEO_MEMORY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Event->kernel, + Record->processID, + gcvDB_VIDEO_MEMORY_LOCKED, + gcmUINT64_TO_PTR(Record->info.u.UnlockVideoMemory.node))); + break; + + case gcvHAL_UNMAP_USER_MEMORY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Event->kernel, + Record->processID, + gcvDB_MAP_USER_MEMORY, + gcmINT2PTR(Record->info.u.UnmapUserMemory.info))); + break; + + case gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Event->kernel, + Record->processID, + gcvDB_COMMAND_BUFFER, + gcmUINT64_TO_PTR(Record->info.u.FreeVirtualCommandBuffer.logical))); + break; + + default: + break; + } + + Record = Record->next; + } + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +_ReleaseVideoMemoryHandle( + IN gckKERNEL Kernel, + IN OUT gcsEVENT_PTR Record, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gckVIDMEM_NODE nodeObject; + gctUINT32 handle; + + switch(Interface->command) + { + case gcvHAL_UNLOCK_VIDEO_MEMORY: + handle = (gctUINT32)Interface->u.UnlockVideoMemory.node; + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup( + Kernel, Record->processID, handle, &nodeObject)); + + Record->info.u.UnlockVideoMemory.node = gcmPTR_TO_UINT64(nodeObject); + + gckVIDMEM_HANDLE_Dereference(Kernel, Record->processID, handle); + break; + + default: + break; + } + + return gcvSTATUS_OK; +OnError: + return status; +} + +/******************************************************************************* +** +** _QueryFlush +** +** Check the type of surfaces which will be released by current event and +** determine the cache needed to flush. +** +*/ +static gceSTATUS +_QueryFlush( + IN gckEVENT Event, + IN gcsEVENT_PTR Record, + OUT gceKERNEL_FLUSH *Flush + ) +{ + gceKERNEL_FLUSH flush = 0; + gcmkHEADER_ARG("Event=0x%x Record=0x%x", Event, Record); + gcmkVERIFY_ARGUMENT(Record != gcvNULL); + + while (Record != gcvNULL) + { + switch (Record->info.command) + { + case gcvHAL_UNLOCK_VIDEO_MEMORY: + switch(Record->info.u.UnlockVideoMemory.type) + { + case gcvSURF_TILE_STATUS: + flush |= gcvFLUSH_TILE_STATUS; + break; + case gcvSURF_RENDER_TARGET: + flush |= gcvFLUSH_COLOR; + break; + case gcvSURF_DEPTH: + flush |= gcvFLUSH_DEPTH; + break; + case gcvSURF_TEXTURE: + flush |= gcvFLUSH_TEXTURE; + break; + case gcvSURF_TYPE_UNKNOWN: + gcmkASSERT(0); + break; + default: + break; + } + break; + case gcvHAL_UNMAP_USER_MEMORY: + *Flush = gcvFLUSH_ALL; + return gcvSTATUS_OK; + + default: + break; + } + + Record = Record->next; + } + + *Flush = flush; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +void +_SubmitTimerFunction( + gctPOINTER Data + ) +{ + gckEVENT event = (gckEVENT)Data; +#if gcdMULTI_GPU + gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE, gcvCORE_3D_ALL_MASK)); +#else + gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE)); +#endif +} + +/******************************************************************************\ +******************************* gckEVENT API Code ******************************* +\******************************************************************************/ + +/******************************************************************************* +** +** gckEVENT_Construct +** +** Construct a new gckEVENT object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** OUTPUT: +** +** gckEVENT * Event +** Pointer to a variable that receives the gckEVENT object pointer. +*/ +gceSTATUS +gckEVENT_Construct( + IN gckKERNEL Kernel, + OUT gckEVENT * Event + ) +{ + gckOS os; + gceSTATUS status; + gckEVENT eventObj = gcvNULL; + int i; + gcsEVENT_PTR record; + gctPOINTER pointer = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Event != gcvNULL); + + /* Extract the pointer to the gckOS object. */ + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Allocate the gckEVENT object. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckEVENT), &pointer)); + + eventObj = pointer; + + /* Reset the object. */ + gcmkVERIFY_OK(gckOS_ZeroMemory(eventObj, gcmSIZEOF(struct _gckEVENT))); + + /* Initialize the gckEVENT object. */ + eventObj->object.type = gcvOBJ_EVENT; + eventObj->kernel = Kernel; + eventObj->os = os; + + /* Create the mutexes. */ + gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventQueueMutex)); + gcmkONERROR(gckOS_CreateMutex(os, &eventObj->freeEventMutex)); + gcmkONERROR(gckOS_CreateMutex(os, &eventObj->eventListMutex)); + + /* Create a bunch of event reccords. */ + for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1) + { + /* Allocate an event record. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsEVENT), &pointer)); + + record = pointer; + + /* Push it on the free list. */ + record->next = eventObj->freeEventList; + eventObj->freeEventList = record; + eventObj->freeEventCount += 1; + } + + /* Initialize the free list of event queues. */ + for (i = 0; i < gcdREPO_LIST_COUNT; i += 1) + { + eventObj->repoList[i].next = eventObj->freeList; + eventObj->freeList = &eventObj->repoList[i]; + } + + /* Construct the atom. */ + gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->freeAtom)); + gcmkONERROR(gckOS_AtomSet(os, + eventObj->freeAtom, + gcmCOUNTOF(eventObj->queues))); + +#if gcdSMP + gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending)); + +#if gcdMULTI_GPU + for (i = 0; i < gcdMULTI_GPU; i++) + { + gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending3D[i])); + gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending3DMask[i])); + } + + gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pendingMask)); +#endif + +#endif + + gcmkVERIFY_OK(gckOS_CreateTimer(os, + _SubmitTimerFunction, + (gctPOINTER)eventObj, + &eventObj->submitTimer)); + +#if gcdINTERRUPT_STATISTIC + gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->interruptCount)); + gcmkONERROR(gckOS_AtomSet(os,eventObj->interruptCount, 0)); +#endif + + /* Return pointer to the gckEVENT object. */ + *Event = eventObj; + + /* Success. */ + gcmkFOOTER_ARG("*Event=0x%x", *Event); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (eventObj != gcvNULL) + { + if (eventObj->eventQueueMutex != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventQueueMutex)); + } + + if (eventObj->freeEventMutex != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->freeEventMutex)); + } + + if (eventObj->eventListMutex != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, eventObj->eventListMutex)); + } + + while (eventObj->freeEventList != gcvNULL) + { + record = eventObj->freeEventList; + eventObj->freeEventList = record->next; + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, record)); + } + + if (eventObj->freeAtom != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->freeAtom)); + } + +#if gcdSMP + if (eventObj->pending != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending)); + } + +#if gcdMULTI_GPU + for (i = 0; i < gcdMULTI_GPU; i++) + { + if (eventObj->pending3D[i] != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending3D[i])); + } + + if (eventObj->pending3DMask[i] != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending3DMask[i])); + } + } +#endif +#endif + +#if gcdINTERRUPT_STATISTIC + if (eventObj->interruptCount) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->interruptCount)); + } +#endif + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, eventObj)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_Destroy +** +** Destroy an gckEVENT object. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Destroy( + IN gckEVENT Event + ) +{ + gcsEVENT_PTR record; + gcsEVENT_QUEUE_PTR queue; + + gcmkHEADER_ARG("Event=0x%x", Event); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + if (Event->submitTimer != gcvNULL) + { + gcmkVERIFY_OK(gckOS_StopTimer(Event->os, Event->submitTimer)); + gcmkVERIFY_OK(gckOS_DestroyTimer(Event->os, Event->submitTimer)); + } + + /* Delete the queue mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventQueueMutex)); + + /* Free all free events. */ + while (Event->freeEventList != gcvNULL) + { + record = Event->freeEventList; + Event->freeEventList = record->next; + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record)); + } + + /* Delete the free mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->freeEventMutex)); + + /* Free all pending queues. */ + while (Event->queueHead != gcvNULL) + { + /* Get the current queue. */ + queue = Event->queueHead; + + /* Free all pending events. */ + while (queue->head != gcvNULL) + { + record = queue->head; + queue->head = record->next; + + gcmkTRACE_ZONE_N( + gcvLEVEL_WARNING, gcvZONE_EVENT, + gcmSIZEOF(record) + gcmSIZEOF(queue->source), + "Event record 0x%x is still pending for %d.", + record, queue->source + ); + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, record)); + } + + /* Remove the top queue from the list. */ + if (Event->queueHead == Event->queueTail) + { + Event->queueHead = + Event->queueTail = gcvNULL; + } + else + { + Event->queueHead = Event->queueHead->next; + } + + /* Free the queue. */ + gcmkVERIFY_OK(gckEVENT_FreeQueue(Event, queue)); + } + + /* Delete the list mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Event->os, Event->eventListMutex)); + + /* Delete the atom. */ + gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->freeAtom)); + +#if gcdSMP + gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending)); + +#if gcdMULTI_GPU + { + gctINT i; + for (i = 0; i < gcdMULTI_GPU; i++) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending3D[i])); + gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending3DMask[i])); + } + } +#endif +#endif + +#if gcdINTERRUPT_STATISTIC + gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->interruptCount)); +#endif + + /* Mark the gckEVENT object as unknown. */ + Event->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckEVENT object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Event->os, Event)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckEVENT_GetEvent +** +** Reserve the next available hardware event. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctBOOL Wait +** Set to gcvTRUE to force the function to wait if no events are +** immediately available. +** +** gceKERNEL_WHERE Source +** Source of the event. +** +** OUTPUT: +** +** gctUINT8 * EventID +** Reserved event ID. +*/ +#define gcdINVALID_EVENT_PTR ((gcsEVENT_PTR)gcvMAXUINTPTR_T) + +#if gcdMULTI_GPU +gceSTATUS +gckEVENT_GetEvent( + IN gckEVENT Event, + IN gctBOOL Wait, + OUT gctUINT8 * EventID, + IN gceKERNEL_WHERE Source, + IN gceCORE_3D_MASK ChipEnable + ) +#else +gceSTATUS +gckEVENT_GetEvent( + IN gckEVENT Event, + IN gctBOOL Wait, + OUT gctUINT8 * EventID, + IN gceKERNEL_WHERE Source + ) +#endif +{ + gctINT i, id; + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gctINT32 free; +#if gcdMULTI_GPU + gctINT j; +#endif + + gcmkHEADER_ARG("Event=0x%x Source=%d", Event, Source); + + while (gcvTRUE) + { + /* Grab the queue mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, + Event->eventQueueMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Walk through all events. */ + id = Event->lastID; + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + gctINT nextID = gckMATH_ModuloInt((id + 1), + gcmCOUNTOF(Event->queues)); + + if (Event->queues[id].head == gcvNULL) + { + *EventID = (gctUINT8) id; + + Event->lastID = (gctUINT8) nextID; + + /* Save time stamp of event. */ + Event->queues[id].head = gcdINVALID_EVENT_PTR; + Event->queues[id].stamp = ++(Event->stamp); + Event->queues[id].source = Source; + +#if gcdMULTI_GPU + Event->queues[id].chipEnable = ChipEnable; + + if (ChipEnable == gcvCORE_3D_ALL_MASK) + { + gckOS_AtomSetMask(Event->pendingMask, (1 << id)); + + for (j = 0; j < gcdMULTI_GPU; j++) + { + gckOS_AtomSetMask(Event->pending3DMask[j], (1 << id)); + } + } + else + { + for (j = 0; j < gcdMULTI_GPU; j++) + { + if (ChipEnable & (1 << j)) + { + gckOS_AtomSetMask(Event->pending3DMask[j], (1 << id)); + } + } + } +#endif + + gcmkONERROR(gckOS_AtomDecrement(Event->os, + Event->freeAtom, + &free)); +#if gcdDYNAMIC_SPEED + if (free <= gcdDYNAMIC_EVENT_THRESHOLD) + { + gcmkONERROR(gckOS_BroadcastHurry( + Event->os, + Event->kernel->hardware, + gcdDYNAMIC_EVENT_THRESHOLD - free)); + } +#endif + + /* Release the queue mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, + Event->eventQueueMutex)); + + /* Success. */ + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_EVENT, + gcmSIZEOF(id), + "Using id=%d", + id + ); + + gcmkFOOTER_ARG("*EventID=%u", *EventID); + return gcvSTATUS_OK; + } + + id = nextID; + } + +#if gcdDYNAMIC_SPEED + /* No free events, speed up the GPU right now! */ + gcmkONERROR(gckOS_BroadcastHurry(Event->os, + Event->kernel->hardware, + gcdDYNAMIC_EVENT_THRESHOLD)); +#endif + + /* Release the queue mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + acquired = gcvFALSE; + + /* Fail if wait is not requested. */ + if (!Wait) + { + /* Out of resources. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Delay a while. */ + gcmkONERROR(gckOS_Delay(Event->os, 1)); + } + +OnError: + if (acquired) + { + /* Release the queue mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_AllocateRecord +** +** Allocate a record for the new event. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctBOOL AllocateAllowed +** State for allocation if out of free events. +** +** OUTPUT: +** +** gcsEVENT_PTR * Record +** Allocated event record. +*/ +gceSTATUS +gckEVENT_AllocateRecord( + IN gckEVENT Event, + IN gctBOOL AllocateAllowed, + OUT gcsEVENT_PTR * Record + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gctINT i; + gcsEVENT_PTR record; + gctPOINTER pointer = gcvNULL; + + gcmkHEADER_ARG("Event=0x%x AllocateAllowed=%d", Event, AllocateAllowed); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Record != gcvNULL); + + /* Acquire the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->freeEventMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Test if we are below the allocation threshold. */ + if ( (AllocateAllowed && (Event->freeEventCount < gcdEVENT_MIN_THRESHOLD)) || + (Event->freeEventCount == 0) ) + { + /* Allocate a bunch of records. */ + for (i = 0; i < gcdEVENT_ALLOCATION_COUNT; i += 1) + { + /* Allocate an event record. */ + gcmkONERROR(gckOS_Allocate(Event->os, + gcmSIZEOF(gcsEVENT), + &pointer)); + + record = pointer; + + /* Push it on the free list. */ + record->next = Event->freeEventList; + Event->freeEventList = record; + Event->freeEventCount += 1; + } + } + + *Record = Event->freeEventList; + Event->freeEventList = Event->freeEventList->next; + Event->freeEventCount -= 1; + + /* Release the mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex)); + + /* Success. */ + gcmkFOOTER_ARG("*Record=0x%x", gcmOPT_POINTER(Record)); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->freeEventMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_AddList +** +** Add a new event to the list of events. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gcsHAL_INTERFACE_PTR Interface +** Pointer to the interface for the event to be added. +** +** gceKERNEL_WHERE FromWhere +** Place in the pipe where the event needs to be generated. +** +** gctBOOL AllocateAllowed +** State for allocation if out of free events. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_AddList( + IN gckEVENT Event, + IN gcsHAL_INTERFACE_PTR Interface, + IN gceKERNEL_WHERE FromWhere, + IN gctBOOL AllocateAllowed, + IN gctBOOL FromKernel + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gcsEVENT_PTR record = gcvNULL; + gcsEVENT_QUEUE_PTR queue; + gckVIRTUAL_COMMAND_BUFFER_PTR buffer; + gckKERNEL kernel = Event->kernel; + + gcmkHEADER_ARG("Event=0x%x Interface=0x%x", + Event, Interface); + + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, _GC_OBJ_ZONE, + "FromWhere=%d AllocateAllowed=%d", + FromWhere, AllocateAllowed); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Interface != gcvNULL); + + /* Verify the event command. */ + gcmkASSERT + ( (Interface->command == gcvHAL_FREE_NON_PAGED_MEMORY) + || (Interface->command == gcvHAL_FREE_CONTIGUOUS_MEMORY) + || (Interface->command == gcvHAL_WRITE_DATA) + || (Interface->command == gcvHAL_UNLOCK_VIDEO_MEMORY) + || (Interface->command == gcvHAL_SIGNAL) + || (Interface->command == gcvHAL_UNMAP_USER_MEMORY) + || (Interface->command == gcvHAL_TIMESTAMP) + || (Interface->command == gcvHAL_COMMIT_DONE) + || (Interface->command == gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER) + || (Interface->command == gcvHAL_SYNC_POINT) + || (Interface->command == gcvHAL_DESTROY_MMU) + ); + + /* Validate the source. */ + if ((FromWhere != gcvKERNEL_COMMAND) && (FromWhere != gcvKERNEL_PIXEL)) + { + /* Invalid argument. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Allocate a free record. */ + gcmkONERROR(gckEVENT_AllocateRecord(Event, AllocateAllowed, &record)); + + /* Termninate the record. */ + record->next = gcvNULL; + + /* Record the committer. */ + record->fromKernel = FromKernel; + + /* Copy the event interface into the record. */ + gckOS_MemCopy(&record->info, Interface, gcmSIZEOF(record->info)); + + /* Get process ID. */ + gcmkONERROR(gckOS_GetProcessID(&record->processID)); + + gcmkONERROR(__RemoveRecordFromProcessDB(Event, record)); + + /* Handle is belonged to current process, it must be released now. */ + if (FromKernel == gcvFALSE) + { + status = _ReleaseVideoMemoryHandle(Event->kernel, record, Interface); + + if (gcmIS_ERROR(status)) + { + /* Ingore error because there are other events in the queue. */ + status = gcvSTATUS_OK; + goto OnError; + } + } + +#ifdef __QNXNTO__ + record->kernel = Event->kernel; +#endif + + /* Acquire the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->eventListMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Do we need to allocate a new queue? */ + if ((Event->queueTail == gcvNULL) || (Event->queueTail->source < FromWhere)) + { + /* Allocate a new queue. */ + gcmkONERROR(gckEVENT_AllocateQueue(Event, &queue)); + + /* Initialize the queue. */ + queue->source = FromWhere; + queue->head = gcvNULL; + queue->next = gcvNULL; + + /* Attach it to the list of allocated queues. */ + if (Event->queueTail == gcvNULL) + { + Event->queueHead = + Event->queueTail = queue; + } + else + { + Event->queueTail->next = queue; + Event->queueTail = queue; + } + } + else + { + queue = Event->queueTail; + } + + /* Attach the record to the queue. */ + if (queue->head == gcvNULL) + { + queue->head = record; + queue->tail = record; + } + else + { + queue->tail->next = record; + queue->tail = record; + } + + /* Unmap user space logical address. + * Linux kernel does not support unmap the memory of other process any more since 3.5. + * Let's unmap memory of self process before submit the event to gpu. + * */ + switch(Interface->command) + { + case gcvHAL_FREE_NON_PAGED_MEMORY: + gcmkONERROR(gckOS_UnmapUserLogical( + Event->os, + gcmNAME_TO_PTR(Interface->u.FreeNonPagedMemory.physical), + (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); + break; + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + gcmkONERROR(gckOS_UnmapUserLogical( + Event->os, + gcmNAME_TO_PTR(Interface->u.FreeContiguousMemory.physical), + (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical))); + break; + + case gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER: + buffer = (gckVIRTUAL_COMMAND_BUFFER_PTR)gcmNAME_TO_PTR(Interface->u.FreeVirtualCommandBuffer.physical); + if (buffer->userLogical) + { + gcmkONERROR(gckOS_DestroyUserVirtualMapping( + Event->os, + buffer->physical, + (gctSIZE_T) Interface->u.FreeVirtualCommandBuffer.bytes, + gcmUINT64_TO_PTR(Interface->u.FreeVirtualCommandBuffer.logical))); + } + break; + + default: + break; + } + + /* Release the mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex)); + } + + if (record != gcvNULL) + { + gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_Unlock +** +** Schedule an event to unlock virtual memory. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gceKERNEL_WHERE FromWhere +** Place in the pipe where the event needs to be generated. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a gcuVIDMEM_NODE union that specifies the virtual memory +** to unlock. +** +** gceSURF_TYPE Type +** Type of surface to unlock. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Unlock( + IN gckEVENT Event, + IN gceKERNEL_WHERE FromWhere, + IN gctPOINTER Node, + IN gceSURF_TYPE Type + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + + gcmkHEADER_ARG("Event=0x%x FromWhere=%d Node=0x%x Type=%d", + Event, FromWhere, Node, Type); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Node != gcvNULL); + + /* Mark the event as an unlock. */ + iface.command = gcvHAL_UNLOCK_VIDEO_MEMORY; + iface.u.UnlockVideoMemory.node = gcmPTR_TO_UINT64(Node); + iface.u.UnlockVideoMemory.type = Type; + iface.u.UnlockVideoMemory.asynchroneous = 0; + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_FreeNonPagedMemory +** +** Schedule an event to free non-paged memory. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctSIZE_T Bytes +** Number of bytes of non-paged memory to free. +** +** gctPHYS_ADDR Physical +** Physical address of non-paged memory to free. +** +** gctPOINTER Logical +** Logical address of non-paged memory to free. +** +** gceKERNEL_WHERE FromWhere +** Place in the pipe where the event needs to be generated. +*/ +gceSTATUS +gckEVENT_FreeNonPagedMemory( + IN gckEVENT Event, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gceKERNEL_WHERE FromWhere + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + gckKERNEL kernel = Event->kernel; + + gcmkHEADER_ARG("Event=0x%x Bytes=%lu Physical=0x%x Logical=0x%x " + "FromWhere=%d", + Event, Bytes, Physical, Logical, FromWhere); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + /* Create an event. */ + iface.command = gcvHAL_FREE_NON_PAGED_MEMORY; + iface.u.FreeNonPagedMemory.bytes = Bytes; + iface.u.FreeNonPagedMemory.physical = gcmPTR_TO_NAME(Physical); + iface.u.FreeNonPagedMemory.logical = gcmPTR_TO_UINT64(Logical); + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckEVENT_DestroyVirtualCommandBuffer( + IN gckEVENT Event, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gceKERNEL_WHERE FromWhere + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + gckKERNEL kernel = Event->kernel; + + gcmkHEADER_ARG("Event=0x%x Bytes=%lu Physical=0x%x Logical=0x%x " + "FromWhere=%d", + Event, Bytes, Physical, Logical, FromWhere); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + /* Create an event. */ + iface.command = gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER; + iface.u.FreeVirtualCommandBuffer.bytes = Bytes; + iface.u.FreeVirtualCommandBuffer.physical = gcmPTR_TO_NAME(Physical); + iface.u.FreeVirtualCommandBuffer.logical = gcmPTR_TO_UINT64(Logical); + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_FreeContigiuousMemory +** +** Schedule an event to free contiguous memory. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctSIZE_T Bytes +** Number of bytes of contiguous memory to free. +** +** gctPHYS_ADDR Physical +** Physical address of contiguous memory to free. +** +** gctPOINTER Logical +** Logical address of contiguous memory to free. +** +** gceKERNEL_WHERE FromWhere +** Place in the pipe where the event needs to be generated. +*/ +gceSTATUS +gckEVENT_FreeContiguousMemory( + IN gckEVENT Event, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gceKERNEL_WHERE FromWhere + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + gckKERNEL kernel = Event->kernel; + + gcmkHEADER_ARG("Event=0x%x Bytes=%lu Physical=0x%x Logical=0x%x " + "FromWhere=%d", + Event, Bytes, Physical, Logical, FromWhere); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + /* Create an event. */ + iface.command = gcvHAL_FREE_CONTIGUOUS_MEMORY; + iface.u.FreeContiguousMemory.bytes = Bytes; + iface.u.FreeContiguousMemory.physical = gcmPTR_TO_NAME(Physical); + iface.u.FreeContiguousMemory.logical = gcmPTR_TO_UINT64(Logical); + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_Signal +** +** Schedule an event to trigger a signal. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctSIGNAL Signal +** Pointer to the signal to trigger. +** +** gceKERNEL_WHERE FromWhere +** Place in the pipe where the event needs to be generated. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Signal( + IN gckEVENT Event, + IN gctSIGNAL Signal, + IN gceKERNEL_WHERE FromWhere + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + + gcmkHEADER_ARG("Event=0x%x Signal=0x%x FromWhere=%d", + Event, Signal, FromWhere); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + + /* Mark the event as a signal. */ + iface.command = gcvHAL_SIGNAL; + iface.u.Signal.signal = gcmPTR_TO_UINT64(Signal); +#ifdef __QNXNTO__ + iface.u.Signal.coid = 0; + iface.u.Signal.rcvid = 0; +#endif + iface.u.Signal.auxSignal = 0; + iface.u.Signal.process = 0; + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_CommitDone +** +** Schedule an event to wake up work thread when commit is done by GPU. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gceKERNEL_WHERE FromWhere +** Place in the pipe where the event needs to be generated. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_CommitDone( + IN gckEVENT Event, + IN gceKERNEL_WHERE FromWhere + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + + gcmkHEADER_ARG("Event=0x%x FromWhere=%d", Event, FromWhere); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + iface.command = gcvHAL_COMMIT_DONE; + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +gckEVENT_DestroyMmu( + IN gckEVENT Event, + IN gckMMU Mmu, + IN gceKERNEL_WHERE FromWhere + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + + gcmkHEADER_ARG("Event=0x%x FromWhere=%d", Event, FromWhere); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + iface.command = gcvHAL_DESTROY_MMU; + iface.u.DestroyMmu.mmu = gcmPTR_TO_UINT64(Mmu); + + /* Append it to the queue. */ + gcmkONERROR(gckEVENT_AddList(Event, &iface, FromWhere, gcvFALSE, gcvTRUE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckEVENT_Submit +** +** Submit the current event queue to the GPU. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctBOOL Wait +** Submit requires one vacant event; if Wait is set to not zero, +** and there are no vacant events at this time, the function will +** wait until an event becomes vacant so that submission of the +** queue is successful. +** +** gctBOOL FromPower +** Determines whether the call originates from inside the power +** management or not. +** +** OUTPUT: +** +** Nothing. +*/ +#if gcdMULTI_GPU +gceSTATUS +gckEVENT_Submit( + IN gckEVENT Event, + IN gctBOOL Wait, + IN gctBOOL FromPower, + IN gceCORE_3D_MASK ChipEnable + ) +#else +gceSTATUS +gckEVENT_Submit( + IN gckEVENT Event, + IN gctBOOL Wait, + IN gctBOOL FromPower + ) +#endif +{ + gceSTATUS status; + gctUINT8 id = 0xFF; + gcsEVENT_QUEUE_PTR queue; + gctBOOL acquired = gcvFALSE; + gckCOMMAND command = gcvNULL; + gctBOOL commitEntered = gcvFALSE; +#if !gcdNULL_DRIVER + gctUINT32 bytes; + gctPOINTER buffer; +#endif + +#if gcdMULTI_GPU + gctSIZE_T chipEnableBytes; +#endif + +#if gcdINTERRUPT_STATISTIC + gctINT32 oldValue; +#endif + +#if gcdSECURITY + gctPOINTER reservedBuffer; +#endif + + gctUINT32 flushBytes; + gctUINT32 executeBytes; + gckHARDWARE hardware; + + gceKERNEL_FLUSH flush = gcvFALSE; + + gcmkHEADER_ARG("Event=0x%x Wait=%d", Event, Wait); + + /* Get gckCOMMAND object. */ + command = Event->kernel->command; + hardware = Event->kernel->hardware; + + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + gckOS_GetTicks(&Event->lastCommitStamp); + + /* Are there event queues? */ + if (Event->queueHead != gcvNULL) + { + /* Acquire the command queue. */ + gcmkONERROR(gckCOMMAND_EnterCommit(command, FromPower)); + commitEntered = gcvTRUE; + + /* Process all queues. */ + while (Event->queueHead != gcvNULL) + { + /* Acquire the list mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, + Event->eventListMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Get the current queue. */ + queue = Event->queueHead; + + /* Allocate an event ID. */ +#if gcdMULTI_GPU + gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source, ChipEnable)); +#else + gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source)); +#endif + + /* Copy event list to event ID queue. */ + Event->queues[id].head = queue->head; + + /* Remove the top queue from the list. */ + if (Event->queueHead == Event->queueTail) + { + Event->queueHead = gcvNULL; + Event->queueTail = gcvNULL; + } + else + { + Event->queueHead = Event->queueHead->next; + } + + /* Free the queue. */ + gcmkONERROR(gckEVENT_FreeQueue(Event, queue)); + + /* Release the list mutex. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventListMutex)); + acquired = gcvFALSE; + + /* Determine cache needed to flush. */ + gcmkVERIFY_OK(_QueryFlush(Event, Event->queues[id].head, &flush)); + +#if gcdINTERRUPT_STATISTIC + gcmkVERIFY_OK(gckOS_AtomIncrement( + Event->os, + Event->interruptCount, + &oldValue + )); +#endif + +#if gcdNULL_DRIVER + /* Notify immediately on infinite hardware. */ + gcmkONERROR(gckEVENT_Interrupt(Event, 1 << id)); + + gcmkONERROR(gckEVENT_Notify(Event, 0)); +#else + /* Get the size of the hardware event. */ + gcmkONERROR(gckHARDWARE_Event( + hardware, + gcvNULL, + id, + Event->queues[id].source, + &bytes + )); + + /* Get the size of flush command. */ + gcmkONERROR(gckHARDWARE_Flush( + hardware, + flush, + gcvNULL, + &flushBytes + )); + + bytes += flushBytes; + +#if gcdMULTI_GPU + gcmkONERROR(gckHARDWARE_ChipEnable( + hardware, + gcvNULL, + 0, + &chipEnableBytes + )); + + bytes += chipEnableBytes * 2; +#endif + + /* Total bytes need to execute. */ + executeBytes = bytes; + + /* Reserve space in the command queue. */ + gcmkONERROR(gckCOMMAND_Reserve(command, bytes, &buffer, &bytes)); +#if gcdSECURITY + reservedBuffer = buffer; +#endif + +#if gcdMULTI_GPU + gcmkONERROR(gckHARDWARE_ChipEnable( + hardware, + buffer, + ChipEnable, + &chipEnableBytes + )); + + buffer = (gctUINT8_PTR)buffer + chipEnableBytes; +#endif + + /* Set the flush in the command queue. */ + gcmkONERROR(gckHARDWARE_Flush( + hardware, + flush, + buffer, + &flushBytes + )); + + /* Advance to next command. */ + buffer = (gctUINT8_PTR)buffer + flushBytes; + + /* Set the hardware event in the command queue. */ + gcmkONERROR(gckHARDWARE_Event( + hardware, + buffer, + id, + Event->queues[id].source, + &bytes + )); + + /* Advance to next command. */ + buffer = (gctUINT8_PTR)buffer + bytes; + +#if gcdMULTI_GPU + gcmkONERROR(gckHARDWARE_ChipEnable( + hardware, + buffer, + gcvCORE_3D_ALL_MASK, + &chipEnableBytes + )); +#endif + +#if gcdSECURITY + gckKERNEL_SecurityExecute( + Event->kernel, + reservedBuffer, + executeBytes + ); +#else + /* Execute the hardware event. */ + gcmkONERROR(gckCOMMAND_Execute(command, executeBytes)); +#endif +#endif + } + + /* Release the command queue. */ + gcmkONERROR(gckCOMMAND_ExitCommit(command, FromPower)); + +#if !gcdNULL_DRIVER + gcmkVERIFY_OK(_TryToIdleGPU(Event)); +#endif + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Need to unroll the mutex acquire. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventListMutex)); + } + + if (commitEntered) + { + /* Release the command queue mutex. */ + gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, FromPower)); + } + + if (id != 0xFF) + { + /* Need to unroll the event allocation. */ + Event->queues[id].head = gcvNULL; + } + + if (status == gcvSTATUS_GPU_NOT_RESPONDING) + { + /* Broadcast GPU stuck. */ + status = gckOS_Broadcast(Event->os, + Event->kernel->hardware, + gcvBROADCAST_GPU_STUCK); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_Commit +** +** Commit an event queue from the user. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gcsQUEUE_PTR Queue +** User event queue. +** +** OUTPUT: +** +** Nothing. +*/ +#if gcdMULTI_GPU +gceSTATUS +gckEVENT_Commit( + IN gckEVENT Event, + IN gcsQUEUE_PTR Queue, + IN gceCORE_3D_MASK ChipEnable + ) +#else +gceSTATUS +gckEVENT_Commit( + IN gckEVENT Event, + IN gcsQUEUE_PTR Queue + ) +#endif +{ + gceSTATUS status; + gcsQUEUE_PTR record = gcvNULL, next; + gctUINT32 processID; + gctBOOL needCopy = gcvFALSE; + + gcmkHEADER_ARG("Event=0x%x Queue=0x%x", Event, Queue); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + /* Get the current process ID. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + + /* Query if we need to copy the client data. */ + gcmkONERROR(gckOS_QueryNeedCopy(Event->os, processID, &needCopy)); + + /* Loop while there are records in the queue. */ + while (Queue != gcvNULL) + { + gcsQUEUE queue; + + if (needCopy) + { + /* Point to stack record. */ + record = &queue; + + /* Copy the data from the client. */ + gcmkONERROR(gckOS_CopyFromUserData(Event->os, + record, + Queue, + gcmSIZEOF(gcsQUEUE))); + } + else + { + gctPOINTER pointer = gcvNULL; + + /* Map record into kernel memory. */ + gcmkONERROR(gckOS_MapUserPointer(Event->os, + Queue, + gcmSIZEOF(gcsQUEUE), + &pointer)); + + record = pointer; + } + + /* Append event record to event queue. */ + gcmkONERROR( + gckEVENT_AddList(Event, &record->iface, gcvKERNEL_PIXEL, gcvTRUE, gcvFALSE)); + + /* Next record in the queue. */ + next = gcmUINT64_TO_PTR(record->next); + + if (!needCopy) + { + /* Unmap record from kernel memory. */ + gcmkONERROR( + gckOS_UnmapUserPointer(Event->os, + Queue, + gcmSIZEOF(gcsQUEUE), + (gctPOINTER *) record)); + record = gcvNULL; + } + + Queue = next; + } + + /* Submit the event list. */ +#if gcdMULTI_GPU + gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE, ChipEnable)); +#else + gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); +#endif + + /* Success */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if ((record != gcvNULL) && !needCopy) + { + /* Roll back. */ + gcmkVERIFY_OK(gckOS_UnmapUserPointer(Event->os, + Queue, + gcmSIZEOF(gcsQUEUE), + (gctPOINTER *) record)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_Compose +** +** Schedule a composition event and start a composition. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gcsHAL_COMPOSE_PTR Info +** Pointer to the composition structure. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Compose( + IN gckEVENT Event, + IN gcsHAL_COMPOSE_PTR Info + ) +{ + gceSTATUS status; + gcsEVENT_PTR headRecord; + gcsEVENT_PTR tailRecord; + gcsEVENT_PTR tempRecord; + gctUINT8 id = 0xFF; + gctUINT32 processID; + + gcmkHEADER_ARG("Event=0x%x Info=0x%x", Event, Info); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + gcmkVERIFY_ARGUMENT(Info != gcvNULL); + + /* Allocate an event ID. */ +#if gcdMULTI_GPU + gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL, gcvCORE_3D_ALL_MASK)); +#else + gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL)); +#endif + + /* Get process ID. */ + gcmkONERROR(gckOS_GetProcessID(&processID)); + + /* Allocate a record. */ + gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord)); + headRecord = tailRecord = tempRecord; + + /* Initialize the record. */ + tempRecord->info.command = gcvHAL_SIGNAL; + tempRecord->info.u.Signal.process = Info->process; +#ifdef __QNXNTO__ + tempRecord->info.u.Signal.coid = Info->coid; + tempRecord->info.u.Signal.rcvid = Info->rcvid; +#endif + tempRecord->info.u.Signal.signal = Info->signal; + tempRecord->info.u.Signal.auxSignal = 0; + tempRecord->next = gcvNULL; + tempRecord->processID = processID; + + /* Allocate another record for user signal #1. */ + if (gcmUINT64_TO_PTR(Info->userSignal1) != gcvNULL) + { + /* Allocate a record. */ + gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord)); + tailRecord->next = tempRecord; + tailRecord = tempRecord; + + /* Initialize the record. */ + tempRecord->info.command = gcvHAL_SIGNAL; + tempRecord->info.u.Signal.process = Info->userProcess; +#ifdef __QNXNTO__ + tempRecord->info.u.Signal.coid = Info->coid; + tempRecord->info.u.Signal.rcvid = Info->rcvid; +#endif + tempRecord->info.u.Signal.signal = Info->userSignal1; + tempRecord->info.u.Signal.auxSignal = 0; + tempRecord->next = gcvNULL; + tempRecord->processID = processID; + } + + /* Allocate another record for user signal #2. */ + if (gcmUINT64_TO_PTR(Info->userSignal2) != gcvNULL) + { + /* Allocate a record. */ + gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &tempRecord)); + tailRecord->next = tempRecord; + + /* Initialize the record. */ + tempRecord->info.command = gcvHAL_SIGNAL; + tempRecord->info.u.Signal.process = Info->userProcess; +#ifdef __QNXNTO__ + tempRecord->info.u.Signal.coid = Info->coid; + tempRecord->info.u.Signal.rcvid = Info->rcvid; +#endif + tempRecord->info.u.Signal.signal = Info->userSignal2; + tempRecord->info.u.Signal.auxSignal = 0; + tempRecord->next = gcvNULL; + tempRecord->processID = processID; + } + + /* Set the event list. */ + Event->queues[id].head = headRecord; + + /* Start composition. */ + gcmkONERROR(gckHARDWARE_Compose( + Event->kernel->hardware, processID, + gcmUINT64_TO_PTR(Info->physical), gcmUINT64_TO_PTR(Info->logical), Info->offset, Info->size, id + )); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckEVENT_Interrupt +** +** Called by the interrupt service routine to store the triggered interrupt +** mask to be later processed by gckEVENT_Notify. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctUINT32 Data +** Mask for the 32 interrupts. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Interrupt( + IN gckEVENT Event, +#if gcdMULTI_GPU + IN gctUINT CoreId, +#endif + IN gctUINT32 Data + ) +{ +#if gcdMULTI_GPU +#if defined(WIN32) + gctUINT32 i; +#endif +#endif + gcmkHEADER_ARG("Event=0x%x Data=0x%x", Event, Data); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + if (Data & 0x20000000) + { + gckENTRYDATA data; + gctUINT32 idle; + Data &= ~0x20000000; + +#if gcdMULTI_GPU + if (Event->kernel->core == gcvCORE_MAJOR) +#endif + { + /* Get first entry information. */ + gcmkVERIFY_OK( + gckENTRYQUEUE_Dequeue(&Event->kernel->command->queue, &data)); + + /* Make sure FE is idle. */ + do + { + gcmkVERIFY_OK(gckOS_ReadRegisterEx( + Event->os, + Event->kernel->core, + 0x4, + &idle)); + } + while (idle != 0x7FFFFFFF); + + /* Start Command Parser. */ + gcmkVERIFY_OK(gckHARDWARE_Execute( + Event->kernel->hardware, + data->physical, + data->bytes + )); + } + } + + /* Combine current interrupt status with pending flags. */ +#if gcdSMP +#if gcdMULTI_GPU + if (Event->kernel->core == gcvCORE_MAJOR) + { + gckOS_AtomSetMask(Event->pending3D[CoreId], Data); + } + else +#endif + { + gckOS_AtomSetMask(Event->pending, Data); + } +#elif defined(__QNXNTO__) +#if gcdMULTI_GPU + if (Event->kernel->core == gcvCORE_MAJOR) + { + atomic_set(&Event->pending3D[CoreId], Data); + } + else +#endif + { + atomic_set(&Event->pending, Data); + } +#else +#if gcdMULTI_GPU +#if defined(WIN32) + if (Event->kernel->core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + Event->pending3D[i] |= Data; + } + } + else +#else + if (Event->kernel->core == gcvCORE_MAJOR) + { + Event->pending3D[CoreId] |= Data; + } + else +#endif +#endif + { + Event->pending |= Data; + } +#endif + +#if gcdINTERRUPT_STATISTIC + { + gctINT j = 0; + gctINT32 oldValue; + + for (j = 0; j < gcmCOUNTOF(Event->queues); j++) + { + if ((Data & (1 << j))) + { + gcmkVERIFY_OK(gckOS_AtomDecrement(Event->os, + Event->interruptCount, + &oldValue)); + } + } + } +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckEVENT_Notify +** +** Process all triggered interrupts. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Notify( + IN gckEVENT Event, + IN gctUINT32 IDs + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctINT i; + gcsEVENT_QUEUE * queue; + gctUINT mask = 0; + gctBOOL acquired = gcvFALSE; + gctPOINTER info; + gctSIGNAL signal; + gctUINT pending = 0; + gckKERNEL kernel = Event->kernel; +#if gcdMULTI_GPU + gceCORE core = Event->kernel->core; + gctUINT32 busy; + gctUINT32 oldValue; + gctUINT pendingMask; +#endif +#if !gcdSMP + gctBOOL suspended = gcvFALSE; +#endif +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gctINT eventNumber = 0; +#endif + gctINT32 free; +#if gcdSECURE_USER + gcskSECURE_CACHE_PTR cache; +#endif + gckVIDMEM_NODE nodeObject; + gcuVIDMEM_NODE_PTR node; + + gcmkHEADER_ARG("Event=0x%x IDs=0x%x", Event, IDs); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + gcmDEBUG_ONLY( + if (IDs != 0) + { + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + if (Event->queues[i].head != gcvNULL) + { + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "Queue(%d): stamp=%llu source=%d", + i, + Event->queues[i].stamp, + Event->queues[i].source); + } + } + } + ); + +#if gcdMULTI_GPU + /* Set busy flag. */ + gckOS_AtomicExchange(Event->os, &Event->busy, 1, &busy); + if (busy) + { + /* Another thread is already busy - abort. */ + goto OnSuccess; + } +#endif + + for (;;) + { + gcsEVENT_PTR record; +#if gcdMULTI_GPU + gctUINT32 pend[gcdMULTI_GPU]; + gctUINT32 pendMask[gcdMULTI_GPU]; +#endif + + /* Grab the mutex queue. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, + Event->eventQueueMutex, + gcvINFINITE)); + acquired = gcvTRUE; + +#if gcdSMP +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + /* Get current interrupts. */ + for (i = 0; i < gcdMULTI_GPU; i++) + { + gckOS_AtomGet(Event->os, Event->pending3D[i], (gctINT32_PTR)&pend[i]); + gckOS_AtomGet(Event->os, Event->pending3DMask[i], (gctINT32_PTR)&pendMask[i]); + } + + gckOS_AtomGet(Event->os, Event->pendingMask, (gctINT32_PTR)&pendingMask); + } + else +#endif + { + gckOS_AtomGet(Event->os, Event->pending, (gctINT32_PTR)&pending); + } +#else + /* Suspend interrupts. */ + gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); + suspended = gcvTRUE; + +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + /* Get current interrupts. */ + pend[i] = Event->pending3D[i]; + pendMask[i] = Event->pending3DMask[i]; + } + + pendingMask = Event->pendingMask; + } + else +#endif + { + pending = Event->pending; + } + + /* Resume interrupts. */ + gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); + suspended = gcvFALSE; +#endif + +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + gctUINT32 bad_pend = (pend[i] & ~pendMask[i]); + + if (bad_pend != 0) + { + gcmkTRACE_ZONE_N( + gcvLEVEL_ERROR, gcvZONE_EVENT, + gcmSIZEOF(bad_pend) + gcmSIZEOF(i), + "Interrupts 0x%x are not unexpected for Core%d.", + bad_pend, i + ); + + gckOS_AtomClearMask(Event->pending3D[i], bad_pend); + + pend[i] &= pendMask[i]; + } + } + + pending = (pend[0] & pend[1] & pendingMask) /* Check combined events on both GPUs */ + | (pend[0] & ~pendingMask) /* Check individual events on GPU 0 */ + | (pend[1] & ~pendingMask); /* Check individual events on GPU 1 */ + } +#endif + + if (pending == 0) + { + /* Release the mutex queue. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + acquired = gcvFALSE; + + /* No more pending interrupts - done. */ + break; + } + + if (pending & 0x80000000) + { + gctUINT32 AQAxiStatus = 0; + gckOS_ReadRegisterEx(Event->os, Event->kernel->hardware->core, 0xC, &AQAxiStatus); + + gcmkPRINT("GPU[%d]: AXI BUS ERROR, AQAxiStatus=0x%x\n", Event->kernel->hardware->core, AQAxiStatus); + pending &= 0x7FFFFFFF; + } + + if (pending & 0x40000000) + { + gckHARDWARE_DumpMMUException(Event->kernel->hardware); + + pending &= 0xBFFFFFFF; + } + + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_EVENT, + gcmSIZEOF(pending), + "Pending interrupts 0x%x", + pending + ); + + queue = gcvNULL; + + gcmDEBUG_ONLY( + if (IDs == 0) + { + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + if (Event->queues[i].head != gcvNULL) + { + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "Queue(%d): stamp=%llu source=%d", + i, + Event->queues[i].stamp, + Event->queues[i].source); + } + } + } + ); + + /* Find the oldest pending interrupt. */ + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + if ((Event->queues[i].head != gcvNULL) + && (pending & (1 << i)) + ) + { + if ((queue == gcvNULL) + || (Event->queues[i].stamp < queue->stamp) + ) + { + queue = &Event->queues[i]; + mask = 1 << i; +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + eventNumber = i; +#endif + } + } + } + + if (queue == gcvNULL) + { + gcmkTRACE_ZONE_N( + gcvLEVEL_ERROR, gcvZONE_EVENT, + gcmSIZEOF(pending), + "Interrupts 0x%x are not pending.", + pending + ); + +#if gcdSMP +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + /* Mark pending interrupts as handled. */ + for (i = 0; i < gcdMULTI_GPU; i++) + { + gckOS_AtomClearMask(Event->pending3D[i], pending); + gckOS_AtomClearMask(Event->pending3DMask[i], pending); + } + + gckOS_AtomClearMask(Event->pendingMask, pending); + } + else +#endif + { + gckOS_AtomClearMask(Event->pending, pending); + } + +#elif defined(__QNXNTO__) +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + atomic_clr((gctUINT32_PTR)&Event->pending3D[i], pending); + atomic_clr((gctUINT32_PTR)&Event->pending3DMask[i], pending); + } + + atomic_clr((gctUINT32_PTR)&Event->pendingMask, pending); + } + else +#endif + { + atomic_clr((gctUINT32_PTR)&Event->pending, pending); + } +#else + /* Suspend interrupts. */ + gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); + suspended = gcvTRUE; + +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + /* Mark pending interrupts as handled. */ + Event->pending3D[i] &= ~pending; + Event->pending3DMask[i] &= ~pending; + } + } + else +#endif + { + Event->pending &= ~pending; + } + + /* Resume interrupts. */ + gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); + suspended = gcvFALSE; +#endif + + /* Release the mutex queue. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + acquired = gcvFALSE; + break; + } + + /* Check whether there is a missed interrupt. */ + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + if ((Event->queues[i].head != gcvNULL) + && (Event->queues[i].stamp < queue->stamp) + && (Event->queues[i].source <= queue->source) +#if gcdMULTI_GPU + && (Event->queues[i].chipEnable == queue->chipEnable) +#endif + ) + { + gcmkTRACE_N( + gcvLEVEL_ERROR, + gcmSIZEOF(i) + gcmSIZEOF(Event->queues[i].stamp), + "Event %d lost (stamp %llu)", + i, Event->queues[i].stamp + ); + + /* Use this event instead. */ + queue = &Event->queues[i]; + mask = 0; + } + } + + if (mask != 0) + { +#if gcmIS_DEBUG(gcdDEBUG_TRACE) + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_EVENT, + gcmSIZEOF(eventNumber), + "Processing interrupt %d", + eventNumber + ); +#endif + } + +#if gcdSMP +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + /* Mark pending interrupt as handled. */ + gckOS_AtomClearMask(Event->pending3D[i], mask); + gckOS_AtomClearMask(Event->pending3DMask[i], mask); + } + + gckOS_AtomClearMask(Event->pendingMask, mask); + } + else +#endif + { + gckOS_AtomClearMask(Event->pending, mask); + } + +#elif defined(__QNXNTO__) +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + atomic_clr(&Event->pending3D[i], mask); + atomic_clr(&Event->pending3DMask[i], mask); + } + + atomic_clr(&Event->pendingMask, mask); + } + else +#endif + { + atomic_clr(&Event->pending, mask); + } +#else + /* Suspend interrupts. */ + gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); + suspended = gcvTRUE; + +#if gcdMULTI_GPU + if (core == gcvCORE_MAJOR) + { + for (i = 0; i < gcdMULTI_GPU; i++) + { + /* Mark pending interrupt as handled. */ + Event->pending3D[i] &= ~mask; + Event->pending3DMask[i] &= ~mask; + } + + Event->pendingMask &= ~mask; + } + else +#endif + { + Event->pending &= ~mask; + } + + /* Resume interrupts. */ + gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); + suspended = gcvFALSE; +#endif + + /* Grab the event head. */ + record = queue->head; + + /* Now quickly clear its event list. */ + queue->head = gcvNULL; + + /* Release the mutex queue. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + acquired = gcvFALSE; + + /* Increase the number of free events. */ + gcmkONERROR(gckOS_AtomIncrement(Event->os, Event->freeAtom, &free)); + + /* Walk all events for this interrupt. */ + while (record != gcvNULL) + { + gcsEVENT_PTR recordNext; +#ifndef __QNXNTO__ + gctPOINTER logical; +#endif +#if gcdSECURE_USER + gctSIZE_T bytes; +#endif + + /* Grab next record. */ + recordNext = record->next; + +#ifdef __QNXNTO__ + /* Assign record->processID as the pid for this galcore thread. + * Used in OS calls like gckOS_UnlockMemory() which do not take a pid. + */ + drv_thread_specific_key_assign(record->processID, 0, Event->kernel->core); +#endif + +#if gcdSECURE_USER + /* Get the cache that belongs to this process. */ + gcmkONERROR(gckKERNEL_GetProcessDBCache(Event->kernel, + record->processID, + &cache)); +#endif + + gcmkTRACE_ZONE_N( + gcvLEVEL_INFO, gcvZONE_EVENT, + gcmSIZEOF(record->info.command), + "Processing event type: %d", + record->info.command + ); + + switch (record->info.command) + { + case gcvHAL_FREE_NON_PAGED_MEMORY: + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "gcvHAL_FREE_NON_PAGED_MEMORY: 0x%x", + gcmNAME_TO_PTR(record->info.u.FreeNonPagedMemory.physical)); + + /* Free non-paged memory. */ + status = gckOS_FreeNonPagedMemory( + Event->os, + (gctSIZE_T) record->info.u.FreeNonPagedMemory.bytes, + gcmNAME_TO_PTR(record->info.u.FreeNonPagedMemory.physical), + gcmUINT64_TO_PTR(record->info.u.FreeNonPagedMemory.logical)); + + if (gcmIS_SUCCESS(status)) + { +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Event->kernel, + cache, + gcmUINT64_TO_PTR(record->record.u.FreeNonPagedMemory.logical), + (gctSIZE_T) record->record.u.FreeNonPagedMemory.bytes)); +#endif + } + gcmRELEASE_NAME(record->info.u.FreeNonPagedMemory.physical); + break; + + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "gcvHAL_FREE_CONTIGUOUS_MEMORY: 0x%x", + gcmNAME_TO_PTR(record->info.u.FreeContiguousMemory.physical)); + + /* Unmap the user memory. */ + status = gckOS_FreeContiguous( + Event->os, + gcmNAME_TO_PTR(record->info.u.FreeContiguousMemory.physical), + gcmUINT64_TO_PTR(record->info.u.FreeContiguousMemory.logical), + (gctSIZE_T) record->info.u.FreeContiguousMemory.bytes); + + if (gcmIS_SUCCESS(status)) + { +#if gcdSECURE_USER + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Event->kernel, + cache, + gcmUINT64_TO_PTR(event->event.u.FreeContiguousMemory.logical), + (gctSIZE_T) event->event.u.FreeContiguousMemory.bytes)); +#endif + } + gcmRELEASE_NAME(record->info.u.FreeContiguousMemory.physical); + break; + + case gcvHAL_WRITE_DATA: +#ifndef __QNXNTO__ + /* Convert physical into logical address. */ + gcmkERR_BREAK( + gckOS_MapPhysical(Event->os, + record->info.u.WriteData.address, + gcmSIZEOF(gctUINT32), + &logical)); + + /* Write data. */ + gcmkERR_BREAK( + gckOS_WriteMemory(Event->os, + logical, + record->info.u.WriteData.data)); + + /* Unmap the physical memory. */ + gcmkERR_BREAK( + gckOS_UnmapPhysical(Event->os, + logical, + gcmSIZEOF(gctUINT32))); +#else + /* Write data. */ + gcmkERR_BREAK( + gckOS_WriteMemory(Event->os, + (gctPOINTER) + record->info.u.WriteData.address, + record->info.u.WriteData.data)); +#endif + break; + + case gcvHAL_UNLOCK_VIDEO_MEMORY: + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "gcvHAL_UNLOCK_VIDEO_MEMORY: 0x%x", + record->info.u.UnlockVideoMemory.node); + + nodeObject = gcmUINT64_TO_PTR(record->info.u.UnlockVideoMemory.node); + + node = nodeObject->node; + + /* Save node information before it disappears. */ +#if gcdSECURE_USER + node = event->event.u.UnlockVideoMemory.node; + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + logical = gcvNULL; + bytes = 0; + } + else + { + logical = node->Virtual.logical; + bytes = node->Virtual.bytes; + } +#endif + + /* Unlock. */ + status = gckVIDMEM_Unlock( + Event->kernel, + nodeObject, + record->info.u.UnlockVideoMemory.type, + gcvNULL); + +#if gcdSECURE_USER + if (gcmIS_SUCCESS(status) && (logical != gcvNULL)) + { + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Event->kernel, + cache, + logical, + bytes)); + } +#endif + +#if gcdPROCESS_ADDRESS_SPACE + gcmkVERIFY_OK(gckVIDMEM_NODE_Unlock( + Event->kernel, + nodeObject, + record->processID + )); +#endif + + status = gckVIDMEM_NODE_Dereference(Event->kernel, nodeObject); + break; + + case gcvHAL_SIGNAL: + signal = gcmUINT64_TO_PTR(record->info.u.Signal.signal); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "gcvHAL_SIGNAL: 0x%x", + signal); + +#ifdef __QNXNTO__ + if ((record->info.u.Signal.coid == 0) + && (record->info.u.Signal.rcvid == 0) + ) + { + /* Kernel signal. */ + gcmkERR_BREAK( + gckOS_Signal(Event->os, + signal, + gcvTRUE)); + } + else + { + /* User signal. */ + gcmkERR_BREAK( + gckOS_UserSignal(Event->os, + signal, + record->info.u.Signal.rcvid, + record->info.u.Signal.coid)); + } +#else + /* Set signal. */ + if (gcmUINT64_TO_PTR(record->info.u.Signal.process) == gcvNULL) + { + /* Kernel signal. */ + gcmkERR_BREAK( + gckOS_Signal(Event->os, + signal, + gcvTRUE)); + } + else + { + /* User signal. */ + gcmkERR_BREAK( + gckOS_UserSignal(Event->os, + signal, + gcmUINT64_TO_PTR(record->info.u.Signal.process))); + } + + gcmkASSERT(record->info.u.Signal.auxSignal == 0); +#endif + break; + + case gcvHAL_UNMAP_USER_MEMORY: + info = gcmNAME_TO_PTR(record->info.u.UnmapUserMemory.info); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "gcvHAL_UNMAP_USER_MEMORY: 0x%x", + info); + + /* Unmap the user memory. */ + status = gckOS_UnmapUserMemory( + Event->os, + Event->kernel->core, + gcmUINT64_TO_PTR(record->info.u.UnmapUserMemory.memory), + (gctSIZE_T) record->info.u.UnmapUserMemory.size, + info, + record->info.u.UnmapUserMemory.address); + +#if gcdSECURE_USER + if (gcmIS_SUCCESS(status)) + { + gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( + Event->kernel, + cache, + gcmUINT64_TO_PTR(record->info.u.UnmapUserMemory.memory), + (gctSIZE_T) record->info.u.UnmapUserMemory.size)); + } +#endif + gcmRELEASE_NAME(record->info.u.UnmapUserMemory.info); + break; + + case gcvHAL_TIMESTAMP: + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "gcvHAL_TIMESTAMP: %d %d", + record->info.u.TimeStamp.timer, + record->info.u.TimeStamp.request); + + /* Process the timestamp. */ + switch (record->info.u.TimeStamp.request) + { + case 0: + status = gckOS_GetTime(&Event->kernel->timers[ + record->info.u.TimeStamp.timer]. + stopTime); + break; + + case 1: + status = gckOS_GetTime(&Event->kernel->timers[ + record->info.u.TimeStamp.timer]. + startTime); + break; + + default: + gcmkTRACE_ZONE_N( + gcvLEVEL_ERROR, gcvZONE_EVENT, + gcmSIZEOF(record->info.u.TimeStamp.request), + "Invalid timestamp request: %d", + record->info.u.TimeStamp.request + ); + + status = gcvSTATUS_INVALID_ARGUMENT; + break; + } + break; + + case gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER: + gcmkVERIFY_OK( + gckKERNEL_DestroyVirtualCommandBuffer(Event->kernel, + (gctSIZE_T) record->info.u.FreeVirtualCommandBuffer.bytes, + gcmNAME_TO_PTR(record->info.u.FreeVirtualCommandBuffer.physical), + gcmUINT64_TO_PTR(record->info.u.FreeVirtualCommandBuffer.logical) + )); + gcmRELEASE_NAME(record->info.u.FreeVirtualCommandBuffer.physical); + break; + +#if gcdANDROID_NATIVE_FENCE_SYNC + case gcvHAL_SYNC_POINT: + { + gctSYNC_POINT syncPoint; + + syncPoint = gcmUINT64_TO_PTR(record->info.u.SyncPoint.syncPoint); + status = gckOS_SignalSyncPoint(Event->os, syncPoint); + } + break; +#endif + +#if gcdPROCESS_ADDRESS_SPACE + case gcvHAL_DESTROY_MMU: + status = gckMMU_Destroy(gcmUINT64_TO_PTR(record->info.u.DestroyMmu.mmu)); + break; +#endif + + case gcvHAL_COMMIT_DONE: + break; + + default: + /* Invalid argument. */ + gcmkTRACE_ZONE_N( + gcvLEVEL_ERROR, gcvZONE_EVENT, + gcmSIZEOF(record->info.command), + "Unknown event type: %d", + record->info.command + ); + + status = gcvSTATUS_INVALID_ARGUMENT; + break; + } + + /* Make sure there are no errors generated. */ + if (gcmIS_ERROR(status)) + { + gcmkTRACE_ZONE_N( + gcvLEVEL_WARNING, gcvZONE_EVENT, + gcmSIZEOF(status), + "Event produced status: %d(%s)", + status, gckOS_DebugStatus2Name(status)); + } + + /* Free the event. */ + gcmkVERIFY_OK(gckEVENT_FreeRecord(Event, record)); + + /* Advance to next record. */ + record = recordNext; + } + + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_EVENT, + "Handled interrupt 0x%x", mask); + } + +#if gcdMULTI_GPU + /* Clear busy flag. */ + gckOS_AtomicExchange(Event->os, &Event->busy, 0, &oldValue); +#endif + + if (IDs == 0) + { + gcmkONERROR(_TryToIdleGPU(Event)); + } + +#if gcdMULTI_GPU +OnSuccess: +#endif + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + } + +#if !gcdSMP + if (suspended) + { + /* Resume interrupts. */ + gcmkVERIFY_OK(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); + } +#endif + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckEVENT_FreeProcess +** +** Free all events owned by a particular process ID. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctUINT32 ProcessID +** Process ID of the process to be freed up. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_FreeProcess( + IN gckEVENT Event, + IN gctUINT32 ProcessID + ) +{ + gctSIZE_T i; + gctBOOL acquired = gcvFALSE; + gcsEVENT_PTR record, next; + gceSTATUS status; + gcsEVENT_PTR deleteHead, deleteTail; + + gcmkHEADER_ARG("Event=0x%x ProcessID=%d", Event, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + /* Walk through all queues. */ + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + if (Event->queues[i].head != gcvNULL) + { + /* Grab the event queue mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, + Event->eventQueueMutex, + gcvINFINITE)); + acquired = gcvTRUE; + + /* Grab the mutex head. */ + record = Event->queues[i].head; + Event->queues[i].head = gcvNULL; + Event->queues[i].tail = gcvNULL; + deleteHead = gcvNULL; + deleteTail = gcvNULL; + + while (record != gcvNULL) + { + next = record->next; + if (record->processID == ProcessID) + { + if (deleteHead == gcvNULL) + { + deleteHead = record; + } + else + { + deleteTail->next = record; + } + + deleteTail = record; + } + else + { + if (Event->queues[i].head == gcvNULL) + { + Event->queues[i].head = record; + } + else + { + Event->queues[i].tail->next = record; + } + + Event->queues[i].tail = record; + } + + record->next = gcvNULL; + record = next; + } + + /* Release the mutex queue. */ + gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + acquired = gcvFALSE; + + /* Loop through the entire list of events. */ + for (record = deleteHead; record != gcvNULL; record = next) + { + /* Get the next event record. */ + next = record->next; + + /* Free the event record. */ + gcmkONERROR(gckEVENT_FreeRecord(Event, record)); + } + } + } + + gcmkONERROR(_TryToIdleGPU(Event)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Release the event queue mutex. */ + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** gckEVENT_Stop +** +** Stop the hardware using the End event mechanism. +** +** INPUT: +** +** gckEVENT Event +** Pointer to an gckEVENT object. +** +** gctUINT32 ProcessID +** Process ID Logical belongs. +** +** gctPHYS_ADDR Handle +** Physical address handle. If gcvNULL it is video memory. +** +** gctPOINTER Logical +** Logical address to flush. +** +** gctSIGNAL Signal +** Pointer to the signal to trigger. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckEVENT_Stop( + IN gckEVENT Event, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Handle, + IN gctPOINTER Logical, + IN gctSIGNAL Signal, + IN OUT gctUINT32 * waitSize + ) +{ + gceSTATUS status; + /* gctSIZE_T waitSize;*/ + gcsEVENT_PTR record; + gctUINT8 id = 0xFF; + + gcmkHEADER_ARG("Event=0x%x ProcessID=%u Handle=0x%x Logical=0x%x " + "Signal=0x%x", + Event, ProcessID, Handle, Logical, Signal); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); + + /* Submit the current event queue. */ +#if gcdMULTI_GPU + gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE, gcvCORE_3D_ALL_MASK)); +#else + gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); +#endif +#if gcdMULTI_GPU + gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL, gcvCORE_3D_ALL_MASK)); +#else + gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL)); +#endif + + /* Allocate a record. */ + gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &record)); + + /* Initialize the record. */ + record->next = gcvNULL; + record->processID = ProcessID; + record->info.command = gcvHAL_SIGNAL; + record->info.u.Signal.signal = gcmPTR_TO_UINT64(Signal); +#ifdef __QNXNTO__ + record->info.u.Signal.coid = 0; + record->info.u.Signal.rcvid = 0; +#endif + record->info.u.Signal.auxSignal = 0; + record->info.u.Signal.process = 0; + + /* Append the record. */ + Event->queues[id].head = record; + + /* Replace last WAIT with END. */ + gcmkONERROR(gckHARDWARE_End( + Event->kernel->hardware, Logical, waitSize + )); + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Flush the cache for the END. */ + gcmkONERROR(gckOS_CacheClean( + Event->os, + ProcessID, + gcvNULL, + (gctUINT32)Handle, + Logical, + *waitSize + )); +#endif + + /* Wait for the signal. */ + gcmkONERROR(gckOS_WaitSignal(Event->os, Signal, gcvINFINITE)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +static void +_PrintRecord( + gcsEVENT_PTR record + ) +{ + switch (record->info.command) + { + case gcvHAL_FREE_NON_PAGED_MEMORY: + gcmkPRINT(" gcvHAL_FREE_NON_PAGED_MEMORY"); + break; + + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + gcmkPRINT(" gcvHAL_FREE_CONTIGUOUS_MEMORY"); + break; + + case gcvHAL_WRITE_DATA: + gcmkPRINT(" gcvHAL_WRITE_DATA"); + break; + + case gcvHAL_UNLOCK_VIDEO_MEMORY: + gcmkPRINT(" gcvHAL_UNLOCK_VIDEO_MEMORY"); + break; + + case gcvHAL_SIGNAL: + gcmkPRINT(" gcvHAL_SIGNAL process=%d signal=0x%x", + record->info.u.Signal.process, + record->info.u.Signal.signal); + break; + + case gcvHAL_UNMAP_USER_MEMORY: + gcmkPRINT(" gcvHAL_UNMAP_USER_MEMORY"); + break; + + case gcvHAL_TIMESTAMP: + gcmkPRINT(" gcvHAL_TIMESTAMP"); + break; + + case gcvHAL_COMMIT_DONE: + gcmkPRINT(" gcvHAL_COMMIT_DONE"); + break; + + case gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER: + gcmkPRINT(" gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER logical=0x%08x", + record->info.u.FreeVirtualCommandBuffer.logical); + break; + + case gcvHAL_SYNC_POINT: + gcmkPRINT(" gcvHAL_SYNC_POINT syncPoint=0x%08x", + gcmUINT64_TO_PTR(record->info.u.SyncPoint.syncPoint)); + + break; + + case gcvHAL_DESTROY_MMU: + gcmkPRINT(" gcvHAL_DESTORY_MMU mmu=0x%08x", + gcmUINT64_TO_PTR(record->info.u.DestroyMmu.mmu)); + + break; + default: + gcmkPRINT(" Illegal Event %d", record->info.command); + break; + } +} + +/******************************************************************************* +** gckEVENT_Dump +** +** Dump record in event queue when stuck happens. +** No protection for the event queue. +**/ +gceSTATUS +gckEVENT_Dump( + IN gckEVENT Event + ) +{ + gcsEVENT_QUEUE_PTR queueHead = Event->queueHead; + gcsEVENT_QUEUE_PTR queue; + gcsEVENT_PTR record = gcvNULL; + gctINT i; +#if gcdINTERRUPT_STATISTIC + gctINT32 pendingInterrupt; + gctUINT32 intrAcknowledge; +#endif + + gcmkHEADER_ARG("Event=0x%x", Event); + + gcmkPRINT("**************************\n"); + gcmkPRINT("*** EVENT STATE DUMP ***\n"); + gcmkPRINT("**************************\n"); + + gcmkPRINT(" Unsumbitted Event:"); + while(queueHead) + { + queue = queueHead; + record = queueHead->head; + + gcmkPRINT(" [%x]:", queue); + while(record) + { + _PrintRecord(record); + record = record->next; + } + + if (queueHead == Event->queueTail) + { + queueHead = gcvNULL; + } + else + { + queueHead = queueHead->next; + } + } + + gcmkPRINT(" Untriggered Event:"); + for (i = 0; i < gcmCOUNTOF(Event->queues); i++) + { + queue = &Event->queues[i]; + record = queue->head; + + gcmkPRINT(" [%d]:", i); + while(record) + { + _PrintRecord(record); + record = record->next; + } + } + +#if gcdINTERRUPT_STATISTIC + gckOS_AtomGet(Event->os, Event->interruptCount, &pendingInterrupt); + gcmkPRINT(" Number of Pending Interrupt: %d", pendingInterrupt); + + if (Event->kernel->recovery == 0) + { + gckOS_ReadRegisterEx( + Event->os, + Event->kernel->core, + 0x10, + &intrAcknowledge + ); + + gcmkPRINT(" INTR_ACKNOWLEDGE=0x%x", intrAcknowledge); + } +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + diff --git a/drivers/gpu/galcore/gc_hal_kernel_heap.c b/drivers/gpu/galcore/gc_hal_kernel_heap.c new file mode 100644 index 00000000000000..be0a60eb6da93e --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_heap.c @@ -0,0 +1,858 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +/** +** @file +** gckHEAP object for kernel HAL layer. The heap implemented here is an arena- +** based memory allocation. An arena-based memory heap allocates data quickly +** from specified arenas and reduces memory fragmentation. +** +*/ +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_HEAP + +/******************************************************************************* +***** Structures *************************************************************** +*******************************************************************************/ +#define gcdIN_USE ((gcskNODE_PTR)gcvMAXUINTPTR_T) + +typedef struct _gcskNODE * gcskNODE_PTR; +typedef struct _gcskNODE +{ + /* Number of byets in node. */ + gctSIZE_T bytes; + + /* Pointer to next free node, or gcvNULL to mark the node as freed, or + ** gcdIN_USE to mark the node as used. */ + gcskNODE_PTR next; + +#if gcmIS_DEBUG(gcdDEBUG_CODE) + /* Time stamp of allocation. */ + gctUINT64 timeStamp; +#endif +} +gcskNODE; + +typedef struct _gcskHEAP * gcskHEAP_PTR; +typedef struct _gcskHEAP +{ + /* Linked list. */ + gcskHEAP_PTR next; + gcskHEAP_PTR prev; + + /* Heap size. */ + gctSIZE_T size; + + /* Free list. */ + gcskNODE_PTR freeList; +} +gcskHEAP; + +struct _gckHEAP +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to a gckOS object. */ + gckOS os; + + /* Locking mutex. */ + gctPOINTER mutex; + + /* Allocation parameters. */ + gctSIZE_T allocationSize; + + /* Heap list. */ + gcskHEAP_PTR heap; +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gctUINT64 timeStamp; +#endif + +#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) + /* Profile information. */ + gctUINT32 allocCount; + gctUINT64 allocBytes; + gctUINT64 allocBytesMax; + gctUINT64 allocBytesTotal; + gctUINT32 heapCount; + gctUINT32 heapCountMax; + gctUINT64 heapMemory; + gctUINT64 heapMemoryMax; +#endif +}; + +/******************************************************************************* +***** Static Support Functions ************************************************* +*******************************************************************************/ + +#if gcmIS_DEBUG(gcdDEBUG_CODE) +static gctSIZE_T +_DumpHeap( + IN gcskHEAP_PTR Heap + ) +{ + gctPOINTER p; + gctSIZE_T leaked = 0; + + /* Start at first node. */ + for (p = Heap + 1;;) + { + /* Convert the pointer. */ + gcskNODE_PTR node = (gcskNODE_PTR) p; + + /* Check if this is a used node. */ + if (node->next == gcdIN_USE) + { + /* Print the leaking node. */ + gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_HEAP, + "Detected leaking: node=0x%x bytes=%lu timeStamp=%llu " + "(%08X %c%c%c%c)", + node, node->bytes, node->timeStamp, + ((gctUINT32_PTR) (node + 1))[0], + gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[0]), + gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[1]), + gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[2]), + gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[3])); + + /* Add leaking byte count. */ + leaked += node->bytes; + } + + /* Test for end of heap. */ + if (node->bytes == 0) + { + break; + } + + else + { + /* Move to next node. */ + p = (gctUINT8_PTR) node + node->bytes; + } + } + + /* Return the number of leaked bytes. */ + return leaked; +} +#endif + +static gceSTATUS +_CompactKernelHeap( + IN gckHEAP Heap + ) +{ + gcskHEAP_PTR heap, next; + gctPOINTER p; + gcskHEAP_PTR freeList = gcvNULL; + + gcmkHEADER_ARG("Heap=0x%x", Heap); + + /* Walk all the heaps. */ + for (heap = Heap->heap; heap != gcvNULL; heap = next) + { + gcskNODE_PTR lastFree = gcvNULL; + + /* Zero out the free list. */ + heap->freeList = gcvNULL; + + /* Start at the first node. */ + for (p = (gctUINT8_PTR) (heap + 1);;) + { + /* Convert the pointer. */ + gcskNODE_PTR node = (gcskNODE_PTR) p; + + gcmkASSERT(p <= (gctPOINTER) ((gctUINT8_PTR) (heap + 1) + heap->size)); + + /* Test if this node not used. */ + if (node->next != gcdIN_USE) + { + /* Test if this is the end of the heap. */ + if (node->bytes == 0) + { + break; + } + + /* Test of this is the first free node. */ + else if (lastFree == gcvNULL) + { + /* Initialzie the free list. */ + heap->freeList = node; + lastFree = node; + } + + else + { + /* Test if this free node is contiguous with the previous + ** free node. */ + if ((gctUINT8_PTR) lastFree + lastFree->bytes == p) + { + /* Just increase the size of the previous free node. */ + lastFree->bytes += node->bytes; + } + else + { + /* Add to linked list. */ + lastFree->next = node; + lastFree = node; + } + } + } + + /* Move to next node. */ + p = (gctUINT8_PTR) node + node->bytes; + } + + /* Mark the end of the chain. */ + if (lastFree != gcvNULL) + { + lastFree->next = gcvNULL; + } + + /* Get next heap. */ + next = heap->next; + + /* Check if the entire heap is free. */ + if ((heap->freeList != gcvNULL) + && (heap->freeList->bytes == heap->size - gcmSIZEOF(gcskNODE)) + ) + { + /* Remove the heap from the linked list. */ + if (heap->prev == gcvNULL) + { + Heap->heap = next; + } + else + { + heap->prev->next = next; + } + + if (heap->next != gcvNULL) + { + heap->next->prev = heap->prev; + } + +#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) + /* Update profiling. */ + Heap->heapCount -= 1; + Heap->heapMemory -= heap->size + gcmSIZEOF(gcskHEAP); +#endif + + /* Add this heap to the list of heaps that need to be freed. */ + heap->next = freeList; + freeList = heap; + } + } + + if (freeList != gcvNULL) + { + /* Release the mutex, remove any chance for a dead lock. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Heap->os, Heap->mutex)); + + /* Free all heaps in the free list. */ + for (heap = freeList; heap != gcvNULL; heap = next) + { + /* Get pointer to the next heap. */ + next = heap->next; + + /* Free the heap. */ + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP, + "Freeing heap 0x%x (%lu bytes)", + heap, heap->size + gcmSIZEOF(gcskHEAP)); + gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap)); + } + + /* Acquire the mutex again. */ + gcmkVERIFY_OK( + gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +***** gckHEAP API Code ********************************************************* +*******************************************************************************/ + +/******************************************************************************* +** +** gckHEAP_Construct +** +** Construct a new gckHEAP object. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctSIZE_T AllocationSize +** Minimum size per arena. +** +** OUTPUT: +** +** gckHEAP * Heap +** Pointer to a variable that will hold the pointer to the gckHEAP +** object. +*/ +gceSTATUS +gckHEAP_Construct( + IN gckOS Os, + IN gctSIZE_T AllocationSize, + OUT gckHEAP * Heap + ) +{ + gceSTATUS status; + gckHEAP heap = gcvNULL; + gctPOINTER pointer = gcvNULL; + + gcmkHEADER_ARG("Os=0x%x AllocationSize=%lu", Os, AllocationSize); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Heap != gcvNULL); + + /* Allocate the gckHEAP object. */ + gcmkONERROR(gckOS_AllocateMemory(Os, + gcmSIZEOF(struct _gckHEAP), + &pointer)); + + heap = pointer; + + /* Initialize the gckHEAP object. */ + heap->object.type = gcvOBJ_HEAP; + heap->os = Os; + heap->allocationSize = AllocationSize; + heap->heap = gcvNULL; +#if gcmIS_DEBUG(gcdDEBUG_CODE) + heap->timeStamp = 0; +#endif + +#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) + /* Zero the counters. */ + heap->allocCount = 0; + heap->allocBytes = 0; + heap->allocBytesMax = 0; + heap->allocBytesTotal = 0; + heap->heapCount = 0; + heap->heapCountMax = 0; + heap->heapMemory = 0; + heap->heapMemoryMax = 0; +#endif + + /* Create the mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &heap->mutex)); + + /* Return the pointer to the gckHEAP object. */ + *Heap = heap; + + /* Success. */ + gcmkFOOTER_ARG("*Heap=0x%x", *Heap); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (heap != gcvNULL) + { + /* Free the heap structure. */ + gcmkVERIFY_OK(gckOS_FreeMemory(Os, heap)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHEAP_Destroy +** +** Destroy a gckHEAP object. +** +** INPUT: +** +** gckHEAP Heap +** Pointer to a gckHEAP object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckHEAP_Destroy( + IN gckHEAP Heap + ) +{ + gcskHEAP_PTR heap; +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gctSIZE_T leaked = 0; +#endif + + gcmkHEADER_ARG("Heap=0x%x", Heap); + + for (heap = Heap->heap; heap != gcvNULL; heap = Heap->heap) + { + /* Unlink heap from linked list. */ + Heap->heap = heap->next; + +#if gcmIS_DEBUG(gcdDEBUG_CODE) + /* Check for leaked memory. */ + leaked += _DumpHeap(heap); +#endif + + /* Free the heap. */ + gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap)); + } + + /* Free the mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Heap->os, Heap->mutex)); + + /* Free the heap structure. */ + gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, Heap)); + + /* Success. */ +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gcmkFOOTER_ARG("leaked=%lu", leaked); +#else + gcmkFOOTER_NO(); +#endif + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckHEAP_Allocate +** +** Allocate data from the heap. +** +** INPUT: +** +** gckHEAP Heap +** Pointer to a gckHEAP object. +** +** IN gctSIZE_T Bytes +** Number of byte to allocate. +** +** OUTPUT: +** +** gctPOINTER * Memory +** Pointer to a variable that will hold the address of the allocated +** memory. +*/ +gceSTATUS +gckHEAP_Allocate( + IN gckHEAP Heap, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ) +{ + gctBOOL acquired = gcvFALSE; + gcskHEAP_PTR heap; + gceSTATUS status; + gctSIZE_T bytes; + gcskNODE_PTR node, used, prevFree = gcvNULL; + gctPOINTER memory = gcvNULL; + + gcmkHEADER_ARG("Heap=0x%x Bytes=%lu", Heap, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + /* Determine number of bytes required for a node. */ + bytes = gcmALIGN(Bytes + gcmSIZEOF(gcskNODE), 8); + + /* Acquire the mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); + + acquired = gcvTRUE; + + /* Check if this allocation is bigger than the default allocation size. */ + if (bytes > Heap->allocationSize - gcmSIZEOF(gcskHEAP) - gcmSIZEOF(gcskNODE)) + { + /* Adjust allocation size. */ + Heap->allocationSize = bytes * 2; + } + + else if (Heap->heap != gcvNULL) + { + gctINT i; + + /* 2 retries, since we might need to compact. */ + for (i = 0; i < 2; ++i) + { + /* Walk all the heaps. */ + for (heap = Heap->heap; heap != gcvNULL; heap = heap->next) + { + /* Check if this heap has enough bytes to hold the request. */ + if (bytes <= heap->size - gcmSIZEOF(gcskNODE)) + { + prevFree = gcvNULL; + + /* Walk the chain of free nodes. */ + for (node = heap->freeList; + node != gcvNULL; + node = node->next + ) + { + gcmkASSERT(node->next != gcdIN_USE); + + /* Check if this free node has enough bytes. */ + if (node->bytes >= bytes) + { + /* Use the node. */ + goto UseNode; + } + + /* Save current free node for linked list management. */ + prevFree = node; + } + } + } + + if (i == 0) + { + /* Compact the heap. */ + gcmkVERIFY_OK(_CompactKernelHeap(Heap)); + +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "===== KERNEL HEAP ====="); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Number of allocations : %12u", + Heap->allocCount); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Number of bytes allocated : %12llu", + Heap->allocBytes); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Maximum allocation size : %12llu", + Heap->allocBytesMax); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Total number of bytes allocated : %12llu", + Heap->allocBytesTotal); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Number of heaps : %12u", + Heap->heapCount); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Heap memory in bytes : %12llu", + Heap->heapMemory); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Maximum number of heaps : %12u", + Heap->heapCountMax); + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, + "Maximum heap memory in bytes : %12llu", + Heap->heapMemoryMax); +#endif + } + } + } + + /* Release the mutex. */ + gcmkONERROR( + gckOS_ReleaseMutex(Heap->os, Heap->mutex)); + + acquired = gcvFALSE; + + /* Allocate a new heap. */ + gcmkONERROR( + gckOS_AllocateMemory(Heap->os, + Heap->allocationSize, + &memory)); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP, + "Allocated heap 0x%x (%lu bytes)", + memory, Heap->allocationSize); + + /* Acquire the mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); + + acquired = gcvTRUE; + + /* Use the allocated memory as the heap. */ + heap = (gcskHEAP_PTR) memory; + + /* Insert this heap to the head of the chain. */ + heap->next = Heap->heap; + heap->prev = gcvNULL; + heap->size = Heap->allocationSize - gcmSIZEOF(gcskHEAP); + + if (heap->next != gcvNULL) + { + heap->next->prev = heap; + } + Heap->heap = heap; + + /* Mark the end of the heap. */ + node = (gcskNODE_PTR) ( (gctUINT8_PTR) heap + + Heap->allocationSize + - gcmSIZEOF(gcskNODE) + ); + node->bytes = 0; + node->next = gcvNULL; + + /* Create a free list. */ + node = (gcskNODE_PTR) (heap + 1); + heap->freeList = node; + + /* Initialize the free list. */ + node->bytes = heap->size - gcmSIZEOF(gcskNODE); + node->next = gcvNULL; + + /* No previous free. */ + prevFree = gcvNULL; + +#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) + /* Update profiling. */ + Heap->heapCount += 1; + Heap->heapMemory += Heap->allocationSize; + + if (Heap->heapCount > Heap->heapCountMax) + { + Heap->heapCountMax = Heap->heapCount; + } + if (Heap->heapMemory > Heap->heapMemoryMax) + { + Heap->heapMemoryMax = Heap->heapMemory; + } +#endif + +UseNode: + /* Verify some stuff. */ + gcmkASSERT(heap != gcvNULL); + gcmkASSERT(node != gcvNULL); + gcmkASSERT(node->bytes >= bytes); + + if (heap->prev != gcvNULL) + { + /* Unlink the heap from the linked list. */ + heap->prev->next = heap->next; + if (heap->next != gcvNULL) + { + heap->next->prev = heap->prev; + } + + /* Move the heap to the front of the list. */ + heap->next = Heap->heap; + heap->prev = gcvNULL; + Heap->heap = heap; + heap->next->prev = heap; + } + + /* Check if there is enough free space left after usage for another free + ** node. */ + if (node->bytes - bytes >= gcmSIZEOF(gcskNODE)) + { + /* Allocated used space from the back of the free list. */ + used = (gcskNODE_PTR) ((gctUINT8_PTR) node + node->bytes - bytes); + + /* Adjust the number of free bytes. */ + node->bytes -= bytes; + gcmkASSERT(node->bytes >= gcmSIZEOF(gcskNODE)); + } + else + { + /* Remove this free list from the chain. */ + if (prevFree == gcvNULL) + { + heap->freeList = node->next; + } + else + { + prevFree->next = node->next; + } + + /* Consume the entire free node. */ + used = (gcskNODE_PTR) node; + bytes = node->bytes; + } + + /* Mark node as used. */ + used->bytes = bytes; + used->next = gcdIN_USE; +#if gcmIS_DEBUG(gcdDEBUG_CODE) + used->timeStamp = ++Heap->timeStamp; +#endif + +#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) + /* Update profile counters. */ + Heap->allocCount += 1; + Heap->allocBytes += bytes; + Heap->allocBytesMax = gcmMAX(Heap->allocBytes, Heap->allocBytesMax); + Heap->allocBytesTotal += bytes; +#endif + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Heap->os, Heap->mutex)); + + /* Return pointer to memory. */ + *Memory = used + 1; + + /* Success. */ + gcmkFOOTER_ARG("*Memory=0x%x", *Memory); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Heap->os, Heap->mutex)); + } + + if (memory != gcvNULL) + { + /* Free the heap memory. */ + gckOS_FreeMemory(Heap->os, memory); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckHEAP_Free +** +** Free allocated memory from the heap. +** +** INPUT: +** +** gckHEAP Heap +** Pointer to a gckHEAP object. +** +** IN gctPOINTER Memory +** Pointer to memory to free. +** +** OUTPUT: +** +** NOTHING. +*/ +gceSTATUS +gckHEAP_Free( + IN gckHEAP Heap, + IN gctPOINTER Memory + ) +{ + gcskNODE_PTR node; + gceSTATUS status; + + gcmkHEADER_ARG("Heap=0x%x Memory=0x%x", Heap, Memory); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + /* Acquire the mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); + + /* Pointer to structure. */ + node = (gcskNODE_PTR) Memory - 1; + + /* Mark the node as freed. */ + node->next = gcvNULL; + +#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) + /* Update profile counters. */ + Heap->allocBytes -= node->bytes; +#endif + + /* Release the mutex. */ + gcmkVERIFY_OK( + gckOS_ReleaseMutex(Heap->os, Heap->mutex)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if VIVANTE_PROFILER +gceSTATUS +gckHEAP_ProfileStart( + IN gckHEAP Heap + ) +{ + gcmkHEADER_ARG("Heap=0x%x", Heap); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); + + /* Zero the counters. */ + Heap->allocCount = 0; + Heap->allocBytes = 0; + Heap->allocBytesMax = 0; + Heap->allocBytesTotal = 0; + Heap->heapCount = 0; + Heap->heapCountMax = 0; + Heap->heapMemory = 0; + Heap->heapMemoryMax = 0; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckHEAP_ProfileEnd( + IN gckHEAP Heap, + IN gctCONST_STRING Title + ) +{ + gcmkHEADER_ARG("Heap=0x%x Title=0x%x", Heap, Title); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); + gcmkVERIFY_ARGUMENT(Title != gcvNULL); + + gcmkPRINT(""); + gcmkPRINT("=====[ HEAP - %s ]=====", Title); + gcmkPRINT("Number of allocations : %12u", Heap->allocCount); + gcmkPRINT("Number of bytes allocated : %12llu", Heap->allocBytes); + gcmkPRINT("Maximum allocation size : %12llu", Heap->allocBytesMax); + gcmkPRINT("Total number of bytes allocated : %12llu", Heap->allocBytesTotal); + gcmkPRINT("Number of heaps : %12u", Heap->heapCount); + gcmkPRINT("Heap memory in bytes : %12llu", Heap->heapMemory); + gcmkPRINT("Maximum number of heaps : %12u", Heap->heapCountMax); + gcmkPRINT("Maximum heap memory in bytes : %12llu", Heap->heapMemoryMax); + gcmkPRINT("=============================================="); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif /* VIVANTE_PROFILER */ + +/******************************************************************************* +***** Test Code **************************************************************** +*******************************************************************************/ + diff --git a/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c b/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c new file mode 100644 index 00000000000000..5716223b9807b6 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c @@ -0,0 +1,877 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#if gcdENABLE_VG + +/******************************************************************************\ +*********************** Support Functions and Definitions ********************** +\******************************************************************************/ + +/* Interruot statistics will be accumulated if not zero. */ +#define gcmENABLE_INTERRUPT_STATISTICS 0 + +#define _GC_OBJ_ZONE gcvZONE_INTERRUPT + +/* Object structure. */ +struct _gckVGINTERRUPT +{ + /* Object. */ + gcsOBJECT object; + + /* gckVGKERNEL pointer. */ + gckVGKERNEL kernel; + + /* gckOS pointer. */ + gckOS os; + + /* Interrupt handlers. */ + gctINTERRUPT_HANDLER handlers[32]; + + /* Main interrupt handler thread. */ + gctTHREAD handler; + gctBOOL terminate; + + /* Interrupt FIFO. */ + gctSEMAPHORE fifoValid; + gctUINT32 fifo[256]; + gctUINT fifoItems; + gctUINT8 head; + gctUINT8 tail; + + /* Interrupt statistics. */ +#if gcmENABLE_INTERRUPT_STATISTICS + gctUINT maxFifoItems; + gctUINT fifoOverflow; + gctUINT maxSimultaneous; + gctUINT multipleCount; +#endif +}; + + +/******************************************************************************* +** +** _ProcessInterrupt +** +** The interrupt processor. +** +** INPUT: +** +** ThreadParameter +** Pointer to the gckVGINTERRUPT object. +** +** OUTPUT: +** +** Nothing. +*/ + +#if gcmENABLE_INTERRUPT_STATISTICS +static void +_ProcessInterrupt( + gckVGINTERRUPT Interrupt, + gctUINT_PTR TriggeredCount + ) +#else +static void +_ProcessInterrupt( + gckVGINTERRUPT Interrupt + ) +#endif +{ + gceSTATUS status; + gctUINT32 triggered; + gctUINT i; + + /* Advance to the next entry. */ + Interrupt->tail += 1; + Interrupt->fifoItems -= 1; + + /* Get the interrupt value. */ + triggered = Interrupt->fifo[Interrupt->tail]; + gcmkASSERT(triggered != 0); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s: triggered=0x%08X\n", + __FUNCTION__, + triggered + ); + + /* Walk through all possible interrupts. */ + for (i = 0; i < gcmSIZEOF(Interrupt->handlers); i += 1) + { + /* Test if interrupt happened. */ + if ((triggered & 1) == 1) + { +#if gcmENABLE_INTERRUPT_STATISTICS + if (TriggeredCount != gcvNULL) + { + (* TriggeredCount) += 1; + } +#endif + + /* Make sure we have valid handler. */ + if (Interrupt->handlers[i] == gcvNULL) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s: Interrupt %d isn't registered.\n", + __FUNCTION__, i + ); + } + else + { + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s: interrupt=%d\n", + __FUNCTION__, + i + ); + + /* Call the handler. */ + status = Interrupt->handlers[i] (Interrupt->kernel); + + if (gcmkIS_ERROR(status)) + { + /* Failed to signal the semaphore. */ + gcmkTRACE( + gcvLEVEL_ERROR, + "%s: Error %d incrementing the semaphore #%d.\n", + __FUNCTION__, status, i + ); + } + } + } + + /* Next interrupt. */ + triggered >>= 1; + + /* No more interrupts to handle? */ + if (triggered == 0) + { + break; + } + } +} + + +/******************************************************************************* +** +** _MainInterruptHandler +** +** The main interrupt thread serves the interrupt FIFO and calls registered +** handlers for the interrupts that occured. The handlers are called in the +** sequence interrupts occured with the exception when multiple interrupts +** occured at the same time. In that case the handler calls are "sorted" by +** the interrupt number therefore giving the interrupts with lower numbers +** higher priority. +** +** INPUT: +** +** ThreadParameter +** Pointer to the gckVGINTERRUPT object. +** +** OUTPUT: +** +** Nothing. +*/ + +static gctTHREADFUNCRESULT gctTHREADFUNCTYPE +_MainInterruptHandler( + gctTHREADFUNCPARAMETER ThreadParameter + ) +{ + gceSTATUS status; + gckVGINTERRUPT interrupt; + +#if gcmENABLE_INTERRUPT_STATISTICS + gctUINT count; +#endif + + /* Cast the object. */ + interrupt = (gckVGINTERRUPT) ThreadParameter; + + /* Enter the loop. */ + while (gcvTRUE) + { + /* Wait for an interrupt. */ + status = gckOS_DecrementSemaphore(interrupt->os, interrupt->fifoValid); + + /* Error? */ + if (gcmkIS_ERROR(status)) + { + break; + } + + /* System termination request? */ + if (status == gcvSTATUS_TERMINATE) + { + break; + } + + /* Driver is shutting down? */ + if (interrupt->terminate) + { + break; + } + +#if gcmENABLE_INTERRUPT_STATISTICS + /* Reset triggered count. */ + count = 0; + + /* Process the interrupt. */ + _ProcessInterrupt(interrupt, &count); + + /* Update conters. */ + if (count > interrupt->maxSimultaneous) + { + interrupt->maxSimultaneous = count; + } + + if (count > 1) + { + interrupt->multipleCount += 1; + } +#else + /* Process the interrupt. */ + _ProcessInterrupt(interrupt); +#endif + } + + return 0; +} + + +/******************************************************************************* +** +** _StartInterruptHandler / _StopInterruptHandler +** +** Main interrupt handler routine control. +** +** INPUT: +** +** ThreadParameter +** Pointer to the gckVGINTERRUPT object. +** +** OUTPUT: +** +** Nothing. +*/ + +static gceSTATUS +_StartInterruptHandler( + gckVGINTERRUPT Interrupt + ) +{ + gceSTATUS status, last; + + do + { + /* Objects must not be already created. */ + gcmkASSERT(Interrupt->fifoValid == gcvNULL); + gcmkASSERT(Interrupt->handler == gcvNULL); + + /* Reset the termination request. */ + Interrupt->terminate = gcvFALSE; + +#if !gcdENABLE_INFINITE_SPEED_HW + /* Construct the fifo semaphore. */ + gcmkERR_BREAK(gckOS_CreateSemaphoreVG( + Interrupt->os, &Interrupt->fifoValid + )); + + /* Start the interrupt handler thread. */ + gcmkERR_BREAK(gckOS_StartThread( + Interrupt->os, + _MainInterruptHandler, + Interrupt, + &Interrupt->handler + )); +#endif + + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Roll back. */ + if (Interrupt->fifoValid != gcvNULL) + { + gcmkCHECK_STATUS(gckOS_DestroySemaphore( + Interrupt->os, Interrupt->fifoValid + )); + + Interrupt->fifoValid = gcvNULL; + } + + /* Return the status. */ + return status; +} + +static gceSTATUS +_StopInterruptHandler( + gckVGINTERRUPT Interrupt + ) +{ + gceSTATUS status; + + do + { + /* Does the thread exist? */ + if (Interrupt->handler == gcvNULL) + { + /* The semaphore must be NULL as well. */ + gcmkASSERT(Interrupt->fifoValid == gcvNULL); + + /* Success. */ + status = gcvSTATUS_OK; + break; + } + + /* The semaphore must exist as well. */ + gcmkASSERT(Interrupt->fifoValid != gcvNULL); + + /* Set the termination request. */ + Interrupt->terminate = gcvTRUE; + + /* Unlock the thread. */ + gcmkERR_BREAK(gckOS_IncrementSemaphore( + Interrupt->os, Interrupt->fifoValid + )); + + /* Wait until the thread quits. */ + gcmkERR_BREAK(gckOS_StopThread( + Interrupt->os, + Interrupt->handler + )); + + /* Destroy the semaphore. */ + gcmkERR_BREAK(gckOS_DestroySemaphore( + Interrupt->os, Interrupt->fifoValid + )); + + /* Reset handles. */ + Interrupt->handler = gcvNULL; + Interrupt->fifoValid = gcvNULL; + } + while (gcvFALSE); + + /* Return the status. */ + return status; +} + + +/******************************************************************************\ +***************************** Interrupt Object API ***************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckVGINTERRUPT_Construct +** +** Construct an interrupt object. +** +** INPUT: +** +** Kernel +** Pointer to the gckVGKERNEL object. +** +** OUTPUT: +** +** Interrupt +** Pointer to the new gckVGINTERRUPT object. +*/ + +gceSTATUS +gckVGINTERRUPT_Construct( + IN gckVGKERNEL Kernel, + OUT gckVGINTERRUPT * Interrupt + ) +{ + gceSTATUS status; + gckVGINTERRUPT interrupt = gcvNULL; + + gcmkHEADER_ARG("Kernel=0x%x Interrupt=0x%x", Kernel, Interrupt); + + /* Verify argeuments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Interrupt != gcvNULL); + + do + { + /* Allocate the gckVGINTERRUPT structure. */ + gcmkERR_BREAK(gckOS_Allocate( + Kernel->os, + gcmSIZEOF(struct _gckVGINTERRUPT), + (gctPOINTER *) &interrupt + )); + + /* Reset the object data. */ + gcmkVERIFY_OK(gckOS_ZeroMemory( + interrupt, gcmSIZEOF(struct _gckVGINTERRUPT) + )); + + /* Initialize the object. */ + interrupt->object.type = gcvOBJ_INTERRUPT; + + /* Initialize the object pointers. */ + interrupt->kernel = Kernel; + interrupt->os = Kernel->os; + + /* Initialize the current FIFO position. */ + interrupt->head = (gctUINT8)~0; + interrupt->tail = (gctUINT8)~0; + + /* Start the thread. */ + gcmkERR_BREAK(_StartInterruptHandler(interrupt)); + + /* Return interrupt object. */ + *Interrupt = interrupt; + + gcmkFOOTER_ARG("*Interrup=0x%x", *Interrupt); + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Roll back. */ + if (interrupt != gcvNULL) + { + /* Free the gckVGINTERRUPT structure. */ + gcmkVERIFY_OK(gckOS_Free(interrupt->os, interrupt)); + } + + gcmkFOOTER(); + + /* Return the status. */ + return status; +} + + +/******************************************************************************* +** +** gckVGINTERRUPT_Destroy +** +** Destroy an interrupt object. +** +** INPUT: +** +** Interrupt +** Pointer to the gckVGINTERRUPT object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ + +gceSTATUS +gckVGINTERRUPT_Destroy( + IN gckVGINTERRUPT Interrupt + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Interrupt=0x%x", Interrupt); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT); + + do + { + /* Stop the interrupt thread. */ + gcmkERR_BREAK(_StopInterruptHandler(Interrupt)); + + /* Mark the object as unknown. */ + Interrupt->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckVGINTERRUPT structure. */ + gcmkERR_BREAK(gckOS_Free(Interrupt->os, Interrupt)); + } + while (gcvFALSE); + + gcmkFOOTER(); + + /* Return the status. */ + return status; +} + + +/******************************************************************************* +** +** gckVGINTERRUPT_DumpState +** +** Print the current state of the interrupt manager. +** +** INPUT: +** +** Interrupt +** Pointer to a gckVGINTERRUPT object. +** +** OUTPUT: +** +** Nothing. +*/ + +#if gcvDEBUG +gceSTATUS +gckVGINTERRUPT_DumpState( + IN gckVGINTERRUPT Interrupt + ) +{ + gcmkHEADER_ARG("Interrupt=0x%x", Interrupt); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT); + + /* Print the header. */ + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + "%s: INTERRUPT OBJECT STATUS\n", + __FUNCTION__ + ); + + /* Print statistics. */ +#if gcmENABLE_INTERRUPT_STATISTICS + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " Maximum number of FIFO items accumulated at a single time: %d\n", + Interrupt->maxFifoItems + ); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " Interrupt FIFO overflow happened times: %d\n", + Interrupt->fifoOverflow + ); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " Maximum number of interrupts simultaneously generated: %d\n", + Interrupt->maxSimultaneous + ); + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " Number of times when there were multiple interrupts generated: %d\n", + Interrupt->multipleCount + ); +#endif + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " The current number of entries in the FIFO: %d\n", + Interrupt->fifoItems + ); + + /* Print the FIFO contents. */ + if (Interrupt->fifoItems != 0) + { + gctUINT8 index; + gctUINT8 last; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " FIFO current contents:\n" + ); + + /* Get the current pointers. */ + index = Interrupt->tail; + last = Interrupt->head; + + while (index != last) + { + /* Advance to the next entry. */ + index += 1; + + gcmkTRACE_ZONE( + gcvLEVEL_VERBOSE, gcvZONE_COMMAND, + " %d: 0x%08X\n", + index, Interrupt->fifo[index] + ); + } + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} +#endif + + +/******************************************************************************* +** +** gckVGINTERRUPT_Enable +** +** Enable the specified interrupt. +** +** INPUT: +** +** Interrupt +** Pointer to a gckVGINTERRUPT object. +** +** Id +** Pointer to the variable that holds the interrupt number to be +** registered in range 0..31. +** If the value is less then 0, gckVGINTERRUPT_Enable will attempt +** to find an unused interrupt. If such interrupt is found, the number +** will be assigned to the variable if the functuion call succeedes. +** +** Handler +** Pointer to the handler to register for the interrupt. +** +** OUTPUT: +** +** Nothing. +*/ + +gceSTATUS +gckVGINTERRUPT_Enable( + IN gckVGINTERRUPT Interrupt, + IN OUT gctINT32_PTR Id, + IN gctINTERRUPT_HANDLER Handler + ) +{ + gceSTATUS status; + gctINT32 i; + + gcmkHEADER_ARG("Interrupt=0x%x Id=0x%x Handler=0x%x", Interrupt, Id, Handler); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT); + gcmkVERIFY_ARGUMENT(Id != gcvNULL); + gcmkVERIFY_ARGUMENT(Handler != gcvNULL); + + do + { + /* See if we need to allocate an ID. */ + if (*Id < 0) + { + /* Find the first unused interrupt handler. */ + for (i = 0; i < gcmCOUNTOF(Interrupt->handlers); ++i) + { + if (Interrupt->handlers[i] == gcvNULL) + { + break; + } + } + + /* No unused innterrupts? */ + if (i == gcmCOUNTOF(Interrupt->handlers)) + { + status = gcvSTATUS_OUT_OF_RESOURCES; + break; + } + + /* Update the interrupt ID. */ + *Id = i; + } + + /* Make sure the ID is in range. */ + else if (*Id >= gcmCOUNTOF(Interrupt->handlers)) + { + status = gcvSTATUS_INVALID_ARGUMENT; + break; + } + + /* Set interrupt handler. */ + Interrupt->handlers[*Id] = Handler; + + /* Success. */ + status = gcvSTATUS_OK; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + + +/******************************************************************************* +** +** gckVGINTERRUPT_Disable +** +** Disable the specified interrupt. +** +** INPUT: +** +** Interrupt +** Pointer to a gckVGINTERRUPT object. +** +** Id +** Interrupt number to be disabled in range 0..31. +** +** OUTPUT: +** +** Nothing. +*/ + +gceSTATUS +gckVGINTERRUPT_Disable( + IN gckVGINTERRUPT Interrupt, + IN gctINT32 Id + ) +{ + gcmkHEADER_ARG("Interrupt=0x%x Id=0x%x", Interrupt, Id); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT); + gcmkVERIFY_ARGUMENT((Id >= 0) && (Id < gcmCOUNTOF(Interrupt->handlers))); + + /* Reset interrupt handler. */ + Interrupt->handlers[Id] = gcvNULL; + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + + +/******************************************************************************* +** +** gckVGINTERRUPT_Enque +** +** Read the interrupt status register and put the value in the interrupt FIFO. +** +** INPUT: +** +** Interrupt +** Pointer to a gckVGINTERRUPT object. +** +** OUTPUT: +** +** Nothing. +*/ + +#ifndef __QNXNTO__ +gceSTATUS +gckVGINTERRUPT_Enque( + IN gckVGINTERRUPT Interrupt + ) +#else +gceSTATUS +gckVGINTERRUPT_Enque( + IN gckVGINTERRUPT Interrupt, + OUT gckOS *Os, + OUT gctSEMAPHORE *Semaphore + ) +#endif +{ + gceSTATUS status; + gctUINT32 triggered; + + gcmkHEADER_ARG("Interrupt=0x%x", Interrupt); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT); + +#ifdef __QNXNTO__ + *Os = gcvNULL; + *Semaphore = gcvNULL; +#endif + + do + { + /* Read interrupt status register. */ + gcmkERR_BREAK(gckVGHARDWARE_ReadInterrupt( + Interrupt->kernel->hardware, &triggered + )); + + /* Mask out TS overflow interrupt */ + triggered &= 0xfffffffe; + + /* No interrupts to process? */ + if (triggered == 0) + { + status = gcvSTATUS_NOT_OUR_INTERRUPT; + break; + } + + /* FIFO overflow? */ + if (Interrupt->fifoItems == gcmCOUNTOF(Interrupt->fifo)) + { +#if gcmENABLE_INTERRUPT_STATISTICS + Interrupt->fifoOverflow += 1; +#endif + + /* OR the interrupt with the last value in the FIFO. */ + Interrupt->fifo[Interrupt->head] |= triggered; + + /* Success (kind of). */ + status = gcvSTATUS_OK; + } + else + { + /* Advance to the next entry. */ + Interrupt->head += 1; + Interrupt->fifoItems += 1; + +#if gcmENABLE_INTERRUPT_STATISTICS + if (Interrupt->fifoItems > Interrupt->maxFifoItems) + { + Interrupt->maxFifoItems = Interrupt->fifoItems; + } +#endif + + /* Set the new value. */ + Interrupt->fifo[Interrupt->head] = triggered; + +#ifndef __QNXNTO__ + /* Increment the FIFO semaphore. */ + gcmkERR_BREAK(gckOS_IncrementSemaphore( + Interrupt->os, Interrupt->fifoValid + )); +#else + *Os = Interrupt->os; + *Semaphore = Interrupt->fifoValid; +#endif + + /* Windows kills our threads prematurely when the application + exists. Verify here that the thread is still alive. */ + status = gckOS_VerifyThread(Interrupt->os, Interrupt->handler); + + /* Has the thread been prematurely terminated? */ + if (status != gcvSTATUS_OK) + { + /* Process all accumulated interrupts. */ + while (Interrupt->head != Interrupt->tail) + { +#if gcmENABLE_INTERRUPT_STATISTICS + /* Process the interrupt. */ + _ProcessInterrupt(Interrupt, gcvNULL); +#else + /* Process the interrupt. */ + _ProcessInterrupt(Interrupt); +#endif + } + + /* Set success. */ + status = gcvSTATUS_OK; + } + } + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +#endif /* gcdENABLE_VG */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_iommu.c b/drivers/gpu/galcore/gc_hal_kernel_iommu.c new file mode 100644 index 00000000000000..cbf9dbbbc2224c --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_iommu.c @@ -0,0 +1,202 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" +#include "gc_hal_kernel_device.h" + +#include +#include + +#define _GC_OBJ_ZONE gcvZONE_OS + +typedef struct _gcsIOMMU +{ + struct iommu_domain * domain; + struct device * device; +} +gcsIOMMU; + +static int +_IOMMU_Fault_Handler( + struct iommu_domain * Domain, + struct device * Dev, + unsigned long DomainAddress, + int flags, + void * args + ) +{ + return 0; +} + +static int +_FlatMapping( + IN gckIOMMU Iommu + ) +{ + gceSTATUS status; + gctUINT32 physical; + + for (physical = 0; physical < 0x80000000; physical += PAGE_SIZE) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "Map %x => %x bytes = %d", + physical, physical, PAGE_SIZE + ); + + gcmkONERROR(gckIOMMU_Map(Iommu, physical, physical, PAGE_SIZE)); + } + + return gcvSTATUS_OK; + +OnError: + return status; +} + +void +gckIOMMU_Destory( + IN gckOS Os, + IN gckIOMMU Iommu + ) +{ + gcmkHEADER(); + + if (Iommu->domain && Iommu->device) + { + iommu_attach_device(Iommu->domain, Iommu->device); + } + + if (Iommu->domain) + { + iommu_domain_free(Iommu->domain); + } + + if (Iommu) + { + gcmkOS_SAFE_FREE(Os, Iommu); + } + + gcmkFOOTER_NO(); +} + +gceSTATUS +gckIOMMU_Construct( + IN gckOS Os, + OUT gckIOMMU * Iommu + ) +{ + gceSTATUS status; + gckIOMMU iommu = gcvNULL; + struct device *dev; + int ret; + + gcmkHEADER(); + + dev = &Os->device->platform->device->dev; + + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsIOMMU), (gctPOINTER *)&iommu)); + + gckOS_ZeroMemory(iommu, gcmSIZEOF(gcsIOMMU)); + + iommu->domain = iommu_domain_alloc(&platform_bus_type); + + if (!iommu->domain) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "iommu_domain_alloc() fail"); + + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + + iommu_set_fault_handler(iommu->domain, _IOMMU_Fault_Handler, dev); + + ret = iommu_attach_device(iommu->domain, dev); + + if (ret) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, "iommu_attach_device() fail %d", ret); + + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + + iommu->device = dev; + + _FlatMapping(iommu); + + *Iommu = iommu; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + + gckIOMMU_Destory(Os, iommu); + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckIOMMU_Map( + IN gckIOMMU Iommu, + IN gctUINT32 DomainAddress, + IN gctUINT32 Physical, + IN gctUINT32 Bytes + ) +{ + gceSTATUS status; + int ret; + + gcmkHEADER_ARG("DomainAddress=%#X, Physical=%#X, Bytes=%d", + DomainAddress, Physical, Bytes); + + ret = iommu_map(Iommu->domain, DomainAddress, Physical, Bytes, 0); + + if (ret) + { + gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + + gcmkFOOTER(); + return status; + +} + +gceSTATUS +gckIOMMU_Unmap( + IN gckIOMMU Iommu, + IN gctUINT32 DomainAddress, + IN gctUINT32 Bytes + ) +{ + gcmkHEADER(); + + iommu_unmap(Iommu->domain, DomainAddress, Bytes); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + diff --git a/drivers/gpu/galcore/gc_hal_kernel_linux.c b/drivers/gpu/galcore/gc_hal_kernel_linux.c new file mode 100644 index 00000000000000..344f05827a866c --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_linux.c @@ -0,0 +1,497 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" + +#define _GC_OBJ_ZONE gcvZONE_KERNEL + +/******************************************************************************\ +******************************* gckKERNEL API Code ****************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckKERNEL_QueryVideoMemory +** +** Query the amount of video memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to an gcsHAL_INTERFACE structure that will be filled in with +** the memory information. +*/ +gceSTATUS +gckKERNEL_QueryVideoMemory( + IN gckKERNEL Kernel, + OUT gcsHAL_INTERFACE * Interface + ) +{ + gckGALDEVICE device; + + gcmkHEADER_ARG("Kernel=%p", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Interface != NULL); + + /* Extract the pointer to the gckGALDEVICE class. */ + device = (gckGALDEVICE) Kernel->context; + + /* Get internal memory size and physical address. */ + Interface->u.QueryVideoMemory.internalSize = device->internalSize; + Interface->u.QueryVideoMemory.internalPhysical = device->internalPhysicalName; + + /* Get external memory size and physical address. */ + Interface->u.QueryVideoMemory.externalSize = device->externalSize; + Interface->u.QueryVideoMemory.externalPhysical = device->externalPhysicalName; + + /* Get contiguous memory size and physical address. */ + Interface->u.QueryVideoMemory.contiguousSize = device->contiguousSize; + Interface->u.QueryVideoMemory.contiguousPhysical = device->contiguousPhysicalName; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckKERNEL_GetVideoMemoryPool +** +** Get the gckVIDMEM object belonging to the specified pool. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcePOOL Pool +** Pool to query gckVIDMEM object for. +** +** OUTPUT: +** +** gckVIDMEM * VideoMemory +** Pointer to a variable that will hold the pointer to the gckVIDMEM +** object belonging to the requested pool. +*/ +gceSTATUS +gckKERNEL_GetVideoMemoryPool( + IN gckKERNEL Kernel, + IN gcePOOL Pool, + OUT gckVIDMEM * VideoMemory + ) +{ + gckGALDEVICE device; + gckVIDMEM videoMemory; + + gcmkHEADER_ARG("Kernel=%p Pool=%d", Kernel, Pool); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(VideoMemory != NULL); + + /* Extract the pointer to the gckGALDEVICE class. */ + device = (gckGALDEVICE) Kernel->context; + + /* Dispatch on pool. */ + switch (Pool) + { + case gcvPOOL_LOCAL_INTERNAL: + /* Internal memory. */ + videoMemory = device->internalVidMem; + break; + + case gcvPOOL_LOCAL_EXTERNAL: + /* External memory. */ + videoMemory = device->externalVidMem; + break; + + case gcvPOOL_SYSTEM: + /* System memory. */ + videoMemory = device->contiguousVidMem; + break; + + default: + /* Unknown pool. */ + videoMemory = NULL; + } + + /* Return pointer to the gckVIDMEM object. */ + *VideoMemory = videoMemory; + + /* Return status. */ + gcmkFOOTER_ARG("*VideoMemory=%p", *VideoMemory); + return (videoMemory == NULL) ? gcvSTATUS_OUT_OF_MEMORY : gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckKERNEL_MapMemory +** +** Map video memory into the current process space. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctPHYS_ADDR Physical +** Physical address of video memory to map. +** +** gctSIZE_T Bytes +** Number of bytes to map. +** +** OUTPUT: +** +** gctPOINTER * Logical +** Pointer to a variable that will hold the base address of the mapped +** memory region. +*/ +gceSTATUS +gckKERNEL_MapMemory( + IN gckKERNEL Kernel, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical + ) +{ + gckKERNEL kernel = Kernel; + gctPHYS_ADDR physical = gcmNAME_TO_PTR(Physical); + + return gckOS_MapMemory(Kernel->os, physical, Bytes, Logical); +} + +/******************************************************************************* +** +** gckKERNEL_UnmapMemory +** +** Unmap video memory from the current process space. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctPHYS_ADDR Physical +** Physical address of video memory to map. +** +** gctSIZE_T Bytes +** Number of bytes to map. +** +** gctPOINTER Logical +** Base address of the mapped memory region. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_UnmapMemory( + IN gckKERNEL Kernel, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ) +{ + gckKERNEL kernel = Kernel; + gctPHYS_ADDR physical = gcmNAME_TO_PTR(Physical); + + return gckOS_UnmapMemory(Kernel->os, physical, Bytes, Logical); +} + +/******************************************************************************* +** +** gckKERNEL_MapVideoMemory +** +** Get the logical address for a hardware specific memory address for the +** current process. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL InUserSpace +** gcvTRUE to map the memory into the user space. +** +** gctUINT32 Address +** Hardware specific memory address. +** +** OUTPUT: +** +** gctPOINTER * Logical +** Pointer to a variable that will hold the logical address of the +** specified memory address. +*/ +gceSTATUS +gckKERNEL_MapVideoMemoryEx( + IN gckKERNEL Kernel, + IN gceCORE Core, + IN gctBOOL InUserSpace, + IN gctUINT32 Address, + OUT gctPOINTER * Logical + ) +{ + gckGALDEVICE device = gcvNULL; + PLINUX_MDL mdl = gcvNULL; + PLINUX_MDL_MAP mdlMap = gcvNULL; + gcePOOL pool = gcvPOOL_UNKNOWN; + gctUINT32 offset = 0; + gctUINT32 base = 0; + gceSTATUS status; + gctPOINTER logical = gcvNULL; + gctUINT32 baseAddress; + + gcmkHEADER_ARG("Kernel=%p InUserSpace=%d Address=%08x", + Kernel, InUserSpace, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Logical != NULL); + + /* Extract the pointer to the gckGALDEVICE class. */ + device = (gckGALDEVICE) Kernel->context; + +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + /* Split the memory address into a pool type and offset. */ + gcmkONERROR( + gckVGHARDWARE_SplitMemory(Kernel->vg->hardware, Address, &pool, &offset)); + } + else +#endif + { + /* Split the memory address into a pool type and offset. */ + gcmkONERROR( + gckHARDWARE_SplitMemory(Kernel->hardware, Address, &pool, &offset)); + } + + /* Dispatch on pool. */ + switch (pool) + { + case gcvPOOL_LOCAL_INTERNAL: + /* Internal memory. */ + logical = device->internalLogical; + break; + + case gcvPOOL_LOCAL_EXTERNAL: + /* External memory. */ + logical = device->externalLogical; + break; + + case gcvPOOL_SYSTEM: + /* System memory. */ + if (device->contiguousMapped) + { + logical = device->contiguousBase; + } + else + { + gctINT processID; + gckOS_GetProcessID(&processID); + + mdl = (PLINUX_MDL) device->contiguousPhysical; + + mdlMap = FindMdlMap(mdl, processID); + gcmkASSERT(mdlMap); + + logical = (gctPOINTER) mdlMap->vmaAddr; + } +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + gcmkVERIFY_OK( + gckVGHARDWARE_SplitMemory(Kernel->vg->hardware, + device->contiguousVidMem->baseAddress, + &pool, + &base)); + } + else +#endif + { + gctUINT32 systemBaseAddress = 0; + + if (Kernel->hardware->mmuVersion == 0) + { + gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &systemBaseAddress)); + } + + gcmkVERIFY_OK( + gckOS_CPUPhysicalToGPUPhysical( + Kernel->os, + device->contiguousVidMem->baseAddress - systemBaseAddress, + &baseAddress + )); + + gcmkVERIFY_OK( + gckHARDWARE_SplitMemory(Kernel->hardware, + baseAddress, + &pool, + &base)); + } + offset -= base; + break; + + default: + /* Invalid memory pool. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Build logical address of specified address. */ + *Logical = (gctPOINTER) ((gctUINT8_PTR) logical + offset); + + /* Success. */ + gcmkFOOTER_ARG("*Logical=%p", *Logical); + return gcvSTATUS_OK; + +OnError: + /* Retunn the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckKERNEL_MapVideoMemory +** +** Get the logical address for a hardware specific memory address for the +** current process. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctBOOL InUserSpace +** gcvTRUE to map the memory into the user space. +** +** gctUINT32 Address +** Hardware specific memory address. +** +** OUTPUT: +** +** gctPOINTER * Logical +** Pointer to a variable that will hold the logical address of the +** specified memory address. +*/ +gceSTATUS +gckKERNEL_MapVideoMemory( + IN gckKERNEL Kernel, + IN gctBOOL InUserSpace, + IN gctUINT32 Address, + OUT gctPOINTER * Logical + ) +{ + return gckKERNEL_MapVideoMemoryEx(Kernel, gcvCORE_MAJOR, InUserSpace, Address, Logical); +} +/******************************************************************************* +** +** gckKERNEL_Notify +** +** This function iscalled by clients to notify the gckKERNRL object of an event. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gceNOTIFY Notification +** Notification event. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_Notify( + IN gckKERNEL Kernel, +#if gcdMULTI_GPU + IN gctUINT CoreId, +#endif + IN gceNOTIFY Notification, + IN gctBOOL Data + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Kernel=%p Notification=%d Data=%d", + Kernel, Notification, Data); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Dispatch on notifcation. */ + switch (Notification) + { + case gcvNOTIFY_INTERRUPT: + /* Process the interrupt. */ +#if COMMAND_PROCESSOR_VERSION > 1 + status = gckINTERRUPT_Notify(Kernel->interrupt, Data); +#else + status = gckHARDWARE_Interrupt(Kernel->hardware, +#if gcdMULTI_GPU + CoreId, +#endif + Data); +#endif + break; + + default: + status = gcvSTATUS_OK; + break; + } + + /* Success. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_QuerySettings( + IN gckKERNEL Kernel, + OUT gcsKERNEL_SETTINGS * Settings + ) +{ + gckGALDEVICE device; + + gcmkHEADER_ARG("Kernel=%p", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Settings != gcvNULL); + + /* Extract the pointer to the gckGALDEVICE class. */ + device = (gckGALDEVICE) Kernel->context; + + /* Fill in signal. */ + Settings->signal = device->signal; + + /* Success. */ + gcmkFOOTER_ARG("Settings->signal=%d", Settings->signal); + return gcvSTATUS_OK; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_linux.h b/drivers/gpu/galcore/gc_hal_kernel_linux.h new file mode 100644 index 00000000000000..d867259c133773 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_linux.h @@ -0,0 +1,364 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_linux_h_ +#define __gc_hal_kernel_linux_h_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef MODVERSIONS +# include +#endif +#include +#include + +#include + +#define NTSTRSAFE_NO_CCH_FUNCTIONS +#include "gc_hal.h" +#include "gc_hal_driver.h" +#include "gc_hal_kernel.h" +#include "gc_hal_kernel_platform.h" +#include "gc_hal_kernel_device.h" +#include "gc_hal_kernel_os.h" +#include "gc_hal_kernel_debugfs.h" + + +#define FIND_TASK_BY_PID(x) pid_task(find_vpid(x), PIDTYPE_PID) + +#define _WIDE(string) L##string +#define WIDE(string) _WIDE(string) + +#define countof(a) (sizeof(a) / sizeof(a[0])) + +#ifndef DEVICE_NAME +# define DEVICE_NAME "galcore" +#endif + +#define GetPageCount(size, offset) ((((size) + ((offset) & ~PAGE_CACHE_MASK)) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT) + +#define gcdVM_FLAGS (VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP) + +/* Protection bit when mapping memroy to user sapce */ +#define gcmkPAGED_MEMROY_PROT(x) pgprot_writecombine(x) + +#if gcdNONPAGED_MEMORY_BUFFERABLE +#define gcmkIOREMAP ioremap_wc +#define gcmkNONPAGED_MEMROY_PROT(x) pgprot_writecombine(x) +#elif !gcdNONPAGED_MEMORY_CACHEABLE +#define gcmkIOREMAP ioremap_nocache +#define gcmkNONPAGED_MEMROY_PROT(x) pgprot_noncached(x) +#endif + +#define gcdSUPPRESS_OOM_MESSAGE 1 + +#if gcdSUPPRESS_OOM_MESSAGE +#define gcdNOWARN __GFP_NOWARN +#else +#define gcdNOWARN 0 +#endif + +/******************************************************************************\ +********************************** Structures ********************************** +\******************************************************************************/ +typedef struct _gcsIOMMU * gckIOMMU; + +typedef struct _gcsUSER_MAPPING * gcsUSER_MAPPING_PTR; +typedef struct _gcsUSER_MAPPING +{ + /* Pointer to next mapping structure. */ + gcsUSER_MAPPING_PTR next; + + /* Physical address of this mapping. */ + gctUINT32 physical; + + /* Logical address of this mapping. */ + gctPOINTER logical; + + /* Number of bytes of this mapping. */ + gctSIZE_T bytes; + + /* Starting address of this mapping. */ + gctINT8_PTR start; + + /* Ending address of this mapping. */ + gctINT8_PTR end; +} +gcsUSER_MAPPING; + +typedef struct _gcsINTEGER_DB * gcsINTEGER_DB_PTR; +typedef struct _gcsINTEGER_DB +{ + struct idr idr; + spinlock_t lock; + gctINT curr; +} +gcsINTEGER_DB; + +struct _gckOS +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to device */ + gckGALDEVICE device; + + /* Memory management */ + gctPOINTER memoryLock; + gctPOINTER memoryMapLock; + + struct _LINUX_MDL *mdlHead; + struct _LINUX_MDL *mdlTail; + + /* Kernel process ID. */ + gctUINT32 kernelProcessID; + + /* Signal management. */ + + /* Lock. */ + gctPOINTER signalMutex; + + /* signal id database. */ + gcsINTEGER_DB signalDB; + +#if gcdANDROID_NATIVE_FENCE_SYNC + /* Lock. */ + gctPOINTER syncPointMutex; + + /* sync point id database. */ + gcsINTEGER_DB syncPointDB; +#endif + + gcsUSER_MAPPING_PTR userMap; + gctPOINTER debugLock; + + /* workqueue for os timer. */ + struct workqueue_struct * workqueue; + + /* Allocate extra page to avoid cache overflow */ + struct page* paddingPage; + + /* Detect unfreed allocation. */ + atomic_t allocateCount; + + struct list_head allocatorList; + + gcsDEBUGFS_DIR allocatorDebugfsDir; + + /* Lock for register access check. */ + struct mutex registerAccessLocks[gcdMAX_GPU_COUNT]; + + /* External power states. */ + gctBOOL powerStates[gcdMAX_GPU_COUNT]; + + /* External clock states. */ + gctBOOL clockStates[gcdMAX_GPU_COUNT]; + + /* IOMMU. */ + gckIOMMU iommu; +}; + +typedef struct _gcsSIGNAL * gcsSIGNAL_PTR; +typedef struct _gcsSIGNAL +{ + /* Kernel sync primitive. */ + struct completion obj; + + /* Manual reset flag. */ + gctBOOL manualReset; + + /* The reference counter. */ + atomic_t ref; + + /* The owner of the signal. */ + gctHANDLE process; + + gckHARDWARE hardware; + + /* ID. */ + gctUINT32 id; +} +gcsSIGNAL; + +#if gcdANDROID_NATIVE_FENCE_SYNC +typedef struct _gcsSYNC_POINT * gcsSYNC_POINT_PTR; +typedef struct _gcsSYNC_POINT +{ + /* The reference counter. */ + atomic_t ref; + + /* State. */ + atomic_t state; + + /* timeline. */ + struct sync_timeline * timeline; + + /* ID. */ + gctUINT32 id; +} +gcsSYNC_POINT; +#endif + +typedef struct _gcsPageInfo * gcsPageInfo_PTR; +typedef struct _gcsPageInfo +{ + struct page **pages; + gctUINT32_PTR pageTable; + gctUINT32 extraPage; + gctUINT32 address; +#if gcdPROCESS_ADDRESS_SPACE + gckMMU mmu; +#endif +} +gcsPageInfo; + +typedef struct _gcsOSTIMER * gcsOSTIMER_PTR; +typedef struct _gcsOSTIMER +{ + struct delayed_work work; + gctTIMERFUNCTION function; + gctPOINTER data; +} gcsOSTIMER; + +gceSTATUS +gckOS_ImportAllocators( + gckOS Os + ); + +gceSTATUS +gckOS_FreeAllocators( + gckOS Os + ); + +gceSTATUS +_HandleOuterCache( + IN gckOS Os, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes, + IN gceCACHEOPERATION Type + ); + +gceSTATUS +_ConvertLogical2Physical( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + IN PLINUX_MDL Mdl, + OUT gctUINT32_PTR Physical + ); + +gctSTRING +_CreateKernelVirtualMapping( + IN PLINUX_MDL Mdl + ); + +void +_DestoryKernelVirtualMapping( + IN gctSTRING Addr + ); + +void +_UnmapUserLogical( + IN gctPOINTER Logical, + IN gctUINT32 Size + ); + +static inline gctINT +_GetProcessID( + void + ) +{ + return task_tgid_vnr(current); +} + +static inline struct page * +_NonContiguousToPage( + IN struct page ** Pages, + IN gctUINT32 Index + ) +{ + gcmkASSERT(Pages != gcvNULL); + return Pages[Index]; +} + +static inline unsigned long +_NonContiguousToPfn( + IN struct page ** Pages, + IN gctUINT32 Index + ) +{ + gcmkASSERT(Pages != gcvNULL); + return page_to_pfn(_NonContiguousToPage(Pages, Index)); +} + +static inline unsigned long +_NonContiguousToPhys( + IN struct page ** Pages, + IN gctUINT32 Index + ) +{ + gcmkASSERT(Pages != gcvNULL); + return page_to_phys(_NonContiguousToPage(Pages, Index)); +} + +#ifdef CONFIG_IOMMU_SUPPORT +void +gckIOMMU_Destory( + IN gckOS Os, + IN gckIOMMU Iommu + ); + +gceSTATUS +gckIOMMU_Construct( + IN gckOS Os, + OUT gckIOMMU * Iommu + ); + +gceSTATUS +gckIOMMU_Map( + IN gckIOMMU Iommu, + IN gctUINT32 DomainAddress, + IN gctUINT32 Physical, + IN gctUINT32 Bytes + ); + +gceSTATUS +gckIOMMU_Unmap( + IN gckIOMMU Iommu, + IN gctUINT32 DomainAddress, + IN gctUINT32 Bytes + ); +#endif + +#endif /* __gc_hal_kernel_linux_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_math.c b/drivers/gpu/galcore/gc_hal_kernel_math.c new file mode 100644 index 00000000000000..326ec28418830f --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_math.c @@ -0,0 +1,32 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" + +gctINT +gckMATH_ModuloInt( + IN gctINT X, + IN gctINT Y + ) +{ + if(Y ==0) {return 0;} + else {return X % Y;} +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_mmu.c b/drivers/gpu/galcore/gc_hal_kernel_mmu.c new file mode 100644 index 00000000000000..7f545f24a6e1d5 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_mmu.c @@ -0,0 +1,2260 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_MMU + +typedef enum _gceMMU_TYPE +{ + gcvMMU_USED = (0 << 4), + gcvMMU_SINGLE = (1 << 4), + gcvMMU_FREE = (2 << 4), +} +gceMMU_TYPE; + +#define gcmENTRY_TYPE(x) (x & 0xF0) + +#define gcdMMU_TABLE_DUMP 0 + +#define gcdUSE_MMU_EXCEPTION 1 + +/* + gcdMMU_CLEAR_VALUE + + The clear value for the entry of the old MMU. +*/ +#ifndef gcdMMU_CLEAR_VALUE +# define gcdMMU_CLEAR_VALUE 0x00000ABC +#endif + +#define gcdVERTEX_START (128 << 10) + +typedef struct _gcsMMU_STLB *gcsMMU_STLB_PTR; + +typedef struct _gcsMMU_STLB +{ + gctPHYS_ADDR physical; + gctUINT32_PTR logical; + gctSIZE_T size; + gctUINT32 physBase; + gctSIZE_T pageCount; + gctUINT32 mtlbIndex; + gctUINT32 mtlbEntryNum; + gcsMMU_STLB_PTR next; +} gcsMMU_STLB; + +#if gcdSHARED_PAGETABLE +typedef struct _gcsSharedPageTable * gcsSharedPageTable_PTR; +typedef struct _gcsSharedPageTable +{ + /* Shared gckMMU object. */ + gckMMU mmu; + + /* Hardwares which use this shared pagetable. */ + gckHARDWARE hardwares[gcdMAX_GPU_COUNT]; + + /* Number of cores use this shared pagetable. */ + gctUINT32 reference; +} +gcsSharedPageTable; + +static gcsSharedPageTable_PTR sharedPageTable = gcvNULL; +#endif + +#if gcdMIRROR_PAGETABLE +typedef struct _gcsMirrorPageTable * gcsMirrorPageTable_PTR; +typedef struct _gcsMirrorPageTable +{ + /* gckMMU objects. */ + gckMMU mmus[gcdMAX_GPU_COUNT]; + + /* Hardwares which use this shared pagetable. */ + gckHARDWARE hardwares[gcdMAX_GPU_COUNT]; + + /* Number of cores use this shared pagetable. */ + gctUINT32 reference; +} +gcsMirrorPageTable; + +static gcsMirrorPageTable_PTR mirrorPageTable = gcvNULL; +static gctPOINTER mirrorPageTableMutex = gcvNULL; +#endif + +typedef struct _gcsDynamicSpaceNode * gcsDynamicSpaceNode_PTR; +typedef struct _gcsDynamicSpaceNode +{ + gctUINT32 start; + gctINT32 entries; +} +gcsDynamicSpaceNode; + +static void +_WritePageEntry( + IN gctUINT32_PTR PageEntry, + IN gctUINT32 EntryValue + ) +{ + static gctUINT16 data = 0xff00; + + if (*(gctUINT8 *)&data == 0xff) + { + *PageEntry = gcmSWAB32(EntryValue); + } + else + { + *PageEntry = EntryValue; + } +} + +static gctUINT32 +_ReadPageEntry( + IN gctUINT32_PTR PageEntry + ) +{ + static gctUINT16 data = 0xff00; + gctUINT32 entryValue; + + if (*(gctUINT8 *)&data == 0xff) + { + entryValue = *PageEntry; + return gcmSWAB32(entryValue); + } + else + { + return *PageEntry; + } +} + +static gceSTATUS +_FillPageTable( + IN gctUINT32_PTR PageTable, + IN gctUINT32 PageCount, + IN gctUINT32 EntryValue +) +{ + gctUINT i; + + for (i = 0; i < PageCount; i++) + { + _WritePageEntry(PageTable + i, EntryValue); + } + + return gcvSTATUS_OK; +} + +static gceSTATUS +_Link( + IN gckMMU Mmu, + IN gctUINT32 Index, + IN gctUINT32 Next + ) +{ + if (Index >= Mmu->pageTableEntries) + { + /* Just move heap pointer. */ + Mmu->heapList = Next; + } + else + { + /* Address page table. */ + gctUINT32_PTR map = Mmu->mapLogical; + + /* Dispatch on node type. */ + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[Index]))) + { + case gcvMMU_SINGLE: + /* Set single index. */ + _WritePageEntry(&map[Index], (Next << 8) | gcvMMU_SINGLE); + break; + + case gcvMMU_FREE: + /* Set index. */ + _WritePageEntry(&map[Index + 1], Next); + break; + + default: + gcmkFATAL("MMU table correcupted at index %u!", Index); + return gcvSTATUS_HEAP_CORRUPTED; + } + } + + /* Success. */ + return gcvSTATUS_OK; +} + +static gceSTATUS +_AddFree( + IN gckMMU Mmu, + IN gctUINT32 Index, + IN gctUINT32 Node, + IN gctUINT32 Count + ) +{ + gctUINT32_PTR map = Mmu->mapLogical; + + if (Count == 1) + { + /* Initialize a single page node. */ + _WritePageEntry(map + Node, (~((1U<<8)-1)) | gcvMMU_SINGLE); + } + else + { + /* Initialize the node. */ + _WritePageEntry(map + Node + 0, (Count << 8) | gcvMMU_FREE); + _WritePageEntry(map + Node + 1, ~0U); + } + + /* Append the node. */ + return _Link(Mmu, Index, Node); +} + +static gceSTATUS +_Collect( + IN gckMMU Mmu + ) +{ + gctUINT32_PTR map = Mmu->mapLogical; + gceSTATUS status; + gctUINT32 i, previous, start = 0, count = 0; + + previous = Mmu->heapList = ~0U; + Mmu->freeNodes = gcvFALSE; + + /* Walk the entire page table. */ + for (i = 0; i < Mmu->pageTableEntries; ++i) + { + /* Dispatch based on type of page. */ + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[i]))) + { + case gcvMMU_USED: + /* Used page, so close any open node. */ + if (count > 0) + { + /* Add the node. */ + gcmkONERROR(_AddFree(Mmu, previous, start, count)); + + /* Reset the node. */ + previous = start; + count = 0; + } + break; + + case gcvMMU_SINGLE: + /* Single free node. */ + if (count++ == 0) + { + /* Start a new node. */ + start = i; + } + break; + + case gcvMMU_FREE: + /* A free node. */ + if (count == 0) + { + /* Start a new node. */ + start = i; + } + + /* Advance the count. */ + count += _ReadPageEntry(&map[i]) >> 8; + + /* Advance the index into the page table. */ + i += (_ReadPageEntry(&map[i]) >> 8) - 1; + break; + + default: + gcmkFATAL("MMU page table correcupted at index %u!", i); + return gcvSTATUS_HEAP_CORRUPTED; + } + } + + /* See if we have an open node left. */ + if (count > 0) + { + /* Add the node to the list. */ + gcmkONERROR(_AddFree(Mmu, previous, start, count)); + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_MMU, + "Performed a garbage collection of the MMU heap."); + + /* Success. */ + return gcvSTATUS_OK; + +OnError: + /* Return the staus. */ + return status; +} + +static gctUINT32 +_SetPage(gctUINT32 PageAddress) +{ + return PageAddress + /* writable */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); +} + +#if gcdPROCESS_ADDRESS_SPACE +gctUINT32 +_AddressToIndex( + IN gckMMU Mmu, + IN gctUINT32 Address + ) +{ + gctUINT32 mtlbOffset = (Address & gcdMMU_MTLB_MASK) >> gcdMMU_MTLB_SHIFT; + gctUINT32 stlbOffset = (Address & gcdMMU_STLB_4K_MASK) >> gcdMMU_STLB_4K_SHIFT; + + return (mtlbOffset - Mmu->dynamicMappingStart) * gcdMMU_STLB_4K_ENTRY_NUM + stlbOffset; +} + +gctUINT32 +_MtlbOffset( + gctUINT32 Address + ) +{ + return (Address & gcdMMU_MTLB_MASK) >> gcdMMU_MTLB_SHIFT; +} + +gctUINT32 +_StlbOffset( + gctUINT32 Address + ) +{ + return (Address & gcdMMU_STLB_4K_MASK) >> gcdMMU_STLB_4K_SHIFT; +} + +static gceSTATUS +_AllocateStlb( + IN gckOS Os, + OUT gcsMMU_STLB_PTR *Stlb + ) +{ + gceSTATUS status; + gcsMMU_STLB_PTR stlb; + gctPOINTER pointer; + + /* Allocate slave TLB record. */ + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcsMMU_STLB), &pointer)); + stlb = pointer; + + stlb->size = gcdMMU_STLB_4K_SIZE; + + /* Allocate slave TLB entries. */ + gcmkONERROR(gckOS_AllocateContiguous( + Os, + gcvFALSE, + &stlb->size, + &stlb->physical, + (gctPOINTER)&stlb->logical + )); + + gcmkONERROR(gckOS_GetPhysicalAddress(Os, stlb->logical, &stlb->physBase)); + +#if gcdUSE_MMU_EXCEPTION + _FillPageTable(stlb->logical, stlb->size / 4, gcdMMU_STLB_EXCEPTION); +#else + gckOS_ZeroMemory(stlb->logical, stlb->size); +#endif + + *Stlb = stlb; + + return gcvSTATUS_OK; + +OnError: + return status; +} + +gceSTATUS +_SetupProcessAddressSpace( + IN gckMMU Mmu + ) +{ + gceSTATUS status; + gctINT numEntries = 0; + gctUINT32_PTR map; + + numEntries = gcdPROCESS_ADDRESS_SPACE_SIZE + /* Address space mapped by one MTLB entry. */ + / (1 << gcdMMU_MTLB_SHIFT); + + Mmu->dynamicMappingStart = 0; + + Mmu->pageTableSize = numEntries * 4096; + + Mmu->pageTableEntries = Mmu->pageTableSize / gcmSIZEOF(gctUINT32); + + gcmkONERROR(gckOS_Allocate(Mmu->os, + Mmu->pageTableSize, + (void **)&Mmu->mapLogical)); + + /* Initilization. */ + map = Mmu->mapLogical; + _WritePageEntry(map, (Mmu->pageTableEntries << 8) | gcvMMU_FREE); + _WritePageEntry(map + 1, ~0U); + Mmu->heapList = 0; + Mmu->freeNodes = gcvFALSE; + + return gcvSTATUS_OK; + +OnError: + return status; +} +#else +static gceSTATUS +_FillFlatMapping( + IN gckMMU Mmu, + IN gctUINT32 PhysBase, + OUT gctSIZE_T Size + ) +{ + gceSTATUS status; + gctBOOL mutex = gcvFALSE; + gcsMMU_STLB_PTR head = gcvNULL, pre = gcvNULL; + gctUINT32 start = PhysBase & (~gcdMMU_PAGE_64K_MASK); + gctUINT32 end = (PhysBase + Size - 1) & (~gcdMMU_PAGE_64K_MASK); + gctUINT32 mStart = start >> gcdMMU_MTLB_SHIFT; + gctUINT32 mEnd = end >> gcdMMU_MTLB_SHIFT; + gctUINT32 sStart = (start & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT; + gctUINT32 sEnd = (end & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT; + gctBOOL ace = gckHARDWARE_IsFeatureAvailable(Mmu->hardware, gcvFEATURE_ACE); + + /* Grab the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE)); + mutex = gcvTRUE; + + while (mStart <= mEnd) + { + gcmkASSERT(mStart < gcdMMU_MTLB_ENTRY_NUM); + if (*(Mmu->mtlbLogical + mStart) == 0) + { + gcsMMU_STLB_PTR stlb; + gctPOINTER pointer = gcvNULL; + gctUINT32 last = (mStart == mEnd) ? sEnd : (gcdMMU_STLB_64K_ENTRY_NUM - 1); + gctUINT32 mtlbEntry; + + gcmkONERROR(gckOS_Allocate(Mmu->os, sizeof(struct _gcsMMU_STLB), &pointer)); + stlb = pointer; + + stlb->mtlbEntryNum = 0; + stlb->next = gcvNULL; + stlb->physical = gcvNULL; + stlb->logical = gcvNULL; + stlb->size = gcdMMU_STLB_64K_SIZE; + stlb->pageCount = 0; + + if (pre == gcvNULL) + { + pre = head = stlb; + } + else + { + gcmkASSERT(pre->next == gcvNULL); + pre->next = stlb; + pre = stlb; + } + + gcmkONERROR( + gckOS_AllocateContiguous(Mmu->os, + gcvFALSE, + &stlb->size, + &stlb->physical, + (gctPOINTER)&stlb->logical)); + + gcmkONERROR(gckOS_ZeroMemory(stlb->logical, stlb->size)); + + gcmkONERROR(gckOS_GetPhysicalAddress( + Mmu->os, + stlb->logical, + &stlb->physBase)); + + if (stlb->physBase & (gcdMMU_STLB_64K_SIZE - 1)) + { + gcmkONERROR(gcvSTATUS_NOT_ALIGNED); + } + + mtlbEntry = stlb->physBase + /* 64KB page size */ + | (1 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + + if (ace) + { + mtlbEntry = mtlbEntry + /* Secure */ + | (1 << 4); + } + + _WritePageEntry(Mmu->mtlbLogical + mStart, mtlbEntry); + +#if gcdMMU_TABLE_DUMP + gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n", + __FUNCTION__, __LINE__, + mStart, + _ReadPageEntry(Mmu->mtlbLogical + mStart)); +#endif + + stlb->mtlbIndex = mStart; + stlb->mtlbEntryNum = 1; +#if gcdMMU_TABLE_DUMP + gckOS_Print("%s(%d): STLB: logical:%08x -> physical:%08x\n", + __FUNCTION__, __LINE__, + stlb->logical, + stlb->physBase); +#endif + + while (sStart <= last) + { + gcmkASSERT(!(start & gcdMMU_PAGE_64K_MASK)); + _WritePageEntry(stlb->logical + sStart, _SetPage(start)); +#if gcdMMU_TABLE_DUMP + gckOS_Print("%s(%d): insert STLB[%d]: %08x\n", + __FUNCTION__, __LINE__, + sStart, + _ReadPageEntry(stlb->logical + sStart)); +#endif + /* next page. */ + start += gcdMMU_PAGE_64K_SIZE; + sStart++; + stlb->pageCount++; + } + + sStart = 0; + ++mStart; + } + else + { + gcmkONERROR(gcvSTATUS_INVALID_REQUEST); + } + } + + /* Insert the stlb into staticSTLB. */ + if (Mmu->staticSTLB == gcvNULL) + { + Mmu->staticSTLB = head; + } + else + { + gcmkASSERT(pre == gcvNULL); + gcmkASSERT(pre->next == gcvNULL); + pre->next = Mmu->staticSTLB; + Mmu->staticSTLB = head; + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + + return gcvSTATUS_OK; + +OnError: + + /* Roll back. */ + while (head != gcvNULL) + { + pre = head; + head = head->next; + + if (pre->physical != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_FreeContiguous(Mmu->os, + pre->physical, + pre->logical, + pre->size)); + } + + if (pre->mtlbEntryNum != 0) + { + gcmkASSERT(pre->mtlbEntryNum == 1); + _WritePageEntry(Mmu->mtlbLogical + pre->mtlbIndex, 0); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre)); + } + + if (mutex) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + } + + return status; +} + +static gceSTATUS +_FindDynamicSpace( + IN gckMMU Mmu, + OUT gcsDynamicSpaceNode_PTR *Array, + OUT gctINT * Size + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gctPOINTER pointer = gcvNULL; + gcsDynamicSpaceNode_PTR array = gcvNULL; + gctINT size = 0; + gctINT i = 0, nodeStart = -1, nodeEntries = 0; + + /* Allocate memory for the array. */ + gcmkONERROR(gckOS_Allocate(Mmu->os, + gcmSIZEOF(*array) * (gcdMMU_MTLB_ENTRY_NUM / 2), + &pointer)); + + array = (gcsDynamicSpaceNode_PTR)pointer; + + /* Loop all the entries. */ + while (i < gcdMMU_MTLB_ENTRY_NUM) + { + if (!Mmu->mtlbLogical[i]) + { + if (nodeStart < 0) + { + /* This is the first entry of the dynamic space. */ + nodeStart = i; + nodeEntries = 1; + } + else + { + /* Other entries of the dynamic space. */ + nodeEntries++; + } + } + else if (nodeStart >= 0) + { + /* Save the previous node. */ + array[size].start = nodeStart; + array[size].entries = nodeEntries; + size++; + + /* Reset the start. */ + nodeStart = -1; + nodeEntries = 0; + } + + i++; + } + + /* Save the previous node. */ + if (nodeStart >= 0) + { + array[size].start = nodeStart; + array[size].entries = nodeEntries; + size++; + } + +#if gcdMMU_TABLE_DUMP + for (i = 0; i < size; i++) + { + gckOS_Print("%s(%d): [%d]: start=%d, entries=%d.\n", + __FUNCTION__, __LINE__, + i, + array[i].start, + array[i].entries); + } +#endif + + *Array = array; + *Size = size; + + return gcvSTATUS_OK; + +OnError: + if (pointer != gcvNULL) + { + gckOS_Free(Mmu->os, pointer); + } + + return status; +} + +static gceSTATUS +_SetupDynamicSpace( + IN gckMMU Mmu + ) +{ + gceSTATUS status; + gcsDynamicSpaceNode_PTR nodeArray = gcvNULL; + gctINT i, nodeArraySize = 0; + gctUINT32 physical; + gctINT numEntries = 0; + gctUINT32_PTR map; + gctBOOL acquired = gcvFALSE; + gctUINT32 mtlbEntry; + gctBOOL ace = gckHARDWARE_IsFeatureAvailable(Mmu->hardware, gcvFEATURE_ACE); + + /* Find all the dynamic address space. */ + gcmkONERROR(_FindDynamicSpace(Mmu, &nodeArray, &nodeArraySize)); + + /* TODO: We only use the largest one for now. */ + for (i = 0; i < nodeArraySize; i++) + { + if (nodeArray[i].entries > numEntries) + { + Mmu->dynamicMappingStart = nodeArray[i].start; + numEntries = nodeArray[i].entries; + } + } + + gckOS_Free(Mmu->os, (gctPOINTER)nodeArray); + + Mmu->pageTableSize = numEntries * 4096; + + gcmkSAFECASTSIZET(Mmu->pageTableEntries, Mmu->pageTableSize / gcmSIZEOF(gctUINT32)); + + gcmkONERROR(gckOS_Allocate(Mmu->os, + Mmu->pageTableSize, + (void **)&Mmu->mapLogical)); + + /* Construct Slave TLB. */ + gcmkONERROR(gckOS_AllocateContiguous(Mmu->os, + gcvFALSE, + &Mmu->pageTableSize, + &Mmu->pageTablePhysical, + (gctPOINTER)&Mmu->pageTableLogical)); + +#if gcdUSE_MMU_EXCEPTION + gcmkONERROR(_FillPageTable(Mmu->pageTableLogical, + Mmu->pageTableEntries, + /* Enable exception */ + 1 << 1)); +#else + /* Invalidate all entries. */ + gcmkONERROR(gckOS_ZeroMemory(Mmu->pageTableLogical, + Mmu->pageTableSize)); +#endif + + /* Initilization. */ + map = Mmu->mapLogical; + _WritePageEntry(map, (Mmu->pageTableEntries << 8) | gcvMMU_FREE); + _WritePageEntry(map + 1, ~0U); + Mmu->heapList = 0; + Mmu->freeNodes = gcvFALSE; + + gcmkONERROR(gckOS_GetPhysicalAddress(Mmu->os, + Mmu->pageTableLogical, + &physical)); + + /* Grab the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Map to Master TLB. */ + for (i = (gctINT)Mmu->dynamicMappingStart; + i < (gctINT)Mmu->dynamicMappingStart + numEntries; + i++) + { + mtlbEntry = physical + /* 4KB page size */ + | (0 << 2) + /* Ignore exception */ + | (0 << 1) + /* Present */ + | (1 << 0); + + if (ace) + { + mtlbEntry = mtlbEntry + /* Secure */ + | (1 << 4); + } + + _WritePageEntry(Mmu->mtlbLogical + i, mtlbEntry); + +#if gcdMMU_TABLE_DUMP + gckOS_Print("%s(%d): insert MTLB[%d]: %08x\n", + __FUNCTION__, __LINE__, + i, + _ReadPageEntry(Mmu->mtlbLogical + i)); +#endif + physical += gcdMMU_STLB_4K_SIZE; + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + + return gcvSTATUS_OK; + +OnError: + if (Mmu->mapLogical) + { + gcmkVERIFY_OK( + gckOS_Free(Mmu->os, (gctPOINTER) Mmu->mapLogical)); + + + gcmkVERIFY_OK( + gckOS_FreeContiguous(Mmu->os, + Mmu->pageTablePhysical, + (gctPOINTER) Mmu->pageTableLogical, + Mmu->pageTableSize)); + } + + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + } + + return status; +} +#endif + +/******************************************************************************* +** +** _Construct +** +** Construct a new gckMMU object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSIZE_T MmuSize +** Number of bytes for the page table. +** +** OUTPUT: +** +** gckMMU * Mmu +** Pointer to a variable that receives the gckMMU object pointer. +*/ +gceSTATUS +_Construct( + IN gckKERNEL Kernel, + IN gctSIZE_T MmuSize, + OUT gckMMU * Mmu + ) +{ + gckOS os; + gckHARDWARE hardware; + gceSTATUS status; + gckMMU mmu = gcvNULL; + gctUINT32_PTR map; + gctPOINTER pointer = gcvNULL; +#if gcdPROCESS_ADDRESS_SPACE + gctUINT32 i; + gctUINT32 physical; +#endif + gctUINT32 physBase; + gctUINT32 physSize; + gctUINT32 gpuAddress; + + gcmkHEADER_ARG("Kernel=0x%x MmuSize=%lu", Kernel, MmuSize); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(MmuSize > 0); + gcmkVERIFY_ARGUMENT(Mmu != gcvNULL); + + /* Extract the gckOS object pointer. */ + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Extract the gckHARDWARE object pointer. */ + hardware = Kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + /* Allocate memory for the gckMMU object. */ + gcmkONERROR(gckOS_Allocate(os, sizeof(struct _gckMMU), &pointer)); + + mmu = pointer; + + /* Initialize the gckMMU object. */ + mmu->object.type = gcvOBJ_MMU; + mmu->os = os; + mmu->hardware = hardware; + mmu->pageTableMutex = gcvNULL; + mmu->pageTableLogical = gcvNULL; + mmu->mtlbLogical = gcvNULL; + mmu->staticSTLB = gcvNULL; + mmu->enabled = gcvFALSE; + mmu->mapLogical = gcvNULL; + + /* Create the page table mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &mmu->pageTableMutex)); + + if (hardware->mmuVersion == 0) + { + mmu->pageTableSize = MmuSize; + + /* Construct address space management table. */ + gcmkONERROR(gckOS_Allocate(mmu->os, + mmu->pageTableSize, + &pointer)); + + mmu->mapLogical = pointer; + + /* Construct page table read by GPU. */ + gcmkONERROR(gckOS_AllocateContiguous(mmu->os, + gcvFALSE, + &mmu->pageTableSize, + &mmu->pageTablePhysical, + (gctPOINTER)&mmu->pageTableLogical)); + + + /* Compute number of entries in page table. */ + gcmkSAFECASTSIZET(mmu->pageTableEntries, mmu->pageTableSize / sizeof(gctUINT32)); + + /* Mark all pages as free. */ + map = mmu->mapLogical; + +#if gcdMMU_CLEAR_VALUE + _FillPageTable(mmu->pageTableLogical, mmu->pageTableEntries, gcdMMU_CLEAR_VALUE); +#endif + + _WritePageEntry(map, (mmu->pageTableEntries << 8) | gcvMMU_FREE); + _WritePageEntry(map + 1, ~0U); + mmu->heapList = 0; + mmu->freeNodes = gcvFALSE; + } + else + { + /* Allocate the 4K mode MTLB table. */ + mmu->mtlbSize = gcdMMU_MTLB_SIZE + 64; + + gcmkONERROR( + gckOS_AllocateContiguous(os, + gcvFALSE, + &mmu->mtlbSize, + &mmu->mtlbPhysical, + &pointer)); + + mmu->mtlbLogical = pointer; + +#if gcdPROCESS_ADDRESS_SPACE + _FillPageTable(pointer, mmu->mtlbSize / 4, gcdMMU_MTLB_EXCEPTION); + + /* Allocate a array to store stlbs. */ + gcmkONERROR(gckOS_Allocate(os, mmu->mtlbSize, &mmu->stlbs)); + + gckOS_ZeroMemory(mmu->stlbs, mmu->mtlbSize); + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + gcmkONERROR(gckOS_AtomConstruct(os, &mmu->pageTableDirty[i])); + } + + _SetupProcessAddressSpace(mmu); + + /* Map kernel command buffer in MMU. */ + for (i = 0; i < gcdCOMMAND_QUEUES; i++) + { + gcmkONERROR(gckOS_GetPhysicalAddress( + mmu->os, + Kernel->command->queues[i].logical, + &physical + )); + + gcmkONERROR(gckMMU_FlatMapping(mmu, physical)); + } +#else + /* Invalid all the entries. */ + gcmkONERROR( + gckOS_ZeroMemory(pointer, mmu->mtlbSize)); + + gcmkONERROR( + gckOS_QueryOption(mmu->os, "physBase", &physBase)); + + gcmkONERROR( + gckOS_QueryOption(mmu->os, "physSize", &physSize)); + + gcmkONERROR( + gckOS_CPUPhysicalToGPUPhysical(mmu->os, physBase, &gpuAddress)); + + /* Setup [physBase - physSize) flat mapping. */ + gcmkONERROR(_FillFlatMapping( + mmu, + gpuAddress, + physSize + )); + + gcmkONERROR(_SetupDynamicSpace(mmu)); +#endif + } + + /* Return the gckMMU object pointer. */ + *Mmu = mmu; + + /* Success. */ + gcmkFOOTER_ARG("*Mmu=0x%x", *Mmu); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (mmu != gcvNULL) + { + if (mmu->mapLogical != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_Free(os, (gctPOINTER) mmu->mapLogical)); + + + gcmkVERIFY_OK( + gckOS_FreeContiguous(os, + mmu->pageTablePhysical, + (gctPOINTER) mmu->pageTableLogical, + mmu->pageTableSize)); + } + + if (mmu->mtlbLogical != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_FreeContiguous(os, + mmu->mtlbPhysical, + (gctPOINTER) mmu->mtlbLogical, + mmu->mtlbSize)); + } + + if (mmu->pageTableMutex != gcvNULL) + { + /* Delete the mutex. */ + gcmkVERIFY_OK( + gckOS_DeleteMutex(os, mmu->pageTableMutex)); + } + + /* Mark the gckMMU object as unknown. */ + mmu->object.type = gcvOBJ_UNKNOWN; + + /* Free the allocates memory. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, mmu)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** _Destroy +** +** Destroy a gckMMU object. +** +** INPUT: +** +** gckMMU Mmu +** Pointer to an gckMMU object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +_Destroy( + IN gckMMU Mmu + ) +{ +#if gcdPROCESS_ADDRESS_SPACE + gctUINT32 i; +#endif + gcmkHEADER_ARG("Mmu=0x%x", Mmu); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + + while (Mmu->staticSTLB != gcvNULL) + { + gcsMMU_STLB_PTR pre = Mmu->staticSTLB; + Mmu->staticSTLB = pre->next; + + if (pre->physical != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_FreeContiguous(Mmu->os, + pre->physical, + pre->logical, + pre->size)); + } + + if (pre->mtlbEntryNum != 0) + { + gcmkASSERT(pre->mtlbEntryNum == 1); + _WritePageEntry(Mmu->mtlbLogical + pre->mtlbIndex, 0); +#if gcdMMU_TABLE_DUMP + gckOS_Print("%s(%d): clean MTLB[%d]\n", + __FUNCTION__, __LINE__, + pre->mtlbIndex); +#endif + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, pre)); + } + + if (Mmu->hardware->mmuVersion != 0) + { + gcmkVERIFY_OK( + gckOS_FreeContiguous(Mmu->os, + Mmu->mtlbPhysical, + (gctPOINTER) Mmu->mtlbLogical, + Mmu->mtlbSize)); + } + + /* Free address space management table. */ + if (Mmu->mapLogical != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_Free(Mmu->os, (gctPOINTER) Mmu->mapLogical)); + } + + if (Mmu->pageTableLogical != gcvNULL) + { + /* Free page table. */ + gcmkVERIFY_OK( + gckOS_FreeContiguous(Mmu->os, + Mmu->pageTablePhysical, + (gctPOINTER) Mmu->pageTableLogical, + Mmu->pageTableSize)); + } + + /* Delete the page table mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->pageTableMutex)); + +#if gcdPROCESS_ADDRESS_SPACE + for (i = 0; i < Mmu->mtlbSize / 4; i++) + { + struct _gcsMMU_STLB *stlb = ((struct _gcsMMU_STLB **)Mmu->stlbs)[i]; + + if (stlb) + { + gcmkVERIFY_OK(gckOS_FreeContiguous( + Mmu->os, + stlb->physical, + stlb->logical, + stlb->size)); + + gcmkOS_SAFE_FREE(Mmu->os, stlb); + } + } + + gcmkOS_SAFE_FREE(Mmu->os, Mmu->stlbs); +#endif + + /* Mark the gckMMU object as unknown. */ + Mmu->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckMMU object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, Mmu)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** _AdjstIndex +** +** Adjust the index from which we search for a usable node to make sure +** index allocated is greater than Start. +*/ +gceSTATUS +_AdjustIndex( + IN gckMMU Mmu, + IN gctUINT32 Index, + IN gctUINT32 PageCount, + IN gctUINT32 Start, + OUT gctUINT32 * IndexAdjusted + ) +{ + gceSTATUS status; + gctUINT32 index = Index; + gctUINT32_PTR map = Mmu->mapLogical; + + gcmkHEADER(); + + for (; index < Mmu->pageTableEntries;) + { + gctUINT32 result = 0; + gctUINT32 nodeSize = 0; + + if (index >= Start) + { + break; + } + + switch (gcmENTRY_TYPE(map[index])) + { + case gcvMMU_SINGLE: + nodeSize = 1; + break; + + case gcvMMU_FREE: + nodeSize = map[index] >> 8; + break; + + default: + gcmkFATAL("MMU table correcupted at index %u!", index); + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + if (nodeSize > PageCount) + { + result = index + (nodeSize - PageCount); + + if (result >= Start) + { + break; + } + } + + switch (gcmENTRY_TYPE(map[index])) + { + case gcvMMU_SINGLE: + index = map[index] >> 8; + break; + + case gcvMMU_FREE: + index = map[index + 1]; + break; + + default: + gcmkFATAL("MMU table correcupted at index %u!", index); + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + + *IndexAdjusted = index; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckMMU_Construct( + IN gckKERNEL Kernel, + IN gctSIZE_T MmuSize, + OUT gckMMU * Mmu + ) +{ +#if gcdSHARED_PAGETABLE + gceSTATUS status; + gctPOINTER pointer; + + gcmkHEADER_ARG("Kernel=0x%08x", Kernel); + + if (sharedPageTable == gcvNULL) + { + gcmkONERROR( + gckOS_Allocate(Kernel->os, + sizeof(struct _gcsSharedPageTable), + &pointer)); + sharedPageTable = pointer; + + gcmkONERROR( + gckOS_ZeroMemory(sharedPageTable, + sizeof(struct _gcsSharedPageTable))); + + gcmkONERROR(_Construct(Kernel, MmuSize, &sharedPageTable->mmu)); + } + + *Mmu = sharedPageTable->mmu; + + sharedPageTable->hardwares[sharedPageTable->reference] = Kernel->hardware; + + sharedPageTable->reference++; + + gcmkFOOTER_ARG("sharedPageTable->reference=%lu", sharedPageTable->reference); + return gcvSTATUS_OK; + +OnError: + if (sharedPageTable) + { + if (sharedPageTable->mmu) + { + gcmkVERIFY_OK(gckMMU_Destroy(sharedPageTable->mmu)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, sharedPageTable)); + } + + gcmkFOOTER(); + return status; +#elif gcdMIRROR_PAGETABLE + gceSTATUS status; + gctPOINTER pointer; + + gcmkHEADER_ARG("Kernel=0x%08x", Kernel); + + if (mirrorPageTable == gcvNULL) + { + gcmkONERROR( + gckOS_Allocate(Kernel->os, + sizeof(struct _gcsMirrorPageTable), + &pointer)); + mirrorPageTable = pointer; + + gcmkONERROR( + gckOS_ZeroMemory(mirrorPageTable, + sizeof(struct _gcsMirrorPageTable))); + + gcmkONERROR( + gckOS_CreateMutex(Kernel->os, &mirrorPageTableMutex)); + } + + gcmkONERROR(_Construct(Kernel, MmuSize, Mmu)); + + mirrorPageTable->mmus[mirrorPageTable->reference] = *Mmu; + + mirrorPageTable->hardwares[mirrorPageTable->reference] = Kernel->hardware; + + mirrorPageTable->reference++; + + gcmkFOOTER_ARG("mirrorPageTable->reference=%lu", mirrorPageTable->reference); + return gcvSTATUS_OK; + +OnError: + if (mirrorPageTable && mirrorPageTable->reference == 0) + { + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, mirrorPageTable)); + } + + gcmkFOOTER(); + return status; +#else + return _Construct(Kernel, MmuSize, Mmu); +#endif +} + +gceSTATUS +gckMMU_Destroy( + IN gckMMU Mmu + ) +{ +#if gcdSHARED_PAGETABLE + gckOS os = Mmu->os; + + sharedPageTable->reference--; + + if (sharedPageTable->reference == 0) + { + if (sharedPageTable->mmu) + { + gcmkVERIFY_OK(_Destroy(Mmu)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, sharedPageTable)); + } + + return gcvSTATUS_OK; +#elif gcdMIRROR_PAGETABLE + mirrorPageTable->reference--; + + if (mirrorPageTable->reference == 0) + { + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, mirrorPageTable)); + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Mmu->os, mirrorPageTableMutex)); + } + + return _Destroy(Mmu); +#else + return _Destroy(Mmu); +#endif +} + +/******************************************************************************* +** +** gckMMU_AllocatePages +** +** Allocate pages inside the page table. +** +** INPUT: +** +** gckMMU Mmu +** Pointer to an gckMMU object. +** +** gctSIZE_T PageCount +** Number of pages to allocate. +** +** OUTPUT: +** +** gctPOINTER * PageTable +** Pointer to a variable that receives the base address of the page +** table. +** +** gctUINT32 * Address +** Pointer to a variable that receives the hardware specific address. +*/ +gceSTATUS +_AllocatePages( + IN gckMMU Mmu, + IN gctSIZE_T PageCount, + IN gceSURF_TYPE Type, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ) +{ + gceSTATUS status; + gctBOOL mutex = gcvFALSE; + gctUINT32 index = 0, previous = ~0U, left; + gctUINT32_PTR map; + gctBOOL gotIt; + gctUINT32 address; + gctUINT32 pageCount; + + gcmkHEADER_ARG("Mmu=0x%x PageCount=%lu", Mmu, PageCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageCount > 0); + gcmkVERIFY_ARGUMENT(PageTable != gcvNULL); + + if (PageCount > Mmu->pageTableEntries) + { + /* Not enough pages avaiable. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + gcmkSAFECASTSIZET(pageCount, PageCount); + + /* Grab the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE)); + mutex = gcvTRUE; + + /* Cast pointer to page table. */ + for (map = Mmu->mapLogical, gotIt = gcvFALSE; !gotIt;) + { + index = Mmu->heapList; + + if ((Mmu->hardware->mmuVersion == 0) && (Type == gcvSURF_VERTEX)) + { + gcmkONERROR(_AdjustIndex( + Mmu, + index, + pageCount, + gcdVERTEX_START / gcmSIZEOF(gctUINT32), + &index + )); + } + + /* Walk the heap list. */ + for (; !gotIt && (index < Mmu->pageTableEntries);) + { + /* Check the node type. */ + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[index]))) + { + case gcvMMU_SINGLE: + /* Single odes are valid if we only need 1 page. */ + if (pageCount == 1) + { + gotIt = gcvTRUE; + } + else + { + /* Move to next node. */ + previous = index; + index = _ReadPageEntry(&map[index]) >> 8; + } + break; + + case gcvMMU_FREE: + /* Test if the node has enough space. */ + if (pageCount <= (_ReadPageEntry(&map[index]) >> 8)) + { + gotIt = gcvTRUE; + } + else + { + /* Move to next node. */ + previous = index; + index = _ReadPageEntry(&map[index + 1]); + } + break; + + default: + gcmkFATAL("MMU table correcupted at index %u!", index); + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + + /* Test if we are out of memory. */ + if (index >= Mmu->pageTableEntries) + { + if (Mmu->freeNodes) + { + /* Time to move out the trash! */ + gcmkONERROR(_Collect(Mmu)); + } + else + { + /* Out of resources. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + } + + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[index]))) + { + case gcvMMU_SINGLE: + /* Unlink single node from free list. */ + gcmkONERROR( + _Link(Mmu, previous, _ReadPageEntry(&map[index]) >> 8)); + break; + + case gcvMMU_FREE: + /* Check how many pages will be left. */ + left = (_ReadPageEntry(&map[index]) >> 8) - pageCount; + switch (left) + { + case 0: + /* The entire node is consumed, just unlink it. */ + gcmkONERROR( + _Link(Mmu, previous, _ReadPageEntry(&map[index + 1]))); + break; + + case 1: + /* One page will remain. Convert the node to a single node and + ** advance the index. */ + _WritePageEntry(&map[index], (_ReadPageEntry(&map[index + 1]) << 8) | gcvMMU_SINGLE); + index ++; + break; + + default: + /* Enough pages remain for a new node. However, we will just adjust + ** the size of the current node and advance the index. */ + _WritePageEntry(&map[index], (left << 8) | gcvMMU_FREE); + index += left; + break; + } + break; + } + + /* Mark node as used. */ + gcmkONERROR(_FillPageTable(&map[index], pageCount, gcvMMU_USED)); + + /* Return pointer to page table. */ + *PageTable = &Mmu->pageTableLogical[index]; + + /* Build virtual address. */ + if (Mmu->hardware->mmuVersion == 0) + { + gcmkONERROR( + gckHARDWARE_BuildVirtualAddress(Mmu->hardware, index, 0, &address)); + } + else + { + gctUINT32 masterOffset = index / gcdMMU_STLB_4K_ENTRY_NUM + + Mmu->dynamicMappingStart; + gctUINT32 slaveOffset = index % gcdMMU_STLB_4K_ENTRY_NUM; + + address = (masterOffset << gcdMMU_MTLB_SHIFT) + | (slaveOffset << gcdMMU_STLB_4K_SHIFT); + } + + if (Address != gcvNULL) + { + *Address = address; + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + + /* Success. */ + gcmkFOOTER_ARG("*PageTable=0x%x *Address=%08x", + *PageTable, gcmOPT_VALUE(Address)); + return gcvSTATUS_OK; + +OnError: + + if (mutex) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckMMU_FreePages +** +** Free pages inside the page table. +** +** INPUT: +** +** gckMMU Mmu +** Pointer to an gckMMU object. +** +** gctPOINTER PageTable +** Base address of the page table to free. +** +** gctSIZE_T PageCount +** Number of pages to free. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +_FreePages( + IN gckMMU Mmu, + IN gctPOINTER PageTable, + IN gctSIZE_T PageCount + ) +{ + gctUINT32_PTR node; + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gctUINT32 pageCount; + + gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=%lu", + Mmu, PageTable, PageCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageTable != gcvNULL); + gcmkVERIFY_ARGUMENT(PageCount > 0); + + gcmkSAFECASTSIZET(pageCount, PageCount); + + /* Get the node by index. */ + node = Mmu->mapLogical + ((gctUINT32_PTR)PageTable - Mmu->pageTableLogical); + + gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE)); + acquired = gcvTRUE; + +#if gcdMMU_CLEAR_VALUE + if (Mmu->hardware->mmuVersion == 0) + { + _FillPageTable(PageTable, pageCount, gcdMMU_CLEAR_VALUE); + } +#endif + + if (PageCount == 1) + { + /* Single page node. */ + _WritePageEntry(node, (~((1U<<8)-1)) | gcvMMU_SINGLE); +#if gcdUSE_MMU_EXCEPTION + /* Enable exception */ + _WritePageEntry(PageTable, (1 << 1)); +#endif + } + else + { + /* Mark the node as free. */ + _WritePageEntry(node, (pageCount << 8) | gcvMMU_FREE); + _WritePageEntry(node + 1, ~0U); + +#if gcdUSE_MMU_EXCEPTION + /* Enable exception */ + gcmkVERIFY_OK(_FillPageTable(PageTable, pageCount, 1 << 1)); +#endif + } + + /* We have free nodes. */ + Mmu->freeNodes = gcvTRUE; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + acquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckMMU_AllocatePages( + IN gckMMU Mmu, + IN gctSIZE_T PageCount, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ) +{ + return gckMMU_AllocatePagesEx( + Mmu, PageCount, gcvSURF_TYPE_UNKNOWN, PageTable, Address); +} + +gceSTATUS +gckMMU_AllocatePagesEx( + IN gckMMU Mmu, + IN gctSIZE_T PageCount, + IN gceSURF_TYPE Type, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ) +{ +#if gcdMIRROR_PAGETABLE + gceSTATUS status; + gctPOINTER pageTable; + gctUINT32 address; + gctINT i; + gckMMU mmu; + gctBOOL acquired = gcvFALSE; + gctBOOL allocated = gcvFALSE; + + gckOS_AcquireMutex(Mmu->os, mirrorPageTableMutex, gcvINFINITE); + acquired = gcvTRUE; + + /* Allocate page table for current MMU. */ + for (i = 0; i < (gctINT)mirrorPageTable->reference; i++) + { + if (Mmu == mirrorPageTable->mmus[i]) + { + gcmkONERROR(_AllocatePages(Mmu, PageCount, Type, PageTable, Address)); + allocated = gcvTRUE; + } + } + + /* Allocate page table for other MMUs. */ + for (i = 0; i < (gctINT)mirrorPageTable->reference; i++) + { + mmu = mirrorPageTable->mmus[i]; + + if (Mmu != mmu) + { + gcmkONERROR(_AllocatePages(mmu, PageCount, Type, &pageTable, &address)); + gcmkASSERT(address == *Address); + } + } + + gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex); + acquired = gcvFALSE; + + return gcvSTATUS_OK; +OnError: + + if (allocated) + { + /* Page tables for multiple GPU always keep the same. So it is impossible + * the fist one allocates successfully but others fail. + */ + gcmkASSERT(0); + } + + if (acquired) + { + gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex); + } + + return status; +#else + return _AllocatePages(Mmu, PageCount, Type, PageTable, Address); +#endif +} + +gceSTATUS +gckMMU_FreePages( + IN gckMMU Mmu, + IN gctPOINTER PageTable, + IN gctSIZE_T PageCount + ) +{ +#if gcdMIRROR_PAGETABLE + gctINT i; + gctUINT32 offset; + gckMMU mmu; + + gckOS_AcquireMutex(Mmu->os, mirrorPageTableMutex, gcvINFINITE); + + gcmkVERIFY_OK(_FreePages(Mmu, PageTable, PageCount)); + + offset = (gctUINT32)PageTable - (gctUINT32)Mmu->pageTableLogical; + + for (i = 0; i < (gctINT)mirrorPageTable->reference; i++) + { + mmu = mirrorPageTable->mmus[i]; + + if (mmu != Mmu) + { + gcmkVERIFY_OK(_FreePages(mmu, mmu->pageTableLogical + offset/4, PageCount)); + } + } + + gckOS_ReleaseMutex(Mmu->os, mirrorPageTableMutex); + + return gcvSTATUS_OK; +#else + return _FreePages(Mmu, PageTable, PageCount); +#endif +} + +gceSTATUS +gckMMU_SetPage( + IN gckMMU Mmu, + IN gctUINT32 PageAddress, + IN gctUINT32 *PageEntry + ) +{ +#if gcdMIRROR_PAGETABLE + gctUINT32_PTR pageEntry; + gctINT i; + gckMMU mmu; + gctUINT32 offset = (gctUINT32)PageEntry - (gctUINT32)Mmu->pageTableLogical; +#endif + + gcmkHEADER_ARG("Mmu=0x%x", Mmu); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageEntry != gcvNULL); + gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF)); + + if (Mmu->hardware->mmuVersion == 0) + { + _WritePageEntry(PageEntry, PageAddress); + } + else + { + _WritePageEntry(PageEntry, _SetPage(PageAddress)); + } + +#if gcdMIRROR_PAGETABLE + for (i = 0; i < (gctINT)mirrorPageTable->reference; i++) + { + mmu = mirrorPageTable->mmus[i]; + + if (mmu != Mmu) + { + pageEntry = mmu->pageTableLogical + offset / 4; + + if (mmu->hardware->mmuVersion == 0) + { + _WritePageEntry(pageEntry, PageAddress); + } + else + { + _WritePageEntry(pageEntry, _SetPage(PageAddress)); + } + } + + } +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +gckMMU_GetPageEntry( + IN gckMMU Mmu, + IN gctUINT32 Address, + IN gctUINT32_PTR *PageTable + ) +{ + gceSTATUS status; + struct _gcsMMU_STLB *stlb; + struct _gcsMMU_STLB **stlbs = Mmu->stlbs; + gctUINT32 offset = _MtlbOffset(Address); + gctUINT32 mtlbEntry; + gctBOOL ace = gckHARDWARE_IsFeatureAvailable(Mmu->hardware, gcvFEATURE_ACE); + + gcmkHEADER_ARG("Mmu=0x%x", Mmu); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT((Address & 0xFFF) == 0); + + stlb = stlbs[offset]; + + if (stlb == gcvNULL) + { + gcmkONERROR(_AllocateStlb(Mmu->os, &stlb)); + + mtlbEntry = stlb->physBase + | gcdMMU_MTLB_4K_PAGE + | gcdMMU_MTLB_PRESENT + ; + + if (ace) + { + mtlbEntry = mtlbEntry + /* Secure */ + | (1 << 4); + } + + /* Insert Slave TLB address to Master TLB entry.*/ + _WritePageEntry(Mmu->mtlbLogical + offset, mtlbEntry); + + /* Record stlb. */ + stlbs[offset] = stlb; + } + + *PageTable = &stlb->logical[_StlbOffset(Address)]; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +_CheckMap( + IN gckMMU Mmu + ) +{ + gceSTATUS status; + gctUINT32_PTR map = Mmu->mapLogical; + gctUINT32 index; + + for (index = Mmu->heapList; index < Mmu->pageTableEntries;) + { + /* Check the node type. */ + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[index]))) + { + case gcvMMU_SINGLE: + /* Move to next node. */ + index = _ReadPageEntry(&map[index]) >> 8; + break; + + case gcvMMU_FREE: + /* Move to next node. */ + index = _ReadPageEntry(&map[index + 1]); + break; + + default: + gcmkFATAL("MMU table correcupted at index [%u] = %x!", index, map[index]); + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + + return gcvSTATUS_OK; + +OnError: + return status; +} + +gceSTATUS +gckMMU_FlatMapping( + IN gckMMU Mmu, + IN gctUINT32 Physical + ) +{ + gceSTATUS status; + gctUINT32 index = _AddressToIndex(Mmu, Physical); + gctUINT32 i; + gctBOOL gotIt = gcvFALSE; + gctUINT32_PTR map = Mmu->mapLogical; + gctUINT32 previous = ~0U; + gctUINT32_PTR pageTable; + + gckMMU_GetPageEntry(Mmu, Physical, &pageTable); + + _WritePageEntry(pageTable, _SetPage(Physical)); + + if (map) + { + /* Find node which contains index. */ + for (i = 0; !gotIt && (i < Mmu->pageTableEntries);) + { + gctUINT32 numPages; + + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[i]))) + { + case gcvMMU_SINGLE: + if (i == index) + { + gotIt = gcvTRUE; + } + else + { + previous = i; + i = _ReadPageEntry(&map[i]) >> 8; + } + break; + + case gcvMMU_FREE: + numPages = _ReadPageEntry(&map[i]) >> 8; + if (index >= i && index < i + numPages) + { + gotIt = gcvTRUE; + } + else + { + previous = i; + i = _ReadPageEntry(&map[i + 1]); + } + break; + + default: + gcmkFATAL("MMU table correcupted at index %u!", index); + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + + switch (gcmENTRY_TYPE(_ReadPageEntry(&map[i]))) + { + case gcvMMU_SINGLE: + /* Unlink single node from free list. */ + gcmkONERROR( + _Link(Mmu, previous, _ReadPageEntry(&map[i]) >> 8)); + break; + + case gcvMMU_FREE: + /* Split the node. */ + { + gctUINT32 start; + gctUINT32 next = _ReadPageEntry(&map[i+1]); + gctUINT32 total = _ReadPageEntry(&map[i]) >> 8; + gctUINT32 countLeft = index - i; + gctUINT32 countRight = total - countLeft - 1; + + if (countLeft) + { + start = i; + _AddFree(Mmu, previous, start, countLeft); + previous = start; + } + + if (countRight) + { + start = index + 1; + _AddFree(Mmu, previous, start, countRight); + previous = start; + } + + _Link(Mmu, previous, next); + } + break; + } + } + + return gcvSTATUS_OK; + +OnError: + + /* Roll back. */ + return status; +} + + + +gceSTATUS +gckMMU_FreePagesEx( + IN gckMMU Mmu, + IN gctUINT32 Address, + IN gctSIZE_T PageCount + ) +{ + gctUINT32_PTR node; + gceSTATUS status; + +#if gcdUSE_MMU_EXCEPTION + gctUINT32 i; + struct _gcsMMU_STLB *stlb; + struct _gcsMMU_STLB **stlbs = Mmu->stlbs; +#endif + + gcmkHEADER_ARG("Mmu=0x%x Address=0x%x PageCount=%lu", + Mmu, Address, PageCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageCount > 0); + + /* Get the node by index. */ + node = Mmu->mapLogical + _AddressToIndex(Mmu, Address); + + gcmkONERROR(gckOS_AcquireMutex(Mmu->os, Mmu->pageTableMutex, gcvINFINITE)); + + if (PageCount == 1) + { + /* Single page node. */ + _WritePageEntry(node, (~((1U<<8)-1)) | gcvMMU_SINGLE); + } + else + { + /* Mark the node as free. */ + _WritePageEntry(node, (PageCount << 8) | gcvMMU_FREE); + _WritePageEntry(node + 1, ~0U); + } + + /* We have free nodes. */ + Mmu->freeNodes = gcvTRUE; + +#if gcdUSE_MMU_EXCEPTION + for (i = 0; i < PageCount; i++) + { + /* Get */ + stlb = stlbs[_MtlbOffset(Address)]; + + /* Enable exception */ + stlb->logical[_StlbOffset(Address)] = gcdMMU_STLB_EXCEPTION; + } +#endif + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->pageTableMutex)); + + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} +#endif + +gceSTATUS +gckMMU_Flush( + IN gckMMU Mmu, + IN gceSURF_TYPE Type + ) +{ + gckHARDWARE hardware; + gctUINT32 mask; + gctINT i; + + if (Type == gcvSURF_VERTEX || Type == gcvSURF_INDEX) + { + mask = gcvPAGE_TABLE_DIRTY_BIT_FE; + } + else + { + mask = gcvPAGE_TABLE_DIRTY_BIT_OTHER; + } + +#if gcdPROCESS_ADDRESS_SPACE + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + gcmkVERIFY_OK( + gckOS_AtomSetMask(Mmu->pageTableDirty[i], mask)); + } +#else +#if gcdSHARED_PAGETABLE + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + hardware = sharedPageTable->hardwares[i]; + if (hardware) + { + gcmkVERIFY_OK(gckOS_AtomSetMask(hardware->pageTableDirty, mask)); + } + } +#elif gcdMIRROR_PAGETABLE + for (i = 0; i < (gctINT)mirrorPageTable->reference; i++) + { + hardware = mirrorPageTable->hardwares[i]; + + /* Notify cores who use this page table. */ + gcmkVERIFY_OK( + gckOS_AtomSetMask(hardware->pageTableDirty, mask)); + } +#else + hardware = Mmu->hardware; + gcmkVERIFY_OK( + gckOS_AtomSetMask(hardware->pageTableDirty, mask)); +#endif +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +gckMMU_DumpPageTableEntry( + IN gckMMU Mmu, + IN gctUINT32 Address + ) +{ +#if gcdPROCESS_ADDRESS_SPACE + gcsMMU_STLB_PTR *stlbs = Mmu->stlbs; + gcsMMU_STLB_PTR stlbDesc = stlbs[_MtlbOffset(Address)]; +#else + gctUINT32_PTR pageTable; + gctUINT32 index; + gctUINT32 mtlb, stlb; +#endif + + gcmkHEADER_ARG("Mmu=0x%08X Address=0x%08X", Mmu, Address); + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + + gcmkASSERT(Mmu->hardware->mmuVersion > 0); + +#if gcdPROCESS_ADDRESS_SPACE + if (stlbDesc) + { + gcmkPRINT(" STLB entry = 0x%08X", + _ReadPageEntry(&stlbDesc->logical[_StlbOffset(Address)])); + } + else + { + gcmkPRINT(" MTLB entry is empty."); + } +#else + mtlb = (Address & gcdMMU_MTLB_MASK) >> gcdMMU_MTLB_SHIFT; + + if (mtlb >= Mmu->dynamicMappingStart) + { + stlb = (Address & gcdMMU_STLB_4K_MASK) >> gcdMMU_STLB_4K_SHIFT; + + pageTable = Mmu->pageTableLogical; + + index = (mtlb - Mmu->dynamicMappingStart) + * gcdMMU_STLB_4K_ENTRY_NUM + + stlb; + + gcmkPRINT(" Page table entry = 0x%08X", _ReadPageEntry(pageTable + index)); + } + else + { + gcsMMU_STLB_PTR stlbObj = Mmu->staticSTLB; + gctUINT32 entry = Mmu->mtlbLogical[mtlb]; + + stlb = (Address & gcdMMU_STLB_64K_MASK) >> gcdMMU_STLB_64K_SHIFT; + + entry &= 0xFFFFFFF0; + + while (stlbObj) + { + + if (entry == stlbObj->physBase) + { + gcmkPRINT(" Page table entry = 0x%08X", stlbObj->logical[stlb]); + break; + } + + stlbObj = stlbObj->next; + } + } +#endif + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/****************************************************************************** +****************************** T E S T C O D E ****************************** +******************************************************************************/ + diff --git a/drivers/gpu/galcore/gc_hal_kernel_mmu_vg.c b/drivers/gpu/galcore/gc_hal_kernel_mmu_vg.c new file mode 100644 index 00000000000000..58c33b1b301d93 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_mmu_vg.c @@ -0,0 +1,522 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#if gcdENABLE_VG + +#define _GC_OBJ_ZONE gcvZONE_MMU + +/******************************************************************************* +** +** gckVGMMU_Construct +** +** Construct a new gckVGMMU object. +** +** INPUT: +** +** gckVGKERNEL Kernel +** Pointer to an gckVGKERNEL object. +** +** gctSIZE_T MmuSize +** Number of bytes for the page table. +** +** OUTPUT: +** +** gckVGMMU * Mmu +** Pointer to a variable that receives the gckVGMMU object pointer. +*/ +gceSTATUS gckVGMMU_Construct( + IN gckVGKERNEL Kernel, + IN gctUINT32 MmuSize, + OUT gckVGMMU * Mmu + ) +{ + gckOS os; + gckVGHARDWARE hardware; + gceSTATUS status; + gckVGMMU mmu; + gctUINT32 * pageTable; + gctUINT32 i; + + gcmkHEADER_ARG("Kernel=0x%x MmuSize=0x%x Mmu=0x%x", Kernel, MmuSize, Mmu); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(MmuSize > 0); + gcmkVERIFY_ARGUMENT(Mmu != gcvNULL); + + /* Extract the gckOS object pointer. */ + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Extract the gckVGHARDWARE object pointer. */ + hardware = Kernel->hardware; + gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); + + /* Allocate memory for the gckVGMMU object. */ + status = gckOS_Allocate(os, sizeof(struct _gckVGMMU), (gctPOINTER *) &mmu); + + if (status < 0) + { + /* Error. */ + gcmkFATAL( + "%s(%d): could not allocate gckVGMMU object.", + __FUNCTION__, __LINE__ + ); + + gcmkFOOTER(); + return status; + } + + /* Initialize the gckVGMMU object. */ + mmu->object.type = gcvOBJ_MMU; + mmu->os = os; + mmu->hardware = hardware; + + /* Create the mutex. */ + status = gckOS_CreateMutex(os, &mmu->mutex); + + if (status < 0) + { + /* Roll back. */ + mmu->object.type = gcvOBJ_UNKNOWN; + gcmkVERIFY_OK(gckOS_Free(os, mmu)); + + gcmkFOOTER(); + /* Error. */ + return status; + } + + /* Allocate the page table. */ + mmu->pageTableSize = (gctUINT32)MmuSize; + status = gckOS_AllocateContiguous(os, + gcvFALSE, + &mmu->pageTableSize, + &mmu->pageTablePhysical, + &mmu->pageTableLogical); + + if (status < 0) + { + /* Roll back. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(os, mmu->mutex)); + + mmu->object.type = gcvOBJ_UNKNOWN; + gcmkVERIFY_OK(gckOS_Free(os, mmu)); + + /* Error. */ + gcmkFATAL( + "%s(%d): could not allocate page table.", + __FUNCTION__, __LINE__ + ); + + gcmkFOOTER(); + return status; + } + + /* Compute number of entries in page table. */ + mmu->entryCount = (gctUINT32)mmu->pageTableSize / sizeof(gctUINT32); + mmu->entry = 0; + + /* Mark the entire page table as available. */ + pageTable = (gctUINT32 *) mmu->pageTableLogical; + for (i = 0; i < mmu->entryCount; i++) + { + pageTable[i] = (gctUINT32)~0; + } + + /* Set page table address. */ + status = gckVGHARDWARE_SetMMU(hardware, mmu->pageTableLogical); + + if (status < 0) + { + /* Free the page table. */ + gcmkVERIFY_OK(gckOS_FreeContiguous(mmu->os, + mmu->pageTablePhysical, + mmu->pageTableLogical, + mmu->pageTableSize)); + + /* Roll back. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(os, mmu->mutex)); + + mmu->object.type = gcvOBJ_UNKNOWN; + gcmkVERIFY_OK(gckOS_Free(os, mmu)); + + /* Error. */ + gcmkFATAL( + "%s(%d): could not program page table.", + __FUNCTION__, __LINE__ + ); + + gcmkFOOTER(); + return status; + } + + /* Return the gckVGMMU object pointer. */ + *Mmu = mmu; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_MMU, + "%s(%d): %u entries at %p.(0x%08X)\n", + __FUNCTION__, __LINE__, + mmu->entryCount, + mmu->pageTableLogical, + mmu->pageTablePhysical + ); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGMMU_Destroy +** +** Destroy a nAQMMU object. +** +** INPUT: +** +** gckVGMMU Mmu +** Pointer to an gckVGMMU object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS gckVGMMU_Destroy( + IN gckVGMMU Mmu + ) +{ + gcmkHEADER_ARG("Mmu=0x%x", Mmu); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + + /* Free the page table. */ + gcmkVERIFY_OK(gckOS_FreeContiguous(Mmu->os, + Mmu->pageTablePhysical, + Mmu->pageTableLogical, + Mmu->pageTableSize)); + + /* Roll back. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Mmu->os, Mmu->mutex)); + + /* Mark the gckVGMMU object as unknown. */ + Mmu->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckVGMMU object. */ + gcmkVERIFY_OK(gckOS_Free(Mmu->os, Mmu)); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVGMMU_AllocatePages +** +** Allocate pages inside the page table. +** +** INPUT: +** +** gckVGMMU Mmu +** Pointer to an gckVGMMU object. +** +** gctSIZE_T PageCount +** Number of pages to allocate. +** +** OUTPUT: +** +** gctPOINTER * PageTable +** Pointer to a variable that receives the base address of the page +** table. +** +** gctUINT32 * Address +** Pointer to a variable that receives the hardware specific address. +*/ +gceSTATUS gckVGMMU_AllocatePages( + IN gckVGMMU Mmu, + IN gctSIZE_T PageCount, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ) +{ + gceSTATUS status; + gctUINT32 tail, index, i; + gctUINT32 * table; + gctBOOL allocated = gcvFALSE; + + gcmkHEADER_ARG("Mmu=0x%x PageCount=0x%x PageTable=0x%x Address=0x%x", + Mmu, PageCount, PageTable, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageCount > 0); + gcmkVERIFY_ARGUMENT(PageTable != gcvNULL); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_MMU, + "%s(%d): %u pages.\n", + __FUNCTION__, __LINE__, + PageCount + ); + + if (PageCount > Mmu->entryCount) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_MMU, + "%s(%d): page table too small for %u pages.\n", + __FUNCTION__, __LINE__, + PageCount + ); + + gcmkFOOTER_NO(); + /* Not enough pages avaiable. */ + return gcvSTATUS_OUT_OF_RESOURCES; + } + + /* Grab the mutex. */ + status = gckOS_AcquireMutex(Mmu->os, Mmu->mutex, gcvINFINITE); + + if (status < 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_MMU, + "%s(%d): could not acquire mutex.\n" + ,__FUNCTION__, __LINE__ + ); + + gcmkFOOTER(); + /* Error. */ + return status; + } + + /* Compute the tail for this allocation. */ + tail = Mmu->entryCount - (gctUINT32)PageCount; + + /* Walk all entries until we find enough slots. */ + for (index = Mmu->entry; index <= tail;) + { + /* Access page table. */ + table = (gctUINT32 *) Mmu->pageTableLogical + index; + + /* See if all slots are available. */ + for (i = 0; i < PageCount; i++, table++) + { + if (*table != ~0) + { + /* Start from next slot. */ + index += i + 1; + break; + } + } + + if (i == PageCount) + { + /* Bail out if we have enough page entries. */ + allocated = gcvTRUE; + break; + } + } + + if (!allocated) + { + if (status >= 0) + { + /* Walk all entries until we find enough slots. */ + for (index = 0; index <= tail;) + { + /* Access page table. */ + table = (gctUINT32 *) Mmu->pageTableLogical + index; + + /* See if all slots are available. */ + for (i = 0; i < PageCount; i++, table++) + { + if (*table != ~0) + { + /* Start from next slot. */ + index += i + 1; + break; + } + } + + if (i == PageCount) + { + /* Bail out if we have enough page entries. */ + allocated = gcvTRUE; + break; + } + } + } + } + + if (!allocated && (status >= 0)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_MMU, + "%s(%d): not enough free pages for %u pages.\n", + __FUNCTION__, __LINE__, + PageCount + ); + + /* Not enough empty slots available. */ + status = gcvSTATUS_OUT_OF_RESOURCES; + } + + if (status >= 0) + { + /* Build virtual address. */ + status = gckVGHARDWARE_BuildVirtualAddress(Mmu->hardware, + index, + 0, + Address); + + if (status >= 0) + { + /* Update current entry into page table. */ + Mmu->entry = index + (gctUINT32)PageCount; + + /* Return pointer to page table. */ + *PageTable = (gctUINT32 *) Mmu->pageTableLogical + index; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_MMU, + "%s(%d): allocated %u pages at index %u (0x%08X) @ %p.\n", + __FUNCTION__, __LINE__, + PageCount, + index, + *Address, + *PageTable + ); + } + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Mmu->os, Mmu->mutex)); + gcmkFOOTER(); + + /* Return status. */ + return status; +} + +/******************************************************************************* +** +** gckVGMMU_FreePages +** +** Free pages inside the page table. +** +** INPUT: +** +** gckVGMMU Mmu +** Pointer to an gckVGMMU object. +** +** gctPOINTER PageTable +** Base address of the page table to free. +** +** gctSIZE_T PageCount +** Number of pages to free. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS gckVGMMU_FreePages( + IN gckVGMMU Mmu, + IN gctPOINTER PageTable, + IN gctSIZE_T PageCount + ) +{ + gctUINT32 * table; + + gcmkHEADER_ARG("Mmu=0x%x PageTable=0x%x PageCount=0x%x", + Mmu, PageTable, PageCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageTable != gcvNULL); + gcmkVERIFY_ARGUMENT(PageCount > 0); + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_MMU, + "%s(%d): freeing %u pages at index %u @ %p.\n", + __FUNCTION__, __LINE__, + PageCount, + ((gctUINT32 *) PageTable - (gctUINT32 *) Mmu->pageTableLogical), + PageTable + ); + + /* Convert pointer. */ + table = (gctUINT32 *) PageTable; + + /* Mark the page table entries as available. */ + while (PageCount-- > 0) + { + *table++ = (gctUINT32)~0; + } + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +gceSTATUS +gckVGMMU_SetPage( + IN gckVGMMU Mmu, + IN gctUINT32 PageAddress, + IN gctUINT32 *PageEntry + ) +{ + gcmkHEADER_ARG("Mmu=0x%x", Mmu); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Mmu, gcvOBJ_MMU); + gcmkVERIFY_ARGUMENT(PageEntry != gcvNULL); + gcmkVERIFY_ARGUMENT(!(PageAddress & 0xFFF)); + + *PageEntry = PageAddress; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckVGMMU_Flush( + IN gckVGMMU Mmu + ) +{ + gckVGHARDWARE hardware; + + gcmkHEADER_ARG("Mmu=0x%x", Mmu); + + hardware = Mmu->hardware; + gcmkVERIFY_OK( + gckOS_AtomSet(hardware->os, hardware->pageTableDirty, 1)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +#endif /* gcdENABLE_VG */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c new file mode 100644 index 00000000000000..e8dcfd632fd8ad --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -0,0 +1,8587 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if gcdANDROID_NATIVE_FENCE_SYNC +#include +#include "gc_hal_kernel_sync.h" +#endif + +#define _GC_OBJ_ZONE gcvZONE_OS + +#include "gc_hal_kernel_allocator.h" + +#define MEMORY_LOCK(os) \ + gcmkVERIFY_OK(gckOS_AcquireMutex( \ + (os), \ + (os)->memoryLock, \ + gcvINFINITE)) + +#define MEMORY_UNLOCK(os) \ + gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryLock)) + +#define MEMORY_MAP_LOCK(os) \ + gcmkVERIFY_OK(gckOS_AcquireMutex( \ + (os), \ + (os)->memoryMapLock, \ + gcvINFINITE)) + +#define MEMORY_MAP_UNLOCK(os) \ + gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryMapLock)) + + +/******************************************************************************\ +******************************* Private Functions ****************************** +\******************************************************************************/ +static gctINT +_GetThreadID( + void + ) +{ + return task_pid_vnr(current); +} + +static PLINUX_MDL +_CreateMdl( + void + ) +{ + PLINUX_MDL mdl; + + gcmkHEADER(); + + mdl = (PLINUX_MDL)kzalloc(sizeof(struct _LINUX_MDL), GFP_KERNEL | gcdNOWARN); + + gcmkFOOTER_ARG("0x%X", mdl); + return mdl; +} + +static gceSTATUS +_DestroyMdlMap( + IN PLINUX_MDL Mdl, + IN PLINUX_MDL_MAP MdlMap + ); + +static gceSTATUS +_DestroyMdl( + IN PLINUX_MDL Mdl + ) +{ + PLINUX_MDL_MAP mdlMap, next; + + gcmkHEADER_ARG("Mdl=0x%X", Mdl); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Mdl != gcvNULL); + + mdlMap = Mdl->maps; + + while (mdlMap != gcvNULL) + { + next = mdlMap->next; + + gcmkVERIFY_OK(_DestroyMdlMap(Mdl, mdlMap)); + + mdlMap = next; + } + + kfree(Mdl); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +static PLINUX_MDL_MAP +_CreateMdlMap( + IN PLINUX_MDL Mdl, + IN gctINT ProcessID + ) +{ + PLINUX_MDL_MAP mdlMap; + + gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID); + + mdlMap = (PLINUX_MDL_MAP)kmalloc(sizeof(struct _LINUX_MDL_MAP), GFP_KERNEL | gcdNOWARN); + if (mdlMap == gcvNULL) + { + gcmkFOOTER_NO(); + return gcvNULL; + } + + mdlMap->pid = ProcessID; + mdlMap->vmaAddr = gcvNULL; + mdlMap->vma = gcvNULL; + mdlMap->count = 0; + + mdlMap->next = Mdl->maps; + Mdl->maps = mdlMap; + + gcmkFOOTER_ARG("0x%X", mdlMap); + return mdlMap; +} + +static gceSTATUS +_DestroyMdlMap( + IN PLINUX_MDL Mdl, + IN PLINUX_MDL_MAP MdlMap + ) +{ + PLINUX_MDL_MAP prevMdlMap; + + gcmkHEADER_ARG("Mdl=0x%X MdlMap=0x%X", Mdl, MdlMap); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(MdlMap != gcvNULL); + gcmkASSERT(Mdl->maps != gcvNULL); + + if (Mdl->maps == MdlMap) + { + Mdl->maps = MdlMap->next; + } + else + { + prevMdlMap = Mdl->maps; + + while (prevMdlMap->next != MdlMap) + { + prevMdlMap = prevMdlMap->next; + + gcmkASSERT(prevMdlMap != gcvNULL); + } + + prevMdlMap->next = MdlMap->next; + } + + kfree(MdlMap); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +extern PLINUX_MDL_MAP +FindMdlMap( + IN PLINUX_MDL Mdl, + IN gctINT ProcessID + ) +{ + PLINUX_MDL_MAP mdlMap; + + gcmkHEADER_ARG("Mdl=0x%X ProcessID=%d", Mdl, ProcessID); + if(Mdl == gcvNULL) + { + gcmkFOOTER_NO(); + return gcvNULL; + } + mdlMap = Mdl->maps; + + while (mdlMap != gcvNULL) + { + if (mdlMap->pid == ProcessID) + { + gcmkFOOTER_ARG("0x%X", mdlMap); + return mdlMap; + } + + mdlMap = mdlMap->next; + } + + gcmkFOOTER_NO(); + return gcvNULL; +} + +/******************************************************************************* +** Integer Id Management. +*/ +gceSTATUS +_AllocateIntegerId( + IN gcsINTEGER_DB_PTR Database, + IN gctPOINTER KernelPointer, + OUT gctUINT32 *Id + ) +{ + int result; + gctINT next; + + idr_preload(GFP_KERNEL | gcdNOWARN); + + spin_lock(&Database->lock); + + next = (Database->curr + 1 <= 0) ? 1 : Database->curr + 1; + + result = idr_alloc(&Database->idr, KernelPointer, next, 0, GFP_ATOMIC); + + /* ID allocated should not be 0. */ + gcmkASSERT(result != 0); + + if (result > 0) + { + Database->curr = *Id = result; + } + + spin_unlock(&Database->lock); + + idr_preload_end(); + + if (result < 0) + { + return gcvSTATUS_OUT_OF_RESOURCES; + } + + return gcvSTATUS_OK; +} + +gceSTATUS +_QueryIntegerId( + IN gcsINTEGER_DB_PTR Database, + IN gctUINT32 Id, + OUT gctPOINTER * KernelPointer + ) +{ + gctPOINTER pointer; + + spin_lock(&Database->lock); + + pointer = idr_find(&Database->idr, Id); + + spin_unlock(&Database->lock); + + if(pointer) + { + *KernelPointer = pointer; + return gcvSTATUS_OK; + } + else + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_OS, + "%s(%d) Id = %d is not found", + __FUNCTION__, __LINE__, Id); + + return gcvSTATUS_NOT_FOUND; + } +} + +gceSTATUS +_DestroyIntegerId( + IN gcsINTEGER_DB_PTR Database, + IN gctUINT32 Id + ) +{ + spin_lock(&Database->lock); + + idr_remove(&Database->idr, Id); + + spin_unlock(&Database->lock); + + return gcvSTATUS_OK; +} + +gceSTATUS +_QueryProcessPageTable( + IN gctPOINTER Logical, + OUT gctUINT32 * Address + ) +{ + spinlock_t *lock; + gctUINTPTR_T logical = (gctUINTPTR_T)Logical; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + if (!current->mm) + { + return gcvSTATUS_NOT_FOUND; + } + + pgd = pgd_offset(current->mm, logical); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + { + return gcvSTATUS_NOT_FOUND; + } + + pud = pud_offset(pgd, logical); + if (pud_none(*pud) || pud_bad(*pud)) + { + return gcvSTATUS_NOT_FOUND; + } + + pmd = pmd_offset(pud, logical); + if (pmd_none(*pmd) || pmd_bad(*pmd)) + { + return gcvSTATUS_NOT_FOUND; + } + + pte = pte_offset_map_lock(current->mm, pmd, logical, &lock); + if (!pte) + { + return gcvSTATUS_NOT_FOUND; + } + + if (!pte_present(*pte)) + { + pte_unmap_unlock(pte, lock); + return gcvSTATUS_NOT_FOUND; + } + + *Address = (pte_pfn(*pte) << PAGE_SHIFT) | (logical & ~PAGE_MASK); + pte_unmap_unlock(pte, lock); + + return gcvSTATUS_OK; +} + +#if !gcdCACHE_FUNCTION_UNIMPLEMENTED && defined(CONFIG_OUTER_CACHE) +static inline gceSTATUS +outer_func( + gceCACHEOPERATION Type, + unsigned long Start, + unsigned long End + ) +{ + switch (Type) + { + case gcvCACHE_CLEAN: + outer_clean_range(Start, End); + break; + case gcvCACHE_INVALIDATE: + outer_inv_range(Start, End); + break; + case gcvCACHE_FLUSH: + outer_flush_range(Start, End); + break; + default: + return gcvSTATUS_INVALID_ARGUMENT; + break; + } + return gcvSTATUS_OK; +} + +#if gcdENABLE_OUTER_CACHE_PATCH +/******************************************************************************* +** _HandleOuterCache +** +** Handle the outer cache for the specified addresses. +** +** ARGUMENTS: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctPOINTER Physical +** Physical address to flush. +** +** gctPOINTER Logical +** Logical address to flush. +** +** gctSIZE_T Bytes +** Size of the address range in bytes to flush. +** +** gceOUTERCACHE_OPERATION Type +** Operation need to be execute. +*/ +gceSTATUS +_HandleOuterCache( + IN gckOS Os, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes, + IN gceCACHEOPERATION Type + ) +{ + gceSTATUS status; + unsigned long paddr; + gctPOINTER vaddr; + gctUINT32 offset, bytes, left; + + gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", + Os, Logical, Bytes); + + if (Physical != gcvINVALID_ADDRESS) + { + /* Non paged memory or gcvPOOL_USER surface */ + paddr = (unsigned long) Physical; + gcmkONERROR(outer_func(Type, paddr, paddr + Bytes)); + } + else + { + /* Non contiguous virtual memory */ + vaddr = Logical; + left = Bytes; + + while (left) + { + /* Handle (part of) current page. */ + offset = (gctUINTPTR_T)vaddr & ~PAGE_MASK; + + bytes = gcmMIN(left, PAGE_SIZE - offset); + + gcmkONERROR(_QueryProcessPageTable(vaddr, (gctUINT32*)&paddr)); + gcmkONERROR(outer_func(Type, paddr, paddr + bytes)); + + vaddr = (gctUINT8_PTR)vaddr + bytes; + left -= bytes; + } + } + + mb(); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif +#endif + +gctBOOL +_AllowAccess( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address + ) +{ + gctUINT32 data; + + /* Check external clock state. */ + if (Os->clockStates[Core] == gcvFALSE) + { + gcmkPRINT("[galcore]: %s(%d) External clock off", __FUNCTION__, __LINE__); + return gcvFALSE; + } + + /* Check internal clock state. */ + if (Address == 0) + { + return gcvTRUE; + } + +#if gcdMULTI_GPU + if (Core == gcvCORE_MAJOR) + { + data = readl((gctUINT8 *)Os->device->registerBases[gcvCORE_3D_0_ID] + 0x0); + } + else +#endif + { + data = readl((gctUINT8 *)Os->device->registerBases[Core] + 0x0); + } + + if ((data & 0x3) == 0x3) + { + gcmkPRINT("[galcore]: %s(%d) Internal clock off", __FUNCTION__, __LINE__); + return gcvFALSE; + } + + return gcvTRUE; +} + +static gceSTATUS +_ShrinkMemory( + IN gckOS Os + ) +{ + gcsPLATFORM * platform; + + gcmkHEADER_ARG("Os=0x%X", Os); + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + platform = Os->device->platform; + + if (platform && platform->ops->shrinkMemory) + { + platform->ops->shrinkMemory(platform); + } + else + { + gcmkFOOTER_NO(); + return gcvSTATUS_NOT_SUPPORTED; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_Construct +** +** Construct a new gckOS object. +** +** INPUT: +** +** gctPOINTER Context +** Pointer to the gckGALDEVICE class. +** +** OUTPUT: +** +** gckOS * Os +** Pointer to a variable that will hold the pointer to the gckOS object. +*/ +gceSTATUS +gckOS_Construct( + IN gctPOINTER Context, + OUT gckOS * Os + ) +{ + gckOS os; + gceSTATUS status; + gctINT i; + + gcmkHEADER_ARG("Context=0x%X", Context); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Os != gcvNULL); + + /* Allocate the gckOS object. */ + os = (gckOS) kmalloc(gcmSIZEOF(struct _gckOS), GFP_KERNEL | gcdNOWARN); + + if (os == gcvNULL) + { + /* Out of memory. */ + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + /* Zero the memory. */ + gckOS_ZeroMemory(os, gcmSIZEOF(struct _gckOS)); + + /* Initialize the gckOS object. */ + os->object.type = gcvOBJ_OS; + + /* Set device device. */ + os->device = Context; + + /* Set allocateCount to 0, gckOS_Allocate has not been used yet. */ + atomic_set(&os->allocateCount, 0); + + /* Initialize the memory lock. */ + gcmkONERROR(gckOS_CreateMutex(os, &os->memoryLock)); + gcmkONERROR(gckOS_CreateMutex(os, &os->memoryMapLock)); + + /* Create debug lock mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &os->debugLock)); + + os->mdlHead = os->mdlTail = gcvNULL; + + /* Get the kernel process ID. */ + gcmkONERROR(gckOS_GetProcessID(&os->kernelProcessID)); + + /* + * Initialize the signal manager. + */ + + /* Initialize mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &os->signalMutex)); + + /* Initialize signal id database lock. */ + spin_lock_init(&os->signalDB.lock); + + /* Initialize signal id database. */ + idr_init(&os->signalDB.idr); + +#if gcdANDROID_NATIVE_FENCE_SYNC + /* + * Initialize the sync point manager. + */ + + /* Initialize mutex. */ + gcmkONERROR(gckOS_CreateMutex(os, &os->syncPointMutex)); + + /* Initialize sync point id database lock. */ + spin_lock_init(&os->syncPointDB.lock); + + /* Initialize sync point id database. */ + idr_init(&os->syncPointDB.idr); +#endif + + /* Create a workqueue for os timer. */ + os->workqueue = create_singlethread_workqueue("galcore workqueue"); + + if (os->workqueue == gcvNULL) + { + /* Out of memory. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + os->paddingPage = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | gcdNOWARN); + if (os->paddingPage == gcvNULL) + { + /* Out of memory. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + else + { + SetPageReserved(os->paddingPage); + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + mutex_init(&os->registerAccessLocks[i]); + } + + gckOS_ImportAllocators(os); + +#ifdef CONFIG_IOMMU_SUPPORT + if (((gckGALDEVICE)(os->device))->mmu == gcvFALSE) + { + /* Only use IOMMU when internal MMU is not enabled. */ + status = gckIOMMU_Construct(os, &os->iommu); + + if (gcmIS_ERROR(status)) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Fail to setup IOMMU", + __FUNCTION__, __LINE__ + ); + } + } +#endif + + /* Return pointer to the gckOS object. */ + *Os = os; + + /* Success. */ + gcmkFOOTER_ARG("*Os=0x%X", *Os); + return gcvSTATUS_OK; + +OnError: + +#if gcdANDROID_NATIVE_FENCE_SYNC + if (os->syncPointMutex != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_DeleteMutex(os, os->syncPointMutex)); + } +#endif + + if (os->signalMutex != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_DeleteMutex(os, os->signalMutex)); + } + + if (os->memoryMapLock != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_DeleteMutex(os, os->memoryMapLock)); + } + + if (os->memoryLock != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_DeleteMutex(os, os->memoryLock)); + } + + if (os->debugLock != gcvNULL) + { + gcmkVERIFY_OK( + gckOS_DeleteMutex(os, os->debugLock)); + } + + if (os->workqueue != gcvNULL) + { + destroy_workqueue(os->workqueue); + } + + kfree(os); + + /* Return the error. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_Destroy +** +** Destroy an gckOS object. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object that needs to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_Destroy( + IN gckOS Os + ) +{ + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + if (Os->paddingPage != gcvNULL) + { + ClearPageReserved(Os->paddingPage); + __free_page(Os->paddingPage); + Os->paddingPage = gcvNULL; + } + +#if gcdANDROID_NATIVE_FENCE_SYNC + /* + * Destroy the sync point manager. + */ + + /* Destroy the mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->syncPointMutex)); +#endif + + /* + * Destroy the signal manager. + */ + + /* Destroy the mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->signalMutex)); + + /* Destroy the memory lock. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->memoryMapLock)); + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->memoryLock)); + + /* Destroy debug lock mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, Os->debugLock)); + + /* Wait for all works done. */ + flush_workqueue(Os->workqueue); + + /* Destory work queue. */ + destroy_workqueue(Os->workqueue); + + gckOS_FreeAllocators(Os); + +#ifdef CONFIG_IOMMU_SUPPORT + if (Os->iommu) + { + gckIOMMU_Destory(Os, Os->iommu); + } +#endif + + /* Flush the debug cache. */ + gcmkDEBUGFLUSH(~0U); + + /* Mark the gckOS object as unknown. */ + Os->object.type = gcvOBJ_UNKNOWN; + + + /* Free the gckOS object. */ + kfree(Os); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_CreateKernelVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical, + OUT gctSIZE_T * PageCount + ) +{ + gceSTATUS status; + PLINUX_MDL mdl = (PLINUX_MDL)Physical; + gckALLOCATOR allocator = mdl->allocator; + + gcmkHEADER(); + + *PageCount = mdl->numPages; + + gcmkONERROR(allocator->ops->MapKernel(allocator, mdl, Logical)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_DestroyKernelVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ) +{ + PLINUX_MDL mdl = (PLINUX_MDL)Physical; + gckALLOCATOR allocator = mdl->allocator; + + gcmkHEADER(); + + allocator->ops->UnmapKernel(allocator, mdl, Logical); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_CreateUserVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical, + OUT gctSIZE_T * PageCount + ) +{ + return gckOS_LockPages(Os, Physical, Bytes, gcvFALSE, Logical, PageCount); +} + +gceSTATUS +gckOS_DestroyUserVirtualMapping( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ) +{ + return gckOS_UnlockPages(Os, Physical, Bytes, Logical); +} + +/******************************************************************************* +** +** gckOS_Allocate +** +** Allocate memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIZE_T Bytes +** Number of bytes to allocate. +** +** OUTPUT: +** +** gctPOINTER * Memory +** Pointer to a variable that will hold the allocated memory location. +*/ +gceSTATUS +gckOS_Allocate( + IN gckOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + gcmkONERROR(gckOS_AllocateMemory(Os, Bytes, Memory)); + + /* Success. */ + gcmkFOOTER_ARG("*Memory=0x%X", *Memory); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_Free +** +** Free allocated memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Memory +** Pointer to memory allocation to free. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_Free( + IN gckOS Os, + IN gctPOINTER Memory + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Memory=0x%X", Os, Memory); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + gcmkONERROR(gckOS_FreeMemory(Os, Memory)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_AllocateMemory +** +** Allocate memory wrapper. +** +** INPUT: +** +** gctSIZE_T Bytes +** Number of bytes to allocate. +** +** OUTPUT: +** +** gctPOINTER * Memory +** Pointer to a variable that will hold the allocated memory location. +*/ +gceSTATUS +gckOS_AllocateMemory( + IN gckOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ) +{ + gctPOINTER memory; + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + if (Bytes > PAGE_SIZE) + { + memory = (gctPOINTER) vmalloc(Bytes); + } + else + { + memory = (gctPOINTER) kmalloc(Bytes, GFP_KERNEL | gcdNOWARN); + } + + if (memory == gcvNULL) + { + /* Out of memory. */ + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Increase count. */ + atomic_inc(&Os->allocateCount); + + /* Return pointer to the memory allocation. */ + *Memory = memory; + + /* Success. */ + gcmkFOOTER_ARG("*Memory=0x%X", *Memory); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_FreeMemory +** +** Free allocated memory wrapper. +** +** INPUT: +** +** gctPOINTER Memory +** Pointer to memory allocation to free. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_FreeMemory( + IN gckOS Os, + IN gctPOINTER Memory + ) +{ + gcmkHEADER_ARG("Memory=0x%X", Memory); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + /* Free the memory from the OS pool. */ + if (is_vmalloc_addr(Memory)) + { + vfree(Memory); + } + else + { + kfree(Memory); + } + + /* Decrease count. */ + atomic_dec(&Os->allocateCount); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_MapMemory +** +** Map physical memory into the current process. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Start of physical address memory. +** +** gctSIZE_T Bytes +** Number of bytes to map. +** +** OUTPUT: +** +** gctPOINTER * Memory +** Pointer to a variable that will hold the logical address of the +** mapped memory. +*/ +gceSTATUS +gckOS_MapMemory( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical + ) +{ + PLINUX_MDL_MAP mdlMap; + PLINUX_MDL mdl = (PLINUX_MDL)Physical; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != 0); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + MEMORY_LOCK(Os); + + mdlMap = FindMdlMap(mdl, _GetProcessID()); + + if (mdlMap == gcvNULL) + { + mdlMap = _CreateMdlMap(mdl, _GetProcessID()); + + if (mdlMap == gcvNULL) + { + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + } + + if (mdlMap->vmaAddr == gcvNULL) + { + mdlMap->vmaAddr = (char *)vm_mmap(gcvNULL, + 0L, + mdl->numPages * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + 0); + + if (IS_ERR(mdlMap->vmaAddr)) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): do_mmap_pgoff error", + __FUNCTION__, __LINE__ + ); + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): mdl->numPages: %d mdl->vmaAddr: 0x%X", + __FUNCTION__, __LINE__, + mdl->numPages, + mdlMap->vmaAddr + ); + + mdlMap->vmaAddr = gcvNULL; + + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + down_write(¤t->mm->mmap_sem); + + mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr); + + if (!mdlMap->vma) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): find_vma error.", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + up_write(¤t->mm->mmap_sem); + + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } + +#ifndef NO_DMA_COHERENT + if (dma_mmap_writecombine(gcvNULL, + mdlMap->vma, + mdl->addr, + mdl->dmaHandle, + mdl->numPages * PAGE_SIZE) < 0) + { + up_write(¤t->mm->mmap_sem); + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): dma_mmap_coherent error.", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } +#else +#if !gcdPAGED_MEMORY_CACHEABLE + mdlMap->vma->vm_page_prot = gcmkPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot); + mdlMap->vma->vm_flags |= gcdVM_FLAGS; +# endif + mdlMap->vma->vm_pgoff = 0; + + if (remap_pfn_range(mdlMap->vma, + mdlMap->vma->vm_start, + mdl->dmaHandle >> PAGE_SHIFT, + mdl->numPages*PAGE_SIZE, + mdlMap->vma->vm_page_prot) < 0) + { + up_write(¤t->mm->mmap_sem); + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): remap_pfn_range error.", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } +#endif + + up_write(¤t->mm->mmap_sem); + } + + MEMORY_UNLOCK(Os); + + *Logical = mdlMap->vmaAddr; + + gcmkFOOTER_ARG("*Logical=0x%X", *Logical); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_UnmapMemory +** +** Unmap physical memory out of the current process. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Start of physical address memory. +** +** gctSIZE_T Bytes +** Number of bytes to unmap. +** +** gctPOINTER Memory +** Pointer to a previously mapped memory region. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnmapMemory( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ) +{ + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X", + Os, Physical, Bytes, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != 0); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + gckOS_UnmapMemoryEx(Os, Physical, Bytes, Logical, _GetProcessID()); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + + +/******************************************************************************* +** +** gckOS_UnmapMemoryEx +** +** Unmap physical memory in the specified process. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Start of physical address memory. +** +** gctSIZE_T Bytes +** Number of bytes to unmap. +** +** gctPOINTER Memory +** Pointer to a previously mapped memory region. +** +** gctUINT32 PID +** Pid of the process that opened the device and mapped this memory. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnmapMemoryEx( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical, + IN gctUINT32 PID + ) +{ + PLINUX_MDL_MAP mdlMap; + PLINUX_MDL mdl = (PLINUX_MDL)Physical; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X PID=%d", + Os, Physical, Bytes, Logical, PID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != 0); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(PID != 0); + + MEMORY_LOCK(Os); + + if (Logical) + { + mdlMap = FindMdlMap(mdl, PID); + + if (mdlMap == gcvNULL || mdlMap->vmaAddr == gcvNULL) + { + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); + return gcvSTATUS_INVALID_ARGUMENT; + } + + _UnmapUserLogical(mdlMap->vmaAddr, mdl->numPages * PAGE_SIZE); + + gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap)); + } + + MEMORY_UNLOCK(Os); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_UnmapUserLogical +** +** Unmap user logical memory out of physical memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Start of physical address memory. +** +** gctSIZE_T Bytes +** Number of bytes to unmap. +** +** gctPOINTER Memory +** Pointer to a previously mapped memory region. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnmapUserLogical( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ) +{ + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu Logical=0x%X", + Os, Physical, Bytes, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != 0); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + gckOS_UnmapMemory(Os, Physical, Bytes, Logical); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +} + +/******************************************************************************* +** +** gckOS_AllocateNonPagedMemory +** +** Allocate a number of pages from non-paged memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctBOOL InUserSpace +** gcvTRUE if the pages need to be mapped into user space. +** +** gctSIZE_T * Bytes +** Pointer to a variable that holds the number of bytes to allocate. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that hold the number of bytes allocated. +** +** gctPHYS_ADDR * Physical +** Pointer to a variable that will hold the physical address of the +** allocation. +** +** gctPOINTER * Logical +** Pointer to a variable that will hold the logical address of the +** allocation. +*/ +gceSTATUS +gckOS_AllocateNonPagedMemory( + IN gckOS Os, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ) +{ + gctSIZE_T bytes; + gctINT numPages; + PLINUX_MDL mdl = gcvNULL; + PLINUX_MDL_MAP mdlMap = gcvNULL; + gctSTRING addr; + gckKERNEL kernel; +#ifdef NO_DMA_COHERENT + struct page * page; + long size, order; + gctPOINTER vaddr; +#endif + gctBOOL locked = gcvFALSE; + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu", + Os, InUserSpace, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes != gcvNULL); + gcmkVERIFY_ARGUMENT(*Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + /* Align number of bytes to page size. */ + bytes = gcmALIGN(*Bytes, PAGE_SIZE); + + /* Get total number of pages.. */ + numPages = GetPageCount(bytes, 0); + + /* Allocate mdl+vector structure */ + mdl = _CreateMdl(); + if (mdl == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + mdl->pagedMem = 0; + mdl->numPages = numPages; + + MEMORY_LOCK(Os); + locked = gcvTRUE; + +#ifndef NO_DMA_COHERENT +#ifdef CONFIG_ARM64 + addr = dma_alloc_coherent(gcvNULL, +#else + addr = dma_alloc_writecombine(gcvNULL, +#endif + mdl->numPages * PAGE_SIZE, + &mdl->dmaHandle, + GFP_KERNEL | gcdNOWARN); +#else + size = mdl->numPages * PAGE_SIZE; + order = get_order(size); + + page = alloc_pages(GFP_KERNEL | gcdNOWARN, order); + + if (page == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + vaddr = (gctPOINTER)page_address(page); + mdl->contiguous = gcvTRUE; + mdl->u.contiguousPages = page; + addr = _CreateKernelVirtualMapping(mdl); + mdl->dmaHandle = virt_to_phys(vaddr); + mdl->kaddr = vaddr; + + /* Trigger a page fault. */ + memset(addr, 0, numPages * PAGE_SIZE); + +#if !defined(CONFIG_PPC) + /* Cache invalidate. */ + dma_sync_single_for_device( + gcvNULL, + page_to_phys(page), + bytes, + DMA_FROM_DEVICE); +#endif + + while (size > 0) + { + SetPageReserved(virt_to_page(vaddr)); + + vaddr += PAGE_SIZE; + size -= PAGE_SIZE; + } +#endif + + if (addr == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + kernel = Os->device->kernels[gcvCORE_MAJOR] != gcvNULL ? + Os->device->kernels[gcvCORE_MAJOR] : Os->device->kernels[gcvCORE_2D]; + if (((Os->device->baseAddress & 0x80000000) != (mdl->dmaHandle & 0x80000000)) && + kernel->hardware->mmuVersion == 0) + { + mdl->dmaHandle = (mdl->dmaHandle & ~0x80000000) + | (Os->device->baseAddress & 0x80000000); + } + + mdl->addr = addr; + + if (InUserSpace) + { + mdlMap = _CreateMdlMap(mdl, _GetProcessID()); + + if (mdlMap == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Only after mmap this will be valid. */ + + /* We need to map this to user space. */ + mdlMap->vmaAddr = (gctSTRING) vm_mmap(gcvNULL, + 0L, + mdl->numPages * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED, + 0); + + if (IS_ERR(mdlMap->vmaAddr)) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): do_mmap_pgoff error", + __FUNCTION__, __LINE__ + ); + + mdlMap->vmaAddr = gcvNULL; + + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + down_write(¤t->mm->mmap_sem); + + mdlMap->vma = find_vma(current->mm, (unsigned long)mdlMap->vmaAddr); + + if (mdlMap->vma == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): find_vma error", + __FUNCTION__, __LINE__ + ); + + up_write(¤t->mm->mmap_sem); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + +#ifndef NO_DMA_COHERENT + if (dma_mmap_coherent(gcvNULL, + mdlMap->vma, + mdl->addr, + mdl->dmaHandle, + mdl->numPages * PAGE_SIZE) < 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): dma_mmap_coherent error", + __FUNCTION__, __LINE__ + ); + + up_write(¤t->mm->mmap_sem); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } +#else +#if !gcdSECURITY + mdlMap->vma->vm_page_prot = gcmkNONPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot); +#endif + mdlMap->vma->vm_flags |= gcdVM_FLAGS; + mdlMap->vma->vm_pgoff = 0; + + if (remap_pfn_range(mdlMap->vma, + mdlMap->vma->vm_start, + mdl->dmaHandle >> PAGE_SHIFT, + mdl->numPages * PAGE_SIZE, + mdlMap->vma->vm_page_prot)) + { + gcmkTRACE_ZONE( + gcvLEVEL_WARNING, gcvZONE_OS, + "%s(%d): remap_pfn_range error", + __FUNCTION__, __LINE__ + ); + + up_write(¤t->mm->mmap_sem); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } +#endif /* NO_DMA_COHERENT */ + + up_write(¤t->mm->mmap_sem); + + *Logical = mdlMap->vmaAddr; + } + else + { +#if gcdSECURITY + *Logical = (gctPOINTER)mdl->kaddr; +#else + *Logical = (gctPOINTER)mdl->addr; +#endif + } + + /* + * Add this to a global list. + * Will be used by get physical address + * and mapuser pointer functions. + */ + + if (!Os->mdlHead) + { + /* Initialize the queue. */ + Os->mdlHead = Os->mdlTail = mdl; + } + else + { + /* Add to the tail. */ + mdl->prev = Os->mdlTail; + Os->mdlTail->next = mdl; + Os->mdlTail = mdl; + } + + MEMORY_UNLOCK(Os); + + /* Return allocated memory. */ + *Bytes = bytes; + *Physical = (gctPHYS_ADDR) mdl; + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X", + *Bytes, *Physical, *Logical); + return gcvSTATUS_OK; + +OnError: + if (mdlMap != gcvNULL) + { + /* Free LINUX_MDL_MAP. */ + gcmkVERIFY_OK(_DestroyMdlMap(mdl, mdlMap)); + } + + if (mdl != gcvNULL) + { + /* Free LINUX_MDL. */ + gcmkVERIFY_OK(_DestroyMdl(mdl)); + } + *Physical = gcvNULL; + *Bytes = 0; + + if (locked) + { + /* Unlock memory. */ + MEMORY_UNLOCK(Os); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_FreeNonPagedMemory +** +** Free previously allocated and mapped pages from non-paged memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIZE_T Bytes +** Number of bytes allocated. +** +** gctPHYS_ADDR Physical +** Physical address of the allocated memory. +** +** gctPOINTER Logical +** Logical address of the allocated memory. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS gckOS_FreeNonPagedMemory( + IN gckOS Os, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical + ) +{ + PLINUX_MDL mdl; + PLINUX_MDL_MAP mdlMap; +#ifdef NO_DMA_COHERENT + unsigned size; + gctPOINTER vaddr; +#endif /* NO_DMA_COHERENT */ + + gcmkHEADER_ARG("Os=0x%X Bytes=%lu Physical=0x%X Logical=0x%X", + Os, Bytes, Physical, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + /* Convert physical address into a pointer to a MDL. */ + mdl = (PLINUX_MDL) Physical; + + MEMORY_LOCK(Os); + +#ifndef NO_DMA_COHERENT +#ifdef CONFIG_ARM64 + dma_free_coherent(gcvNULL, +#else + dma_free_writecombine(gcvNULL, +#endif + mdl->numPages * PAGE_SIZE, + mdl->addr, + mdl->dmaHandle); +#else + size = mdl->numPages * PAGE_SIZE; + vaddr = mdl->kaddr; + + while (size > 0) + { + ClearPageReserved(virt_to_page(vaddr)); + + vaddr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + free_pages((unsigned long)mdl->kaddr, get_order(mdl->numPages * PAGE_SIZE)); + + _DestoryKernelVirtualMapping(mdl->addr); +#endif /* NO_DMA_COHERENT */ + + mdlMap = mdl->maps; + + while (mdlMap != gcvNULL) + { + /* No mapped memory exists when free nonpaged memory */ + gcmkASSERT(mdlMap->vmaAddr == gcvNULL); + + mdlMap = mdlMap->next; + } + + /* Remove the node from global list.. */ + if (mdl == Os->mdlHead) + { + if ((Os->mdlHead = mdl->next) == gcvNULL) + { + Os->mdlTail = gcvNULL; + } + } + else + { + mdl->prev->next = mdl->next; + if (mdl == Os->mdlTail) + { + Os->mdlTail = mdl->prev; + } + else + { + mdl->next->prev = mdl->prev; + } + } + + MEMORY_UNLOCK(Os); + + gcmkVERIFY_OK(_DestroyMdl(mdl)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_ReadRegister +** +** Read data from a register. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 Address +** Address of register. +** +** OUTPUT: +** +** gctUINT32 * Data +** Pointer to a variable that receives the data read from the register. +*/ +gceSTATUS +gckOS_ReadRegister( + IN gckOS Os, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ) +{ + return gckOS_ReadRegisterEx(Os, gcvCORE_MAJOR, Address, Data); +} + +gceSTATUS +gckOS_ReadRegisterEx( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X", Os, Core, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); +#if !gcdMULTI_GPU + gcmkVERIFY_ARGUMENT(Address < Os->device->requestedRegisterMemSizes[Core]); +#endif + gcmkVERIFY_ARGUMENT(Data != gcvNULL); + + if (!in_interrupt()) + { + mutex_lock(&Os->registerAccessLocks[Core]); + } + + BUG_ON(!_AllowAccess(Os, Core, Address)); + +#if gcdMULTI_GPU + if (Core == gcvCORE_MAJOR) + { + *Data = readl((gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_0_ID] + Address); + } + else +#endif + { + *Data = readl((gctUINT8 *)Os->device->registerBases[Core] + Address); + } + + if (!in_interrupt()) + { + mutex_unlock(&Os->registerAccessLocks[Core]); + } + + /* Success. */ + gcmkFOOTER_ARG("*Data=0x%08x", *Data); + return gcvSTATUS_OK; +} + +#if gcdMULTI_GPU +gceSTATUS +gckOS_ReadRegisterByCoreId( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 CoreId, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d CoreId=%d Address=0x%X", + Os, Core, CoreId, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Data != gcvNULL); + + *Data = readl((gctUINT8 *)Os->device->registerBase3D[CoreId] + Address); + + /* Success. */ + gcmkFOOTER_ARG("*Data=0x%08x", *Data); + return gcvSTATUS_OK; +} +#endif + +/******************************************************************************* +** +** gckOS_WriteRegister +** +** Write data to a register. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 Address +** Address of register. +** +** gctUINT32 Data +** Data for register. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_WriteRegister( + IN gckOS Os, + IN gctUINT32 Address, + IN gctUINT32 Data + ) +{ + return gckOS_WriteRegisterEx(Os, gcvCORE_MAJOR, Address, Data); +} + +gceSTATUS +gckOS_WriteRegisterEx( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address, + IN gctUINT32 Data + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X Data=0x%08x", Os, Core, Address, Data); + +#if !gcdMULTI_GPU + gcmkVERIFY_ARGUMENT(Address < Os->device->requestedRegisterMemSizes[Core]); +#endif + + if (!in_interrupt()) + { + mutex_lock(&Os->registerAccessLocks[Core]); + } + + BUG_ON(!_AllowAccess(Os, Core, Address)); + +#if gcdMULTI_GPU + if (Core == gcvCORE_MAJOR) + { + writel(Data, (gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_0_ID] + Address); +#if gcdMULTI_GPU > 1 + writel(Data, (gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_1_ID] + Address); +#endif + } + else +#endif + { + writel(Data, (gctUINT8 *)Os->device->registerBases[Core] + Address); + } + + if (!in_interrupt()) + { + mutex_unlock(&Os->registerAccessLocks[Core]); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +#if gcdMULTI_GPU +gceSTATUS +gckOS_WriteRegisterByCoreId( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 CoreId, + IN gctUINT32 Address, + IN gctUINT32 Data + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d CoreId=%d Address=0x%X Data=0x%08x", + Os, Core, CoreId, Address, Data); + + writel(Data, (gctUINT8 *)Os->device->registerBase3D[CoreId] + Address); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif + +/******************************************************************************* +** +** gckOS_GetPageSize +** +** Get the system's page size. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** OUTPUT: +** +** gctSIZE_T * PageSize +** Pointer to a variable that will receive the system's page size. +*/ +gceSTATUS gckOS_GetPageSize( + IN gckOS Os, + OUT gctSIZE_T * PageSize + ) +{ + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(PageSize != gcvNULL); + + /* Return the page size. */ + *PageSize = (gctSIZE_T) PAGE_SIZE; + + /* Success. */ + gcmkFOOTER_ARG("*PageSize", *PageSize); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_GetPhysicalAddress +** +** Get the physical system address of a corresponding virtual address. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Logical +** Logical address. +** +** OUTPUT: +** +** gctUINT32 * Address +** Poinetr to a variable that receives the 32-bit physical adress. +*/ +gceSTATUS +gckOS_GetPhysicalAddress( + IN gckOS Os, + IN gctPOINTER Logical, + OUT gctUINT32 * Address + ) +{ + gceSTATUS status; + gctUINT32 processID; + + gcmkHEADER_ARG("Os=0x%X Logical=0x%X", Os, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + /* Query page table of current process first. */ + status = _QueryProcessPageTable(Logical, Address); + + if (gcmIS_ERROR(status)) + { + /* Get current process ID. */ + processID = _GetProcessID(); + + /* Route through other function. */ + gcmkONERROR( + gckOS_GetPhysicalAddressProcess(Os, Logical, processID, Address)); + } + + gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Os, *Address, Address)); + + /* Success. */ + gcmkFOOTER_ARG("*Address=0x%08x", *Address); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_UserLogicalToPhysical +** +** Get the physical system address of a corresponding user virtual address. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Logical +** Logical address. +** +** OUTPUT: +** +** gctUINT32 * Address +** Pointer to a variable that receives the 32-bit physical address. +*/ +gceSTATUS gckOS_UserLogicalToPhysical( + IN gckOS Os, + IN gctPOINTER Logical, + OUT gctUINT32 * Address + ) +{ + return gckOS_GetPhysicalAddress(Os, Logical, Address); +} + +#if gcdSECURE_USER +static gceSTATUS +gckOS_AddMapping( + IN gckOS Os, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gceSTATUS status; + gcsUSER_MAPPING_PTR map; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu", + Os, Physical, Logical, Bytes); + + gcmkONERROR(gckOS_Allocate(Os, + gcmSIZEOF(gcsUSER_MAPPING), + (gctPOINTER *) &map)); + + map->next = Os->userMap; + map->physical = Physical - Os->device->baseAddress; + map->logical = Logical; + map->bytes = Bytes; + map->start = (gctINT8_PTR) Logical; + map->end = map->start + Bytes; + + Os->userMap = map; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +static gceSTATUS +gckOS_RemoveMapping( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gceSTATUS status; + gcsUSER_MAPPING_PTR map, prev; + + gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes); + + for (map = Os->userMap, prev = gcvNULL; map != gcvNULL; map = map->next) + { + if ((map->logical == Logical) + && (map->bytes == Bytes) + ) + { + break; + } + + prev = map; + } + + if (map == gcvNULL) + { + gcmkONERROR(gcvSTATUS_INVALID_ADDRESS); + } + + if (prev == gcvNULL) + { + Os->userMap = map->next; + } + else + { + prev->next = map->next; + } + + gcmkONERROR(gcmkOS_SAFE_FREE(Os, map)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} +#endif + +gceSTATUS +_ConvertLogical2Physical( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + IN PLINUX_MDL Mdl, + OUT gctUINT32_PTR Physical + ) +{ + gctINT8_PTR base, vBase; + gctUINT32 offset; + PLINUX_MDL_MAP map; + gcsUSER_MAPPING_PTR userMap; + +#if gcdSECURITY + base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->kaddr; +#else + base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->addr; +#endif + + /* Check for the logical address match. */ + if ((base != gcvNULL) + && ((gctINT8_PTR) Logical >= base) + && ((gctINT8_PTR) Logical < base + Mdl->numPages * PAGE_SIZE) + ) + { + offset = (gctINT8_PTR) Logical - base; + + if (Mdl->dmaHandle != 0) + { + /* The memory was from coherent area. */ + *Physical = (gctUINT32) Mdl->dmaHandle + offset; + } + else if (Mdl->pagedMem && !Mdl->contiguous) + { + /* paged memory is not mapped to kernel space. */ + return gcvSTATUS_INVALID_ADDRESS; + } + else + { + *Physical = gcmPTR2INT32(virt_to_phys(base)) + offset; + } + + return gcvSTATUS_OK; + } + + /* Walk user maps. */ + for (userMap = Os->userMap; userMap != gcvNULL; userMap = userMap->next) + { + if (((gctINT8_PTR) Logical >= userMap->start) + && ((gctINT8_PTR) Logical < userMap->end) + ) + { + *Physical = userMap->physical + + (gctUINT32) ((gctINT8_PTR) Logical - userMap->start); + + return gcvSTATUS_OK; + } + } + + if (ProcessID != Os->kernelProcessID) + { + map = FindMdlMap(Mdl, (gctINT) ProcessID); + vBase = (map == gcvNULL) ? gcvNULL : (gctINT8_PTR) map->vmaAddr; + + /* Is the given address within that range. */ + if ((vBase != gcvNULL) + && ((gctINT8_PTR) Logical >= vBase) + && ((gctINT8_PTR) Logical < vBase + Mdl->numPages * PAGE_SIZE) + ) + { + offset = (gctINT8_PTR) Logical - vBase; + + if (Mdl->dmaHandle != 0) + { + /* The memory was from coherent area. */ + *Physical = (gctUINT32) Mdl->dmaHandle + offset; + } + else if (Mdl->pagedMem && !Mdl->contiguous) + { + *Physical = _NonContiguousToPhys(Mdl->u.nonContiguousPages, offset/PAGE_SIZE); + } + else + { + *Physical = page_to_phys(Mdl->u.contiguousPages) + offset; + } + + return gcvSTATUS_OK; + } + } + + /* Address not yet found. */ + return gcvSTATUS_INVALID_ADDRESS; +} + +/******************************************************************************* +** +** gckOS_GetPhysicalAddressProcess +** +** Get the physical system address of a corresponding virtual address for a +** given process. +** +** INPUT: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctPOINTER Logical +** Logical address. +** +** gctUINT32 ProcessID +** Process ID. +** +** OUTPUT: +** +** gctUINT32 * Address +** Poinetr to a variable that receives the 32-bit physical adress. +*/ +gceSTATUS +gckOS_GetPhysicalAddressProcess( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + OUT gctUINT32 * Address + ) +{ + PLINUX_MDL mdl; + gctINT8_PTR base; + gckALLOCATOR allocator = gcvNULL; + gceSTATUS status = gcvSTATUS_INVALID_ADDRESS; + + gcmkHEADER_ARG("Os=0x%X Logical=0x%X ProcessID=%d", Os, Logical, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + MEMORY_LOCK(Os); + + /* First try the contiguous memory pool. */ + if (Os->device->contiguousMapped) + { + base = (gctINT8_PTR) Os->device->contiguousBase; + + if (((gctINT8_PTR) Logical >= base) + && ((gctINT8_PTR) Logical < base + Os->device->contiguousSize) + ) + { + /* Convert logical address into physical. */ + *Address = Os->device->contiguousVidMem->baseAddress + + (gctINT8_PTR) Logical - base; + status = gcvSTATUS_OK; + } + } + else + { + /* Try the contiguous memory pool. */ + mdl = (PLINUX_MDL) Os->device->contiguousPhysical; + status = _ConvertLogical2Physical(Os, + Logical, + ProcessID, + mdl, + Address); + } + + if (gcmIS_ERROR(status)) + { + /* Walk all MDLs. */ + for (mdl = Os->mdlHead; mdl != gcvNULL; mdl = mdl->next) + { + /* Try this MDL. */ + allocator = mdl->allocator; + + if (allocator) + { + status = allocator->ops->LogicalToPhysical( + allocator, + mdl, + Logical, + ProcessID, + Address + ); + } + else + { + status = _ConvertLogical2Physical(Os, + Logical, + ProcessID, + mdl, + Address); + } + + if (gcmIS_SUCCESS(status)) + { + break; + } + } + } + + MEMORY_UNLOCK(Os); + + gcmkONERROR(status); + + /* Success. */ + gcmkFOOTER_ARG("*Address=0x%08x", *Address); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_MapPhysical +** +** Map a physical address into kernel space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 Physical +** Physical address of the memory to map. +** +** gctSIZE_T Bytes +** Number of bytes to map. +** +** OUTPUT: +** +** gctPOINTER * Logical +** Pointer to a variable that receives the base address of the mapped +** memory. +*/ +gceSTATUS +gckOS_MapPhysical( + IN gckOS Os, + IN gctUINT32 Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical + ) +{ + gctPOINTER logical; + PLINUX_MDL mdl; + gctUINT32 physical = Physical; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + MEMORY_LOCK(Os); + + /* Go through our mapping to see if we know this physical address already. */ + mdl = Os->mdlHead; + + while (mdl != gcvNULL) + { + if (mdl->dmaHandle != 0) + { + if ((physical >= mdl->dmaHandle) + && (physical < mdl->dmaHandle + mdl->numPages * PAGE_SIZE) + ) + { + *Logical = mdl->addr + (physical - mdl->dmaHandle); + break; + } + } + + mdl = mdl->next; + } + + MEMORY_UNLOCK(Os); + + if (mdl == gcvNULL) + { + struct page * page = pfn_to_page(physical >> PAGE_SHIFT); + + if (pfn_valid(page_to_pfn(page))) + { + gctUINT32 offset = physical & ~PAGE_MASK; + struct page ** pages; + gctUINT numPages; + gctINT i; + + numPages = GetPageCount(PAGE_ALIGN(offset + Bytes), 0); + + pages = kmalloc(sizeof(struct page *) * numPages, GFP_KERNEL | gcdNOWARN); + + if (!pages) + { + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + for (i = 0; i < numPages; i++) + { + pages[i] = nth_page(page, i); + } + + logical = vmap(pages, numPages, 0, gcmkNONPAGED_MEMROY_PROT(PAGE_KERNEL)); + + kfree(pages); + + if (logical == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Failed to vmap", + __FUNCTION__, __LINE__ + ); + + /* Out of resources. */ + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } + + logical += offset; + } + else + { + /* Map memory as cached memory. */ + request_mem_region(physical, Bytes, "MapRegion"); + logical = (gctPOINTER) ioremap_nocache(physical, Bytes); + + if (logical == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Failed to ioremap", + __FUNCTION__, __LINE__ + ); + + /* Out of resources. */ + gcmkFOOTER_ARG("status=%d", gcvSTATUS_OUT_OF_RESOURCES); + return gcvSTATUS_OUT_OF_RESOURCES; + } + } + + /* Return pointer to mapped memory. */ + *Logical = logical; + } + + + /* Success. */ + gcmkFOOTER_ARG("*Logical=0x%X", *Logical); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_UnmapPhysical +** +** Unmap a previously mapped memory region from kernel memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Logical +** Pointer to the base address of the memory to unmap. +** +** gctSIZE_T Bytes +** Number of bytes to unmap. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnmapPhysical( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + PLINUX_MDL mdl; + + gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + MEMORY_LOCK(Os); + + mdl = Os->mdlHead; + + while (mdl != gcvNULL) + { + if (mdl->addr != gcvNULL) + { + if (Logical >= (gctPOINTER)mdl->addr + && Logical < (gctPOINTER)((gctSTRING)mdl->addr + mdl->numPages * PAGE_SIZE)) + { + break; + } + } + + mdl = mdl->next; + } + + if (mdl == gcvNULL) + { + /* Unmap the memory. */ + vunmap((void *)((unsigned long)Logical & PAGE_MASK)); + } + + MEMORY_UNLOCK(Os); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_CreateMutex +** +** Create a new mutex. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** OUTPUT: +** +** gctPOINTER * Mutex +** Pointer to a variable that will hold a pointer to the mutex. +*/ +gceSTATUS +gckOS_CreateMutex( + IN gckOS Os, + OUT gctPOINTER * Mutex + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Validate the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); + + /* Allocate the mutex structure. */ + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct mutex), Mutex)); + + /* Initialize the mutex. */ + mutex_init(*Mutex); + + /* Return status. */ + gcmkFOOTER_ARG("*Mutex=0x%X", *Mutex); + return gcvSTATUS_OK; + +OnError: + /* Return status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_DeleteMutex +** +** Delete a mutex. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Mutex +** Pointer to the mute to be deleted. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_DeleteMutex( + IN gckOS Os, + IN gctPOINTER Mutex + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Mutex=0x%X", Os, Mutex); + + /* Validate the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); + + /* Destroy the mutex. */ + mutex_destroy((struct mutex *)Mutex); + + /* Free the mutex structure. */ + gcmkONERROR(gckOS_Free(Os, Mutex)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_AcquireMutex +** +** Acquire a mutex. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Mutex +** Pointer to the mutex to be acquired. +** +** gctUINT32 Timeout +** Timeout value specified in milliseconds. +** Specify the value of gcvINFINITE to keep the thread suspended +** until the mutex has been acquired. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AcquireMutex( + IN gckOS Os, + IN gctPOINTER Mutex, + IN gctUINT32 Timeout + ) +{ + gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x Timeout=%u", Os, Mutex, Timeout); + + /* Validate the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); + + if (Timeout == gcvINFINITE) + { + /* Lock the mutex. */ + mutex_lock(Mutex); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + for (;;) + { + /* Try to acquire the mutex. */ + if (mutex_trylock(Mutex)) + { + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + if (Timeout-- == 0) + { + break; + } + + /* Wait for 1 millisecond. */ + gcmkVERIFY_OK(gckOS_Delay(Os, 1)); + } + + /* Timeout. */ + gcmkFOOTER_ARG("status=%d", gcvSTATUS_TIMEOUT); + return gcvSTATUS_TIMEOUT; +} + +/******************************************************************************* +** +** gckOS_ReleaseMutex +** +** Release an acquired mutex. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Mutex +** Pointer to the mutex to be released. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_ReleaseMutex( + IN gckOS Os, + IN gctPOINTER Mutex + ) +{ + gcmkHEADER_ARG("Os=0x%X Mutex=0x%0x", Os, Mutex); + + /* Validate the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); + + /* Release the mutex. */ + mutex_unlock(Mutex); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomicExchange +** +** Atomically exchange a pair of 32-bit values. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** IN OUT gctINT32_PTR Target +** Pointer to the 32-bit value to exchange. +** +** IN gctINT32 NewValue +** Specifies a new value for the 32-bit value pointed to by Target. +** +** OUT gctINT32_PTR OldValue +** The old value of the 32-bit value pointed to by Target. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomicExchange( + IN gckOS Os, + IN OUT gctUINT32_PTR Target, + IN gctUINT32 NewValue, + OUT gctUINT32_PTR OldValue + ) +{ + gcmkHEADER_ARG("Os=0x%X Target=0x%X NewValue=%u", Os, Target, NewValue); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(OldValue != gcvNULL); + + /* Exchange the pair of 32-bit values. */ + *OldValue = (gctUINT32) atomic_xchg((atomic_t *) Target, (int) NewValue); + + /* Success. */ + gcmkFOOTER_ARG("*OldValue=%u", *OldValue); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomicExchangePtr +** +** Atomically exchange a pair of pointers. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** IN OUT gctPOINTER * Target +** Pointer to the 32-bit value to exchange. +** +** IN gctPOINTER NewValue +** Specifies a new value for the pointer pointed to by Target. +** +** OUT gctPOINTER * OldValue +** The old value of the pointer pointed to by Target. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomicExchangePtr( + IN gckOS Os, + IN OUT gctPOINTER * Target, + IN gctPOINTER NewValue, + OUT gctPOINTER * OldValue + ) +{ + gcmkHEADER_ARG("Os=0x%X Target=0x%X NewValue=0x%X", Os, Target, NewValue); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(OldValue != gcvNULL); + + /* Exchange the pair of pointers. */ + *OldValue = (gctPOINTER)(gctUINTPTR_T) atomic_xchg((atomic_t *) Target, (int)(gctUINTPTR_T) NewValue); + + /* Success. */ + gcmkFOOTER_ARG("*OldValue=0x%X", *OldValue); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomicSetMask +** +** Atomically set mask to Atom +** +** INPUT: +** IN OUT gctPOINTER Atom +** Pointer to the atom to set. +** +** IN gctUINT32 Mask +** Mask to set. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomSetMask( + IN gctPOINTER Atom, + IN gctUINT32 Mask + ) +{ + gctUINT32 oval, nval; + + gcmkHEADER_ARG("Atom=0x%0x", Atom); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + do + { + oval = atomic_read((atomic_t *) Atom); + nval = oval | Mask; + } while (atomic_cmpxchg((atomic_t *) Atom, oval, nval) != oval); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomClearMask +** +** Atomically clear mask from Atom +** +** INPUT: +** IN OUT gctPOINTER Atom +** Pointer to the atom to clear. +** +** IN gctUINT32 Mask +** Mask to clear. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomClearMask( + IN gctPOINTER Atom, + IN gctUINT32 Mask + ) +{ + gctUINT32 oval, nval; + + gcmkHEADER_ARG("Atom=0x%0x", Atom); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + do + { + oval = atomic_read((atomic_t *) Atom); + nval = oval & ~Mask; + } while (atomic_cmpxchg((atomic_t *) Atom, oval, nval) != oval); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomConstruct +** +** Create an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** OUTPUT: +** +** gctPOINTER * Atom +** Pointer to a variable receiving the constructed atom. +*/ +gceSTATUS +gckOS_AtomConstruct( + IN gckOS Os, + OUT gctPOINTER * Atom + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + /* Allocate the atom. */ + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(atomic_t), Atom)); + + /* Initialize the atom. */ + atomic_set((atomic_t *) *Atom, 0); + + /* Success. */ + gcmkFOOTER_ARG("*Atom=0x%X", *Atom); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_AtomDestroy +** +** Destroy an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomDestroy( + IN gckOS Os, + OUT gctPOINTER Atom + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + /* Free the atom. */ + gcmkONERROR(gcmkOS_SAFE_FREE(Os, Atom)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_AtomGet +** +** Get the 32-bit value protected by an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** OUTPUT: +** +** gctINT32_PTR Value +** Pointer to a variable the receives the value of the atom. +*/ +gceSTATUS +gckOS_AtomGet( + IN gckOS Os, + IN gctPOINTER Atom, + OUT gctINT32_PTR Value + ) +{ + gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + /* Return the current value of atom. */ + *Value = atomic_read((atomic_t *) Atom); + + /* Success. */ + gcmkFOOTER_ARG("*Value=%d", *Value); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomSet +** +** Set the 32-bit value protected by an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** gctINT32 Value +** The value of the atom. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomSet( + IN gckOS Os, + IN gctPOINTER Atom, + IN gctINT32 Value + ) +{ + gcmkHEADER_ARG("Os=0x%X Atom=0x%0x Value=%d", Os, Atom); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + /* Set the current value of atom. */ + atomic_set((atomic_t *) Atom, Value); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomIncrement +** +** Atomically increment the 32-bit integer value inside an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** OUTPUT: +** +** gctINT32_PTR Value +** Pointer to a variable that receives the original value of the atom. +*/ +gceSTATUS +gckOS_AtomIncrement( + IN gckOS Os, + IN gctPOINTER Atom, + OUT gctINT32_PTR Value + ) +{ + gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + /* Increment the atom. */ + *Value = atomic_inc_return((atomic_t *) Atom) - 1; + + /* Success. */ + gcmkFOOTER_ARG("*Value=%d", *Value); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AtomDecrement +** +** Atomically decrement the 32-bit integer value inside an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** OUTPUT: +** +** gctINT32_PTR Value +** Pointer to a variable that receives the original value of the atom. +*/ +gceSTATUS +gckOS_AtomDecrement( + IN gckOS Os, + IN gctPOINTER Atom, + OUT gctINT32_PTR Value + ) +{ + gcmkHEADER_ARG("Os=0x%X Atom=0x%0x", Os, Atom); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Atom != gcvNULL); + + /* Decrement the atom. */ + *Value = atomic_dec_return((atomic_t *) Atom) + 1; + + /* Success. */ + gcmkFOOTER_ARG("*Value=%d", *Value); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_Delay +** +** Delay execution of the current thread for a number of milliseconds. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 Delay +** Delay to sleep, specified in milliseconds. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_Delay( + IN gckOS Os, + IN gctUINT32 Delay + ) +{ + gcmkHEADER_ARG("Os=0x%X Delay=%u", Os, Delay); + + if (Delay > 0) + { + ktime_t delay = ktime_set((Delay / MSEC_PER_SEC), (Delay % MSEC_PER_SEC) * NSEC_PER_MSEC); + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&delay, HRTIMER_MODE_REL); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_GetTicks +** +** Get the number of milliseconds since the system started. +** +** INPUT: +** +** OUTPUT: +** +** gctUINT32_PTR Time +** Pointer to a variable to get time. +** +*/ +gceSTATUS +gckOS_GetTicks( + OUT gctUINT32_PTR Time + ) +{ + gcmkHEADER(); + + *Time = jiffies_to_msecs(jiffies); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_TicksAfter +** +** Compare time values got from gckOS_GetTicks. +** +** INPUT: +** gctUINT32 Time1 +** First time value to be compared. +** +** gctUINT32 Time2 +** Second time value to be compared. +** +** OUTPUT: +** +** gctBOOL_PTR IsAfter +** Pointer to a variable to result. +** +*/ +gceSTATUS +gckOS_TicksAfter( + IN gctUINT32 Time1, + IN gctUINT32 Time2, + OUT gctBOOL_PTR IsAfter + ) +{ + gcmkHEADER(); + + *IsAfter = time_after((unsigned long)Time1, (unsigned long)Time2); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_GetTime +** +** Get the number of microseconds since the system started. +** +** INPUT: +** +** OUTPUT: +** +** gctUINT64_PTR Time +** Pointer to a variable to get time. +** +*/ +gceSTATUS +gckOS_GetTime( + OUT gctUINT64_PTR Time + ) +{ + struct timeval tv; + gcmkHEADER(); + + /* Return the time of day in microseconds. */ + do_gettimeofday(&tv); + *Time = (tv.tv_sec * 1000000ULL) + tv.tv_usec; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_MemoryBarrier +** +** Make sure the CPU has executed everything up to this point and the data got +** written to the specified pointer. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Address +** Address of memory that needs to be barriered. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_MemoryBarrier( + IN gckOS Os, + IN gctPOINTER Address + ) +{ + gcmkHEADER_ARG("Os=0x%X Address=0x%X", Os, Address); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + mb(); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_AllocatePagedMemory +** +** Allocate memory from the paged pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIZE_T Bytes +** Number of bytes to allocate. +** +** OUTPUT: +** +** gctPHYS_ADDR * Physical +** Pointer to a variable that receives the physical address of the +** memory allocation. +*/ +gceSTATUS +gckOS_AllocatePagedMemory( + IN gckOS Os, + IN gctSIZE_T Bytes, + OUT gctPHYS_ADDR * Physical + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Bytes=%lu", Os, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + + /* Allocate the memory. */ + gcmkONERROR(gckOS_AllocatePagedMemoryEx(Os, gcvALLOC_FLAG_NONE, Bytes, gcvNULL, Physical)); + + /* Success. */ + gcmkFOOTER_ARG("*Physical=0x%X", *Physical); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_AllocatePagedMemoryEx +** +** Allocate memory from the paged pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 Flag +** Allocation attribute. +** +** gctSIZE_T Bytes +** Number of bytes to allocate. +** +** OUTPUT: +** +** gctUINT32 * Gid +** Save the global ID for the piece of allocated memory. +** +** gctPHYS_ADDR * Physical +** Pointer to a variable that receives the physical address of the +** memory allocation. +*/ +gceSTATUS +gckOS_AllocatePagedMemoryEx( + IN gckOS Os, + IN gctUINT32 Flag, + IN gctSIZE_T Bytes, + OUT gctUINT32 * Gid, + OUT gctPHYS_ADDR * Physical + ) +{ + gctINT numPages; + PLINUX_MDL mdl = gcvNULL; + gctSIZE_T bytes; + gceSTATUS status = gcvSTATUS_OUT_OF_MEMORY; + gckALLOCATOR allocator; + + gcmkHEADER_ARG("Os=0x%X Flag=%x Bytes=%lu", Os, Flag, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + + bytes = gcmALIGN(Bytes, PAGE_SIZE); + + numPages = GetPageCount(bytes, 0); + + mdl = _CreateMdl(); + if (mdl == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Walk all allocators. */ + list_for_each_entry(allocator, &Os->allocatorList, head) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d) flag = %x allocator->capability = %x", + __FUNCTION__, __LINE__, Flag, allocator->capability); + + if ((Flag & allocator->capability) != Flag) + { + continue; + } + + status = allocator->ops->Alloc(allocator, mdl, numPages, Flag); + + if (gcmIS_SUCCESS(status)) + { + mdl->allocator = allocator; + break; + } + } + + /* Check status. */ + gcmkONERROR(status); + + mdl->dmaHandle = 0; + mdl->addr = 0; + mdl->numPages = numPages; + mdl->pagedMem = 1; + mdl->contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; + + if (Gid != gcvNULL) + { + *Gid = mdl->gid; + } + + MEMORY_LOCK(Os); + + /* + * Add this to a global list. + * Will be used by get physical address + * and mapuser pointer functions. + */ + if (!Os->mdlHead) + { + /* Initialize the queue. */ + Os->mdlHead = Os->mdlTail = mdl; + } + else + { + /* Add to tail. */ + mdl->prev = Os->mdlTail; + Os->mdlTail->next = mdl; + Os->mdlTail = mdl; + } + + MEMORY_UNLOCK(Os); + + /* Return physical address. */ + *Physical = (gctPHYS_ADDR) mdl; + + /* Success. */ + gcmkFOOTER_ARG("*Physical=0x%X", *Physical); + return gcvSTATUS_OK; + +OnError: + if (mdl != gcvNULL) + { + /* Free the memory. */ + _DestroyMdl(mdl); + } + *Physical = gcvNULL; + + /* Return the status. */ + gcmkFOOTER_ARG("Os=0x%X Flag=%x Bytes=%lu", Os, Flag, Bytes); + return status; +} + +/******************************************************************************* +** +** gckOS_FreePagedMemory +** +** Free memory allocated from the paged pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Physical address of the allocation. +** +** gctSIZE_T Bytes +** Number of bytes of the allocation. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_FreePagedMemory( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes + ) +{ + PLINUX_MDL mdl = (PLINUX_MDL) Physical; + gckALLOCATOR allocator = (gckALLOCATOR)mdl->allocator; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + MEMORY_LOCK(Os); + + /* Remove the node from global list. */ + if (mdl == Os->mdlHead) + { + if ((Os->mdlHead = mdl->next) == gcvNULL) + { + Os->mdlTail = gcvNULL; + } + } + else + { + mdl->prev->next = mdl->next; + + if (mdl == Os->mdlTail) + { + Os->mdlTail = mdl->prev; + } + else + { + mdl->next->prev = mdl->prev; + } + } + + MEMORY_UNLOCK(Os); + + allocator->ops->Free(allocator, mdl); + + /* Free the structure... */ + gcmkVERIFY_OK(_DestroyMdl(mdl)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_LockPages +** +** Lock memory allocated from the paged pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Physical address of the allocation. +** +** gctSIZE_T Bytes +** Number of bytes of the allocation. +** +** gctBOOL Cacheable +** Cache mode of mapping. +** +** OUTPUT: +** +** gctPOINTER * Logical +** Pointer to a variable that receives the address of the mapped +** memory. +** +** gctSIZE_T * PageCount +** Pointer to a variable that receives the number of pages required for +** the page table according to the GPU page size. +*/ +gceSTATUS +gckOS_LockPages( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctBOOL Cacheable, + OUT gctPOINTER * Logical, + OUT gctSIZE_T * PageCount + ) +{ + gceSTATUS status; + PLINUX_MDL mdl; + PLINUX_MDL_MAP mdlMap; + gckALLOCATOR allocator; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(PageCount != gcvNULL); + + mdl = (PLINUX_MDL) Physical; + allocator = mdl->allocator; + + MEMORY_LOCK(Os); + + mdlMap = FindMdlMap(mdl, _GetProcessID()); + + if (mdlMap == gcvNULL) + { + mdlMap = _CreateMdlMap(mdl, _GetProcessID()); + + if (mdlMap == gcvNULL) + { + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + } + + if (mdlMap->vmaAddr == gcvNULL) + { + status = allocator->ops->MapUser(allocator, mdl, mdlMap, Cacheable); + + if (gcmIS_ERROR(status)) + { + MEMORY_UNLOCK(Os); + + gcmkFOOTER_ARG("*status=%d", status); + return status; + } + } + + mdlMap->count++; + + /* Convert pointer to MDL. */ + *Logical = mdlMap->vmaAddr; + + /* Return the page number according to the GPU page size. */ + gcmkASSERT((PAGE_SIZE % 4096) == 0); + gcmkASSERT((PAGE_SIZE / 4096) >= 1); + + *PageCount = mdl->numPages * (PAGE_SIZE / 4096); + + MEMORY_UNLOCK(Os); + + gcmkVERIFY_OK(gckOS_CacheFlush( + Os, + _GetProcessID(), + Physical, + gcvINVALID_ADDRESS, + (gctPOINTER)mdlMap->vmaAddr, + mdl->numPages * PAGE_SIZE + )); + + /* Success. */ + gcmkFOOTER_ARG("*Logical=0x%X *PageCount=%lu", *Logical, *PageCount); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_MapPages +** +** Map paged memory into a page table. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Physical address of the allocation. +** +** gctSIZE_T PageCount +** Number of pages required for the physical address. +** +** gctPOINTER PageTable +** Pointer to the page table to fill in. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_MapPages( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T PageCount, + IN gctPOINTER PageTable + ) +{ + return gckOS_MapPagesEx(Os, + gcvCORE_MAJOR, + Physical, + PageCount, + 0, + PageTable); +} + +gceSTATUS +gckOS_MapPagesEx( + IN gckOS Os, + IN gceCORE Core, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T PageCount, + IN gctUINT32 Address, + IN gctPOINTER PageTable + ) +{ + gceSTATUS status = gcvSTATUS_OK; + PLINUX_MDL mdl; + gctUINT32* table; + gctUINT32 offset; +#if gcdNONPAGED_MEMORY_CACHEABLE + gckMMU mmu; + PLINUX_MDL mmuMdl; + gctUINT32 bytes; + gctPHYS_ADDR pageTablePhysical; +#endif + +#if gcdPROCESS_ADDRESS_SPACE + gckKERNEL kernel = Os->device->kernels[Core]; + gckMMU mmu; +#endif + gckALLOCATOR allocator; + + gcmkHEADER_ARG("Os=0x%X Core=%d Physical=0x%X PageCount=%u PageTable=0x%X", + Os, Core, Physical, PageCount, PageTable); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(PageCount > 0); + gcmkVERIFY_ARGUMENT(PageTable != gcvNULL); + + /* Convert pointer to MDL. */ + mdl = (PLINUX_MDL)Physical; + + allocator = mdl->allocator; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Physical->0x%X PageCount->0x%X PagedMemory->?%d", + __FUNCTION__, __LINE__, + (gctUINT32)(gctUINTPTR_T)Physical, + (gctUINT32)(gctUINTPTR_T)PageCount, + mdl->pagedMem + ); + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckKERNEL_GetProcessMMU(kernel, &mmu)); +#endif + + table = (gctUINT32 *)PageTable; +#if gcdNONPAGED_MEMORY_CACHEABLE + mmu = Os->device->kernels[Core]->mmu; + bytes = PageCount * sizeof(*table); + mmuMdl = (PLINUX_MDL)mmu->pageTablePhysical; +#endif + + /* Get all the physical addresses and store them in the page table. */ + + offset = 0; + PageCount = PageCount / (PAGE_SIZE / 4096); + + /* Try to get the user pages so DMA can happen. */ + while (PageCount-- > 0) + { + gctUINT i; + gctUINT32 phys = ~0; + + if (mdl->pagedMem && !mdl->contiguous) + { + allocator->ops->Physical(allocator, mdl, offset, &phys); + } + else + { + if (!mdl->pagedMem) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): we should not get this call for Non Paged Memory!", + __FUNCTION__, __LINE__ + ); + } + + phys = page_to_phys(nth_page(mdl->u.contiguousPages, offset)); + } + + gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(Os, phys, &phys)); + +#ifdef CONFIG_IOMMU_SUPPORT + if (Os->iommu) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Setup mapping in IOMMU %x => %x", + __FUNCTION__, __LINE__, + Address + (offset * PAGE_SIZE), phys + ); + + /* When use IOMMU, GPU use system PAGE_SIZE. */ + gcmkONERROR(gckIOMMU_Map( + Os->iommu, Address + (offset * PAGE_SIZE), phys, PAGE_SIZE)); + } + else +#endif + { + +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + for (i = 0; i < (PAGE_SIZE / 4096); i++) + { + gcmkONERROR( + gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu, + phys + (i * 4096), + table++)); + } + } + else +#endif + { + for (i = 0; i < (PAGE_SIZE / 4096); i++) + { +#if gcdPROCESS_ADDRESS_SPACE + gctUINT32_PTR pageTableEntry; + gckMMU_GetPageEntry(mmu, Address + (offset * 4096), &pageTableEntry); + gcmkONERROR( + gckMMU_SetPage(mmu, + phys + (i * 4096), + pageTableEntry)); +#else + gcmkONERROR( + gckMMU_SetPage(Os->device->kernels[Core]->mmu, + phys + (i * 4096), + table++)); +#endif + } + } + } + + offset += 1; + } + +#if gcdNONPAGED_MEMORY_CACHEABLE + /* Get physical address of pageTable */ + pageTablePhysical = (gctPHYS_ADDR)(mmuMdl->dmaHandle + + ((gctUINT32 *)PageTable - mmu->pageTableLogical)); + + /* Flush the mmu page table cache. */ + gcmkONERROR(gckOS_CacheClean( + Os, + _GetProcessID(), + gcvNULL, + pageTablePhysical, + PageTable, + bytes + )); +#endif + +OnError: + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_UnmapPages( + IN gckOS Os, + IN gctSIZE_T PageCount, + IN gctUINT32 Address + ) +{ +#ifdef CONFIG_IOMMU_SUPPORT + if (Os->iommu) + { + gcmkVERIFY_OK(gckIOMMU_Unmap( + Os->iommu, Address, PageCount * PAGE_SIZE)); + } +#endif + + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_UnlockPages +** +** Unlock memory allocated from the paged pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Physical address of the allocation. +** +** gctSIZE_T Bytes +** Number of bytes of the allocation. +** +** gctPOINTER Logical +** Address of the mapped memory. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnlockPages( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ) +{ + PLINUX_MDL_MAP mdlMap; + PLINUX_MDL mdl = (PLINUX_MDL)Physical; + gckALLOCATOR allocator = mdl->allocator; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%u Logical=0x%X", + Os, Physical, Bytes, Logical); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + MEMORY_LOCK(Os); + + mdlMap = mdl->maps; + + while (mdlMap != gcvNULL) + { + if ((mdlMap->vmaAddr != gcvNULL) && (_GetProcessID() == mdlMap->pid)) + { + if (--mdlMap->count == 0) + { + allocator->ops->UnmapUser( + allocator, + mdlMap->vmaAddr, + mdl->numPages * PAGE_SIZE); + + mdlMap->vmaAddr = gcvNULL; + } + } + + mdlMap = mdlMap->next; + } + + MEMORY_UNLOCK(Os); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + + +/******************************************************************************* +** +** gckOS_AllocateContiguous +** +** Allocate memory from the contiguous pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctBOOL InUserSpace +** gcvTRUE if the pages need to be mapped into user space. +** +** gctSIZE_T * Bytes +** Pointer to the number of bytes to allocate. +** +** OUTPUT: +** +** gctSIZE_T * Bytes +** Pointer to a variable that receives the number of bytes allocated. +** +** gctPHYS_ADDR * Physical +** Pointer to a variable that receives the physical address of the +** memory allocation. +** +** gctPOINTER * Logical +** Pointer to a variable that receives the logical address of the +** memory allocation. +*/ +gceSTATUS +gckOS_AllocateContiguous( + IN gckOS Os, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X InUserSpace=%d *Bytes=%lu", + Os, InUserSpace, gcmOPT_VALUE(Bytes)); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes != gcvNULL); + gcmkVERIFY_ARGUMENT(*Bytes > 0); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + + /* Same as non-paged memory for now. */ + gcmkONERROR(gckOS_AllocateNonPagedMemory(Os, + InUserSpace, + Bytes, + Physical, + Logical)); + + /* Success. */ + gcmkFOOTER_ARG("*Bytes=%lu *Physical=0x%X *Logical=0x%X", + *Bytes, *Physical, *Logical); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_FreeContiguous +** +** Free memory allocated from the contiguous pool. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPHYS_ADDR Physical +** Physical address of the allocation. +** +** gctPOINTER Logical +** Logicval address of the allocation. +** +** gctSIZE_T Bytes +** Number of bytes of the allocation. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_FreeContiguous( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu", + Os, Physical, Logical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + /* Same of non-paged memory for now. */ + gcmkONERROR(gckOS_FreeNonPagedMemory(Os, Bytes, Physical, Logical)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdENABLE_VG +/****************************************************************************** +** +** gckOS_GetKernelLogical +** +** Return the kernel logical pointer that corresponods to the specified +** hardware address. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 Address +** Hardware physical address. +** +** OUTPUT: +** +** gctPOINTER * KernelPointer +** Pointer to a variable receiving the pointer in kernel address space. +*/ +gceSTATUS +gckOS_GetKernelLogical( + IN gckOS Os, + IN gctUINT32 Address, + OUT gctPOINTER * KernelPointer + ) +{ + return gckOS_GetKernelLogicalEx(Os, gcvCORE_MAJOR, Address, KernelPointer); +} + +gceSTATUS +gckOS_GetKernelLogicalEx( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address, + OUT gctPOINTER * KernelPointer + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%08x", Os, Core, Address); + + do + { + gckGALDEVICE device; + gckKERNEL kernel; + gcePOOL pool; + gctUINT32 offset; + gctPOINTER logical; + + /* Extract the pointer to the gckGALDEVICE class. */ + device = (gckGALDEVICE) Os->device; + + /* Kernel shortcut. */ + kernel = device->kernels[Core]; +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + gcmkERR_BREAK(gckVGHARDWARE_SplitMemory( + kernel->vg->hardware, Address, &pool, &offset + )); + } + else +#endif + { + /* Split the memory address into a pool type and offset. */ + gcmkERR_BREAK(gckHARDWARE_SplitMemory( + kernel->hardware, Address, &pool, &offset + )); + } + + /* Dispatch on pool. */ + switch (pool) + { + case gcvPOOL_LOCAL_INTERNAL: + /* Internal memory. */ + logical = device->internalLogical; + break; + + case gcvPOOL_LOCAL_EXTERNAL: + /* External memory. */ + logical = device->externalLogical; + break; + + case gcvPOOL_SYSTEM: + /* System memory. */ + logical = device->contiguousBase; + break; + + default: + /* Invalid memory pool. */ + gcmkFOOTER(); + return gcvSTATUS_INVALID_ARGUMENT; + } + + /* Build logical address of specified address. */ + * KernelPointer = ((gctUINT8_PTR) logical) + offset; + + /* Success. */ + gcmkFOOTER_ARG("*KernelPointer=0x%X", *KernelPointer); + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Return status. */ + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckOS_MapUserPointer +** +** Map a pointer from the user process into the kernel address space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Pointer +** Pointer in user process space that needs to be mapped. +** +** gctSIZE_T Size +** Number of bytes that need to be mapped. +** +** OUTPUT: +** +** gctPOINTER * KernelPointer +** Pointer to a variable receiving the mapped pointer in kernel address +** space. +*/ +gceSTATUS +gckOS_MapUserPointer( + IN gckOS Os, + IN gctPOINTER Pointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ) +{ + gctPOINTER buf = gcvNULL; + gctUINT32 len; + + gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu", Os, Pointer, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + + buf = kmalloc(Size, GFP_KERNEL | gcdNOWARN); + if (buf == gcvNULL) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): Failed to allocate memory.", + __FUNCTION__, __LINE__ + ); + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_OUT_OF_MEMORY); + return gcvSTATUS_OUT_OF_MEMORY; + } + + len = copy_from_user(buf, Pointer, Size); + if (len != 0) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): Failed to copy data from user.", + __FUNCTION__, __LINE__ + ); + + if (buf != gcvNULL) + { + kfree(buf); + } + + gcmkFOOTER_ARG("*status=%d", gcvSTATUS_GENERIC_IO); + return gcvSTATUS_GENERIC_IO; + } + + *KernelPointer = buf; + + gcmkFOOTER_ARG("*KernelPointer=0x%X", *KernelPointer); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_UnmapUserPointer +** +** Unmap a user process pointer from the kernel address space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Pointer +** Pointer in user process space that needs to be unmapped. +** +** gctSIZE_T Size +** Number of bytes that need to be unmapped. +** +** gctPOINTER KernelPointer +** Pointer in kernel address space that needs to be unmapped. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnmapUserPointer( + IN gckOS Os, + IN gctPOINTER Pointer, + IN gctSIZE_T Size, + IN gctPOINTER KernelPointer + ) +{ + gctUINT32 len; + + gcmkHEADER_ARG("Os=0x%X Pointer=0x%X Size=%lu KernelPointer=0x%X", + Os, Pointer, Size, KernelPointer); + + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + + len = copy_to_user(Pointer, KernelPointer, Size); + + kfree(KernelPointer); + + if (len != 0) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): Failed to copy data to user.", + __FUNCTION__, __LINE__ + ); + + gcmkFOOTER_ARG("status=%d", gcvSTATUS_GENERIC_IO); + return gcvSTATUS_GENERIC_IO; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_QueryNeedCopy +** +** Query whether the memory can be accessed or mapped directly or it has to be +** copied. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 ProcessID +** Process ID of the current process. +** +** OUTPUT: +** +** gctBOOL_PTR NeedCopy +** Pointer to a boolean receiving gcvTRUE if the memory needs a copy or +** gcvFALSE if the memory can be accessed or mapped dircetly. +*/ +gceSTATUS +gckOS_QueryNeedCopy( + IN gckOS Os, + IN gctUINT32 ProcessID, + OUT gctBOOL_PTR NeedCopy + ) +{ + gcmkHEADER_ARG("Os=0x%X ProcessID=%d", Os, ProcessID); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(NeedCopy != gcvNULL); + + /* We need to copy data. */ + *NeedCopy = gcvTRUE; + + /* Success. */ + gcmkFOOTER_ARG("*NeedCopy=%d", *NeedCopy); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_CopyFromUserData +** +** Copy data from user to kernel memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER KernelPointer +** Pointer to kernel memory. +** +** gctPOINTER Pointer +** Pointer to user memory. +** +** gctSIZE_T Size +** Number of bytes to copy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_CopyFromUserData( + IN gckOS Os, + IN gctPOINTER KernelPointer, + IN gctPOINTER Pointer, + IN gctSIZE_T Size + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X KernelPointer=0x%X Pointer=0x%X Size=%lu", + Os, KernelPointer, Pointer, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + + /* Copy data from user. */ + if (copy_from_user(KernelPointer, Pointer, Size) != 0) + { + /* Could not copy all the bytes. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_CopyToUserData +** +** Copy data from kernel to user memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER KernelPointer +** Pointer to kernel memory. +** +** gctPOINTER Pointer +** Pointer to user memory. +** +** gctSIZE_T Size +** Number of bytes to copy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_CopyToUserData( + IN gckOS Os, + IN gctPOINTER KernelPointer, + IN gctPOINTER Pointer, + IN gctSIZE_T Size + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X KernelPointer=0x%X Pointer=0x%X Size=%lu", + Os, KernelPointer, Pointer, Size); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(KernelPointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Pointer != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + + /* Copy data to user. */ + if (copy_to_user(Pointer, KernelPointer, Size) != 0) + { + /* Could not copy all the bytes. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_WriteMemory +** +** Write data to a memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER Address +** Address of the memory to write to. +** +** gctUINT32 Data +** Data for register. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_WriteMemory( + IN gckOS Os, + IN gctPOINTER Address, + IN gctUINT32 Data + ) +{ + gceSTATUS status; + gcmkHEADER_ARG("Os=0x%X Address=0x%X Data=%u", Os, Address, Data); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + /* Write memory. */ + if (access_ok(VERIFY_WRITE, Address, 4)) + { + /* User address. */ + if(put_user(Data, (gctUINT32*)Address)) + { + gcmkONERROR(gcvSTATUS_INVALID_ADDRESS); + } + } + else + { + /* Kernel address. */ + *(gctUINT32 *)Address = Data; + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_MapUserMemory +** +** Lock down a user buffer and return an DMA'able address to be used by the +** hardware to access it. +** +** INPUT: +** +** gctPOINTER Memory +** Pointer to memory to lock down. +** +** gctSIZE_T Size +** Size in bytes of the memory to lock down. +** +** OUTPUT: +** +** gctPOINTER * Info +** Pointer to variable receiving the information record required by +** gckOS_UnmapUserMemory. +** +** gctUINT32_PTR Address +** Pointer to a variable that will receive the address DMA'able by the +** hardware. +*/ +gceSTATUS +gckOS_MapUserMemory( + IN gckOS Os, + IN gceCORE Core, + IN gctPOINTER Memory, + IN gctUINT32 Physical, + IN gctSIZE_T Size, + OUT gctPOINTER * Info, + OUT gctUINT32_PTR Address + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%x Core=%d Memory=0x%x Size=%lu", Os, Core, Memory, Size); + +#if gcdSECURE_USER + gcmkONERROR(gckOS_AddMapping(Os, *Address, Memory, Size)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +#else +{ + gctSIZE_T pageCount, i, j; + gctUINT32_PTR pageTable; + gctUINT32 address = 0, physical = ~0U; + gctUINTPTR_T start, end, memory; + gctUINT32 offset; + gctINT result = 0; +#if gcdPROCESS_ADDRESS_SPACE + gckMMU mmu; +#endif + + gcsPageInfo_PTR info = gcvNULL; + struct page **pages = gcvNULL; + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL || Physical != ~0U); + gcmkVERIFY_ARGUMENT(Size > 0); + gcmkVERIFY_ARGUMENT(Info != gcvNULL); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + do + { + gctSIZE_T extraPage; + + memory = (gctUINTPTR_T) Memory; + + /* Get the number of required pages. */ + end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT; + start = memory >> PAGE_SHIFT; + pageCount = end - start; + + /* Allocate extra 64 bytes to avoid cache overflow */ + extraPage = (((memory + gcmALIGN(Size + 64, 64) + PAGE_SIZE - 1) >> PAGE_SHIFT) > end) ? 1 : 0; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): pageCount: %d.", + __FUNCTION__, __LINE__, + pageCount + ); + + /* Overflow. */ + if ((memory + Size) < memory) + { + gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); + return gcvSTATUS_INVALID_ARGUMENT; + } + + MEMORY_MAP_LOCK(Os); + + /* Allocate the Info struct. */ + info = (gcsPageInfo_PTR)kmalloc(sizeof(gcsPageInfo), GFP_KERNEL | gcdNOWARN); + + if (info == gcvNULL) + { + status = gcvSTATUS_OUT_OF_MEMORY; + break; + } + + info->extraPage = 0; + + /* Allocate the array of page addresses. */ + pages = (struct page **)kmalloc((pageCount + extraPage) * sizeof(struct page *), GFP_KERNEL | gcdNOWARN); + + if (pages == gcvNULL) + { + status = gcvSTATUS_OUT_OF_MEMORY; + break; + } + + if (Physical != ~0U) + { + for (i = 0; i < pageCount; i++) + { + pages[i] = pfn_to_page((Physical >> PAGE_SHIFT) + i); + + if (pfn_valid(page_to_pfn(pages[i]))) + { + get_page(pages[i]); + } + } + } + else + { + /* Get the user pages. */ + down_read(¤t->mm->mmap_sem); + + result = get_user_pages(current, + current->mm, + memory & PAGE_MASK, + pageCount, + 1, + 0, + pages, + gcvNULL + ); + + up_read(¤t->mm->mmap_sem); + + if (result <=0 || result < pageCount) + { + struct vm_area_struct *vma; + + /* Release the pages if any. */ + if (result > 0) + { + for (i = 0; i < result; i++) + { + if (pages[i] == gcvNULL) + { + break; + } + + page_cache_release(pages[i]); + pages[i] = gcvNULL; + } + + result = 0; + } + + vma = find_vma(current->mm, memory); + + if (vma && (vma->vm_flags & VM_PFNMAP)) + { + pte_t * pte; + spinlock_t * ptl; + gctUINTPTR_T logical = memory; + + for (i = 0; i < pageCount; i++) + { + pgd_t * pgd = pgd_offset(current->mm, logical); + pud_t * pud = pud_offset(pgd, logical); + + if (pud) + { + pmd_t * pmd = pmd_offset(pud, logical); + pte = pte_offset_map_lock(current->mm, pmd, logical, &ptl); + if (!pte) + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + } + else + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + pages[i] = pte_page(*pte); + pte_unmap_unlock(pte, ptl); + + /* Advance to next. */ + logical += PAGE_SIZE; + } + } + else + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Check if this memory is contiguous for old mmu. */ + if (Os->device->kernels[Core]->hardware->mmuVersion == 0) + { + for (i = 1; i < pageCount; i++) + { + if (pages[i] != nth_page(pages[0], i)) + { + /* Non-contiguous. */ + break; + } + } + + if (i == pageCount) + { + /* Contiguous memory. */ + physical = page_to_phys(pages[0]) | (memory & ~PAGE_MASK); + + if (!((physical - Os->device->baseAddress) & 0x80000000)) + { + kfree(pages); + pages = gcvNULL; + + info->pages = gcvNULL; + info->pageTable = gcvNULL; + + MEMORY_MAP_UNLOCK(Os); + + *Address = physical - Os->device->baseAddress; + *Info = info; + + gcmkVERIFY_OK( + gckOS_CPUPhysicalToGPUPhysical(Os, *Address, Address)); + + gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x", + *Info, *Address); + + return gcvSTATUS_OK; + } + } + } + + /* Reference pages. */ + for (i = 0; i < pageCount; i++) + { + if (pfn_valid(page_to_pfn(pages[i]))) + { + get_page(pages[i]); + } + } + } + } + + for (i = 0; i < pageCount; i++) + { +#ifdef CONFIG_ARM + gctUINT32 data; + get_user(data, (gctUINT32*)((memory & PAGE_MASK) + i * PAGE_SIZE)); +#endif + + /* Flush(clean) the data cache. */ + gcmkONERROR(gckOS_CacheFlush(Os, _GetProcessID(), gcvNULL, + page_to_phys(pages[i]), + (gctPOINTER)(memory & PAGE_MASK) + i*PAGE_SIZE, + PAGE_SIZE)); + } + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckKERNEL_GetProcessMMU(Os->device->kernels[Core], &mmu)); +#endif + + if (extraPage) + { + pages[pageCount++] = Os->paddingPage; + info->extraPage = 1; + } + +#if gcdSECURITY + { + gctPHYS_ADDR physicalArrayPhysical; + gctPOINTER physicalArrayLogical; + gctUINT32_PTR logical; + gctSIZE_T bytes = pageCount * gcmSIZEOF(gctUINT32); + pageTable = gcvNULL; + + gcmkONERROR(gckOS_AllocateNonPagedMemory( + Os, + gcvFALSE, + &bytes, + &physicalArrayPhysical, + &physicalArrayLogical + )); + + logical = physicalArrayLogical; + + /* Fill the page table. */ + for (i = 0; i < pageCount; i++) + { + gctUINT32 phys; + phys = page_to_phys(pages[i]); + + logical[i] = phys; + } + j = 0; + + + gcmkONERROR(gckKERNEL_SecurityMapMemory( + Os->device->kernels[Core], + physicalArrayLogical, + pageCount, + &address + )); + + gcmkONERROR(gckOS_FreeNonPagedMemory( + Os, + 1, + physicalArrayPhysical, + physicalArrayLogical + )); + } + +#else +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + /* Allocate pages inside the page table. */ + gcmkERR_BREAK(gckVGMMU_AllocatePages(Os->device->kernels[Core]->vg->mmu, + pageCount * (PAGE_SIZE/4096), + (gctPOINTER *) &pageTable, + &address)); + } + else +#endif + { +#if gcdPROCESS_ADDRESS_SPACE + /* Allocate pages inside the page table. */ + gcmkERR_BREAK(gckMMU_AllocatePages(mmu, + pageCount * (PAGE_SIZE/4096), + (gctPOINTER *) &pageTable, + &address)); +#else + /* Allocate pages inside the page table. */ + gcmkERR_BREAK(gckMMU_AllocatePages(Os->device->kernels[Core]->mmu, + pageCount * (PAGE_SIZE/4096), + (gctPOINTER *) &pageTable, + &address)); +#endif + } + + /* Fill the page table. */ + for (i = 0; i < pageCount; i++) + { + gctUINT32 phys; + gctUINT32_PTR tab = pageTable + i * (PAGE_SIZE/4096); + +#if gcdPROCESS_ADDRESS_SPACE + gckMMU_GetPageEntry(mmu, address + i * 4096, &tab); +#endif + phys = page_to_phys(pages[i]); + +#ifdef CONFIG_IOMMU_SUPPORT + if (Os->iommu) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Setup mapping in IOMMU %x => %x", + __FUNCTION__, __LINE__, + Address + (i * PAGE_SIZE), phys + ); + + gcmkONERROR(gckIOMMU_Map( + Os->iommu, address + i * PAGE_SIZE, phys, PAGE_SIZE)); + } + else +#endif + { + +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + gcmkVERIFY_OK( + gckOS_CPUPhysicalToGPUPhysical(Os, phys, &phys)); + + /* Get the physical address from page struct. */ + gcmkONERROR( + gckVGMMU_SetPage(Os->device->kernels[Core]->vg->mmu, + phys, + tab)); + } + else +#endif + { + /* Get the physical address from page struct. */ + gcmkONERROR( + gckMMU_SetPage(Os->device->kernels[Core]->mmu, + phys, + tab)); + } + + for (j = 1; j < (PAGE_SIZE/4096); j++) + { + pageTable[i * (PAGE_SIZE/4096) + j] = pageTable[i * (PAGE_SIZE/4096)] + 4096 * j; + } + } + +#if !gcdPROCESS_ADDRESS_SPACE + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): pageTable[%d]: 0x%X 0x%X.", + __FUNCTION__, __LINE__, + i, phys, pageTable[i]); +#endif + } + +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + gcmkONERROR(gckVGMMU_Flush(Os->device->kernels[Core]->vg->mmu)); + } + else +#endif + { +#if gcdPROCESS_ADDRESS_SPACE + info->mmu = mmu; + gcmkONERROR(gckMMU_Flush(mmu)); +#else + gcmkONERROR(gckMMU_Flush(Os->device->kernels[Core]->mmu, gcvSURF_TYPE_UNKNOWN)); +#endif + } +#endif + info->address = address; + + /* Save pointer to page table. */ + info->pageTable = pageTable; + info->pages = pages; + + *Info = (gctPOINTER) info; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): info->pages: 0x%X, info->pageTable: 0x%X, info: 0x%X.", + __FUNCTION__, __LINE__, + info->pages, + info->pageTable, + info + ); + + offset = (Physical != ~0U) + ? (Physical & ~PAGE_MASK) + : (memory & ~PAGE_MASK); + + /* Return address. */ + *Address = address + offset; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): Address: 0x%X.", + __FUNCTION__, __LINE__, + *Address + ); + + /* Success. */ + status = gcvSTATUS_OK; + } + while (gcvFALSE); + +OnError: + + if (gcmIS_ERROR(status)) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): error occured: %d.", + __FUNCTION__, __LINE__, + status + ); + + /* Release page array. */ + if (result > 0 && pages != gcvNULL) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): error: page table is freed.", + __FUNCTION__, __LINE__ + ); + + for (i = 0; i < result; i++) + { + if (pages[i] == gcvNULL) + { + break; + } + page_cache_release(pages[i]); + } + } + + if (info!= gcvNULL && pages != gcvNULL) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): error: pages is freed.", + __FUNCTION__, __LINE__ + ); + + /* Free the page table. */ + kfree(pages); + info->pages = gcvNULL; + } + + /* Release page info struct. */ + if (info != gcvNULL) + { + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): error: info is freed.", + __FUNCTION__, __LINE__ + ); + + /* Free the page info struct. */ + kfree(info); + *Info = gcvNULL; + } + } + + MEMORY_MAP_UNLOCK(Os); + + /* Return the status. */ + if (gcmIS_SUCCESS(status)) + { + gcmkFOOTER_ARG("*Info=0x%X *Address=0x%08x", *Info, *Address); + } + else + { + gcmkFOOTER(); + } + + return status; +} +#endif +} + +/******************************************************************************* +** +** gckOS_UnmapUserMemory +** +** Unlock a user buffer and that was previously locked down by +** gckOS_MapUserMemory. +** +** INPUT: +** +** gctPOINTER Memory +** Pointer to memory to unlock. +** +** gctSIZE_T Size +** Size in bytes of the memory to unlock. +** +** gctPOINTER Info +** Information record returned by gckOS_MapUserMemory. +** +** gctUINT32_PTR Address +** The address returned by gckOS_MapUserMemory. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UnmapUserMemory( + IN gckOS Os, + IN gceCORE Core, + IN gctPOINTER Memory, + IN gctSIZE_T Size, + IN gctPOINTER Info, + IN gctUINT32 Address + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Core=%d Memory=0x%X Size=%lu Info=0x%X Address0x%08x", + Os, Core, Memory, Size, Info, Address); + +#if gcdSECURE_USER + gcmkONERROR(gckOS_RemoveMapping(Os, Memory, Size)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +#else +{ + gctUINTPTR_T memory, start, end; + gcsPageInfo_PTR info; + gctSIZE_T pageCount, i; + struct page **pages; + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + gcmkVERIFY_ARGUMENT(Size > 0); + gcmkVERIFY_ARGUMENT(Info != gcvNULL); + + do + { + info = (gcsPageInfo_PTR) Info; + + pages = info->pages; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): info=0x%X, pages=0x%X.", + __FUNCTION__, __LINE__, + info, pages + ); + + /* Invalid page array. */ + if (pages == gcvNULL && info->pageTable == gcvNULL) + { + kfree(info); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + memory = (gctUINTPTR_T)Memory; + end = (memory + Size + PAGE_SIZE - 1) >> PAGE_SHIFT; + start = memory >> PAGE_SHIFT; + pageCount = end - start; + + /* Overflow. */ + if ((memory + Size) < memory) + { + gcmkFOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); + return gcvSTATUS_INVALID_ARGUMENT; + } + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): memory: 0x%X, pageCount: %d, pageTable: 0x%X.", + __FUNCTION__, __LINE__, + memory, pageCount, info->pageTable + ); + + MEMORY_MAP_LOCK(Os); + +#if !gcdSECURITY + gcmkASSERT(info->pageTable != gcvNULL); +#endif + + if (info->extraPage) + { + pageCount += 1; + } + +#if gcdSECURITY + if (info->address > 0x80000000) + { + gckKERNEL_SecurityUnmapMemory( + Os->device->kernels[Core], + info->address, + pageCount + ); + } + else + { + gcmkPRINT("Wrong address %s(%d) %x", __FUNCTION__, __LINE__, info->address); + } +#else +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + /* Free the pages from the MMU. */ + gcmkERR_BREAK(gckVGMMU_FreePages(Os->device->kernels[Core]->vg->mmu, + info->pageTable, + pageCount * (PAGE_SIZE/4096) + )); + } + else +#endif + { + /* Free the pages from the MMU. */ +#if gcdPROCESS_ADDRESS_SPACE + gcmkERR_BREAK(gckMMU_FreePagesEx(info->mmu, + info->address, + pageCount * (PAGE_SIZE/4096) + )); + +#else + gcmkERR_BREAK(gckMMU_FreePages(Os->device->kernels[Core]->mmu, + info->pageTable, + pageCount * (PAGE_SIZE/4096) + )); +#endif + + gcmkERR_BREAK(gckOS_UnmapPages( + Os, + pageCount * (PAGE_SIZE/4096), + info->address + )); + } +#endif + + if (info->extraPage) + { + pageCount -= 1; + info->extraPage = 0; + } + + /* Release the page cache. */ + if (pages) + { + for (i = 0; i < pageCount; i++) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): pages[%d]: 0x%X.", + __FUNCTION__, __LINE__, + i, pages[i] + ); + + if (!PageReserved(pages[i])) + { + SetPageDirty(pages[i]); + } + + if (pfn_valid(page_to_pfn(pages[i]))) + { + page_cache_release(pages[i]); + } + } + } + + /* Success. */ + status = gcvSTATUS_OK; + } + while (gcvFALSE); + + if (info != gcvNULL) + { + /* Free the page array. */ + if (info->pages != gcvNULL) + { + kfree(info->pages); + } + + kfree(info); + } + + MEMORY_MAP_UNLOCK(Os); + + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif +} + +/******************************************************************************* +** +** gckOS_GetBaseAddress +** +** Get the base address for the physical memory. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** OUTPUT: +** +** gctUINT32_PTR BaseAddress +** Pointer to a variable that will receive the base address. +*/ +gceSTATUS +gckOS_GetBaseAddress( + IN gckOS Os, + OUT gctUINT32_PTR BaseAddress + ) +{ + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(BaseAddress != gcvNULL); + + /* Return base address. */ + *BaseAddress = Os->device->baseAddress; + + /* Success. */ + gcmkFOOTER_ARG("*BaseAddress=0x%08x", *BaseAddress); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_SuspendInterrupt( + IN gckOS Os + ) +{ + return gckOS_SuspendInterruptEx(Os, gcvCORE_MAJOR); +} + +#if gcdMULTI_GPU +gceSTATUS +gckOS_SuspendInterruptEx( + IN gckOS Os, + IN gceCORE Core + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + if (Core == gcvCORE_MAJOR) + { + disable_irq(Os->device->irqLine3D[gcvCORE_3D_0_ID]); + disable_irq(Os->device->irqLine3D[gcvCORE_3D_1_ID]); + } + else + { + disable_irq(Os->device->irqLines[Core]); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#else +gceSTATUS +gckOS_SuspendInterruptEx( + IN gckOS Os, + IN gceCORE Core + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + disable_irq(Os->device->irqLines[Core]); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif + +gceSTATUS +gckOS_ResumeInterrupt( + IN gckOS Os + ) +{ + return gckOS_ResumeInterruptEx(Os, gcvCORE_MAJOR); +} + +#if gcdMULTI_GPU +gceSTATUS +gckOS_ResumeInterruptEx( + IN gckOS Os, + IN gceCORE Core + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + if (Core == gcvCORE_MAJOR) + { + enable_irq(Os->device->irqLine3D[gcvCORE_3D_0_ID]); + enable_irq(Os->device->irqLine3D[gcvCORE_3D_1_ID]); + } + else + { + enable_irq(Os->device->irqLines[Core]); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#else +gceSTATUS +gckOS_ResumeInterruptEx( + IN gckOS Os, + IN gceCORE Core + ) +{ + gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + enable_irq(Os->device->irqLines[Core]); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif + +gceSTATUS +gckOS_MemCopy( + IN gctPOINTER Destination, + IN gctCONST_POINTER Source, + IN gctSIZE_T Bytes + ) +{ + gcmkHEADER_ARG("Destination=0x%X Source=0x%X Bytes=%lu", + Destination, Source, Bytes); + + gcmkVERIFY_ARGUMENT(Destination != gcvNULL); + gcmkVERIFY_ARGUMENT(Source != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + memcpy(Destination, Source, Bytes); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_ZeroMemory( + IN gctPOINTER Memory, + IN gctSIZE_T Bytes + ) +{ + gcmkHEADER_ARG("Memory=0x%X Bytes=%lu", Memory, Bytes); + + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + memset(Memory, 0, Bytes); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +********************************* Cache Control ******************************** +*******************************************************************************/ + +/******************************************************************************* +** gckOS_CacheClean +** +** Clean the cache for the specified addresses. The GPU is going to need the +** data. If the system is allocating memory as non-cachable, this function can +** be ignored. +** +** ARGUMENTS: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctUINT32 ProcessID +** Process ID Logical belongs. +** +** gctPHYS_ADDR Handle +** Physical address handle. If gcvNULL it is video memory. +** +** gctPOINTER Physical +** Physical address to flush. +** +** gctPOINTER Logical +** Logical address to flush. +** +** gctSIZE_T Bytes +** Size of the address range in bytes to flush. +*/ +gceSTATUS +gckOS_CacheClean( + IN gckOS Os, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Handle, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gcsPLATFORM * platform; + + gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu", + Os, ProcessID, Handle, Logical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + platform = Os->device->platform; + + if (platform && platform->ops->cache) + { + platform->ops->cache( + platform, + ProcessID, + Handle, + Physical, + Logical, + Bytes, + gcvCACHE_CLEAN + ); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + +#if !gcdCACHE_FUNCTION_UNIMPLEMENTED +#ifdef CONFIG_ARM + + /* Inner cache. */ + dmac_map_area(Logical, Bytes, DMA_TO_DEVICE); + +#if defined(CONFIG_OUTER_CACHE) + /* Outer cache. */ +#if gcdENABLE_OUTER_CACHE_PATCH + _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_CLEAN); +#else + outer_clean_range((unsigned long) Handle, (unsigned long) Handle + Bytes); +#endif +#endif + +#elif defined(CONFIG_MIPS) + + dma_cache_wback((unsigned long) Logical, Bytes); + +#elif defined(CONFIG_PPC) + + /* TODO */ + +#else + dma_sync_single_for_device( + gcvNULL, + (dma_addr_t)Physical, + Bytes, + DMA_TO_DEVICE); +#endif +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** gckOS_CacheInvalidate +** +** Invalidate the cache for the specified addresses. The GPU is going to need +** data. If the system is allocating memory as non-cachable, this function can +** be ignored. +** +** ARGUMENTS: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctUINT32 ProcessID +** Process ID Logical belongs. +** +** gctPHYS_ADDR Handle +** Physical address handle. If gcvNULL it is video memory. +** +** gctPOINTER Logical +** Logical address to flush. +** +** gctSIZE_T Bytes +** Size of the address range in bytes to flush. +*/ +gceSTATUS +gckOS_CacheInvalidate( + IN gckOS Os, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Handle, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gcsPLATFORM * platform; + + gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu", + Os, ProcessID, Handle, Logical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + platform = Os->device->platform; + + if (platform && platform->ops->cache) + { + platform->ops->cache( + platform, + ProcessID, + Handle, + Physical, + Logical, + Bytes, + gcvCACHE_INVALIDATE + ); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + +#if !gcdCACHE_FUNCTION_UNIMPLEMENTED +#ifdef CONFIG_ARM + + /* Inner cache. */ + dmac_map_area(Logical, Bytes, DMA_FROM_DEVICE); + +#if defined(CONFIG_OUTER_CACHE) + /* Outer cache. */ +#if gcdENABLE_OUTER_CACHE_PATCH + _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_INVALIDATE); +#else + outer_inv_range((unsigned long) Handle, (unsigned long) Handle + Bytes); +#endif +#endif + +#elif defined(CONFIG_MIPS) + dma_cache_inv((unsigned long) Logical, Bytes); +#elif defined(CONFIG_PPC) + /* TODO */ +#else + dma_sync_single_for_device( + gcvNULL, + (dma_addr_t)Physical, + Bytes, + DMA_FROM_DEVICE); +#endif +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** gckOS_CacheFlush +** +** Clean the cache for the specified addresses and invalidate the lines as +** well. The GPU is going to need and modify the data. If the system is +** allocating memory as non-cachable, this function can be ignored. +** +** ARGUMENTS: +** +** gckOS Os +** Pointer to gckOS object. +** +** gctUINT32 ProcessID +** Process ID Logical belongs. +** +** gctPHYS_ADDR Handle +** Physical address handle. If gcvNULL it is video memory. +** +** gctPOINTER Logical +** Logical address to flush. +** +** gctSIZE_T Bytes +** Size of the address range in bytes to flush. +*/ +gceSTATUS +gckOS_CacheFlush( + IN gckOS Os, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Handle, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) +{ + gcsPLATFORM * platform; + + gcmkHEADER_ARG("Os=0x%X ProcessID=%d Handle=0x%X Logical=0x%X Bytes=%lu", + Os, ProcessID, Handle, Logical, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Logical != gcvNULL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + + platform = Os->device->platform; + + if (platform && platform->ops->cache) + { + platform->ops->cache( + platform, + ProcessID, + Handle, + Physical, + Logical, + Bytes, + gcvCACHE_FLUSH + ); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + +#if !gcdCACHE_FUNCTION_UNIMPLEMENTED +#ifdef CONFIG_ARM + /* Inner cache. */ + dmac_flush_range(Logical, Logical + Bytes); + +#if defined(CONFIG_OUTER_CACHE) + /* Outer cache. */ +#if gcdENABLE_OUTER_CACHE_PATCH + _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_FLUSH); +#else + outer_flush_range((unsigned long) Handle, (unsigned long) Handle + Bytes); +#endif +#endif + +#elif defined(CONFIG_MIPS) + dma_cache_wback_inv((unsigned long) Logical, Bytes); +#elif defined(CONFIG_PPC) + /* TODO */ +#else + dma_sync_single_for_device( + gcvNULL, + (dma_addr_t)Physical, + Bytes, + DMA_BIDIRECTIONAL); +#endif +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +********************************* Broadcasting ********************************* +*******************************************************************************/ + +/******************************************************************************* +** +** gckOS_Broadcast +** +** System hook for broadcast events from the kernel driver. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** gceBROADCAST Reason +** Reason for the broadcast. Can be one of the following values: +** +** gcvBROADCAST_GPU_IDLE +** Broadcasted when the kernel driver thinks the GPU might be +** idle. This can be used to handle power management. +** +** gcvBROADCAST_GPU_COMMIT +** Broadcasted when any client process commits a command +** buffer. This can be used to handle power management. +** +** gcvBROADCAST_GPU_STUCK +** Broadcasted when the kernel driver hits the timeout waiting +** for the GPU. +** +** gcvBROADCAST_FIRST_PROCESS +** First process is trying to connect to the kernel. +** +** gcvBROADCAST_LAST_PROCESS +** Last process has detached from the kernel. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_Broadcast( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gceBROADCAST Reason + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Hardware=0x%X Reason=%d", Os, Hardware, Reason); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + + switch (Reason) + { + case gcvBROADCAST_FIRST_PROCESS: + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "First process has attached"); + break; + + case gcvBROADCAST_LAST_PROCESS: + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "Last process has detached"); + + /* Put GPU OFF. */ + gcmkONERROR( + gckHARDWARE_SetPowerManagementState(Hardware, + gcvPOWER_OFF_BROADCAST)); + break; + + case gcvBROADCAST_GPU_IDLE: + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "GPU idle."); + + /* Put GPU IDLE. */ + gcmkONERROR( + gckHARDWARE_SetPowerManagementState(Hardware, +#if gcdPOWER_SUSPEND_WHEN_IDLE + gcvPOWER_SUSPEND_BROADCAST)); +#else + gcvPOWER_IDLE_BROADCAST)); +#endif + + /* Add idle process DB. */ + gcmkONERROR(gckKERNEL_AddProcessDB(Hardware->kernel, + 1, + gcvDB_IDLE, + gcvNULL, gcvNULL, 0)); + break; + + case gcvBROADCAST_GPU_COMMIT: + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_OS, "COMMIT has arrived."); + + /* Add busy process DB. */ + gcmkONERROR(gckKERNEL_AddProcessDB(Hardware->kernel, + 0, + gcvDB_IDLE, + gcvNULL, gcvNULL, 0)); + + /* Put GPU ON. */ + gcmkONERROR( + gckHARDWARE_SetPowerManagementState(Hardware, gcvPOWER_ON_AUTO)); + break; + + case gcvBROADCAST_GPU_STUCK: + gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_GPU_STUCK\n"); + gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel)); + break; + + case gcvBROADCAST_AXI_BUS_ERROR: + gcmkTRACE_N(gcvLEVEL_ERROR, 0, "gcvBROADCAST_AXI_BUS_ERROR\n"); + gcmkONERROR(gckHARDWARE_DumpGPUState(Hardware)); + gcmkONERROR(gckKERNEL_Recovery(Hardware->kernel)); + break; + + case gcvBROADCAST_OUT_OF_MEMORY: + gcmkTRACE_N(gcvLEVEL_INFO, 0, "gcvBROADCAST_OUT_OF_MEMORY\n"); + + status = _ShrinkMemory(Os); + + if (status == gcvSTATUS_NOT_SUPPORTED) + { + goto OnError; + } + + gcmkONERROR(status); + + break; + + default: + /* Skip unimplemented broadcast. */ + break; + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_BroadcastHurry +** +** The GPU is running too slow. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** gctUINT Urgency +** The higher the number, the higher the urgency to speed up the GPU. +** The maximum value is defined by the gcdDYNAMIC_EVENT_THRESHOLD. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_BroadcastHurry( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gctUINT Urgency + ) +{ + gcmkHEADER_ARG("Os=0x%x Hardware=0x%x Urgency=%u", Os, Hardware, Urgency); + + /* Do whatever you need to do to speed up the GPU now. */ + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_BroadcastCalibrateSpeed +** +** Calibrate the speed of the GPU. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gckHARDWARE Hardware +** Pointer to the gckHARDWARE object. +** +** gctUINT Idle, Time +** Idle/Time will give the percentage the GPU is idle, so you can use +** this to calibrate the working point of the GPU. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_BroadcastCalibrateSpeed( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gctUINT Idle, + IN gctUINT Time + ) +{ + gcmkHEADER_ARG("Os=0x%x Hardware=0x%x Idle=%u Time=%u", + Os, Hardware, Idle, Time); + + /* Do whatever you need to do to callibrate the GPU speed. */ + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +********************************** Semaphores ********************************** +*******************************************************************************/ + +/******************************************************************************* +** +** gckOS_CreateSemaphore +** +** Create a semaphore. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** OUTPUT: +** +** gctPOINTER * Semaphore +** Pointer to the variable that will receive the created semaphore. +*/ +gceSTATUS +gckOS_CreateSemaphore( + IN gckOS Os, + OUT gctPOINTER * Semaphore + ) +{ + gceSTATUS status; + struct semaphore *sem = gcvNULL; + + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + /* Allocate the semaphore structure. */ + sem = (struct semaphore *)kmalloc(gcmSIZEOF(struct semaphore), GFP_KERNEL | gcdNOWARN); + if (sem == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Initialize the semaphore. */ + sema_init(sem, 1); + + /* Return to caller. */ + *Semaphore = (gctPOINTER) sem; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_AcquireSemaphore +** +** Acquire a semaphore. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Semaphore +** Pointer to the semaphore thet needs to be acquired. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AcquireSemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%08X Semaphore=0x%08X", Os, Semaphore); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + /* Acquire the semaphore. */ + if (down_interruptible((struct semaphore *) Semaphore)) + { + gcmkONERROR(gcvSTATUS_INTERRUPTED); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_TryAcquireSemaphore +** +** Try to acquire a semaphore. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Semaphore +** Pointer to the semaphore thet needs to be acquired. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_TryAcquireSemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%x", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + /* Acquire the semaphore. */ + if (down_trylock((struct semaphore *) Semaphore)) + { + /* Timeout. */ + status = gcvSTATUS_TIMEOUT; + gcmkFOOTER(); + return status; + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_ReleaseSemaphore +** +** Release a previously acquired semaphore. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Semaphore +** Pointer to the semaphore thet needs to be released. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_ReleaseSemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ) +{ + gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + /* Release the semaphore. */ + up((struct semaphore *) Semaphore); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_DestroySemaphore +** +** Destroy a semaphore. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Semaphore +** Pointer to the semaphore thet needs to be destroyed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_DestroySemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ) +{ + gcmkHEADER_ARG("Os=0x%X Semaphore=0x%X", Os, Semaphore); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + /* Free the sempahore structure. */ + kfree(Semaphore); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_GetProcessID +** +** Get current process ID. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** gctUINT32_PTR ProcessID +** Pointer to the variable that receives the process ID. +*/ +gceSTATUS +gckOS_GetProcessID( + OUT gctUINT32_PTR ProcessID + ) +{ + /* Get process ID. */ + if (ProcessID != gcvNULL) + { + *ProcessID = _GetProcessID(); + } + + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_GetThreadID +** +** Get current thread ID. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** gctUINT32_PTR ThreadID +** Pointer to the variable that receives the thread ID. +*/ +gceSTATUS +gckOS_GetThreadID( + OUT gctUINT32_PTR ThreadID + ) +{ + /* Get thread ID. */ + if (ThreadID != gcvNULL) + { + *ThreadID = _GetThreadID(); + } + + /* Success. */ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_SetGPUPower +** +** Set the power of the GPU on or off. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gceCORE Core +** GPU whose power is set. +** +** gctBOOL Clock +** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock. +** +** gctBOOL Power +** gcvTRUE to turn on the power, or gcvFALSE to turn off the power. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_SetGPUPower( + IN gckOS Os, + IN gceCORE Core, + IN gctBOOL Clock, + IN gctBOOL Power + ) +{ + gcsPLATFORM * platform; + + gctBOOL powerChange = gcvFALSE; + gctBOOL clockChange = gcvFALSE; + + gcmkHEADER_ARG("Os=0x%X Core=%d Clock=%d Power=%d", Os, Core, Clock, Power); + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + platform = Os->device->platform; + + powerChange = (Power != Os->powerStates[Core]); + + clockChange = (Clock != Os->clockStates[Core]); + + if (powerChange && (Power == gcvTRUE)) + { + if (platform && platform->ops->setPower) + { + gcmkVERIFY_OK(platform->ops->setPower(platform, Core, Power)); + } + + Os->powerStates[Core] = Power; + } + + if (clockChange) + { + mutex_lock(&Os->registerAccessLocks[Core]); + + if (platform && platform->ops->setClock) + { + gcmkVERIFY_OK(platform->ops->setClock(platform, Core, Clock)); + } + + Os->clockStates[Core] = Clock; + + mutex_unlock(&Os->registerAccessLocks[Core]); + } + + if (powerChange && (Power == gcvFALSE)) + { + if (platform && platform->ops->setPower) + { + gcmkVERIFY_OK(platform->ops->setPower(platform, Core, Power)); + } + + Os->powerStates[Core] = Power; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_ResetGPU +** +** Reset the GPU. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gckCORE Core +** GPU whose power is set. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_ResetGPU( + IN gckOS Os, + IN gceCORE Core + ) +{ + gceSTATUS status = gcvSTATUS_NOT_SUPPORTED; + gcsPLATFORM * platform; + + gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + platform = Os->device->platform; + + if (platform && platform->ops->reset) + { + status = platform->ops->reset(platform, Core); + } + + gcmkFOOTER_NO(); + return status; +} + +/******************************************************************************* +** +** gckOS_PrepareGPUFrequency +** +** Prepare to set GPU frequency and voltage. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gckCORE Core +** GPU whose frequency and voltage will be set. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_PrepareGPUFrequency( + IN gckOS Os, + IN gceCORE Core + ) +{ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_FinishGPUFrequency +** +** Finish GPU frequency setting. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gckCORE Core +** GPU whose frequency and voltage is set. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_FinishGPUFrequency( + IN gckOS Os, + IN gceCORE Core + ) +{ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_QueryGPUFrequency +** +** Query the current frequency of the GPU. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gckCORE Core +** GPU whose power is set. +** +** gctUINT32 * Frequency +** Pointer to a gctUINT32 to obtain current frequency, in MHz. +** +** gctUINT8 * Scale +** Pointer to a gctUINT8 to obtain current scale(1 - 64). +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_QueryGPUFrequency( + IN gckOS Os, + IN gceCORE Core, + OUT gctUINT32 * Frequency, + OUT gctUINT8 * Scale + ) +{ + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_SetGPUFrequency +** +** Set frequency and voltage of the GPU. +** +** 1. DVFS manager gives the target scale of full frequency, BSP must find +** a real frequency according to this scale and board's configure. +** +** 2. BSP should find a suitable voltage for this frequency. +** +** 3. BSP must make sure setting take effect before this function returns. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gckCORE Core +** GPU whose power is set. +** +** gctUINT8 Scale +** Target scale of full frequency, range is [1, 64]. 1 means 1/64 of +** full frequency and 64 means 64/64 of full frequency. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_SetGPUFrequency( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT8 Scale + ) +{ + return gcvSTATUS_OK; +} + +/*----------------------------------------------------------------------------*/ +/*----- Profile --------------------------------------------------------------*/ + +gceSTATUS +gckOS_GetProfileTick( + OUT gctUINT64_PTR Tick + ) +{ + struct timespec time; + + ktime_get_ts(&time); + + *Tick = time.tv_nsec + time.tv_sec * 1000000000ULL; + + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_QueryProfileTickRate( + OUT gctUINT64_PTR TickRate + ) +{ + struct timespec res; + + hrtimer_get_res(CLOCK_MONOTONIC, &res); + + *TickRate = res.tv_nsec + res.tv_sec * 1000000000ULL; + + return gcvSTATUS_OK; +} + +gctUINT32 +gckOS_ProfileToMS( + IN gctUINT64 Ticks + ) +{ + return div_u64(Ticks, 1000000); +} + +/******************************************************************************\ +******************************* Signal Management ****************************** +\******************************************************************************/ + +#undef _GC_OBJ_ZONE +#define _GC_OBJ_ZONE gcvZONE_SIGNAL + +/******************************************************************************* +** +** gckOS_CreateSignal +** +** Create a new signal. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctBOOL ManualReset +** If set to gcvTRUE, gckOS_Signal with gcvFALSE must be called in +** order to set the signal to nonsignaled state. +** If set to gcvFALSE, the signal will automatically be set to +** nonsignaled state by gckOS_WaitSignal function. +** +** OUTPUT: +** +** gctSIGNAL * Signal +** Pointer to a variable receiving the created gctSIGNAL. +*/ +gceSTATUS +gckOS_CreateSignal( + IN gckOS Os, + IN gctBOOL ManualReset, + OUT gctSIGNAL * Signal + ) +{ + gceSTATUS status; + gcsSIGNAL_PTR signal; + + gcmkHEADER_ARG("Os=0x%X ManualReset=%d", Os, ManualReset); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + + /* Create an event structure. */ + signal = (gcsSIGNAL_PTR) kmalloc(sizeof(gcsSIGNAL), GFP_KERNEL | gcdNOWARN); + + if (signal == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Save the process ID. */ + signal->process = (gctHANDLE)(gctUINTPTR_T) _GetProcessID(); + signal->manualReset = ManualReset; + signal->hardware = gcvNULL; + init_completion(&signal->obj); + atomic_set(&signal->ref, 1); + + gcmkONERROR(_AllocateIntegerId(&Os->signalDB, signal, &signal->id)); + + *Signal = (gctSIGNAL)(gctUINTPTR_T)signal->id; + + gcmkFOOTER_ARG("*Signal=0x%X", *Signal); + return gcvSTATUS_OK; + +OnError: + if (signal != gcvNULL) + { + kfree(signal); + } + + gcmkFOOTER_NO(); + return status; +} + +gceSTATUS +gckOS_SignalQueryHardware( + IN gckOS Os, + IN gctSIGNAL Signal, + OUT gckHARDWARE * Hardware + ) +{ + gceSTATUS status; + gcsSIGNAL_PTR signal; + + gcmkHEADER_ARG("Os=0x%X Signal=0x%X Hardware=0x%X", Os, Signal, Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + gcmkVERIFY_ARGUMENT(Hardware != gcvNULL); + + gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); + + *Hardware = signal->hardware; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_SignalSetHardware( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gckHARDWARE Hardware + ) +{ + gceSTATUS status; + gcsSIGNAL_PTR signal; + + gcmkHEADER_ARG("Os=0x%X Signal=0x%X Hardware=0x%X", Os, Signal, Hardware); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + + gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); + + signal->hardware = Hardware; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_DestroySignal +** +** Destroy a signal. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIGNAL Signal +** Pointer to the gctSIGNAL. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_DestroySignal( + IN gckOS Os, + IN gctSIGNAL Signal + ) +{ + gceSTATUS status; + gcsSIGNAL_PTR signal; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Os=0x%X Signal=0x%X", Os, Signal); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + + gcmkONERROR(gckOS_AcquireMutex(Os, Os->signalMutex, gcvINFINITE)); + acquired = gcvTRUE; + + gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); + + gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); + + if (atomic_dec_and_test(&signal->ref)) + { + gcmkVERIFY_OK(_DestroyIntegerId(&Os->signalDB, signal->id)); + + /* Free the sgianl. */ + kfree(signal); + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); + acquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_Signal +** +** Set a state of the specified signal. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIGNAL Signal +** Pointer to the gctSIGNAL. +** +** gctBOOL State +** If gcvTRUE, the signal will be set to signaled state. +** If gcvFALSE, the signal will be set to nonsignaled state. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_Signal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctBOOL State + ) +{ + gceSTATUS status; + gcsSIGNAL_PTR signal; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Os=0x%X Signal=0x%X State=%d", Os, Signal, State); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + + gcmkONERROR(gckOS_AcquireMutex(Os, Os->signalMutex, gcvINFINITE)); + acquired = gcvTRUE; + + gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); + + gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); + + if (State) + { + /* unbind the signal from hardware. */ + signal->hardware = gcvNULL; + + /* Set the event to a signaled state. */ + complete(&signal->obj); + } + else + { + /* Set the event to an unsignaled state. */ + reinit_completion(&signal->obj); + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); + acquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->signalMutex)); + } + + gcmkFOOTER(); + return status; +} + +#if gcdENABLE_VG +gceSTATUS +gckOS_SetSignalVG( + IN gckOS Os, + IN gctHANDLE Process, + IN gctSIGNAL Signal + ) +{ + gceSTATUS status; + gctINT result; + struct task_struct * userTask; + struct siginfo info; + + userTask = FIND_TASK_BY_PID((pid_t)(gctUINTPTR_T) Process); + + if (userTask != gcvNULL) + { + info.si_signo = 48; + info.si_code = __SI_CODE(__SI_RT, SI_KERNEL); + info.si_pid = 0; + info.si_uid = 0; + info.si_ptr = (gctPOINTER) Signal; + + /* Signals with numbers between 32 and 63 are real-time, + send a real-time signal to the user process. */ + result = send_sig_info(48, &info, userTask); + + printk("gckOS_SetSignalVG:0x%x\n", result); + /* Error? */ + if (result < 0) + { + status = gcvSTATUS_GENERIC_IO; + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): an error has occurred.\n", + __FUNCTION__, __LINE__ + ); + } + else + { + status = gcvSTATUS_OK; + } + } + else + { + status = gcvSTATUS_GENERIC_IO; + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): an error has occurred.\n", + __FUNCTION__, __LINE__ + ); + } + + /* Return status. */ + return status; +} +#endif + +/******************************************************************************* +** +** gckOS_UserSignal +** +** Set the specified signal which is owned by a process to signaled state. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIGNAL Signal +** Pointer to the gctSIGNAL. +** +** gctHANDLE Process +** Handle of process owning the signal. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_UserSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctHANDLE Process + ) +{ + gceSTATUS status; + gctSIGNAL signal; + + gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=%d", + Os, Signal, (gctINT32)(gctUINTPTR_T)Process); + + /* Map the signal into kernel space. */ + gcmkONERROR(gckOS_MapSignal(Os, Signal, Process, &signal)); + + /* Signal. */ + status = gckOS_Signal(Os, signal, gcvTRUE); + + /* Unmap the signal */ + gcmkVERIFY_OK(gckOS_UnmapSignal(Os, Signal)); + + gcmkFOOTER(); + return status; + +OnError: + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_WaitSignal +** +** Wait for a signal to become signaled. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIGNAL Signal +** Pointer to the gctSIGNAL. +** +** gctUINT32 Wait +** Number of milliseconds to wait. +** Pass the value of gcvINFINITE for an infinite wait. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_WaitSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctUINT32 Wait + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gcsSIGNAL_PTR signal; + + gcmkHEADER_ARG("Os=0x%X Signal=0x%X Wait=0x%08X", Os, Signal, Wait); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + + gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); + + gcmkASSERT(signal->id == (gctUINT32)(gctUINTPTR_T)Signal); + + might_sleep(); + + spin_lock_irq(&signal->obj.wait.lock); + + if (signal->obj.done) + { + if (!signal->manualReset) + { + signal->obj.done = 0; + } + + status = gcvSTATUS_OK; + } + else if (Wait == 0) + { + status = gcvSTATUS_TIMEOUT; + } + else + { + /* Convert wait to milliseconds. */ + long timeout = (Wait == gcvINFINITE) + ? MAX_SCHEDULE_TIMEOUT + : Wait * HZ / 1000; + + DECLARE_WAITQUEUE(wait, current); + wait.flags |= WQ_FLAG_EXCLUSIVE; + __add_wait_queue_tail(&signal->obj.wait, &wait); + + while (gcvTRUE) + { + if (signal_pending(current)) + { + /* Interrupt received. */ + status = gcvSTATUS_INTERRUPTED; + break; + } + + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irq(&signal->obj.wait.lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&signal->obj.wait.lock); + + if (signal->obj.done) + { + if (!signal->manualReset) + { + signal->obj.done = 0; + } + + status = gcvSTATUS_OK; + break; + } + + if (timeout == 0) + { + + status = gcvSTATUS_TIMEOUT; + break; + } + } + + __remove_wait_queue(&signal->obj.wait, &wait); + } + + spin_unlock_irq(&signal->obj.wait.lock); + +OnError: + /* Return status. */ + gcmkFOOTER_ARG("Signal=0x%X status=%d", Signal, status); + return status; +} + +/******************************************************************************* +** +** gckOS_MapSignal +** +** Map a signal in to the current process space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIGNAL Signal +** Pointer to tha gctSIGNAL to map. +** +** gctHANDLE Process +** Handle of process owning the signal. +** +** OUTPUT: +** +** gctSIGNAL * MappedSignal +** Pointer to a variable receiving the mapped gctSIGNAL. +*/ +gceSTATUS +gckOS_MapSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctHANDLE Process, + OUT gctSIGNAL * MappedSignal + ) +{ + gceSTATUS status; + gcsSIGNAL_PTR signal; + gcmkHEADER_ARG("Os=0x%X Signal=0x%X Process=0x%X", Os, Signal, Process); + + gcmkVERIFY_ARGUMENT(Signal != gcvNULL); + gcmkVERIFY_ARGUMENT(MappedSignal != gcvNULL); + + gcmkONERROR(_QueryIntegerId(&Os->signalDB, (gctUINT32)(gctUINTPTR_T)Signal, (gctPOINTER)&signal)); + + if(atomic_inc_return(&signal->ref) <= 1) + { + /* The previous value is 0, it has been deleted. */ + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + *MappedSignal = (gctSIGNAL) Signal; + + /* Success. */ + gcmkFOOTER_ARG("*MappedSignal=0x%X", *MappedSignal); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER_NO(); + return status; +} + +/******************************************************************************* +** +** gckOS_UnmapSignal +** +** Unmap a signal . +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctSIGNAL Signal +** Pointer to that gctSIGNAL mapped. +*/ +gceSTATUS +gckOS_UnmapSignal( + IN gckOS Os, + IN gctSIGNAL Signal + ) +{ + return gckOS_DestroySignal(Os, Signal); +} + +/******************************************************************************* +** +** gckOS_CreateUserSignal +** +** Create a new signal to be used in the user space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctBOOL ManualReset +** If set to gcvTRUE, gckOS_Signal with gcvFALSE must be called in +** order to set the signal to nonsignaled state. +** If set to gcvFALSE, the signal will automatically be set to +** nonsignaled state by gckOS_WaitSignal function. +** +** OUTPUT: +** +** gctINT * SignalID +** Pointer to a variable receiving the created signal's ID. +*/ +gceSTATUS +gckOS_CreateUserSignal( + IN gckOS Os, + IN gctBOOL ManualReset, + OUT gctINT * SignalID + ) +{ + gceSTATUS status; + gctSIZE_T signal; + + /* Create a new signal. */ + gcmkONERROR(gckOS_CreateSignal(Os, ManualReset, (gctSIGNAL *) &signal)); + *SignalID = (gctINT) signal; + +OnError: + return status; +} + +/******************************************************************************* +** +** gckOS_DestroyUserSignal +** +** Destroy a signal to be used in the user space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctINT SignalID +** The signal's ID. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_DestroyUserSignal( + IN gckOS Os, + IN gctINT SignalID + ) +{ + return gckOS_DestroySignal(Os, (gctSIGNAL)(gctUINTPTR_T)SignalID); +} + +/******************************************************************************* +** +** gckOS_WaitUserSignal +** +** Wait for a signal used in the user mode to become signaled. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctINT SignalID +** Signal ID. +** +** gctUINT32 Wait +** Number of milliseconds to wait. +** Pass the value of gcvINFINITE for an infinite wait. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_WaitUserSignal( + IN gckOS Os, + IN gctINT SignalID, + IN gctUINT32 Wait + ) +{ + return gckOS_WaitSignal(Os, (gctSIGNAL)(gctUINTPTR_T)SignalID, Wait); +} + +/******************************************************************************* +** +** gckOS_SignalUserSignal +** +** Set a state of the specified signal to be used in the user space. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctINT SignalID +** SignalID. +** +** gctBOOL State +** If gcvTRUE, the signal will be set to signaled state. +** If gcvFALSE, the signal will be set to nonsignaled state. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_SignalUserSignal( + IN gckOS Os, + IN gctINT SignalID, + IN gctBOOL State + ) +{ + return gckOS_Signal(Os, (gctSIGNAL)(gctUINTPTR_T)SignalID, State); +} + +#if gcdENABLE_VG +gceSTATUS +gckOS_CreateSemaphoreVG( + IN gckOS Os, + OUT gctSEMAPHORE * Semaphore + ) +{ + gceSTATUS status; + struct semaphore * newSemaphore; + + gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + do + { + /* Allocate the semaphore structure. */ + newSemaphore = (struct semaphore *)kmalloc(gcmSIZEOF(struct semaphore), GFP_KERNEL | gcdNOWARN); + if (newSemaphore == gcvNULL) + { + gcmkERR_BREAK(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Initialize the semaphore. */ + sema_init(newSemaphore, 0); + + /* Set the handle. */ + * Semaphore = (gctSEMAPHORE) newSemaphore; + + /* Success. */ + status = gcvSTATUS_OK; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + + +gceSTATUS +gckOS_IncrementSemaphore( + IN gckOS Os, + IN gctSEMAPHORE Semaphore + ) +{ + gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + /* Increment the semaphore's count. */ + up((struct semaphore *) Semaphore); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_DecrementSemaphore( + IN gckOS Os, + IN gctSEMAPHORE Semaphore + ) +{ + gceSTATUS status; + gctINT result; + + gcmkHEADER_ARG("Os=0x%X Semaphore=0x%x", Os, Semaphore); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Semaphore != gcvNULL); + + do + { + /* Decrement the semaphore's count. If the count is zero, wait + until it gets incremented. */ + result = down_interruptible((struct semaphore *) Semaphore); + + /* Signal received? */ + if (result != 0) + { + status = gcvSTATUS_TERMINATE; + break; + } + + /* Success. */ + status = gcvSTATUS_OK; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckOS_SetSignal +** +** Set the specified signal to signaled state. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctHANDLE Process +** Handle of process owning the signal. +** +** gctSIGNAL Signal +** Pointer to the gctSIGNAL. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_SetSignal( + IN gckOS Os, + IN gctHANDLE Process, + IN gctSIGNAL Signal + ) +{ + gceSTATUS status; + gctINT result; + struct task_struct * userTask; + struct siginfo info; + + userTask = FIND_TASK_BY_PID((pid_t)(gctUINTPTR_T) Process); + + if (userTask != gcvNULL) + { + info.si_signo = 48; + info.si_code = __SI_CODE(__SI_RT, SI_KERNEL); + info.si_pid = 0; + info.si_uid = 0; + info.si_ptr = (gctPOINTER) Signal; + + /* Signals with numbers between 32 and 63 are real-time, + send a real-time signal to the user process. */ + result = send_sig_info(48, &info, userTask); + + /* Error? */ + if (result < 0) + { + status = gcvSTATUS_GENERIC_IO; + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): an error has occurred.\n", + __FUNCTION__, __LINE__ + ); + } + else + { + status = gcvSTATUS_OK; + } + } + else + { + status = gcvSTATUS_GENERIC_IO; + + gcmkTRACE( + gcvLEVEL_ERROR, + "%s(%d): an error has occurred.\n", + __FUNCTION__, __LINE__ + ); + } + + /* Return status. */ + return status; +} + +/******************************************************************************\ +******************************** Thread Object ********************************* +\******************************************************************************/ + +gceSTATUS +gckOS_StartThread( + IN gckOS Os, + IN gctTHREADFUNC ThreadFunction, + IN gctPOINTER ThreadParameter, + OUT gctTHREAD * Thread + ) +{ + gceSTATUS status; + struct task_struct * thread; + + gcmkHEADER_ARG("Os=0x%X ", Os); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(ThreadFunction != gcvNULL); + gcmkVERIFY_ARGUMENT(Thread != gcvNULL); + + do + { + /* Create the thread. */ + thread = kthread_create( + ThreadFunction, + ThreadParameter, + "Vivante Kernel Thread" + ); + + /* Failed? */ + if (IS_ERR(thread)) + { + status = gcvSTATUS_GENERIC_IO; + break; + } + + /* Start the thread. */ + wake_up_process(thread); + + /* Set the thread handle. */ + * Thread = (gctTHREAD) thread; + + /* Success. */ + status = gcvSTATUS_OK; + } + while (gcvFALSE); + + gcmkFOOTER(); + /* Return the status. */ + return status; +} + +gceSTATUS +gckOS_StopThread( + IN gckOS Os, + IN gctTHREAD Thread + ) +{ + gcmkHEADER_ARG("Os=0x%X Thread=0x%x", Os, Thread); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Thread != gcvNULL); + + /* Thread should have already been enabled to terminate. */ + kthread_stop((struct task_struct *) Thread); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_VerifyThread( + IN gckOS Os, + IN gctTHREAD Thread + ) +{ + gcmkHEADER_ARG("Os=0x%X Thread=0x%x", Os, Thread); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Thread != gcvNULL); + + gcmkFOOTER_NO(); + /* Success. */ + return gcvSTATUS_OK; +} +#endif + +/******************************************************************************\ +******************************** Software Timer ******************************** +\******************************************************************************/ + +void +_TimerFunction( + struct work_struct * work + ) +{ + gcsOSTIMER_PTR timer = (gcsOSTIMER_PTR)work; + + gctTIMERFUNCTION function = timer->function; + + function(timer->data); +} + +/******************************************************************************* +** +** gckOS_CreateTimer +** +** Create a software timer. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctTIMERFUNCTION Function. +** Pointer to a call back function which will be called when timer is +** expired. +** +** gctPOINTER Data. +** Private data which will be passed to call back function. +** +** OUTPUT: +** +** gctPOINTER * Timer +** Pointer to a variable receiving the created timer. +*/ +gceSTATUS +gckOS_CreateTimer( + IN gckOS Os, + IN gctTIMERFUNCTION Function, + IN gctPOINTER Data, + OUT gctPOINTER * Timer + ) +{ + gceSTATUS status; + gcsOSTIMER_PTR pointer; + gcmkHEADER_ARG("Os=0x%X Function=0x%X Data=0x%X", Os, Function, Data); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Timer != gcvNULL); + + gcmkONERROR(gckOS_Allocate(Os, sizeof(gcsOSTIMER), (gctPOINTER)&pointer)); + + pointer->function = Function; + pointer->data = Data; + + INIT_DELAYED_WORK(&pointer->work, _TimerFunction); + + *Timer = pointer; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckOS_DestroyTimer +** +** Destory a software timer. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Timer +** Pointer to the timer to be destoryed. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_DestroyTimer( + IN gckOS Os, + IN gctPOINTER Timer + ) +{ + gcsOSTIMER_PTR timer; + gcmkHEADER_ARG("Os=0x%X Timer=0x%X", Os, Timer); + + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Timer != gcvNULL); + + timer = (gcsOSTIMER_PTR)Timer; + + cancel_delayed_work_sync(&timer->work); + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, Timer)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_StartTimer +** +** Schedule a software timer. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Timer +** Pointer to the timer to be scheduled. +** +** gctUINT32 Delay +** Delay in milliseconds. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_StartTimer( + IN gckOS Os, + IN gctPOINTER Timer, + IN gctUINT32 Delay + ) +{ + gcsOSTIMER_PTR timer; + + gcmkHEADER_ARG("Os=0x%X Timer=0x%X Delay=%u", Os, Timer, Delay); + + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Timer != gcvNULL); + gcmkVERIFY_ARGUMENT(Delay != 0); + + timer = (gcsOSTIMER_PTR)Timer; + + mod_delayed_work(Os->workqueue, &timer->work, msecs_to_jiffies(Delay)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_StopTimer +** +** Cancel a unscheduled timer. +** +** INPUT: +** +** gckOS Os +** Pointer to the gckOS object. +** +** gctPOINTER Timer +** Pointer to the timer to be cancel. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_StopTimer( + IN gckOS Os, + IN gctPOINTER Timer + ) +{ + gcsOSTIMER_PTR timer; + gcmkHEADER_ARG("Os=0x%X Timer=0x%X", Os, Timer); + + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Timer != gcvNULL); + + timer = (gcsOSTIMER_PTR)Timer; + + cancel_delayed_work(&timer->work); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_GetProcessNameByPid( + IN gctINT Pid, + IN gctSIZE_T Length, + OUT gctUINT8_PTR String + ) +{ + struct task_struct *task; + + /* Get the task_struct of the task with pid. */ + rcu_read_lock(); + + task = FIND_TASK_BY_PID(Pid); + + if (task == gcvNULL) + { + rcu_read_unlock(); + return gcvSTATUS_NOT_FOUND; + } + + /* Get name of process. */ + strncpy(String, task->comm, Length); + + rcu_read_unlock(); + + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_DumpCallStack( + IN gckOS Os + ) +{ + gcmkHEADER_ARG("Os=0x%X", Os); + + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + dump_stack(); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckOS_DetectProcessByName +** +** task->comm maybe part of process name, so this function +** can only be used for debugging. +** +** INPUT: +** +** gctCONST_POINTER Name +** Pointer to a string to hold name to be check. If the length +** of name is longer than TASK_COMM_LEN (16), use part of name +** to detect. +** +** OUTPUT: +** +** gcvSTATUS_TRUE if name of current process matches Name. +** +*/ +gceSTATUS +gckOS_DetectProcessByName( + IN gctCONST_POINTER Name + ) +{ + char comm[sizeof(current->comm)]; + + memset(comm, 0, sizeof(comm)); + + gcmkVERIFY_OK( + gckOS_GetProcessNameByPid(_GetProcessID(), sizeof(current->comm), comm)); + + return strstr(comm, Name) ? gcvSTATUS_TRUE + : gcvSTATUS_FALSE; +} + +#if gcdANDROID_NATIVE_FENCE_SYNC + +gceSTATUS +gckOS_CreateSyncPoint( + IN gckOS Os, + OUT gctSYNC_POINT * SyncPoint + ) +{ + gceSTATUS status; + gcsSYNC_POINT_PTR syncPoint; + + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + + /* Create an sync point structure. */ + syncPoint = (gcsSYNC_POINT_PTR) kmalloc( + sizeof(gcsSYNC_POINT), GFP_KERNEL | gcdNOWARN); + + if (syncPoint == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Initialize the sync point. */ + atomic_set(&syncPoint->ref, 1); + atomic_set(&syncPoint->state, 0); + + gcmkONERROR(_AllocateIntegerId(&Os->syncPointDB, syncPoint, &syncPoint->id)); + + *SyncPoint = (gctSYNC_POINT)(gctUINTPTR_T)syncPoint->id; + + gcmkFOOTER_ARG("*SyncPonint=%d", syncPoint->id); + return gcvSTATUS_OK; + +OnError: + if (syncPoint != gcvNULL) + { + kfree(syncPoint); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_ReferenceSyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint + ) +{ + gceSTATUS status; + gcsSYNC_POINT_PTR syncPoint; + + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); + + gcmkONERROR( + _QueryIntegerId(&Os->syncPointDB, + (gctUINT32)(gctUINTPTR_T)SyncPoint, + (gctPOINTER)&syncPoint)); + + /* Initialize the sync point. */ + atomic_inc(&syncPoint->ref); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_DestroySyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint + ) +{ + gceSTATUS status; + gcsSYNC_POINT_PTR syncPoint; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Os=0x%X SyncPoint=%d", Os, (gctUINT32)(gctUINTPTR_T)SyncPoint); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); + + gcmkONERROR(gckOS_AcquireMutex(Os, Os->syncPointMutex, gcvINFINITE)); + acquired = gcvTRUE; + + gcmkONERROR( + _QueryIntegerId(&Os->syncPointDB, + (gctUINT32)(gctUINTPTR_T)SyncPoint, + (gctPOINTER)&syncPoint)); + + gcmkASSERT(syncPoint->id == (gctUINT32)(gctUINTPTR_T)SyncPoint); + + if (atomic_dec_and_test(&syncPoint->ref)) + { + gcmkVERIFY_OK(_DestroyIntegerId(&Os->syncPointDB, syncPoint->id)); + + /* Free the sgianl. */ + syncPoint->timeline = gcvNULL; + kfree(syncPoint); + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); + acquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_SignalSyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint + ) +{ + gceSTATUS status; + gcsSYNC_POINT_PTR syncPoint; + struct sync_timeline * timeline; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Os=0x%X SyncPoint=%d", Os, (gctUINT32)(gctUINTPTR_T)SyncPoint); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); + + gcmkONERROR(gckOS_AcquireMutex(Os, Os->syncPointMutex, gcvINFINITE)); + acquired = gcvTRUE; + + gcmkONERROR( + _QueryIntegerId(&Os->syncPointDB, + (gctUINT32)(gctUINTPTR_T)SyncPoint, + (gctPOINTER)&syncPoint)); + + gcmkASSERT(syncPoint->id == (gctUINT32)(gctUINTPTR_T)SyncPoint); + + /* Set signaled state. */ + atomic_set(&syncPoint->state, 1); + + /* Get parent timeline. */ + timeline = syncPoint->timeline; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); + acquired = gcvFALSE; + + /* Signal timeline. */ + if (timeline) + { + sync_timeline_signal(timeline); + } + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Os, Os->syncPointMutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_QuerySyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint, + OUT gctBOOL_PTR State + ) +{ + gceSTATUS status; + gcsSYNC_POINT_PTR syncPoint; + + gcmkHEADER_ARG("Os=0x%X SyncPoint=%d", Os, (gctUINT32)(gctUINTPTR_T)SyncPoint); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(SyncPoint != gcvNULL); + + gcmkONERROR( + _QueryIntegerId(&Os->syncPointDB, + (gctUINT32)(gctUINTPTR_T)SyncPoint, + (gctPOINTER)&syncPoint)); + + gcmkASSERT(syncPoint->id == (gctUINT32)(gctUINTPTR_T)SyncPoint); + + /* Get state. */ + *State = atomic_read(&syncPoint->state); + + /* Success. */ + gcmkFOOTER_ARG("*State=%d", *State); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_CreateSyncTimeline( + IN gckOS Os, + OUT gctHANDLE * Timeline + ) +{ + struct viv_sync_timeline * timeline; + + /* Create viv sync timeline. */ + timeline = viv_sync_timeline_create("viv timeline", Os); + + if (timeline == gcvNULL) + { + /* Out of memory. */ + return gcvSTATUS_OUT_OF_MEMORY; + } + + *Timeline = (gctHANDLE) timeline; + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_DestroySyncTimeline( + IN gckOS Os, + IN gctHANDLE Timeline + ) +{ + struct viv_sync_timeline * timeline; + gcmkASSERT(Timeline != gcvNULL); + + /* Destroy timeline. */ + timeline = (struct viv_sync_timeline *) Timeline; + sync_timeline_destroy(&timeline->obj); + + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_CreateNativeFence( + IN gckOS Os, + IN gctHANDLE Timeline, + IN gctSYNC_POINT SyncPoint, + OUT gctINT * FenceFD + ) +{ + int fd = -1; + struct viv_sync_timeline *timeline; + struct sync_pt * pt = gcvNULL; + struct sync_fence * fence; + char name[32]; + gcsSYNC_POINT_PTR syncPoint; + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X Timeline=0x%X SyncPoint=%d", + Os, Timeline, (gctUINT)(gctUINTPTR_T)SyncPoint); + + gcmkONERROR( + _QueryIntegerId(&Os->syncPointDB, + (gctUINT32)(gctUINTPTR_T)SyncPoint, + (gctPOINTER)&syncPoint)); + + /* Cast timeline. */ + timeline = (struct viv_sync_timeline *) Timeline; + + fd = get_unused_fd(); + + if (fd < 0) + { + /* Out of resources. */ + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + /* Create viv_sync_pt. */ + pt = viv_sync_pt_create(timeline, SyncPoint); + + if (pt == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Reference sync_timeline. */ + syncPoint->timeline = &timeline->obj; + + /* Build fence name. */ + snprintf(name, 32, "viv sync_fence-%u", (gctUINT)(gctUINTPTR_T)SyncPoint); + + /* Create sync_fence. */ + fence = sync_fence_create(name, pt); + + if (fence == NULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + /* Install fence to fd. */ + sync_fence_install(fence, fd); + + *FenceFD = fd; + gcmkFOOTER_ARG("*FenceFD=%d", fd); + return gcvSTATUS_OK; + +OnError: + /* Error roll back. */ + if (pt) + { + sync_pt_free(pt); + } + + if (fd > 0) + { + put_unused_fd(fd); + } + + gcmkFOOTER(); + return status; +} +#endif + +#if gcdSECURITY +gceSTATUS +gckOS_AllocatePageArray( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T PageCount, + OUT gctPOINTER * PageArrayLogical, + OUT gctPHYS_ADDR * PageArrayPhysical + ) +{ + gceSTATUS status = gcvSTATUS_OK; + PLINUX_MDL mdl; + gctUINT32* table; + gctUINT32 offset; + gctSIZE_T bytes; + gckALLOCATOR allocator; + + gcmkHEADER_ARG("Os=0x%X Physical=0x%X PageCount=%u", + Os, Physical, PageCount); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Physical != gcvNULL); + gcmkVERIFY_ARGUMENT(PageCount > 0); + + bytes = PageCount * gcmSIZEOF(gctUINT32); + gcmkONERROR(gckOS_AllocateNonPagedMemory( + Os, + gcvFALSE, + &bytes, + PageArrayPhysical, + PageArrayLogical + )); + + table = *PageArrayLogical; + + /* Convert pointer to MDL. */ + mdl = (PLINUX_MDL)Physical; + + allocator = mdl->allocator; + + /* Get all the physical addresses and store them in the page table. */ + + offset = 0; + PageCount = PageCount / (PAGE_SIZE / 4096); + + /* Try to get the user pages so DMA can happen. */ + while (PageCount-- > 0) + { + unsigned long phys = ~0; + + if (mdl->pagedMem && !mdl->contiguous) + { + if (allocator) + { + gctUINT32 phys_addr; + allocator->ops->Physical(allocator, mdl, offset, &phys_addr); + phys = (unsigned long)phys_addr; + } + } + else + { + if (!mdl->pagedMem) + { + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_OS, + "%s(%d): we should not get this call for Non Paged Memory!", + __FUNCTION__, __LINE__ + ); + } + + phys = page_to_phys(nth_page(mdl->u.contiguousPages, offset)); + } + + table[offset] = phys; + + offset += 1; + } + +OnError: + + /* Return the status. */ + gcmkFOOTER(); + return status; +} +#endif + +gceSTATUS +gckOS_CPUPhysicalToGPUPhysical( + IN gckOS Os, + IN gctUINT32 CPUPhysical, + IN gctUINT32_PTR GPUPhysical + ) +{ + gcsPLATFORM * platform; + gcmkHEADER_ARG("CPUPhysical=0x%X", CPUPhysical); + + platform = Os->device->platform; + + if (platform && platform->ops->getGPUPhysical) + { + gcmkVERIFY_OK( + platform->ops->getGPUPhysical(platform, CPUPhysical, GPUPhysical)); + } + else + { + *GPUPhysical = CPUPhysical; + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_GPUPhysicalToCPUPhysical( + IN gckOS Os, + IN gctUINT32 GPUPhysical, + IN gctUINT32_PTR CPUPhysical + ) +{ + gcmkHEADER_ARG("GPUPhysical=0x%X", GPUPhysical); + + *CPUPhysical = GPUPhysical; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_PhysicalToPhysicalAddress( + IN gckOS Os, + IN gctPOINTER Physical, + OUT gctUINT32 * PhysicalAddress + ) +{ + PLINUX_MDL mdl = (PLINUX_MDL)Physical; + gckALLOCATOR allocator = mdl->allocator; + + if (allocator) + { + return allocator->ops->Physical(allocator, mdl, 0, PhysicalAddress); + } + + return gcvSTATUS_NOT_SUPPORTED; +} + +gceSTATUS +gckOS_QueryOption( + IN gckOS Os, + IN gctCONST_STRING Option, + OUT gctUINT32 * Value + ) +{ + gckGALDEVICE device = Os->device; + + if (!strcmp(Option, "physBase")) + { + *Value = device->physBase; + return gcvSTATUS_OK; + } + else if (!strcmp(Option, "physSize")) + { + *Value = device->physSize; + return gcvSTATUS_OK; + } + else if (!strcmp(Option, "mmu")) + { +#if gcdSECURITY + *Value = 0; +#else + *Value = device->mmu; +#endif + return gcvSTATUS_OK; + } + + return gcvSTATUS_NOT_SUPPORTED; +} + +static int +fd_release( + struct inode *inode, + struct file *file + ) +{ + gcsFDPRIVATE_PTR private = (gcsFDPRIVATE_PTR)file->private_data; + + if (private && private->release) + { + return private->release(private); + } + + return 0; +} + +static const struct file_operations fd_fops = { + .release = fd_release, +}; + +gceSTATUS +gckOS_GetFd( + IN gctSTRING Name, + IN gcsFDPRIVATE_PTR Private, + OUT gctINT *Fd + ) +{ + *Fd = anon_inode_getfd(Name, &fd_fops, Private, O_RDWR); + + if (*Fd < 0) + { + return gcvSTATUS_OUT_OF_RESOURCES; + } + + return gcvSTATUS_OK; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.h b/drivers/gpu/galcore/gc_hal_kernel_os.h new file mode 100644 index 00000000000000..d991d3513013f2 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_os.h @@ -0,0 +1,88 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_os_h_ +#define __gc_hal_kernel_os_h_ + +typedef struct _LINUX_MDL_MAP +{ + gctINT pid; + gctPOINTER vmaAddr; + gctUINT32 count; + struct vm_area_struct * vma; + struct _LINUX_MDL_MAP * next; +} +LINUX_MDL_MAP; + +typedef struct _LINUX_MDL_MAP * PLINUX_MDL_MAP; + +typedef struct _LINUX_MDL +{ + char * addr; + + union _pages + { + /* Pointer to a array of pages. */ + struct page * contiguousPages; + /* Pointer to a array of pointers to page. */ + struct page ** nonContiguousPages; + } + u; + +#ifdef NO_DMA_COHERENT + gctPOINTER kaddr; +#endif /* NO_DMA_COHERENT */ + + gctINT numPages; + gctINT pagedMem; + gctBOOL contiguous; + gctBOOL exact; + dma_addr_t dmaHandle; + PLINUX_MDL_MAP maps; + struct _LINUX_MDL * prev; + struct _LINUX_MDL * next; + + /* Pointer to allocator which allocates memory for this mdl. */ + void * allocator; + + /* Private data used by allocator. */ + void * priv; + + uint gid; +} +LINUX_MDL, *PLINUX_MDL; + +extern PLINUX_MDL_MAP +FindMdlMap( + IN PLINUX_MDL Mdl, + IN gctINT PID + ); + +typedef struct _DRIVER_ARGS +{ + gctUINT64 InputBuffer; + gctUINT64 InputBufferSize; + gctUINT64 OutputBuffer; + gctUINT64 OutputBufferSize; +} +DRIVER_ARGS; + +#endif /* __gc_hal_kernel_os_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_platform.h b/drivers/gpu/galcore/gc_hal_kernel_platform.h new file mode 100644 index 00000000000000..23b7178d83ba6f --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_platform.h @@ -0,0 +1,279 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef _gc_hal_kernel_platform_h_ +#define _gc_hal_kernel_platform_h_ +#include + +typedef struct _gcsMODULE_PARAMETERS +{ +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + gctINT irqLine3D0; + gctUINT registerMemBase3D0; + gctUINT registerMemSize3D0; + gctINT irqLine3D1; + gctUINT registerMemBase3D1; + gctUINT registerMemSize3D1; +#else + gctINT irqLine; + gctUINT registerMemBase; + gctUINT registerMemSize; +#endif + gctINT irqLine2D; + gctUINT registerMemBase2D; + gctUINT registerMemSize2D; + gctINT irqLineVG; + gctUINT registerMemBaseVG; + gctUINT registerMemSizeVG; + gctUINT contiguousSize; + gctUINT contiguousBase; + gctUINT contiguousRequested; + gctUINT bankSize; + gctINT fastClear; + gctINT compression; + gctINT powerManagement; + gctINT gpuProfiler; + gctINT signal; + gctUINT baseAddress; + gctUINT physSize; + gctUINT logFileSize; + gctUINT recovery; + gctUINT stuckDump; + gctUINT showArgs; + gctUINT gpu3DMinClock; +} +gcsMODULE_PARAMETERS; + +typedef struct _gcsPLATFORM * gckPLATFORM; + +typedef struct _gcsPLATFORM_OPERATIONS +{ + /******************************************************************************* + ** + ** needAddDevice + ** + ** Determine whether platform_device is created by initialization code. + ** If platform_device is created by BSP, return gcvFLASE here. + */ + gctBOOL + (*needAddDevice)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** adjustParam + ** + ** Override content of arguments, if a argument is not changed here, it will + ** keep as default value or value set by insmod command line. + */ + gceSTATUS + (*adjustParam)( + IN gckPLATFORM Platform, + OUT gcsMODULE_PARAMETERS *Args + ); + + /******************************************************************************* + ** + ** adjustDriver + ** + ** Override content of platform_driver which will be registered. + */ + gceSTATUS + (*adjustDriver)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** getPower + ** + ** Prepare power and clock operation. + */ + gceSTATUS + (*getPower)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** putPower + ** + ** Finish power and clock operation. + */ + gceSTATUS + (*putPower)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** allocPriv + ** + ** Construct platform private data. + */ + gceSTATUS + (*allocPriv)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** freePriv + ** + ** free platform private data. + */ + gceSTATUS + (*freePriv)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** setPower + ** + ** Set power state of specified GPU. + ** + ** INPUT: + ** + ** gceCORE GPU + ** GPU neeed to config. + ** + ** gceBOOL Enable + ** Enable or disable power. + */ + gceSTATUS + (*setPower)( + IN gckPLATFORM Platform, + IN gceCORE GPU, + IN gctBOOL Enable + ); + + /******************************************************************************* + ** + ** setClock + ** + ** Set clock state of specified GPU. + ** + ** INPUT: + ** + ** gceCORE GPU + ** GPU neeed to config. + ** + ** gceBOOL Enable + ** Enable or disable clock. + */ + gceSTATUS + (*setClock)( + IN gckPLATFORM Platform, + IN gceCORE GPU, + IN gctBOOL Enable + ); + + /******************************************************************************* + ** + ** reset + ** + ** Reset GPU outside. + ** + ** INPUT: + ** + ** gceCORE GPU + ** GPU neeed to reset. + */ + gceSTATUS + (*reset)( + IN gckPLATFORM Platform, + IN gceCORE GPU + ); + + /******************************************************************************* + ** + ** getGPUPhysical + ** + ** Convert CPU physical address to GPU physical address if they are + ** different. + */ + gceSTATUS + (*getGPUPhysical)( + IN gckPLATFORM Platform, + IN gctUINT32 CPUPhysical, + OUT gctUINT32_PTR GPUPhysical + ); + + /******************************************************************************* + ** + ** adjustProt + ** + ** Override Prot flag when mapping paged memory to userspace. + */ + gceSTATUS + (*adjustProt)( + IN struct vm_area_struct * vma + ); + + /******************************************************************************* + ** + ** shrinkMemory + ** + ** Do something to collect memory, eg, act as oom killer. + */ + gceSTATUS + (*shrinkMemory)( + IN gckPLATFORM Platform + ); + + /******************************************************************************* + ** + ** cache + ** + ** Cache operation. + */ + gceSTATUS + (*cache)( + IN gckPLATFORM Platform, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Handle, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes, + IN gceCACHEOPERATION Operation + ); +} +gcsPLATFORM_OPERATIONS; + +typedef struct _gcsPLATFORM +{ + struct platform_device* device; + struct platform_driver* driver; + + gcsPLATFORM_OPERATIONS* ops; + + void* priv; +} +gcsPLATFORM; + +void +gckPLATFORM_QueryOperations( + IN gcsPLATFORM_OPERATIONS ** Operations + ); + +#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_power.c b/drivers/gpu/galcore/gc_hal_kernel_power.c new file mode 100644 index 00000000000000..b060310a435bdc --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_power.c @@ -0,0 +1,347 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_POWER + +/******************************************************************************\ +************************ Dynamic Voltage Frequency Setting ********************* +\******************************************************************************/ +#if gcdDVFS +static gctUINT32 +_GetLoadHistory( + IN gckDVFS Dvfs, + IN gctUINT32 Select, + IN gctUINT32 Index +) +{ + return Dvfs->loads[Index]; +} + +static void +_IncreaseScale( + IN gckDVFS Dvfs, + IN gctUINT32 Load, + OUT gctUINT8 *Scale + ) +{ + if (Dvfs->currentScale < 32) + { + *Scale = Dvfs->currentScale + 8; + } + else + { + *Scale = Dvfs->currentScale + 8; + *Scale = gcmMIN(64, *Scale); + } +} + +static void +_RecordFrequencyHistory( + gckDVFS Dvfs, + gctUINT32 Frequency + ) +{ + gctUINT32 i = 0; + + struct _FrequencyHistory *history = Dvfs->frequencyHistory; + + for (i = 0; i < 16; i++) + { + if (history->frequency == Frequency) + { + break; + } + + if (history->frequency == 0) + { + history->frequency = Frequency; + break; + } + + history++; + } + + if (i < 16) + { + history->count++; + } +} + +static gctUINT32 +_GetFrequencyHistory( + gckDVFS Dvfs, + gctUINT32 Frequency + ) +{ + gctUINT32 i = 0; + + struct _FrequencyHistory * history = Dvfs->frequencyHistory; + + for (i = 0; i < 16; i++) + { + if (history->frequency == Frequency) + { + break; + } + + history++; + } + + if (i < 16) + { + return history->count; + } + + return 0; +} + +static void +_Policy( + IN gckDVFS Dvfs, + IN gctUINT32 Load, + OUT gctUINT8 *Scale + ) +{ + gctUINT8 load[4], nextLoad; + gctUINT8 scale; + + /* Last 4 history. */ + load[0] = (Load & 0xFF); + load[1] = (Load & 0xFF00) >> 8; + load[2] = (Load & 0xFF0000) >> 16; + load[3] = (Load & 0xFF000000) >> 24; + + /* Determine target scale. */ + if (load[0] > 54) + { + _IncreaseScale(Dvfs, Load, &scale); + } + else + { + nextLoad = (load[0] + load[1] + load[2] + load[3])/4; + + scale = Dvfs->currentScale * (nextLoad) / 54; + + scale = gcmMAX(1, scale); + scale = gcmMIN(64, scale); + } + + Dvfs->totalConfig++; + + Dvfs->loads[(load[0]-1)/8]++; + + *Scale = scale; + + + if (Dvfs->totalConfig % 100 == 0) + { + gcmkPRINT("======================================================="); + gcmkPRINT("GPU Load: %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d", + 8, 16, 24, 32, 40, 48, 56, 64); + gcmkPRINT(" %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d", + _GetLoadHistory(Dvfs,2, 0), + _GetLoadHistory(Dvfs,2, 1), + _GetLoadHistory(Dvfs,2, 2), + _GetLoadHistory(Dvfs,2, 3), + _GetLoadHistory(Dvfs,2, 4), + _GetLoadHistory(Dvfs,2, 5), + _GetLoadHistory(Dvfs,2, 6), + _GetLoadHistory(Dvfs,2, 7) + ); + + gcmkPRINT("Frequency(MHz) %-8d %-8d %-8d %-8d %-8d", + 58, 120, 240, 360, 480); + gcmkPRINT(" %-8d %-8d %-8d %-8d %-8d", + _GetFrequencyHistory(Dvfs, 58), + _GetFrequencyHistory(Dvfs,120), + _GetFrequencyHistory(Dvfs,240), + _GetFrequencyHistory(Dvfs,360), + _GetFrequencyHistory(Dvfs,480) + ); + } +} + +static void +_TimerFunction( + gctPOINTER Data + ) +{ + gceSTATUS status; + gckDVFS dvfs = (gckDVFS) Data; + gckHARDWARE hardware = dvfs->hardware; + gctUINT32 value; + gctUINT32 frequency; + gctUINT8 scale; + gctUINT32 t1, t2, consumed; + + gckOS_GetTicks(&t1); + + gcmkONERROR(gckHARDWARE_QueryLoad(hardware, &value)); + + /* determine target sacle. */ + _Policy(dvfs, value, &scale); + + /* Set frequency and voltage. */ + gcmkONERROR(gckOS_SetGPUFrequency(hardware->os, hardware->core, scale)); + + /* Query real frequency. */ + gcmkONERROR( + gckOS_QueryGPUFrequency(hardware->os, + hardware->core, + &frequency, + &dvfs->currentScale)); + + _RecordFrequencyHistory(dvfs, frequency); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_POWER, + "Current frequency = %d", + frequency); + + /* Set period. */ + gcmkONERROR(gckHARDWARE_SetDVFSPeroid(hardware, frequency)); + +OnError: + /* Determine next querying time. */ + gckOS_GetTicks(&t2); + + consumed = gcmMIN(((long)t2 - (long)t1), 5); + + if (dvfs->stop == gcvFALSE) + { + gcmkVERIFY_OK(gckOS_StartTimer(hardware->os, + dvfs->timer, + dvfs->pollingTime - consumed)); + } + + return; +} + +gceSTATUS +gckDVFS_Construct( + IN gckHARDWARE Hardware, + OUT gckDVFS * Dvfs + ) +{ + gceSTATUS status; + gctPOINTER pointer; + gckDVFS dvfs = gcvNULL; + gckOS os = Hardware->os; + + gcmkHEADER_ARG("Hardware=0x%X", Hardware); + + gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + /* Allocate a gckDVFS manager. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(struct _gckDVFS), &pointer)); + + gckOS_ZeroMemory(pointer, gcmSIZEOF(struct _gckDVFS)); + + dvfs = pointer; + + /* Initialization. */ + dvfs->hardware = Hardware; + dvfs->pollingTime = gcdDVFS_POLLING_TIME; + dvfs->os = Hardware->os; + dvfs->currentScale = 64; + + /* Create a polling timer. */ + gcmkONERROR(gckOS_CreateTimer(os, _TimerFunction, pointer, &dvfs->timer)); + + /* Initialize frequency and voltage adjustment helper. */ + gcmkONERROR(gckOS_PrepareGPUFrequency(os, Hardware->core)); + + /* Return result. */ + *Dvfs = dvfs; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (dvfs) + { + if (dvfs->timer) + { + gcmkVERIFY_OK(gckOS_DestroyTimer(os, dvfs->timer)); + } + + gcmkOS_SAFE_FREE(os, dvfs); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckDVFS_Destroy( + IN gckDVFS Dvfs + ) +{ + gcmkHEADER_ARG("Dvfs=0x%X", Dvfs); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + /* Deinitialize helper fuunction. */ + gcmkVERIFY_OK(gckOS_FinishGPUFrequency(Dvfs->os, Dvfs->hardware->core)); + + /* DestroyTimer. */ + gcmkVERIFY_OK(gckOS_DestroyTimer(Dvfs->os, Dvfs->timer)); + + gcmkOS_SAFE_FREE(Dvfs->os, Dvfs); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckDVFS_Start( + IN gckDVFS Dvfs + ) +{ + gcmkHEADER_ARG("Dvfs=0x%X", Dvfs); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + gckHARDWARE_InitDVFS(Dvfs->hardware); + + Dvfs->stop = gcvFALSE; + + gckOS_StartTimer(Dvfs->os, Dvfs->timer, Dvfs->pollingTime); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckDVFS_Stop( + IN gckDVFS Dvfs + ) +{ + gcmkHEADER_ARG("Dvfs=0x%X", Dvfs); + gcmkVERIFY_ARGUMENT(Dvfs != gcvNULL); + + Dvfs->stop = gcvTRUE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} +#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_precomp.h b/drivers/gpu/galcore/gc_hal_kernel_precomp.h new file mode 100644 index 00000000000000..9ea3d53626b40e --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_precomp.h @@ -0,0 +1,29 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_precomp_h_ +#define __gc_hal_kernel_precomp_h_ + +#include "gc_hal.h" +#include "gc_hal_driver.h" +#include "gc_hal_kernel.h" + +#endif /* __gc_hal_kernel_precomp_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_probe.c b/drivers/gpu/galcore/gc_hal_kernel_probe.c new file mode 100644 index 00000000000000..cd5f8ed6e51f93 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_probe.c @@ -0,0 +1,1324 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include +#include + +#include "gc_hal_kernel_linux.h" +#include "gc_hal_driver.h" + +#if USE_PLATFORM_DRIVER +# include +#endif + +#ifdef CONFIG_PXA_DVFM +# include +# include +#endif + + +/* Zone used for header/footer. */ +#define _GC_OBJ_ZONE gcvZONE_DRIVER + +MODULE_DESCRIPTION("Vivante Graphics Driver"); +MODULE_LICENSE("GPL"); + +static struct class* gpuClass; + +static gcsPLATFORM platform; + +static gckGALDEVICE galDevice; + +static uint major = 199; +module_param(major, uint, 0644); + +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY +static int irqLine3D0 = -1; +module_param(irqLine3D0, int, 0644); + +static ulong registerMemBase3D0 = 0; +module_param(registerMemBase3D0, ulong, 0644); + +static ulong registerMemSize3D0 = 2 << 10; +module_param(registerMemSize3D0, ulong, 0644); + +static int irqLine3D1 = -1; +module_param(irqLine3D1, int, 0644); + +static ulong registerMemBase3D1 = 0; +module_param(registerMemBase3D1, ulong, 0644); + +static ulong registerMemSize3D1 = 2 << 10; +module_param(registerMemSize3D1, ulong, 0644); +#else +static int irqLine = -1; +module_param(irqLine, int, 0644); + +static ulong registerMemBase = 0x80000000; +module_param(registerMemBase, ulong, 0644); + +static ulong registerMemSize = 2 << 10; +module_param(registerMemSize, ulong, 0644); +#endif + +static int irqLine2D = -1; +module_param(irqLine2D, int, 0644); + +static ulong registerMemBase2D = 0x00000000; +module_param(registerMemBase2D, ulong, 0644); + +static ulong registerMemSize2D = 2 << 10; +module_param(registerMemSize2D, ulong, 0644); + +static int irqLineVG = -1; +module_param(irqLineVG, int, 0644); + +static ulong registerMemBaseVG = 0x00000000; +module_param(registerMemBaseVG, ulong, 0644); + +static ulong registerMemSizeVG = 2 << 10; +module_param(registerMemSizeVG, ulong, 0644); + +#ifndef gcdDEFAULT_CONTIGUOUS_SIZE +#define gcdDEFAULT_CONTIGUOUS_SIZE (4 << 20) +#endif +static ulong contiguousSize = gcdDEFAULT_CONTIGUOUS_SIZE; +module_param(contiguousSize, ulong, 0644); + +static ulong contiguousBase = 0; +module_param(contiguousBase, ulong, 0644); + +static ulong bankSize = 0; +module_param(bankSize, ulong, 0644); + +static int fastClear = -1; +module_param(fastClear, int, 0644); + +static int compression = -1; +module_param(compression, int, 0644); + +static int powerManagement = -1; +module_param(powerManagement, int, 0644); + +static int gpuProfiler = 0; +module_param(gpuProfiler, int, 0644); + +static int signal = 48; +module_param(signal, int, 0644); + +static ulong baseAddress = 0; +module_param(baseAddress, ulong, 0644); + +static ulong physSize = 0; +module_param(physSize, ulong, 0644); + +static uint logFileSize = 0; +module_param(logFileSize,uint, 0644); + +static uint recovery = 1; +module_param(recovery, uint, 0644); +MODULE_PARM_DESC(recovery, "Recover GPU from stuck (1: Enable, 0: Disable)"); + +/* Middle needs about 40KB buffer, Maximal may need more than 200KB buffer. */ +static uint stuckDump = 1; +module_param(stuckDump, uint, 0644); +MODULE_PARM_DESC(stuckDump, "Level of stuck dump content (1: Minimal, 2: Middle, 3: Maximal)"); + +static int showArgs = 0; +module_param(showArgs, int, 0644); + +static int mmu = 1; +module_param(mmu, int, 0644); + +static int gpu3DMinClock = 1; + +static int contiguousRequested = 0; + +static int drv_open( + struct inode* inode, + struct file* filp + ); + +static int drv_release( + struct inode* inode, + struct file* filp + ); + +static long drv_ioctl( + struct file* filp, + unsigned int ioctlCode, + unsigned long arg + ); + +static int drv_mmap( + struct file* filp, + struct vm_area_struct* vma + ); + +static struct file_operations driver_fops = +{ + .owner = THIS_MODULE, + .open = drv_open, + .release = drv_release, + .unlocked_ioctl = drv_ioctl, +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = drv_ioctl, +#endif + .mmap = drv_mmap, +}; + +void +_UpdateModuleParam( + gcsMODULE_PARAMETERS *Param + ) +{ +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY +#else + irqLine = Param->irqLine ; + registerMemBase = Param->registerMemBase; + registerMemSize = Param->registerMemSize; +#endif + irqLine2D = Param->irqLine2D ; + registerMemBase2D = Param->registerMemBase2D; + registerMemSize2D = Param->registerMemSize2D; + irqLineVG = Param->irqLineVG; + registerMemBaseVG = Param->registerMemBaseVG; + registerMemSizeVG = Param->registerMemSizeVG; + contiguousSize = Param->contiguousSize; + contiguousBase = Param->contiguousBase; + bankSize = Param->bankSize; + fastClear = Param->fastClear; + compression = Param->compression; + powerManagement = Param->powerManagement; + gpuProfiler = Param->gpuProfiler; + signal = Param->signal; + baseAddress = Param->baseAddress; + physSize = Param->physSize; + logFileSize = Param->logFileSize; + recovery = Param->recovery; + stuckDump = Param->stuckDump; + showArgs = Param->showArgs; + contiguousRequested = Param->contiguousRequested; + gpu3DMinClock = Param->gpu3DMinClock; +} + +void +gckOS_DumpParam( + void + ) +{ + printk("Galcore options:\n"); +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + printk(" irqLine3D0 = %d\n", irqLine3D0); + printk(" registerMemBase3D0 = 0x%08lX\n", registerMemBase3D0); + printk(" registerMemSize3D0 = 0x%08lX\n", registerMemSize3D0); + + if (irqLine3D1 != -1) + { + printk(" irqLine3D1 = %d\n", irqLine3D1); + printk(" registerMemBase3D1 = 0x%08lX\n", registerMemBase3D1); + printk(" registerMemSize3D1 = 0x%08lX\n", registerMemSize3D1); + } +#else + printk(" irqLine = %d\n", irqLine); + printk(" registerMemBase = 0x%08lX\n", registerMemBase); + printk(" registerMemSize = 0x%08lX\n", registerMemSize); +#endif + + if (irqLine2D != -1) + { + printk(" irqLine2D = %d\n", irqLine2D); + printk(" registerMemBase2D = 0x%08lX\n", registerMemBase2D); + printk(" registerMemSize2D = 0x%08lX\n", registerMemSize2D); + } + + if (irqLineVG != -1) + { + printk(" irqLineVG = %d\n", irqLineVG); + printk(" registerMemBaseVG = 0x%08lX\n", registerMemBaseVG); + printk(" registerMemSizeVG = 0x%08lX\n", registerMemSizeVG); + } + + printk(" contiguousSize = %ld\n", contiguousSize); + printk(" contiguousBase = 0x%08lX\n", contiguousBase); + printk(" bankSize = 0x%08lX\n", bankSize); + printk(" fastClear = %d\n", fastClear); + printk(" compression = %d\n", compression); + printk(" signal = %d\n", signal); + printk(" powerManagement = %d\n", powerManagement); + printk(" baseAddress = 0x%08lX\n", baseAddress); + printk(" physSize = 0x%08lX\n", physSize); + printk(" logFileSize = %d KB \n", logFileSize); + printk(" recovery = %d\n", recovery); + printk(" stuckDump = %d\n", stuckDump); + printk(" gpuProfiler = %d\n", gpuProfiler); +} + +int drv_open( + struct inode* inode, + struct file* filp + ) +{ + gceSTATUS status; + gctBOOL attached = gcvFALSE; + gcsHAL_PRIVATE_DATA_PTR data = gcvNULL; + gctINT i; + + gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = kmalloc(sizeof(gcsHAL_PRIVATE_DATA), GFP_KERNEL | __GFP_NOWARN); + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + data->device = galDevice; + data->mappedMemory = gcvNULL; + data->contiguousLogical = gcvNULL; + gcmkONERROR(gckOS_GetProcessID(&data->pidOpen)); + + /* Attached the process. */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (galDevice->kernels[i] != gcvNULL) + { + gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE)); + } + } + attached = gcvTRUE; + + if (!galDevice->contiguousMapped) + { + if (galDevice->contiguousPhysical != gcvNULL) + { + gcmkONERROR(gckOS_MapMemory( + galDevice->os, + galDevice->contiguousPhysical, + galDevice->contiguousSize, + &data->contiguousLogical + )); + } + } + + filp->private_data = data; + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + if (data != gcvNULL) + { + if (data->contiguousLogical != gcvNULL) + { + gcmkVERIFY_OK(gckOS_UnmapMemory( + galDevice->os, + galDevice->contiguousPhysical, + galDevice->contiguousSize, + data->contiguousLogical + )); + } + + kfree(data); + } + + if (attached) + { + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (galDevice->kernels[i] != gcvNULL) + { + gcmkVERIFY_OK(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvFALSE)); + } + } + } + + gcmkFOOTER(); + return -ENOTTY; +} + +int drv_release( + struct inode* inode, + struct file* filp + ) +{ + gceSTATUS status; + gcsHAL_PRIVATE_DATA_PTR data; + gckGALDEVICE device; + gctINT i; + + gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = filp->private_data; + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + device = data->device; + + if (device == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): device is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (!device->contiguousMapped) + { + if (data->contiguousLogical != gcvNULL) + { + gcmkONERROR(gckOS_UnmapMemoryEx( + galDevice->os, + galDevice->contiguousPhysical, + galDevice->contiguousSize, + data->contiguousLogical, + data->pidOpen + )); + + data->contiguousLogical = gcvNULL; + } + } + + /* A process gets detached. */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (galDevice->kernels[i] != gcvNULL) + { + gcmkONERROR(gckKERNEL_AttachProcessEx(galDevice->kernels[i], gcvFALSE, data->pidOpen)); + } + } + + kfree(data); + filp->private_data = NULL; + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + gcmkFOOTER(); + return -ENOTTY; +} + +long drv_ioctl( + struct file* filp, + unsigned int ioctlCode, + unsigned long arg + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + gctUINT32 copyLen; + DRIVER_ARGS drvArgs; + gckGALDEVICE device; + gcsHAL_PRIVATE_DATA_PTR data; + gctINT32 i, count; + gckVIDMEM_NODE nodeObject; + + gcmkHEADER_ARG( + "filp=0x%08X ioctlCode=0x%08X arg=0x%08X", + filp, ioctlCode, arg + ); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = filp->private_data; + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + device = data->device; + + if (device == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): device is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if ((ioctlCode != IOCTL_GCHAL_INTERFACE) + && (ioctlCode != IOCTL_GCHAL_KERNEL_INTERFACE) + ) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): unknown command %d\n", + __FUNCTION__, __LINE__, + ioctlCode + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Get the drvArgs. */ + copyLen = copy_from_user( + &drvArgs, (void *) arg, sizeof(DRIVER_ARGS) + ); + + if (copyLen != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): error copying of the input arguments.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Now bring in the gcsHAL_INTERFACE structure. */ + if ((drvArgs.InputBufferSize != sizeof(gcsHAL_INTERFACE)) + || (drvArgs.OutputBufferSize != sizeof(gcsHAL_INTERFACE)) + ) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): input or/and output structures are invalid.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + copyLen = copy_from_user( + &iface, gcmUINT64_TO_PTR(drvArgs.InputBuffer), sizeof(gcsHAL_INTERFACE) + ); + + if (copyLen != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): error copying of input HAL interface.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (iface.command == gcvHAL_CHIP_INFO) + { + count = 0; + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) + { +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + iface.u.ChipInfo.types[count] = gcvHARDWARE_VG; + } + else +#endif + { + gcmkVERIFY_OK(gckHARDWARE_GetType(device->kernels[i]->hardware, + &iface.u.ChipInfo.types[count])); + } + count++; + } + } + + iface.u.ChipInfo.count = count; + iface.status = status = gcvSTATUS_OK; + } + else + { + if (iface.hardwareType > 7) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): unknown hardwareType %d\n", + __FUNCTION__, __LINE__, + iface.hardwareType + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +#if gcdENABLE_VG + if (device->coreMapping[iface.hardwareType] == gcvCORE_VG) + { + status = gckVGKERNEL_Dispatch(device->kernels[gcvCORE_VG], + (ioctlCode == IOCTL_GCHAL_INTERFACE), + &iface); + } + else +#endif + { + status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]], + (ioctlCode == IOCTL_GCHAL_INTERFACE), + &iface); + } + } + + /* Redo system call after pending signal is handled. */ + if (status == gcvSTATUS_INTERRUPTED) + { + gcmkFOOTER(); + return -ERESTARTSYS; + } + + if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY)) + { + gcuVIDMEM_NODE_PTR node; + gctUINT32 processID; + + gckOS_GetProcessID(&processID); + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup(device->kernels[device->coreMapping[iface.hardwareType]], + processID, + (gctUINT32)iface.u.LockVideoMemory.node, + &nodeObject)); + node = nodeObject->node; + + /* Special case for mapped memory. */ + if ((data->mappedMemory != gcvNULL) + && (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + ) + { + /* Compute offset into mapped memory. */ + gctUINT32 offset + = (gctUINT8 *) gcmUINT64_TO_PTR(iface.u.LockVideoMemory.memory) + - (gctUINT8 *) device->contiguousBase; + + /* Compute offset into user-mapped region. */ + iface.u.LockVideoMemory.memory = + gcmPTR_TO_UINT64((gctUINT8 *) data->mappedMemory + offset); + } + } + + /* Copy data back to the user. */ + copyLen = copy_to_user( + gcmUINT64_TO_PTR(drvArgs.OutputBuffer), &iface, sizeof(gcsHAL_INTERFACE) + ); + + if (copyLen != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): error copying of output HAL interface.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + gcmkFOOTER(); + return -ENOTTY; +} + +static int drv_mmap( + struct file* filp, + struct vm_area_struct* vma + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gcsHAL_PRIVATE_DATA_PTR data; + gckGALDEVICE device; + + gcmkHEADER_ARG("filp=0x%08X vma=0x%08X", filp, vma); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = filp->private_data; + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + device = data->device; + + if (device == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): device is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +#if !gcdPAGED_MEMORY_CACHEABLE + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_flags |= gcdVM_FLAGS; +#endif + vma->vm_pgoff = 0; + + if (device->contiguousMapped) + { + unsigned long size = vma->vm_end - vma->vm_start; + int ret = 0; + + if (size > device->contiguousSize) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Invalid mapping size.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + ret = io_remap_pfn_range( + vma, + vma->vm_start, + device->requestedContiguousBase >> PAGE_SHIFT, + size, + vma->vm_page_prot + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): io_remap_pfn_range failed %d\n", + __FUNCTION__, __LINE__, + ret + ); + + data->mappedMemory = gcvNULL; + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + data->mappedMemory = (gctPOINTER) vma->vm_start; + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + } + +OnError: + gcmkFOOTER(); + return -ENOTTY; +} + + +#if !USE_PLATFORM_DRIVER +static int __init drv_init(void) +#else +static int drv_init(void) +#endif +{ + int ret; + int result = -EINVAL; + gceSTATUS status; + gckGALDEVICE device = gcvNULL; + struct class* device_class = gcvNULL; + + gcsDEVICE_CONSTRUCT_ARGS args = { + .recovery = recovery, + .stuckDump = stuckDump, + .gpu3DMinClock = gpu3DMinClock, + .contiguousRequested = contiguousRequested, + .platform = &platform, + .mmu = mmu, + }; + + gcmkHEADER(); + + printk(KERN_INFO "Galcore version %d.%d.%d.%d\n", + gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD); + +#if !VIVANTE_PROFILER_PM + /* when enable gpu profiler, we need to turn off gpu powerMangement */ + if (gpuProfiler) + { + powerManagement = 0; + } +#endif + + if (showArgs) + { + gckOS_DumpParam(); + } + + if (logFileSize != 0) + { + gckDEBUGFS_Initialize(); + } + + /* Create the GAL device. */ + status = gckGALDEVICE_Construct( +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + irqLine3D0, + registerMemBase3D0, registerMemSize3D0, + irqLine3D1, + registerMemBase3D1, registerMemSize3D1, +#else + irqLine, + registerMemBase, registerMemSize, +#endif + irqLine2D, + registerMemBase2D, registerMemSize2D, + irqLineVG, + registerMemBaseVG, registerMemSizeVG, + contiguousBase, contiguousSize, + bankSize, fastClear, compression, baseAddress, physSize, signal, + logFileSize, + powerManagement, + gpuProfiler, + &args, + &device + ); + + if (gcmIS_ERROR(status)) + { + gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to create the GAL device: status=%d\n", + __FUNCTION__, __LINE__, status); + + goto OnError; + } + + /* Start the GAL device. */ + gcmkONERROR(gckGALDEVICE_Start(device)); + + if ((physSize != 0) + && (device->kernels[gcvCORE_MAJOR] != gcvNULL) + && (device->kernels[gcvCORE_MAJOR]->hardware->mmuVersion != 0)) + { + /* Reset the base address */ + device->baseAddress = 0; + } + + /* Register the character device. */ + ret = register_chrdev(major, DEVICE_NAME, &driver_fops); + + if (ret < 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not allocate major number for mmap.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + if (major == 0) + { + major = ret; + } + + /* Create the device class. */ + device_class = class_create(THIS_MODULE, "graphics_class"); + + if (IS_ERR(device_class)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to create the class.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + device_create(device_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); + + galDevice = device; + gpuClass = device_class; + +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_DRIVER, + "%s(%d): irqLine3D0=%d, contiguousSize=%lu, memBase3D0=0x%lX\n", + __FUNCTION__, __LINE__, + irqLine3D0, contiguousSize, registerMemBase3D0 + ); +#else + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_DRIVER, + "%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n", + __FUNCTION__, __LINE__, + irqLine, contiguousSize, registerMemBase + ); +#endif + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + /* Roll back. */ + if (device_class != gcvNULL) + { + device_destroy(device_class, MKDEV(major, 0)); + class_destroy(device_class); + } + + if (device != gcvNULL) + { + gcmkVERIFY_OK(gckGALDEVICE_Stop(device)); + gcmkVERIFY_OK(gckGALDEVICE_Destroy(device)); + } + + gcmkFOOTER(); + return result; +} + +#if !USE_PLATFORM_DRIVER +static void __exit drv_exit(void) +#else +static void drv_exit(void) +#endif +{ + gcmkHEADER(); + + gcmkASSERT(gpuClass != gcvNULL); + device_destroy(gpuClass, MKDEV(major, 0)); + class_destroy(gpuClass); + + unregister_chrdev(major, DEVICE_NAME); + + gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice)); + gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice)); + + if(gckDEBUGFS_IsEnabled()) + { + gckDEBUGFS_Terminate(); + } + + gcmkFOOTER_NO(); +} + +#if !USE_PLATFORM_DRIVER + module_init(drv_init); + module_exit(drv_exit); +#else + +static int gpu_probe(struct platform_device *pdev) +{ + int ret = -ENODEV; + gcsMODULE_PARAMETERS moduleParam = { +#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY +#else + .irqLine = irqLine, + .registerMemBase = registerMemBase, + .registerMemSize = registerMemSize, +#endif + .irqLine2D = irqLine2D, + .registerMemBase2D = registerMemBase2D, + .registerMemSize2D = registerMemSize2D, + .irqLineVG = irqLineVG, + .registerMemBaseVG = registerMemBaseVG, + .registerMemSizeVG = registerMemSizeVG, + .contiguousSize = contiguousSize, + .contiguousBase = contiguousBase, + .bankSize = bankSize, + .fastClear = fastClear, + .compression = compression, + .powerManagement = powerManagement, + .gpuProfiler = gpuProfiler, + .signal = signal, + .baseAddress = baseAddress, + .physSize = physSize, + .logFileSize = logFileSize, + .recovery = recovery, + .stuckDump = stuckDump, + .showArgs = showArgs, + .gpu3DMinClock = gpu3DMinClock, + }; + + gcmkHEADER(); + + platform.device = pdev; + + if (platform.ops->getPower) + { + if (gcmIS_ERROR(platform.ops->getPower(&platform))) + { + gcmkFOOTER_NO(); + return ret; + } + } + + if (platform.ops->adjustParam) + { + /* Override default module param. */ + platform.ops->adjustParam(&platform, &moduleParam); + + /* Update module param because drv_init() uses them directly. */ + _UpdateModuleParam(&moduleParam); + } + + ret = drv_init(); + + if (!ret) + { + platform_set_drvdata(pdev, galDevice); + + gcmkFOOTER_NO(); + return ret; + } + + gcmkFOOTER_ARG(KERN_INFO "Failed to register gpu driver: %d\n", ret); + return ret; +} + +static int gpu_remove(struct platform_device *pdev) +{ + gcmkHEADER(); + + drv_exit(); + + if (platform.ops->putPower) + { + platform.ops->putPower(&platform); + } + + gcmkFOOTER_NO(); + return 0; +} + +static int gpu_suspend(struct platform_device *dev, pm_message_t state) +{ + gceSTATUS status; + gckGALDEVICE device; + gctINT i; + + device = platform_get_drvdata(dev); + + if (!device) + { + return -1; + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) + { + /* Store states. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_QueryPowerManagementState(device->kernels[i]->vg->hardware, &device->statesStored[i]); + } + else +#endif + { + status = gckHARDWARE_QueryPowerManagementState(device->kernels[i]->hardware, &device->statesStored[i]); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_OFF); + } + else +#endif + { + status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + + } + } + + return 0; +} + +static int gpu_resume(struct platform_device *dev) +{ + gceSTATUS status; + gckGALDEVICE device; + gctINT i; + gceCHIPPOWERSTATE statesStored; + + device = platform_get_drvdata(dev); + + if (!device) + { + return -1; + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) + { +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_ON); + } + else +#endif + { + status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_ON); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + + /* Convert global state to crossponding internal state. */ + switch(device->statesStored[i]) + { + case gcvPOWER_OFF: + statesStored = gcvPOWER_OFF_BROADCAST; + break; + case gcvPOWER_IDLE: + statesStored = gcvPOWER_IDLE_BROADCAST; + break; + case gcvPOWER_SUSPEND: + statesStored = gcvPOWER_SUSPEND_BROADCAST; + break; + case gcvPOWER_ON: + statesStored = gcvPOWER_ON_AUTO; + break; + default: + statesStored = device->statesStored[i]; + break; + } + + /* Restore states. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, statesStored); + } + else +#endif + { + status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, statesStored); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + } + } + + return 0; +} + +#if defined(CONFIG_PM) +static int gpu_runtime_suspend(struct device *dev) +{ + pm_message_t state={0}; + return gpu_suspend(to_platform_device(dev), state); +} + +static int gpu_runtime_resume(struct device *dev) +{ + return gpu_resume(to_platform_device(dev)); +} + +static const struct dev_pm_ops gpu_pm_ops = { + SET_RUNTIME_PM_OPS(gpu_runtime_suspend, gpu_runtime_resume, NULL) +}; +#endif + +static struct platform_driver gpu_driver = { + .probe = gpu_probe, + .remove = gpu_remove, + + .driver = { + .name = DEVICE_NAME, + .pm = &gpu_pm_ops, + } +}; + +static int __init gpu_init(void) +{ + int ret = 0; + + memset(&platform, 0, sizeof(gcsPLATFORM)); + + gckPLATFORM_QueryOperations(&platform.ops); + + if (platform.ops == gcvNULL) + { + printk(KERN_ERR "galcore: No platform specific operations.\n"); + ret = -ENODEV; + goto out; + } + + if (platform.ops->allocPriv) + { + /* Allocate platform private data. */ + if (gcmIS_ERROR(platform.ops->allocPriv(&platform))) + { + ret = -ENOMEM; + goto out; + } + } + + if (platform.ops->needAddDevice + && platform.ops->needAddDevice(&platform)) + { + /* Allocate device */ + platform.device = platform_device_alloc(DEVICE_NAME, -1); + if (!platform.device) + { + printk(KERN_ERR "galcore: platform_device_alloc failed.\n"); + ret = -ENOMEM; + goto out; + } + + /* Add device */ + ret = platform_device_add(platform.device); + if (ret) + { + printk(KERN_ERR "galcore: platform_device_add failed.\n"); + goto put_dev; + } + } + + platform.driver = &gpu_driver; + + if (platform.ops->adjustDriver) + { + /* Override default platform_driver struct. */ + platform.ops->adjustDriver(&platform); + } + + ret = platform_driver_register(&gpu_driver); + if (!ret) + { + goto out; + } + + platform_device_del(platform.device); +put_dev: + platform_device_put(platform.device); + +out: + return ret; +} + +static void __exit gpu_exit(void) +{ + platform_driver_unregister(&gpu_driver); + + if (platform.ops->needAddDevice + && platform.ops->needAddDevice(&platform)) + { + platform_device_unregister(platform.device); + } + + if (platform.priv) + { + /* Free platform private data. */ + platform.ops->freePriv(&platform); + } +} + +module_init(gpu_init); +module_exit(gpu_exit); + +#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_security.c b/drivers/gpu/galcore/gc_hal_kernel_security.c new file mode 100644 index 00000000000000..54e5ce39f44ce3 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_security.c @@ -0,0 +1,239 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + + + + +#define _GC_OBJ_ZONE gcvZONE_KERNEL + +#if gcdSECURITY + +/* +** Open a security service channel. +*/ +gceSTATUS +gckKERNEL_SecurityOpen( + IN gckKERNEL Kernel, + IN gctUINT32 GPU, + OUT gctUINT32 *Channel + ) +{ + gceSTATUS status; + + gcmkONERROR(gckOS_OpenSecurityChannel(Kernel->os, Kernel->core, Channel)); + gcmkONERROR(gckOS_InitSecurityChannel(*Channel)); + + return gcvSTATUS_OK; + +OnError: + return status; +} + +/* +** Close a security service channel +*/ +gceSTATUS +gckKERNEL_SecurityClose( + IN gctUINT32 Channel + ) +{ + return gcvSTATUS_OK; +} + +/* +** Security service interface. +*/ +gceSTATUS +gckKERNEL_SecurityCallService( + IN gctUINT32 Channel, + IN OUT gcsTA_INTERFACE * Interface +) +{ + gceSTATUS status; + gcmkHEADER(); + + gcmkVERIFY_ARGUMENT(Interface != gcvNULL); + + gckOS_CallSecurityService(Channel, Interface); + + status = Interface->result; + + gcmkONERROR(status); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_SecurityStartCommand( + IN gckKERNEL Kernel + ) +{ + gceSTATUS status; + gcsTA_INTERFACE iface; + + gcmkHEADER(); + + iface.command = KERNEL_START_COMMAND; + iface.u.StartCommand.gpu = Kernel->core; + + gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_SecurityAllocateSecurityMemory( + IN gckKERNEL Kernel, + IN gctUINT32 Bytes, + OUT gctUINT32 * Handle + ) +{ + gceSTATUS status; + gcsTA_INTERFACE iface; + + gcmkHEADER(); + + iface.command = KERNEL_ALLOCATE_SECRUE_MEMORY; + iface.u.AllocateSecurityMemory.bytes = Bytes; + + gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); + + *Handle = iface.u.AllocateSecurityMemory.memory_handle; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_SecurityExecute( + IN gckKERNEL Kernel, + IN gctPOINTER Buffer, + IN gctUINT32 Bytes + ) +{ + gceSTATUS status; + gcsTA_INTERFACE iface; + + gcmkHEADER(); + + iface.command = KERNEL_EXECUTE; + iface.u.Execute.command_buffer = (gctUINT32 *)Buffer; + iface.u.Execute.gpu = Kernel->core; + iface.u.Execute.command_buffer_length = Bytes; + +#if defined(LINUX) + gcmkONERROR(gckOS_GetPhysicalAddress(Kernel->os, Buffer, + (gctUINT32 *)&iface.u.Execute.command_buffer)); +#endif + + gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); + + /* Update queue tail pointer. */ + gcmkONERROR(gckHARDWARE_UpdateQueueTail( + Kernel->hardware, 0, 0 + )); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_SecurityMapMemory( + IN gckKERNEL Kernel, + IN gctUINT32 *PhysicalArray, + IN gctUINT32 PageCount, + OUT gctUINT32 * GPUAddress + ) +{ + gceSTATUS status; + gcsTA_INTERFACE iface; + + gcmkHEADER(); + + iface.command = KERNEL_MAP_MEMORY; + +#if defined(LINUX) + gcmkONERROR(gckOS_GetPhysicalAddress(Kernel->os, PhysicalArray, + (gctUINT32 *)&iface.u.MapMemory.physicals)); +#endif + + iface.u.MapMemory.pageCount = PageCount; + + gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); + + *GPUAddress = iface.u.MapMemory.gpuAddress; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckKERNEL_SecurityUnmapMemory( + IN gckKERNEL Kernel, + IN gctUINT32 GPUAddress, + IN gctUINT32 PageCount + ) +{ + gceSTATUS status; + gcsTA_INTERFACE iface; + + gcmkHEADER(); + + iface.command = KERNEL_UNMAP_MEMORY; + + iface.u.UnmapMemory.gpuAddress = GPUAddress; + iface.u.UnmapMemory.pageCount = PageCount; + + gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_security_channel.c b/drivers/gpu/galcore/gc_hal_kernel_security_channel.c new file mode 100644 index 00000000000000..e745a6f7c199ca --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_security_channel.c @@ -0,0 +1,385 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" +#include + +#include "tee_client_api.h" + +#define _GC_OBJ_ZONE gcvZONE_OS + +#define GPU3D_UUID { 0xcc9f80ea, 0xa836, 0x11e3, { 0x9b, 0x07, 0x78, 0x2b, 0xcb, 0x5c, 0xf3, 0xe3 } } + +static const TEEC_UUID gpu3d_uuid = GPU3D_UUID; +TEEC_Context teecContext; + +typedef struct _gcsSecurityChannel { + gckOS os; + TEEC_Session session; + int * virtual; + TEEC_SharedMemory inputBuffer; + gctUINT32 bytes; + gctPOINTER mutex; +} gcsSecurityChannel; + +TEEC_SharedMemory * +gpu3d_allocate_secure_mem( + gckOS Os, + unsigned int size + ) +{ + TEEC_Result result; + TEEC_Context *context = &teecContext; + TEEC_SharedMemory *shm = NULL; + void *handle = NULL; + unsigned int phyAddr = 0xFFFFFFFF; + gceSTATUS status; + gctSIZE_T bytes = size; + + shm = kmalloc(sizeof(TEEC_SharedMemory), GFP_KERNEL); + + if (NULL == shm) + { + return NULL; + } + + memset(shm, 0, sizeof(TEEC_SharedMemory)); + + status = gckOS_AllocatePagedMemoryEx( + Os, + gcvALLOC_FLAG_SECURITY, + bytes, + gcvNULL, + (gctPHYS_ADDR *)&handle); + + if (gcmIS_ERROR(status)) + { + kfree(shm); + return NULL; + } + + status = gckOS_PhysicalToPhysicalAddress( + Os, + handle, + &phyAddr); + + if (gcmIS_ERROR(status)) + { + kfree(shm); + return NULL; + } + + /* record the handle into shm->user_data */ + shm->userdata = handle; + + /* [b] Bulk input buffer. */ + shm->size = size; + shm->flags = TEEC_MEM_INPUT; + + /* Use TEE Client API to register the underlying memory buffer. */ + shm->phyAddr = (void *)phyAddr; + + result = TEEC_RegisterSharedMemory( + context, + shm); + + if (result != TEEC_SUCCESS) + { + gckOS_FreePagedMemory(Os, (gctPHYS_ADDR)handle, shm->size); + kfree(shm); + return NULL; + } + + return shm; +} + +void gpu3d_release_secure_mem( + gckOS Os, + void *shm_handle + ) +{ + TEEC_SharedMemory *shm = shm_handle; + void * handle; + + if (!shm) + { + return; + } + + handle = shm->userdata; + + TEEC_ReleaseSharedMemory(shm); + gckOS_FreePagedMemory(Os, (gctPHYS_ADDR)handle, shm->size); + + kfree(shm); + + return; +} + +static TEEC_Result gpu3d_session_callback( + TEEC_Session* session, + uint32_t commandID, + TEEC_Operation* operation, + void* userdata + ) +{ + gcsSecurityChannel *channel = userdata; + + if (channel == gcvNULL) + { + return TEEC_ERROR_BAD_PARAMETERS; + } + + switch(commandID) + { + case gcvTA_CALLBACK_ALLOC_SECURE_MEM: + { + uint32_t size = operation->params[0].value.a; + TEEC_SharedMemory *shm = NULL; + + shm = gpu3d_allocate_secure_mem(channel->os, size); + + /* use the value to save the pointer in client side */ + operation->params[0].value.a = (uint32_t)shm; + operation->params[0].value.b = (uint32_t)shm->phyAddr; + + break; + } + case gcvTA_CALLBACK_FREE_SECURE_MEM: + { + TEEC_SharedMemory *shm = (TEEC_SharedMemory *)operation->params[0].value.a; + + gpu3d_release_secure_mem(channel->os, shm); + break; + } + default: + break; + } + + return TEEC_SUCCESS; +} + +gceSTATUS +gckOS_OpenSecurityChannel( + IN gckOS Os, + IN gceCORE GPU, + OUT gctUINT32 *Channel + ) +{ + gceSTATUS status; + TEEC_Result result; + static bool initialized = gcvFALSE; + gcsSecurityChannel *channel = gcvNULL; + + TEEC_Operation operation = {0}; + + /* Connect to TEE. */ + if (initialized == gcvFALSE) + { + result = TEEC_InitializeContext(NULL, &teecContext); + + if (result != TEEC_SUCCESS) { + gcmkONERROR(gcvSTATUS_CHIP_NOT_READY); + } + + initialized = gcvTRUE; + } + + /* Construct channel. */ + gcmkONERROR( + gckOS_Allocate(Os, gcmSIZEOF(*channel), (gctPOINTER *)&channel)); + + gckOS_ZeroMemory(channel, gcmSIZEOF(gcsSecurityChannel)); + + channel->os = Os; + + gcmkONERROR(gckOS_CreateMutex(Os, &channel->mutex)); + + /* Allocate shared memory for passing gcTA_INTERFACE. */ + channel->bytes = gcmSIZEOF(gcsTA_INTERFACE); + channel->virtual = kmalloc(channel->bytes, GFP_KERNEL | __GFP_NOWARN); + + if (!channel->virtual) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + channel->inputBuffer.size = channel->bytes; + channel->inputBuffer.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; + channel->inputBuffer.phyAddr = (void *)virt_to_phys(channel->virtual); + + result = TEEC_RegisterSharedMemory(&teecContext, &channel->inputBuffer); + + if (result != TEEC_SUCCESS) + { + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + operation.paramTypes = TEEC_PARAM_TYPES( + TEEC_VALUE_INPUT, + TEEC_NONE, + TEEC_NONE, + TEEC_NONE); + + operation.params[0].value.a = GPU; + + /* Open session with TEE application. */ + result = TEEC_OpenSession( + &teecContext, + &channel->session, + &gpu3d_uuid, + TEEC_LOGIN_USER, + NULL, + &operation, + NULL); + + /* Prepare callback. */ + TEEC_RegisterCallback(&channel->session, gpu3d_session_callback, channel); + + *Channel = (gctUINT32)channel; + + return gcvSTATUS_OK; + +OnError: + if (channel) + { + if (channel->virtual) + { + } + + if (channel->mutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, channel->mutex)); + } + + gcmkVERIFY_OK(gckOS_Free(Os, channel)); + } + + return status; +} + +gceSTATUS +gckOS_CloseSecurityChannel( + IN gctUINT32 Channel + ) +{ + /* TODO . */ + return gcvSTATUS_OK; +} + +gceSTATUS +gckOS_CallSecurityService( + IN gctUINT32 Channel, + IN gcsTA_INTERFACE *Interface + ) +{ + gceSTATUS status; + TEEC_Result result; + gcsSecurityChannel *channel = (gcsSecurityChannel *)Channel; + TEEC_Operation operation = {0}; + + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(Channel != 0); + + gckOS_AcquireMutex(channel->os, channel->mutex, gcvINFINITE); + + gckOS_MemCopy(channel->virtual, Interface, channel->bytes); + + operation.paramTypes = TEEC_PARAM_TYPES( + TEEC_MEMREF_PARTIAL_INPUT, + TEEC_NONE, + TEEC_NONE, + TEEC_NONE); + + /* Note: we use the updated size in the MemRef output by the encryption. */ + operation.params[0].memref.parent = &channel->inputBuffer; + operation.params[0].memref.offset = 0; + operation.params[0].memref.size = sizeof(gcsTA_INTERFACE); + operation.started = true; + + /* Start the commit command within the TEE application. */ + result = TEEC_InvokeCommand( + &channel->session, + gcvTA_COMMAND_DISPATCH, + &operation, + NULL); + + gckOS_MemCopy(Interface, channel->virtual, channel->bytes); + + gckOS_ReleaseMutex(channel->os, channel->mutex); + + if (result != TEEC_SUCCESS) + { + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckOS_InitSecurityChannel( + IN gctUINT32 Channel + ) +{ + gceSTATUS status; + TEEC_Result result; + gcsSecurityChannel *channel = (gcsSecurityChannel *)Channel; + TEEC_Operation operation = {0}; + + gcmkHEADER(); + gcmkVERIFY_ARGUMENT(Channel != 0); + + operation.paramTypes = TEEC_PARAM_TYPES( + TEEC_MEMREF_PARTIAL_INPUT, + TEEC_NONE, + TEEC_NONE, + TEEC_NONE); + + /* Note: we use the updated size in the MemRef output by the encryption. */ + operation.params[0].memref.parent = &channel->inputBuffer; + operation.params[0].memref.offset = 0; + operation.params[0].memref.size = gcmSIZEOF(gcsTA_INTERFACE); + operation.started = true; + + /* Start the commit command within the TEE application. */ + result = TEEC_InvokeCommand( + &channel->session, + gcvTA_COMMAND_INIT, + &operation, + NULL); + + if (result != TEEC_SUCCESS) + { + gcmkONERROR(gcvSTATUS_GENERIC_IO); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} diff --git a/drivers/gpu/galcore/gc_hal_kernel_sync.c b/drivers/gpu/galcore/gc_hal_kernel_sync.c new file mode 100644 index 00000000000000..0e5a816974231b --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_sync.c @@ -0,0 +1,177 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include +#include + +#if gcdANDROID_NATIVE_FENCE_SYNC + +#include +#include +#include +#include +#include +#include +#include + +#include "gc_hal_kernel_sync.h" + +static struct sync_pt * +viv_sync_pt_dup( + struct sync_pt * sync_pt + ) +{ + gceSTATUS status; + struct viv_sync_pt *pt; + struct viv_sync_pt *src; + struct viv_sync_timeline *obj; + + src = (struct viv_sync_pt *) sync_pt; + obj = (struct viv_sync_timeline *) sync_pt->parent; + + /* Create the new sync_pt. */ + pt = (struct viv_sync_pt *) + sync_pt_create(&obj->obj, sizeof(struct viv_sync_pt)); + + pt->stamp = src->stamp; + pt->sync = src->sync; + + /* Reference sync point. */ + status = gckOS_ReferenceSyncPoint(obj->os, pt->sync); + + if (gcmIS_ERROR(status)) + { + sync_pt_free((struct sync_pt *)pt); + return NULL; + } + + return (struct sync_pt *)pt; +} + +static int +viv_sync_pt_has_signaled( + struct sync_pt * sync_pt + ) +{ + gceSTATUS status; + gctBOOL state; + struct viv_sync_pt * pt; + struct viv_sync_timeline * obj; + + pt = (struct viv_sync_pt *)sync_pt; + obj = (struct viv_sync_timeline *)sync_pt->parent; + + status = gckOS_QuerySyncPoint(obj->os, pt->sync, &state); + + if (gcmIS_ERROR(status)) + { + /* Error. */ + return -1; + } + + return state; +} + +static int +viv_sync_pt_compare( + struct sync_pt * a, + struct sync_pt * b + ) +{ + int ret; + struct viv_sync_pt * pt1 = (struct viv_sync_pt *) a; + struct viv_sync_pt * pt2 = (struct viv_sync_pt *) b; + + ret = (pt1->stamp < pt2->stamp) ? -1 + : (pt1->stamp == pt2->stamp) ? 0 + : 1; + + return ret; +} + +static void +viv_sync_pt_free( + struct sync_pt * sync_pt + ) +{ + struct viv_sync_pt * pt; + struct viv_sync_timeline * obj; + + pt = (struct viv_sync_pt *) sync_pt; + obj = (struct viv_sync_timeline *) sync_pt->parent; + + gckOS_DestroySyncPoint(obj->os, pt->sync); +} + +static struct sync_timeline_ops viv_timeline_ops = +{ + .driver_name = "viv_sync", + .dup = viv_sync_pt_dup, + .has_signaled = viv_sync_pt_has_signaled, + .compare = viv_sync_pt_compare, + .free_pt = viv_sync_pt_free, +}; + +struct viv_sync_timeline * +viv_sync_timeline_create( + const char * name, + gckOS os + ) +{ + struct viv_sync_timeline * obj; + + obj = (struct viv_sync_timeline *) + sync_timeline_create(&viv_timeline_ops, sizeof(struct viv_sync_timeline), name); + + obj->os = os; + obj->stamp = 0; + + return obj; +} + +struct sync_pt * +viv_sync_pt_create( + struct viv_sync_timeline * obj, + gctSYNC_POINT SyncPoint + ) +{ + gceSTATUS status; + struct viv_sync_pt * pt; + + pt = (struct viv_sync_pt *) + sync_pt_create(&obj->obj, sizeof(struct viv_sync_pt)); + + pt->stamp = obj->stamp++; + pt->sync = SyncPoint; + + /* Dup signal. */ + status = gckOS_ReferenceSyncPoint(obj->os, SyncPoint); + + if (gcmIS_ERROR(status)) + { + sync_pt_free((struct sync_pt *)pt); + return NULL; + } + + return (struct sync_pt *) pt; +} + +#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_sync.h b/drivers/gpu/galcore/gc_hal_kernel_sync.h new file mode 100644 index 00000000000000..8a69921801ae6a --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_sync.h @@ -0,0 +1,72 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_sync_h_ +#define __gc_hal_kernel_sync_h_ + +#include + +/* sync.h is in drivers/staging/android/ for now. */ +#include + +#include +#include + +struct viv_sync_timeline +{ + /* Parent object. */ + struct sync_timeline obj; + + /* Timestamp when sync_pt is created. */ + gctUINT stamp; + + /* Pointer to os struct. */ + gckOS os; +}; + + +struct viv_sync_pt +{ + /* Parent object. */ + struct sync_pt pt; + + /* Reference sync point*/ + gctSYNC_POINT sync; + + /* Timestamp when sync_pt is created. */ + gctUINT stamp; +}; + +/* Create viv_sync_timeline object. */ +struct viv_sync_timeline * +viv_sync_timeline_create( + const char * Name, + gckOS Os + ); + +/* Create viv_sync_pt object. */ +struct sync_pt * +viv_sync_pt_create( + struct viv_sync_timeline * Obj, + gctSYNC_POINT SyncPoint + ); + +#endif /* __gc_hal_kernel_sync_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_vg.c b/drivers/gpu/galcore/gc_hal_kernel_vg.c new file mode 100644 index 00000000000000..eac565cd65f928 --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_vg.c @@ -0,0 +1,833 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#if gcdENABLE_VG + +#define _GC_OBJ_ZONE gcvZONE_VG + +/******************************************************************************\ +******************************* gckKERNEL API Code ****************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckKERNEL_Construct +** +** Construct a new gckKERNEL object. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** IN gctPOINTER Context +** Pointer to a driver defined context. +** +** OUTPUT: +** +** gckKERNEL * Kernel +** Pointer to a variable that will hold the pointer to the gckKERNEL +** object. +*/ +gceSTATUS gckVGKERNEL_Construct( + IN gckOS Os, + IN gctPOINTER Context, + IN gckKERNEL inKernel, + OUT gckVGKERNEL * Kernel + ) +{ + gceSTATUS status; + gckVGKERNEL kernel = gcvNULL; + + gcmkHEADER_ARG("Os=0x%x Context=0x%x", Os, Context); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Kernel != gcvNULL); + + do + { + /* Allocate the gckKERNEL object. */ + gcmkERR_BREAK(gckOS_Allocate( + Os, + sizeof(struct _gckVGKERNEL), + (gctPOINTER *) &kernel + )); + + /* Initialize the gckKERNEL object. */ + kernel->object.type = gcvOBJ_KERNEL; + kernel->os = Os; + kernel->context = Context; + kernel->hardware = gcvNULL; + kernel->interrupt = gcvNULL; + kernel->command = gcvNULL; + kernel->mmu = gcvNULL; + kernel->kernel = inKernel; + + /* Construct the gckVGHARDWARE object. */ + gcmkERR_BREAK(gckVGHARDWARE_Construct( + Os, &kernel->hardware + )); + + /* Set pointer to gckKERNEL object in gckVGHARDWARE object. */ + kernel->hardware->kernel = kernel; + + /* Construct the gckVGINTERRUPT object. */ + gcmkERR_BREAK(gckVGINTERRUPT_Construct( + kernel, &kernel->interrupt + )); + + /* Construct the gckVGCOMMAND object. */ + gcmkERR_BREAK(gckVGCOMMAND_Construct( + kernel, gcmKB2BYTES(8), gcmKB2BYTES(2), &kernel->command + )); + + /* Construct the gckVGMMU object. */ + gcmkERR_BREAK(gckVGMMU_Construct( + kernel, gcmKB2BYTES(32), &kernel->mmu + )); + + /* Return pointer to the gckKERNEL object. */ + *Kernel = kernel; + + gcmkFOOTER_ARG("*Kernel=0x%x", *Kernel); + /* Success. */ + return gcvSTATUS_OK; + } + while (gcvFALSE); + + /* Roll back. */ + if (kernel != gcvNULL) + { + if (kernel->mmu != gcvNULL) + { + gcmkVERIFY_OK(gckVGMMU_Destroy(kernel->mmu)); + } + + if (kernel->command != gcvNULL) + { + gcmkVERIFY_OK(gckVGCOMMAND_Destroy(kernel->command)); + } + + if (kernel->interrupt != gcvNULL) + { + gcmkVERIFY_OK(gckVGINTERRUPT_Destroy(kernel->interrupt)); + } + + if (kernel->hardware != gcvNULL) + { + gcmkVERIFY_OK(gckVGHARDWARE_Destroy(kernel->hardware)); + } + + gcmkVERIFY_OK(gckOS_Free(Os, kernel)); + } + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +/******************************************************************************* +** +** gckKERNEL_Destroy +** +** Destroy an gckKERNEL object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS gckVGKERNEL_Destroy( + IN gckVGKERNEL Kernel + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Kernel=0x%x", Kernel); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + do + { + /* Destroy the gckVGMMU object. */ + if (Kernel->mmu != gcvNULL) + { + gcmkERR_BREAK(gckVGMMU_Destroy(Kernel->mmu)); + Kernel->mmu = gcvNULL; + } + + /* Destroy the gckVGCOMMAND object. */ + if (Kernel->command != gcvNULL) + { + gcmkERR_BREAK(gckVGCOMMAND_Destroy(Kernel->command)); + Kernel->command = gcvNULL; + } + + /* Destroy the gckVGINTERRUPT object. */ + if (Kernel->interrupt != gcvNULL) + { + gcmkERR_BREAK(gckVGINTERRUPT_Destroy(Kernel->interrupt)); + Kernel->interrupt = gcvNULL; + } + + /* Destroy the gckVGHARDWARE object. */ + if (Kernel->hardware != gcvNULL) + { + gcmkERR_BREAK(gckVGHARDWARE_Destroy(Kernel->hardware)); + Kernel->hardware = gcvNULL; + } + + /* Mark the gckKERNEL object as unknown. */ + Kernel->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckKERNEL object. */ + gcmkERR_BREAK(gckOS_Free(Kernel->os, Kernel)); + } + while (gcvFALSE); + + gcmkFOOTER(); + + /* Return status. */ + return status; +} + +/******************************************************************************* +** +** gckKERNEL_AllocateLinearMemory +** +** Function walks all required memory pools and allocates the requested +** amount of video memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcePOOL * Pool +** Pointer the desired memory pool. +** +** gctSIZE_T Bytes +** Number of bytes to allocate. +** +** gctSIZE_T Alignment +** Required buffer alignment. +** +** gceSURF_TYPE Type +** Surface type. +** +** OUTPUT: +** +** gcePOOL * Pool +** Pointer to the actual pool where the memory was allocated. +** +** gcuVIDMEM_NODE_PTR * Node +** Allocated node. +*/ +gceSTATUS +gckVGKERNEL_AllocateLinearMemory( + IN gckKERNEL Kernel, + IN OUT gcePOOL * Pool, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + OUT gcuVIDMEM_NODE_PTR * Node + ) +{ + gcePOOL pool; + gceSTATUS status; + gckVIDMEM videoMemory; + + /* Get initial pool. */ + switch (pool = *Pool) + { + case gcvPOOL_DEFAULT: + case gcvPOOL_LOCAL: + pool = gcvPOOL_LOCAL_INTERNAL; + break; + + case gcvPOOL_UNIFIED: + pool = gcvPOOL_SYSTEM; + break; + + default: + break; + } + + do + { + /* Verify the number of bytes to allocate. */ + if (Bytes == 0) + { + status = gcvSTATUS_INVALID_ARGUMENT; + break; + } + + if (pool == gcvPOOL_VIRTUAL) + { + /* Create a gcuVIDMEM_NODE for virtual memory. */ + gcmkERR_BREAK(gckVIDMEM_ConstructVirtual(Kernel, gcvFALSE, Bytes, Node)); + + /* Success. */ + break; + } + + else + { + /* Get pointer to gckVIDMEM object for pool. */ + status = gckKERNEL_GetVideoMemoryPool(Kernel, pool, &videoMemory); + + if (status == gcvSTATUS_OK) + { + /* Allocate memory. */ + status = gckVIDMEM_AllocateLinear(Kernel, + videoMemory, + Bytes, + Alignment, + Type, + (*Pool == gcvPOOL_SYSTEM), + Node); + + if (status == gcvSTATUS_OK) + { + /* Memory allocated. */ + break; + } + } + } + + if (pool == gcvPOOL_LOCAL_INTERNAL) + { + /* Advance to external memory. */ + pool = gcvPOOL_LOCAL_EXTERNAL; + } + else if (pool == gcvPOOL_LOCAL_EXTERNAL) + { + /* Advance to contiguous system memory. */ + pool = gcvPOOL_SYSTEM; + } + else if (pool == gcvPOOL_SYSTEM) + { + /* Advance to virtual memory. */ + pool = gcvPOOL_VIRTUAL; + } + else + { + /* Out of pools. */ + break; + } + } + /* Loop only for multiple selection pools. */ + while ((*Pool == gcvPOOL_DEFAULT) + || (*Pool == gcvPOOL_LOCAL) + || (*Pool == gcvPOOL_UNIFIED) + ); + + if (gcmIS_SUCCESS(status)) + { + /* Return pool used for allocation. */ + *Pool = pool; + } + + /* Return status. */ + return status; +} + +/******************************************************************************* +** +** gckKERNEL_Dispatch +** +** Dispatch a command received from the user HAL layer. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that defines the command to +** be dispatched. +** +** OUTPUT: +** +** gcsHAL_INTERFACE * Interface +** Pointer to a gcsHAL_INTERFACE structure that receives any data to be +** returned. +*/ +gceSTATUS gckVGKERNEL_Dispatch( + IN gckKERNEL Kernel, + IN gctBOOL FromUser, + IN OUT gcsHAL_INTERFACE * Interface + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE * kernelInterface = Interface; + gctUINT32 processID; + gckKERNEL kernel = Kernel; + gctPOINTER info = gcvNULL; + gctPHYS_ADDR physical = gcvNULL; + gctPOINTER logical = gcvNULL; + gctSIZE_T bytes = 0; + + gcmkHEADER_ARG("Kernel=0x%x Interface=0x%x ", Kernel, Interface); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Interface != gcvNULL); + + gcmkONERROR(gckOS_GetProcessID(&processID)); + + /* Dispatch on command. */ + switch (Interface->command) + { + case gcvHAL_QUERY_VIDEO_MEMORY: + /* Query video memory size. */ + gcmkERR_BREAK(gckKERNEL_QueryVideoMemory( + Kernel, kernelInterface + )); + break; + + case gcvHAL_QUERY_CHIP_IDENTITY: + /* Query chip identity. */ + gcmkERR_BREAK(gckVGHARDWARE_QueryChipIdentity( + Kernel->vg->hardware, + &kernelInterface->u.QueryChipIdentity.chipModel, + &kernelInterface->u.QueryChipIdentity.chipRevision, + &kernelInterface->u.QueryChipIdentity.chipFeatures, + &kernelInterface->u.QueryChipIdentity.chipMinorFeatures, + &kernelInterface->u.QueryChipIdentity.chipMinorFeatures2 + )); + break; + + case gcvHAL_QUERY_COMMAND_BUFFER: + /* Query command buffer information. */ + gcmkERR_BREAK(gckKERNEL_QueryCommandBuffer( + Kernel, + &kernelInterface->u.QueryCommandBuffer.information + )); + break; + case gcvHAL_ALLOCATE_NON_PAGED_MEMORY: + bytes = (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes; + /* Allocate non-paged memory. */ + gcmkERR_BREAK(gckOS_AllocateNonPagedMemory( + Kernel->os, + gcvTRUE, + &bytes, + &physical, + &logical + )); + + kernelInterface->u.AllocateNonPagedMemory.bytes = bytes; + kernelInterface->u.AllocateNonPagedMemory.logical = gcmPTR_TO_UINT64(logical); + kernelInterface->u.AllocateNonPagedMemory.physical = gcmPTR_TO_NAME(physical); + break; + + case gcvHAL_FREE_NON_PAGED_MEMORY: + physical = gcmNAME_TO_PTR(kernelInterface->u.AllocateNonPagedMemory.physical); + + /* Unmap user logical out of physical memory first. */ + gcmkERR_BREAK(gckOS_UnmapUserLogical( + Kernel->os, + physical, + (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes, + gcmUINT64_TO_PTR(kernelInterface->u.AllocateNonPagedMemory.logical) + )); + + /* Free non-paged memory. */ + gcmkERR_BREAK(gckOS_FreeNonPagedMemory( + Kernel->os, + (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes, + physical, + gcmUINT64_TO_PTR(kernelInterface->u.AllocateNonPagedMemory.logical) + )); + + gcmRELEASE_NAME(kernelInterface->u.AllocateNonPagedMemory.physical); + break; + + case gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY: + bytes = (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes; + /* Allocate contiguous memory. */ + gcmkERR_BREAK(gckOS_AllocateContiguous( + Kernel->os, + gcvTRUE, + &bytes, + &physical, + &logical + )); + + kernelInterface->u.AllocateNonPagedMemory.bytes = bytes; + kernelInterface->u.AllocateNonPagedMemory.logical = gcmPTR_TO_UINT64(logical); + kernelInterface->u.AllocateNonPagedMemory.physical = gcmPTR_TO_NAME(physical); + break; + + case gcvHAL_FREE_CONTIGUOUS_MEMORY: + physical = gcmNAME_TO_PTR(kernelInterface->u.AllocateNonPagedMemory.physical); + /* Unmap user logical out of physical memory first. */ + gcmkERR_BREAK(gckOS_UnmapUserLogical( + Kernel->os, + physical, + (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes, + gcmUINT64_TO_PTR(kernelInterface->u.AllocateNonPagedMemory.logical) + )); + + /* Free contiguous memory. */ + gcmkERR_BREAK(gckOS_FreeContiguous( + Kernel->os, + physical, + gcmUINT64_TO_PTR(kernelInterface->u.AllocateNonPagedMemory.logical), + (gctSIZE_T) kernelInterface->u.AllocateNonPagedMemory.bytes + )); + + gcmRELEASE_NAME(kernelInterface->u.AllocateNonPagedMemory.physical); + break; + + case gcvHAL_ALLOCATE_VIDEO_MEMORY: + gcmkERR_BREAK(gcvSTATUS_NOT_SUPPORTED); + break; + + case gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY: + /* Allocate memory. */ + gcmkERR_BREAK(gckKERNEL_AllocateLinearMemory( + Kernel, processID, + &kernelInterface->u.AllocateLinearVideoMemory.pool, + kernelInterface->u.AllocateLinearVideoMemory.bytes, + kernelInterface->u.AllocateLinearVideoMemory.alignment, + kernelInterface->u.AllocateLinearVideoMemory.type, + kernelInterface->u.AllocateLinearVideoMemory.flag, + &kernelInterface->u.AllocateLinearVideoMemory.node + )); + + break; + + case gcvHAL_RELEASE_VIDEO_MEMORY: + /* Free video memory. */ + gcmkERR_BREAK(gckKERNEL_ReleaseVideoMemory( + Kernel, processID, + (gctUINT32)kernelInterface->u.ReleaseVideoMemory.node + )); + + break; + + case gcvHAL_MAP_MEMORY: + /* Map memory. */ + gcmkERR_BREAK(gckKERNEL_MapMemory( + Kernel, + gcmINT2PTR(kernelInterface->u.MapMemory.physical), + (gctSIZE_T) kernelInterface->u.MapMemory.bytes, + &logical + )); + kernelInterface->u.MapMemory.logical = gcmPTR_TO_UINT64(logical); + break; + + case gcvHAL_UNMAP_MEMORY: + /* Unmap memory. */ + gcmkERR_BREAK(gckKERNEL_UnmapMemory( + Kernel, + gcmINT2PTR(kernelInterface->u.MapMemory.physical), + (gctSIZE_T) kernelInterface->u.MapMemory.bytes, + gcmUINT64_TO_PTR(kernelInterface->u.MapMemory.logical) + )); + break; + + case gcvHAL_MAP_USER_MEMORY: + /* Map user memory to DMA. */ + gcmkERR_BREAK(gckOS_MapUserMemory( + Kernel->os, + gcvCORE_VG, + gcmUINT64_TO_PTR(kernelInterface->u.MapUserMemory.memory), + kernelInterface->u.MapUserMemory.physical, + (gctSIZE_T) kernelInterface->u.MapUserMemory.size, + &info, + &kernelInterface->u.MapUserMemory.address + )); + + kernelInterface->u.MapUserMemory.info = gcmPTR_TO_NAME(info); + + /* Clear temp storage. */ + info = gcvNULL; + break; + + case gcvHAL_UNMAP_USER_MEMORY: + /* Unmap user memory. */ + gcmkERR_BREAK(gckOS_UnmapUserMemory( + Kernel->os, + gcvCORE_VG, + gcmUINT64_TO_PTR(kernelInterface->u.UnmapUserMemory.memory), + (gctSIZE_T) kernelInterface->u.UnmapUserMemory.size, + gcmNAME_TO_PTR(kernelInterface->u.UnmapUserMemory.info), + kernelInterface->u.UnmapUserMemory.address + )); + + gcmRELEASE_NAME(kernelInterface->u.UnmapUserMemory.info); + break; + + case gcvHAL_LOCK_VIDEO_MEMORY: + gcmkONERROR(gckKERNEL_LockVideoMemory(Kernel, gcvCORE_VG, processID, FromUser, Interface)); + break; + + case gcvHAL_UNLOCK_VIDEO_MEMORY: + gcmkONERROR(gckKERNEL_UnlockVideoMemory(Kernel, processID, Interface)); + break; + + case gcvHAL_USER_SIGNAL: +#if !USE_NEW_LINUX_SIGNAL + /* Dispatch depends on the user signal subcommands. */ + switch(Interface->u.UserSignal.command) + { + case gcvUSER_SIGNAL_CREATE: + /* Create a signal used in the user space. */ + gcmkERR_BREAK( + gckOS_CreateUserSignal(Kernel->os, + Interface->u.UserSignal.manualReset, + &Interface->u.UserSignal.id)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id), + gcvNULL, + 0)); + break; + + case gcvUSER_SIGNAL_DESTROY: + gcmkVERIFY_OK(gckKERNEL_RemoveProcessDB( + Kernel, + processID, gcvDB_SIGNAL, + gcmINT2PTR(Interface->u.UserSignal.id))); + + /* Destroy the signal. */ + gcmkERR_BREAK( + gckOS_DestroyUserSignal(Kernel->os, + Interface->u.UserSignal.id)); + + break; + + case gcvUSER_SIGNAL_SIGNAL: + /* Signal the signal. */ + gcmkERR_BREAK( + gckOS_SignalUserSignal(Kernel->os, + Interface->u.UserSignal.id, + Interface->u.UserSignal.state)); + break; + + case gcvUSER_SIGNAL_WAIT: + /* Wait on the signal. */ + status = gckOS_WaitUserSignal(Kernel->os, + Interface->u.UserSignal.id, + Interface->u.UserSignal.wait); + break; + + default: + /* Invalid user signal command. */ + gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT); + } +#endif + break; + + case gcvHAL_COMMIT: + /* Commit a command and context buffer. */ + gcmkERR_BREAK(gckVGCOMMAND_Commit( + Kernel->vg->command, + gcmUINT64_TO_PTR(kernelInterface->u.VGCommit.context), + gcmUINT64_TO_PTR(kernelInterface->u.VGCommit.queue), + kernelInterface->u.VGCommit.entryCount, + gcmUINT64_TO_PTR(kernelInterface->u.VGCommit.taskTable) + )); + break; + case gcvHAL_VERSION: + kernelInterface->u.Version.major = gcvVERSION_MAJOR; + kernelInterface->u.Version.minor = gcvVERSION_MINOR; + kernelInterface->u.Version.patch = gcvVERSION_PATCH; + kernelInterface->u.Version.build = gcvVERSION_BUILD; + status = gcvSTATUS_OK; + break; + + case gcvHAL_GET_BASE_ADDRESS: + /* Get base address. */ + gcmkERR_BREAK( + gckOS_GetBaseAddress(Kernel->os, + &kernelInterface->u.GetBaseAddress.baseAddress)); + break; + case gcvHAL_IMPORT_VIDEO_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_Import(Kernel, + Interface->u.ImportVideoMemory.name, + &Interface->u.ImportVideoMemory.handle)); + gcmkONERROR(gckKERNEL_AddProcessDB(Kernel, + processID, gcvDB_VIDEO_MEMORY, + gcmINT2PTR(Interface->u.ImportVideoMemory.handle), + gcvNULL, + 0)); + break; + + case gcvHAL_NAME_VIDEO_MEMORY: + gcmkONERROR(gckVIDMEM_NODE_Name(Kernel, + Interface->u.NameVideoMemory.handle, + &Interface->u.NameVideoMemory.name)); + break; + + case gcvHAL_DATABASE: + gcmkONERROR(gckKERNEL_QueryDatabase(Kernel, processID, Interface)); + break; + case gcvHAL_SHBUF: + { + gctSHBUF shBuf; + gctPOINTER uData; + gctUINT32 bytes; + + switch (Interface->u.ShBuf.command) + { + case gcvSHBUF_CREATE: + bytes = Interface->u.ShBuf.bytes; + + /* Create. */ + gcmkONERROR(gckKERNEL_CreateShBuffer(Kernel, bytes, &shBuf)); + + Interface->u.ShBuf.id = gcmPTR_TO_UINT64(shBuf); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf, + gcvNULL, + 0)); + break; + + case gcvSHBUF_DESTROY: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + + /* Check db first to avoid illegal destroy in the process. */ + gcmkONERROR( + gckKERNEL_RemoveProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf)); + + gcmkONERROR(gckKERNEL_DestroyShBuffer(Kernel, shBuf)); + break; + + case gcvSHBUF_MAP: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + + /* Map for current process access. */ + gcmkONERROR(gckKERNEL_MapShBuffer(Kernel, shBuf)); + + gcmkVERIFY_OK( + gckKERNEL_AddProcessDB(Kernel, + processID, + gcvDB_SHBUF, + shBuf, + gcvNULL, + 0)); + break; + + case gcvSHBUF_WRITE: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + uData = gcmUINT64_TO_PTR(Interface->u.ShBuf.data); + bytes = Interface->u.ShBuf.bytes; + + /* Write. */ + gcmkONERROR( + gckKERNEL_WriteShBuffer(Kernel, shBuf, uData, bytes)); + break; + + case gcvSHBUF_READ: + shBuf = gcmUINT64_TO_PTR(Interface->u.ShBuf.id); + uData = gcmUINT64_TO_PTR(Interface->u.ShBuf.data); + bytes = Interface->u.ShBuf.bytes; + + /* Read. */ + gcmkONERROR( + gckKERNEL_ReadShBuffer(Kernel, + shBuf, + uData, + bytes, + &bytes)); + + /* Return copied size. */ + Interface->u.ShBuf.bytes = bytes; + break; + + default: + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + break; + } + } + break; + default: + /* Invalid command. */ + status = gcvSTATUS_INVALID_ARGUMENT; + } + +OnError: + /* Save status. */ + kernelInterface->status = status; + + gcmkFOOTER(); + + /* Return the status. */ + return status; +} + +/******************************************************************************* +** +** gckKERNEL_QueryCommandBuffer +** +** Query command buffer attributes. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckVGHARDWARE object. +** +** OUTPUT: +** +** gcsCOMMAND_BUFFER_INFO_PTR Information +** Pointer to the information structure to receive buffer attributes. +*/ +gceSTATUS +gckKERNEL_QueryCommandBuffer( + IN gckKERNEL Kernel, + OUT gcsCOMMAND_BUFFER_INFO_PTR Information + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Kernel=0x%x *Pool=0x%x", + Kernel, Information); + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Get the information. */ + status = gckVGCOMMAND_QueryCommandBuffer(Kernel->vg->command, Information); + + gcmkFOOTER(); + /* Return status. */ + return status; +} + +#endif /* gcdENABLE_VG */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_vg.h b/drivers/gpu/galcore/gc_hal_kernel_vg.h new file mode 100644 index 00000000000000..8bdeb3896f229d --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_vg.h @@ -0,0 +1,85 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_vg_h_ +#define __gc_hal_kernel_vg_h_ + +#include "gc_hal.h" +#include "gc_hal_driver.h" +#include "gc_hal_kernel_hardware.h" + +/******************************************************************************\ +********************************** Structures ********************************** +\******************************************************************************/ + +/* gckKERNEL object. */ +struct _gckVGKERNEL +{ + /* Object. */ + gcsOBJECT object; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Pointer to gckHARDWARE object. */ + gckVGHARDWARE hardware; + + /* Pointer to gckINTERRUPT object. */ + gckVGINTERRUPT interrupt; + + /* Pointer to gckCOMMAND object. */ + gckVGCOMMAND command; + + /* Pointer to context. */ + gctPOINTER context; + + /* Pointer to gckMMU object. */ + gckVGMMU mmu; + + gckKERNEL kernel; +}; + +/* gckMMU object. */ +struct _gckVGMMU +{ + /* The object. */ + gcsOBJECT object; + + /* Pointer to gckOS object. */ + gckOS os; + + /* Pointer to gckHARDWARE object. */ + gckVGHARDWARE hardware; + + /* The page table mutex. */ + gctPOINTER mutex; + + /* Page table information. */ + gctSIZE_T pageTableSize; + gctPHYS_ADDR pageTablePhysical; + gctPOINTER pageTableLogical; + + /* Allocation index. */ + gctUINT32 entryCount; + gctUINT32 entry; +}; + +#endif /* __gc_hal_kernel_h_ */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c new file mode 100644 index 00000000000000..237b856749066c --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c @@ -0,0 +1,2807 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_precomp.h" + +#define _GC_OBJ_ZONE gcvZONE_VIDMEM + +/******************************************************************************\ +******************************* Private Functions ****************************** +\******************************************************************************/ + +/******************************************************************************* +** +** _Split +** +** Split a node on the required byte boundary. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to the node to split. +** +** gctSIZE_T Bytes +** Number of bytes to keep in the node. +** +** OUTPUT: +** +** Nothing. +** +** RETURNS: +** +** gctBOOL +** gcvTRUE if the node was split successfully, or gcvFALSE if there is an +** error. +** +*/ +static gctBOOL +_Split( + IN gckOS Os, + IN gcuVIDMEM_NODE_PTR Node, + IN gctSIZE_T Bytes + ) +{ + gcuVIDMEM_NODE_PTR node; + gctPOINTER pointer = gcvNULL; + + /* Make sure the byte boundary makes sense. */ + if ((Bytes <= 0) || (Bytes > Node->VidMem.bytes)) + { + return gcvFALSE; + } + + /* Allocate a new gcuVIDMEM_NODE object. */ + if (gcmIS_ERROR(gckOS_Allocate(Os, + gcmSIZEOF(gcuVIDMEM_NODE), + &pointer))) + { + /* Error. */ + return gcvFALSE; + } + + node = pointer; + + /* Initialize gcuVIDMEM_NODE structure. */ + node->VidMem.offset = Node->VidMem.offset + Bytes; + node->VidMem.bytes = Node->VidMem.bytes - Bytes; + node->VidMem.alignment = 0; + node->VidMem.locked = 0; + node->VidMem.memory = Node->VidMem.memory; + node->VidMem.pool = Node->VidMem.pool; + node->VidMem.physical = Node->VidMem.physical; +#ifdef __QNXNTO__ + node->VidMem.processID = 0; + node->VidMem.logical = gcvNULL; +#endif + + /* Insert node behind specified node. */ + node->VidMem.next = Node->VidMem.next; + node->VidMem.prev = Node; + Node->VidMem.next = node->VidMem.next->VidMem.prev = node; + + /* Insert free node behind specified node. */ + node->VidMem.nextFree = Node->VidMem.nextFree; + node->VidMem.prevFree = Node; + Node->VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node; + + /* Adjust size of specified node. */ + Node->VidMem.bytes = Bytes; + + /* Success. */ + return gcvTRUE; +} + +/******************************************************************************* +** +** _Merge +** +** Merge two adjacent nodes together. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to the first of the two nodes to merge. +** +** OUTPUT: +** +** Nothing. +** +*/ +static gceSTATUS +_Merge( + IN gckOS Os, + IN gcuVIDMEM_NODE_PTR Node + ) +{ + gcuVIDMEM_NODE_PTR node; + gceSTATUS status; + + /* Save pointer to next node. */ + node = Node->VidMem.next; + + /* This is a good time to make sure the heap is not corrupted. */ + if (Node->VidMem.offset + Node->VidMem.bytes != node->VidMem.offset) + { + /* Corrupted heap. */ + gcmkASSERT( + Node->VidMem.offset + Node->VidMem.bytes == node->VidMem.offset); + return gcvSTATUS_HEAP_CORRUPTED; + } + + /* Adjust byte count. */ + Node->VidMem.bytes += node->VidMem.bytes; + + /* Unlink next node from linked list. */ + Node->VidMem.next = node->VidMem.next; + Node->VidMem.nextFree = node->VidMem.nextFree; + + Node->VidMem.next->VidMem.prev = + Node->VidMem.nextFree->VidMem.prevFree = Node; + + /* Free next node. */ + status = gcmkOS_SAFE_FREE(Os, node); + return status; +} + +/******************************************************************************\ +******************************* gckVIDMEM API Code ****************************** +\******************************************************************************/ + +/******************************************************************************* +** +** gckVIDMEM_ConstructVirtual +** +** Construct a new gcuVIDMEM_NODE union for virtual memory. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctSIZE_T Bytes +** Number of byte to allocate. +** +** OUTPUT: +** +** gcuVIDMEM_NODE_PTR * Node +** Pointer to a variable that receives the gcuVIDMEM_NODE union pointer. +*/ +gceSTATUS +gckVIDMEM_ConstructVirtual( + IN gckKERNEL Kernel, + IN gctUINT32 Flag, + IN gctSIZE_T Bytes, + OUT gcuVIDMEM_NODE_PTR * Node + ) +{ + gckOS os; + gceSTATUS status; + gcuVIDMEM_NODE_PTR node = gcvNULL; + gctPOINTER pointer = gcvNULL; + gctINT i; + + gcmkHEADER_ARG("Kernel=0x%x Flag=%x Bytes=%lu", Kernel, Flag, Bytes); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Node != gcvNULL); + + /* Extract the gckOS object pointer. */ + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Allocate an gcuVIDMEM_NODE union. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer)); + + node = pointer; + + /* Initialize gcuVIDMEM_NODE union for virtual memory. */ + node->Virtual.kernel = Kernel; + node->Virtual.contiguous = Flag & gcvALLOC_FLAG_CONTIGUOUS; + node->Virtual.logical = gcvNULL; +#if gcdENABLE_VG + node->Virtual.kernelVirtual = gcvNULL; +#endif + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + node->Virtual.lockeds[i] = 0; + node->Virtual.pageTables[i] = gcvNULL; + node->Virtual.lockKernels[i] = gcvNULL; + } + + gcmkONERROR(gckOS_GetProcessID(&node->Virtual.processID)); + + /* Allocate the virtual memory. */ + gcmkONERROR( + gckOS_AllocatePagedMemoryEx(os, + Flag, + node->Virtual.bytes = Bytes, + &node->Virtual.gid, + &node->Virtual.physical)); + + /* Return pointer to the gcuVIDMEM_NODE union. */ + *Node = node; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Created virtual node 0x%x for %u bytes @ 0x%x", + node, Bytes, node->Virtual.physical); + + /* Success. */ + gcmkFOOTER_ARG("*Node=0x%x", *Node); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (node != gcvNULL) + { + /* Free the structure. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVIDMEM_DestroyVirtual +** +** Destroy an gcuVIDMEM_NODE union for virtual memory. +** +** INPUT: +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a gcuVIDMEM_NODE union. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVIDMEM_DestroyVirtual( + IN gcuVIDMEM_NODE_PTR Node + ) +{ + gckOS os; + + gcmkHEADER_ARG("Node=0x%x", Node); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL); + + /* Extact the gckOS object pointer. */ + os = Node->Virtual.kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Delete the gcuVIDMEM_NODE union. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, Node)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVIDMEM_Construct +** +** Construct a new gckVIDMEM object. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 BaseAddress +** Base address for the video memory heap. +** +** gctSIZE_T Bytes +** Number of bytes in the video memory heap. +** +** gctSIZE_T Threshold +** Minimum number of bytes beyond am allocation before the node is +** split. Can be used as a minimum alignment requirement. +** +** gctSIZE_T BankSize +** Number of bytes per physical memory bank. Used by bank +** optimization. +** +** OUTPUT: +** +** gckVIDMEM * Memory +** Pointer to a variable that will hold the pointer to the gckVIDMEM +** object. +*/ +gceSTATUS +gckVIDMEM_Construct( + IN gckOS Os, + IN gctUINT32 BaseAddress, + IN gctSIZE_T Bytes, + IN gctSIZE_T Threshold, + IN gctSIZE_T BankSize, + OUT gckVIDMEM * Memory + ) +{ + gckVIDMEM memory = gcvNULL; + gceSTATUS status; + gcuVIDMEM_NODE_PTR node; + gctINT i, banks = 0; + gctPOINTER pointer = gcvNULL; + gctUINT32 heapBytes; + gctUINT32 bankSize; + + gcmkHEADER_ARG("Os=0x%x BaseAddress=%08x Bytes=%lu Threshold=%lu " + "BankSize=%lu", + Os, BaseAddress, Bytes, Threshold, BankSize); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Memory != gcvNULL); + + gcmkSAFECASTSIZET(heapBytes, Bytes); + gcmkSAFECASTSIZET(bankSize, BankSize); + + /* Allocate the gckVIDMEM object. */ + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct _gckVIDMEM), &pointer)); + + memory = pointer; + + /* Initialize the gckVIDMEM object. */ + memory->object.type = gcvOBJ_VIDMEM; + memory->os = Os; + + /* Set video memory heap information. */ + memory->baseAddress = BaseAddress; + memory->bytes = heapBytes; + memory->freeBytes = heapBytes; + memory->threshold = Threshold; + memory->mutex = gcvNULL; + + BaseAddress = 0; + + /* Walk all possible banks. */ + for (i = 0; i < gcmCOUNTOF(memory->sentinel); ++i) + { + gctUINT32 bytes; + + if (BankSize == 0) + { + /* Use all bytes for the first bank. */ + bytes = heapBytes; + } + else + { + /* Compute number of bytes for this bank. */ + bytes = gcmALIGN(BaseAddress + 1, bankSize) - BaseAddress; + + if (bytes > heapBytes) + { + /* Make sure we don't exceed the total number of bytes. */ + bytes = heapBytes; + } + } + + if (bytes == 0) + { + /* Mark heap is not used. */ + memory->sentinel[i].VidMem.next = + memory->sentinel[i].VidMem.prev = + memory->sentinel[i].VidMem.nextFree = + memory->sentinel[i].VidMem.prevFree = gcvNULL; + continue; + } + + /* Allocate one gcuVIDMEM_NODE union. */ + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer)); + + node = pointer; + + /* Initialize gcuVIDMEM_NODE union. */ + node->VidMem.memory = memory; + + node->VidMem.next = + node->VidMem.prev = + node->VidMem.nextFree = + node->VidMem.prevFree = &memory->sentinel[i]; + + node->VidMem.offset = BaseAddress; + node->VidMem.bytes = bytes; + node->VidMem.alignment = 0; + node->VidMem.physical = 0; + node->VidMem.pool = gcvPOOL_UNKNOWN; + + node->VidMem.locked = 0; + +#ifdef __QNXNTO__ + node->VidMem.processID = 0; + node->VidMem.logical = gcvNULL; +#endif + +#if gcdENABLE_VG + node->VidMem.kernelVirtual = gcvNULL; +#endif + + /* Initialize the linked list of nodes. */ + memory->sentinel[i].VidMem.next = + memory->sentinel[i].VidMem.prev = + memory->sentinel[i].VidMem.nextFree = + memory->sentinel[i].VidMem.prevFree = node; + + /* Mark sentinel. */ + memory->sentinel[i].VidMem.bytes = 0; + + /* Adjust address for next bank. */ + BaseAddress += bytes; + heapBytes -= bytes; + banks ++; + } + + /* Assign all the bank mappings. */ + memory->mapping[gcvSURF_RENDER_TARGET] = banks - 1; + memory->mapping[gcvSURF_BITMAP] = banks - 1; + if (banks > 1) --banks; + memory->mapping[gcvSURF_DEPTH] = banks - 1; + memory->mapping[gcvSURF_HIERARCHICAL_DEPTH] = banks - 1; + if (banks > 1) --banks; + memory->mapping[gcvSURF_TEXTURE] = banks - 1; + if (banks > 1) --banks; + memory->mapping[gcvSURF_VERTEX] = banks - 1; + if (banks > 1) --banks; + memory->mapping[gcvSURF_INDEX] = banks - 1; + if (banks > 1) --banks; + memory->mapping[gcvSURF_TILE_STATUS] = banks - 1; + if (banks > 1) --banks; + memory->mapping[gcvSURF_TYPE_UNKNOWN] = 0; + +#if gcdENABLE_VG + memory->mapping[gcvSURF_IMAGE] = 0; + memory->mapping[gcvSURF_MASK] = 0; + memory->mapping[gcvSURF_SCISSOR] = 0; +#endif + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "[GALCORE] INDEX: bank %d", + memory->mapping[gcvSURF_INDEX]); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "[GALCORE] VERTEX: bank %d", + memory->mapping[gcvSURF_VERTEX]); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "[GALCORE] TEXTURE: bank %d", + memory->mapping[gcvSURF_TEXTURE]); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "[GALCORE] RENDER_TARGET: bank %d", + memory->mapping[gcvSURF_RENDER_TARGET]); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "[GALCORE] DEPTH: bank %d", + memory->mapping[gcvSURF_DEPTH]); + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "[GALCORE] TILE_STATUS: bank %d", + memory->mapping[gcvSURF_TILE_STATUS]); + + /* Allocate the mutex. */ + gcmkONERROR(gckOS_CreateMutex(Os, &memory->mutex)); + + /* Return pointer to the gckVIDMEM object. */ + *Memory = memory; + + /* Success. */ + gcmkFOOTER_ARG("*Memory=0x%x", *Memory); + return gcvSTATUS_OK; + +OnError: + /* Roll back. */ + if (memory != gcvNULL) + { + if (memory->mutex != gcvNULL) + { + /* Delete the mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Os, memory->mutex)); + } + + for (i = 0; i < banks; ++i) + { + /* Free the heap. */ + gcmkASSERT(memory->sentinel[i].VidMem.next != gcvNULL); + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory->sentinel[i].VidMem.next)); + } + + /* Free the object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVIDMEM_Destroy +** +** Destroy an gckVIDMEM object. +** +** INPUT: +** +** gckVIDMEM Memory +** Pointer to an gckVIDMEM object to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVIDMEM_Destroy( + IN gckVIDMEM Memory + ) +{ + gcuVIDMEM_NODE_PTR node, next; + gctINT i; + + gcmkHEADER_ARG("Memory=0x%x", Memory); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM); + + /* Walk all sentinels. */ + for (i = 0; i < gcmCOUNTOF(Memory->sentinel); ++i) + { + /* Bail out of the heap is not used. */ + if (Memory->sentinel[i].VidMem.next == gcvNULL) + { + break; + } + + /* Walk all the nodes until we reach the sentinel. */ + for (node = Memory->sentinel[i].VidMem.next; + node->VidMem.bytes != 0; + node = next) + { + /* Save pointer to the next node. */ + next = node->VidMem.next; + + /* Free the node. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, node)); + } + } + + /* Free the mutex. */ + gcmkVERIFY_OK(gckOS_DeleteMutex(Memory->os, Memory->mutex)); + + /* Mark the object as unknown. */ + Memory->object.type = gcvOBJ_UNKNOWN; + + /* Free the gckVIDMEM object. */ + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, Memory)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +#if gcdENABLE_BANK_ALIGNMENT + +#if !gcdBANK_BIT_START +#error gcdBANK_BIT_START not defined. +#endif + +#if !gcdBANK_BIT_END +#error gcdBANK_BIT_END not defined. +#endif +/******************************************************************************* +** _GetSurfaceBankAlignment +** +** Return the required offset alignment required to the make BaseAddress +** aligned properly. +** +** INPUT: +** +** gckOS Os +** Pointer to gcoOS object. +** +** gceSURF_TYPE Type +** Type of allocation. +** +** gctUINT32 BaseAddress +** Base address of current video memory node. +** +** OUTPUT: +** +** gctUINT32_PTR AlignmentOffset +** Pointer to a variable that will hold the number of bytes to skip in +** the current video memory node in order to make the alignment bank +** aligned. +*/ +static gceSTATUS +_GetSurfaceBankAlignment( + IN gckKERNEL Kernel, + IN gceSURF_TYPE Type, + IN gctUINT32 BaseAddress, + OUT gctUINT32_PTR AlignmentOffset + ) +{ + gctUINT32 bank; + /* To retrieve the bank. */ + static const gctUINT32 bankMask = (0xFFFFFFFF << gcdBANK_BIT_START) + ^ (0xFFFFFFFF << (gcdBANK_BIT_END + 1)); + + /* To retrieve the bank and all the lower bytes. */ + static const gctUINT32 byteMask = ~(0xFFFFFFFF << (gcdBANK_BIT_END + 1)); + + gcmkHEADER_ARG("Type=%d BaseAddress=0x%x ", Type, BaseAddress); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(AlignmentOffset != gcvNULL); + + switch (Type) + { + case gcvSURF_RENDER_TARGET: + bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START); + + /* Align to the first bank. */ + *AlignmentOffset = (bank == 0) ? + 0 : + ((1 << (gcdBANK_BIT_END + 1)) + 0) - (BaseAddress & byteMask); + break; + + case gcvSURF_DEPTH: + bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START); + + /* Align to the third bank. */ + *AlignmentOffset = (bank == 2) ? + 0 : + ((1 << (gcdBANK_BIT_END + 1)) + (2 << gcdBANK_BIT_START)) - (BaseAddress & byteMask); + + /* Minimum 256 byte alignment needed for fast_msaa. */ + if ((gcdBANK_CHANNEL_BIT > 7) || + ((gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_FAST_MSAA) != gcvSTATUS_TRUE) && + (gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_SMALL_MSAA) != gcvSTATUS_TRUE))) + { + /* Add a channel offset at the channel bit. */ + *AlignmentOffset += (1 << gcdBANK_CHANNEL_BIT); + } + break; + + default: + /* no alignment needed. */ + *AlignmentOffset = 0; + } + + /* Return the status. */ + gcmkFOOTER_ARG("*AlignmentOffset=%u", *AlignmentOffset); + return gcvSTATUS_OK; +} +#endif + +static gcuVIDMEM_NODE_PTR +_FindNode( + IN gckKERNEL Kernel, + IN gckVIDMEM Memory, + IN gctINT Bank, + IN gctSIZE_T Bytes, + IN gceSURF_TYPE Type, + IN OUT gctUINT32_PTR Alignment + ) +{ + gcuVIDMEM_NODE_PTR node; + gctUINT32 alignment; + +#if gcdENABLE_BANK_ALIGNMENT + gctUINT32 bankAlignment; + gceSTATUS status; +#endif + + if (Memory->sentinel[Bank].VidMem.nextFree == gcvNULL) + { + /* No free nodes left. */ + return gcvNULL; + } + +#if gcdENABLE_BANK_ALIGNMENT + /* Walk all free nodes until we have one that is big enough or we have + ** reached the sentinel. */ + for (node = Memory->sentinel[Bank].VidMem.nextFree; + node->VidMem.bytes != 0; + node = node->VidMem.nextFree) + { + if (node->VidMem.bytes < Bytes) + { + continue; + } + + gcmkONERROR(_GetSurfaceBankAlignment( + Kernel, + Type, + node->VidMem.memory->baseAddress + node->VidMem.offset, + &bankAlignment)); + + bankAlignment = gcmALIGN(bankAlignment, *Alignment); + + /* Compute number of bytes to skip for alignment. */ + alignment = (*Alignment == 0) + ? 0 + : (*Alignment - (node->VidMem.offset % *Alignment)); + + if (alignment == *Alignment) + { + /* Node is already aligned. */ + alignment = 0; + } + + if (node->VidMem.bytes >= Bytes + alignment + bankAlignment) + { + /* This node is big enough. */ + *Alignment = alignment + bankAlignment; + return node; + } + } +#endif + + /* Walk all free nodes until we have one that is big enough or we have + reached the sentinel. */ + for (node = Memory->sentinel[Bank].VidMem.nextFree; + node->VidMem.bytes != 0; + node = node->VidMem.nextFree) + { + gctUINT offset; + + gctINT modulo; + + gcmkSAFECASTSIZET(offset, node->VidMem.offset); + + modulo = gckMATH_ModuloInt(offset, *Alignment); + + /* Compute number of bytes to skip for alignment. */ + alignment = (*Alignment == 0) ? 0 : (*Alignment - modulo); + + if (alignment == *Alignment) + { + /* Node is already aligned. */ + alignment = 0; + } + + if (node->VidMem.bytes >= Bytes + alignment) + { + /* This node is big enough. */ + *Alignment = alignment; + return node; + } + } + +#if gcdENABLE_BANK_ALIGNMENT +OnError: +#endif + /* Not enough memory. */ + return gcvNULL; +} + +/******************************************************************************* +** +** gckVIDMEM_AllocateLinear +** +** Allocate linear memory from the gckVIDMEM object. +** +** INPUT: +** +** gckVIDMEM Memory +** Pointer to an gckVIDMEM object. +** +** gctSIZE_T Bytes +** Number of bytes to allocate. +** +** gctUINT32 Alignment +** Byte alignment for allocation. +** +** gceSURF_TYPE Type +** Type of surface to allocate (use by bank optimization). +** +** gctBOOL Specified +** If user must use this pool, it should set Specified to gcvTRUE, +** otherwise allocator may reserve some memory for other usage, such +** as small block size allocation request. +** +** OUTPUT: +** +** gcuVIDMEM_NODE_PTR * Node +** Pointer to a variable that will hold the allocated memory node. +*/ +gceSTATUS +gckVIDMEM_AllocateLinear( + IN gckKERNEL Kernel, + IN gckVIDMEM Memory, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + IN gctBOOL Specified, + OUT gcuVIDMEM_NODE_PTR * Node + ) +{ + gceSTATUS status; + gcuVIDMEM_NODE_PTR node; + gctUINT32 alignment; + gctINT bank, i; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Memory=0x%x Bytes=%lu Alignment=%u Type=%d", + Memory, Bytes, Alignment, Type); + + /* Verify the arguments. */ + gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM); + gcmkVERIFY_ARGUMENT(Bytes > 0); + gcmkVERIFY_ARGUMENT(Node != gcvNULL); + gcmkVERIFY_ARGUMENT(Type < gcvSURF_NUM_TYPES); + + /* Acquire the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(Memory->os, Memory->mutex, gcvINFINITE)); + + acquired = gcvTRUE; + + if (Bytes > Memory->freeBytes) + { + /* Not enough memory. */ + status = gcvSTATUS_OUT_OF_MEMORY; + goto OnError; + } + +#if gcdSMALL_BLOCK_SIZE + if ((Memory->freeBytes < (Memory->bytes/gcdRATIO_FOR_SMALL_MEMORY)) + && (Bytes >= gcdSMALL_BLOCK_SIZE) + && (Specified == gcvFALSE) + ) + { + /* The left memory is for small memory.*/ + status = gcvSTATUS_OUT_OF_MEMORY; + goto OnError; + } +#endif + + /* Find the default bank for this surface type. */ + gcmkASSERT((gctINT) Type < gcmCOUNTOF(Memory->mapping)); + bank = Memory->mapping[Type]; + alignment = Alignment; + + /* Find a free node in the default bank. */ + node = _FindNode(Kernel, Memory, bank, Bytes, Type, &alignment); + + /* Out of memory? */ + if (node == gcvNULL) + { + /* Walk all lower banks. */ + for (i = bank - 1; i >= 0; --i) + { + /* Find a free node inside the current bank. */ + node = _FindNode(Kernel, Memory, i, Bytes, Type, &alignment); + if (node != gcvNULL) + { + break; + } + } + } + + if (node == gcvNULL) + { + /* Walk all upper banks. */ + for (i = bank + 1; i < gcmCOUNTOF(Memory->sentinel); ++i) + { + if (Memory->sentinel[i].VidMem.nextFree == gcvNULL) + { + /* Abort when we reach unused banks. */ + break; + } + + /* Find a free node inside the current bank. */ + node = _FindNode(Kernel, Memory, i, Bytes, Type, &alignment); + if (node != gcvNULL) + { + break; + } + } + } + + if (node == gcvNULL) + { + /* Out of memory. */ + status = gcvSTATUS_OUT_OF_MEMORY; + goto OnError; + } + + /* Do we have an alignment? */ + if (alignment > 0) + { + /* Split the node so it is aligned. */ + if (_Split(Memory->os, node, alignment)) + { + /* Successful split, move to aligned node. */ + node = node->VidMem.next; + + /* Remove alignment. */ + alignment = 0; + } + } + + /* Do we have enough memory after the allocation to split it? */ + if (node->VidMem.bytes - Bytes > Memory->threshold) + { + /* Adjust the node size. */ + _Split(Memory->os, node, Bytes); + } + + /* Remove the node from the free list. */ + node->VidMem.prevFree->VidMem.nextFree = node->VidMem.nextFree; + node->VidMem.nextFree->VidMem.prevFree = node->VidMem.prevFree; + node->VidMem.nextFree = + node->VidMem.prevFree = gcvNULL; + + /* Fill in the information. */ + node->VidMem.alignment = alignment; + node->VidMem.memory = Memory; +#ifdef __QNXNTO__ + node->VidMem.logical = gcvNULL; + gcmkONERROR(gckOS_GetProcessID(&node->VidMem.processID)); +#endif + + /* Adjust the number of free bytes. */ + Memory->freeBytes -= node->VidMem.bytes; + +#if gcdENABLE_VG + node->VidMem.kernelVirtual = gcvNULL; +#endif + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex)); + + /* Return the pointer to the node. */ + *Node = node; + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Allocated %u bytes @ 0x%x [0x%08X]", + node->VidMem.bytes, node, node->VidMem.offset); + + /* Success. */ + gcmkFOOTER_ARG("*Node=0x%x", *Node); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVIDMEM_Free +** +** Free an allocated video memory node. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a gcuVIDMEM_NODE object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckVIDMEM_Free( + IN gckKERNEL Kernel, + IN gcuVIDMEM_NODE_PTR Node + ) +{ + gceSTATUS status; + gckKERNEL kernel = gcvNULL; + gckVIDMEM memory = gcvNULL; + gcuVIDMEM_NODE_PTR node; + gctBOOL mutexAcquired = gcvFALSE; + + gcmkHEADER_ARG("Node=0x%x", Node); + + /* Verify the arguments. */ + if ((Node == gcvNULL) + || (Node->VidMem.memory == gcvNULL) + ) + { + /* Invalid object. */ + gcmkONERROR(gcvSTATUS_INVALID_OBJECT); + } + + /**************************** Video Memory ********************************/ + + if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + /* Extract pointer to gckVIDMEM object owning the node. */ + memory = Node->VidMem.memory; + + /* Acquire the mutex. */ + gcmkONERROR( + gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE)); + + mutexAcquired = gcvTRUE; + +#ifdef __QNXNTO__ + /* Unmap the video memory. */ + if (Node->VidMem.logical != gcvNULL) + { + gckKERNEL_UnmapVideoMemory( + Kernel, + Node->VidMem.logical, + Node->VidMem.processID, + Node->VidMem.bytes); + Node->VidMem.logical = gcvNULL; + } + + /* Reset. */ + Node->VidMem.processID = 0; + + /* Don't try to re-free an already freed node. */ + if ((Node->VidMem.nextFree == gcvNULL) + && (Node->VidMem.prevFree == gcvNULL) + ) +#endif + { +#if gcdENABLE_VG + if (Node->VidMem.kernelVirtual) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "%s(%d) Unmap %x from kernel space.", + __FUNCTION__, __LINE__, + Node->VidMem.kernelVirtual); + + gcmkVERIFY_OK( + gckOS_UnmapPhysical(memory->os, + Node->VidMem.kernelVirtual, + Node->VidMem.bytes)); + + Node->VidMem.kernelVirtual = gcvNULL; + } +#endif + + /* Check if Node is already freed. */ + if (Node->VidMem.nextFree) + { + /* Node is alread freed. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + /* Update the number of free bytes. */ + memory->freeBytes += Node->VidMem.bytes; + + /* Find the next free node. */ + for (node = Node->VidMem.next; + node != gcvNULL && node->VidMem.nextFree == gcvNULL; + node = node->VidMem.next) ; + + /* Insert this node in the free list. */ + Node->VidMem.nextFree = node; + Node->VidMem.prevFree = node->VidMem.prevFree; + + Node->VidMem.prevFree->VidMem.nextFree = + node->VidMem.prevFree = Node; + + /* Is the next node a free node and not the sentinel? */ + if ((Node->VidMem.next == Node->VidMem.nextFree) + && (Node->VidMem.next->VidMem.bytes != 0) + ) + { + /* Merge this node with the next node. */ + gcmkONERROR(_Merge(memory->os, node = Node)); + gcmkASSERT(node->VidMem.nextFree != node); + gcmkASSERT(node->VidMem.prevFree != node); + } + + /* Is the previous node a free node and not the sentinel? */ + if ((Node->VidMem.prev == Node->VidMem.prevFree) + && (Node->VidMem.prev->VidMem.bytes != 0) + ) + { + /* Merge this node with the previous node. */ + gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev)); + gcmkASSERT(node->VidMem.nextFree != node); + gcmkASSERT(node->VidMem.prevFree != node); + } + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex)); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Node 0x%x is freed.", + Node); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + } + + /*************************** Virtual Memory *******************************/ + + /* Get gckKERNEL object. */ + kernel = Node->Virtual.kernel; + + /* Verify the gckKERNEL object pointer. */ + gcmkVERIFY_OBJECT(kernel, gcvOBJ_KERNEL); + +#if gcdENABLE_VG + if (Node->Virtual.kernelVirtual) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "%s(%d) Unmap %x from kernel space.", + __FUNCTION__, __LINE__, + Node->Virtual.kernelVirtual); + + gcmkVERIFY_OK( + gckOS_UnmapPhysical(kernel->os, + Node->Virtual.kernelVirtual, + Node->Virtual.bytes)); + + Node->Virtual.kernelVirtual = gcvNULL; + } +#endif + + /* Free the virtual memory. */ + gcmkVERIFY_OK(gckOS_FreePagedMemory(kernel->os, + Node->Virtual.physical, + Node->Virtual.bytes)); + + /* Destroy the gcuVIDMEM_NODE union. */ + gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node)); + + /* Success. */ + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (mutexAcquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex( + memory->os, memory->mutex + )); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if !gcdPROCESS_ADDRESS_SPACE +/******************************************************************************* +** +** _NeedVirtualMapping +** +** Whether setup GPU page table for video node. +** +** INPUT: +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a gcuVIDMEM_NODE union. +** +** gceCORE Core +** Id of current GPU. +** +** OUTPUT: +** gctBOOL * NeedMapping +** A pointer hold the result whether Node should be mapping. +*/ +static gceSTATUS +_NeedVirtualMapping( + IN gckKERNEL Kernel, + IN gceCORE Core, + IN gcuVIDMEM_NODE_PTR Node, + OUT gctBOOL * NeedMapping +) +{ + gceSTATUS status; + gctUINT32 phys; + gctUINT32 end; + gcePOOL pool; + gctUINT32 offset; + gctUINT32 baseAddress; + gctUINT32 bytes; + + gcmkHEADER_ARG("Node=0x%X", Node); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Kernel != gcvNULL); + gcmkVERIFY_ARGUMENT(Node != gcvNULL); + gcmkVERIFY_ARGUMENT(NeedMapping != gcvNULL); + gcmkVERIFY_ARGUMENT(Core < gcdMAX_GPU_COUNT); + + if (Node->Virtual.contiguous) + { +#if gcdENABLE_VG + if (Core == gcvCORE_VG) + { + *NeedMapping = gcvFALSE; + } + else +#endif + { + /* Convert logical address into a physical address. */ + gcmkONERROR(gckOS_UserLogicalToPhysical( + Kernel->os, Node->Virtual.logical, &phys + )); + + gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &baseAddress)); + + gcmkASSERT(phys >= baseAddress); + + /* Subtract baseAddress to get a GPU address used for programming. */ + phys -= baseAddress; + + /* If part of region is belong to gcvPOOL_VIRTUAL, + ** whole region has to be mapped. */ + gcmkSAFECASTSIZET(bytes, Node->Virtual.bytes); + end = phys + bytes - 1; + + gcmkONERROR(gckHARDWARE_SplitMemory( + Kernel->hardware, end, &pool, &offset + )); + + *NeedMapping = (pool == gcvPOOL_VIRTUAL); + } + } + else + { + *NeedMapping = gcvTRUE; + } + + gcmkFOOTER_ARG("*NeedMapping=%d", *NeedMapping); + return gcvSTATUS_OK; + +OnError: + gcmkFOOTER(); + return status; +} +#endif + +#if gcdPROCESS_ADDRESS_SPACE +gcsGPU_MAP_PTR +_FindGPUMap( + IN gcsGPU_MAP_PTR Head, + IN gctINT ProcessID + ) +{ + gcsGPU_MAP_PTR map = Head; + + while (map) + { + if (map->pid == ProcessID) + { + return map; + } + + map = map->next; + } + + return gcvNULL; +} + +gcsGPU_MAP_PTR +_CreateGPUMap( + IN gckOS Os, + IN gcsGPU_MAP_PTR *Head, + IN gcsGPU_MAP_PTR *Tail, + IN gctINT ProcessID + ) +{ + gcsGPU_MAP_PTR gpuMap; + gctPOINTER pointer = gcvNULL; + + gckOS_Allocate(Os, sizeof(gcsGPU_MAP), &pointer); + + if (pointer == gcvNULL) + { + return gcvNULL; + } + + gpuMap = pointer; + + gckOS_ZeroMemory(pointer, sizeof(gcsGPU_MAP)); + + gpuMap->pid = ProcessID; + + if (!*Head) + { + *Head = *Tail = gpuMap; + } + else + { + gpuMap->prev = *Tail; + (*Tail)->next = gpuMap; + *Tail = gpuMap; + } + + return gpuMap; +} + +void +_DestroyGPUMap( + IN gckOS Os, + IN gcsGPU_MAP_PTR *Head, + IN gcsGPU_MAP_PTR *Tail, + IN gcsGPU_MAP_PTR gpuMap + ) +{ + + if (gpuMap == *Head) + { + if ((*Head = gpuMap->next) == gcvNULL) + { + *Tail = gcvNULL; + } + } + else + { + gpuMap->prev->next = gpuMap->next; + if (gpuMap == *Tail) + { + *Tail = gpuMap->prev; + } + else + { + gpuMap->next->prev = gpuMap->prev; + } + } + + gcmkOS_SAFE_FREE(Os, gpuMap); +} +#endif + +/******************************************************************************* +** +** gckVIDMEM_Lock +** +** Lock a video memory node and return its hardware specific address. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a gcuVIDMEM_NODE union. +** +** OUTPUT: +** +** gctUINT32 * Address +** Pointer to a variable that will hold the hardware specific address. +** +** gctUINT32 * PhysicalAddress +** Pointer to a variable that will hold the bus address of a contiguous +** video node. +*/ +gceSTATUS +gckVIDMEM_Lock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + IN gctBOOL Cacheable, + OUT gctUINT32 * Address, + OUT gctUINT32 * Gid, + OUT gctUINT64 * PhysicalAddress + ) +{ + gceSTATUS status; + gctBOOL acquired = gcvFALSE; + gctBOOL locked = gcvFALSE; + gckOS os = gcvNULL; +#if !gcdPROCESS_ADDRESS_SPACE + gctBOOL needMapping = gcvFALSE; +#endif + gctUINT32 baseAddress; + gctUINT32 physicalAddress; + gcuVIDMEM_NODE_PTR node = Node->node; + + gcmkHEADER_ARG("Node=0x%x", Node); + + /* Verify the arguments. */ + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Extract the gckOS object pointer. */ + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + if ((node == gcvNULL) + || (node->VidMem.memory == gcvNULL) + ) + { + /* Invalid object. */ + gcmkONERROR(gcvSTATUS_INVALID_OBJECT); + } + + /* Grab the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(os, Node->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + /**************************** Video Memory ********************************/ + + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + gctUINT32 offset; + + if (Cacheable == gcvTRUE) + { + gcmkONERROR(gcvSTATUS_INVALID_REQUEST); + } + + /* Increment the lock count. */ + node->VidMem.locked ++; + + /* Return the physical address of the node. */ + gcmkSAFECASTSIZET(offset, node->VidMem.offset); + + *Address = node->VidMem.memory->baseAddress + + offset + + node->VidMem.alignment; + + physicalAddress = *Address; + + /* Get hardware specific address. */ +#if gcdENABLE_VG + if (Kernel->vg == gcvNULL) +#endif + { + if (Kernel->hardware->mmuVersion == 0) + { + /* Convert physical to GPU address for old mmu. */ + gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &baseAddress)); + gcmkASSERT(*Address > baseAddress); + *Address -= baseAddress; + } + } + + gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical( + Kernel->os, + *Address, + Address + )); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Locked node 0x%x (%d) @ 0x%08X", + node, + node->VidMem.locked, + *Address); + } + + /*************************** Virtual Memory *******************************/ + + else + { + + *Gid = node->Virtual.gid; + +#if gcdPAGED_MEMORY_CACHEABLE + /* Force video memory cacheable. */ + Cacheable = gcvTRUE; +#endif + + gcmkONERROR( + gckOS_LockPages(os, + node->Virtual.physical, + node->Virtual.bytes, + Cacheable, + &node->Virtual.logical, + &node->Virtual.pageCount)); + + gcmkONERROR(gckOS_GetPhysicalAddress( + os, + node->Virtual.logical, + &physicalAddress + )); + +#if gcdENABLE_VG + node->Virtual.physicalAddress = physicalAddress; +#endif + +#if !gcdPROCESS_ADDRESS_SPACE + /* Increment the lock count. */ + if (node->Virtual.lockeds[Kernel->core] ++ == 0) + { + locked = gcvTRUE; + + gcmkONERROR(_NeedVirtualMapping(Kernel, Kernel->core, node, &needMapping)); + + if (needMapping == gcvFALSE) + { + /* Get hardware specific address. */ +#if gcdENABLE_VG + if (Kernel->vg != gcvNULL) + { + gcmkONERROR(gckVGHARDWARE_ConvertLogical( + Kernel->vg->hardware, + node->Virtual.logical, + gcvTRUE, + &node->Virtual.addresses[Kernel->core])); + } + else +#endif + { + gcmkONERROR(gckHARDWARE_ConvertLogical( + Kernel->hardware, + node->Virtual.logical, + gcvTRUE, + &node->Virtual.addresses[Kernel->core])); + } + } + else + { +#if gcdSECURITY + gctPHYS_ADDR physicalArrayPhysical; + gctPOINTER physicalArrayLogical; + + gcmkONERROR(gckOS_AllocatePageArray( + os, + node->Virtual.physical, + node->Virtual.pageCount, + &physicalArrayLogical, + &physicalArrayPhysical + )); + + gcmkONERROR(gckKERNEL_SecurityMapMemory( + Kernel, + physicalArrayLogical, + node->Virtual.pageCount, + &node->Virtual.addresses[Kernel->core] + )); + + gcmkONERROR(gckOS_FreeNonPagedMemory( + os, + 1, + physicalArrayPhysical, + physicalArrayLogical + )); +#else +#if gcdENABLE_VG + if (Kernel->vg != gcvNULL) + { + /* Allocate pages inside the MMU. */ + gcmkONERROR( + gckVGMMU_AllocatePages(Kernel->vg->mmu, + node->Virtual.pageCount, + &node->Virtual.pageTables[Kernel->core], + &node->Virtual.addresses[Kernel->core])); + } + else +#endif + { + /* Allocate pages inside the MMU. */ + gcmkONERROR( + gckMMU_AllocatePagesEx(Kernel->mmu, + node->Virtual.pageCount, + node->Virtual.type, + &node->Virtual.pageTables[Kernel->core], + &node->Virtual.addresses[Kernel->core])); + } + + node->Virtual.lockKernels[Kernel->core] = Kernel; + + /* Map the pages. */ + gcmkONERROR( + gckOS_MapPagesEx(os, + Kernel->core, + node->Virtual.physical, + node->Virtual.pageCount, + node->Virtual.addresses[Kernel->core], + node->Virtual.pageTables[Kernel->core])); + +#if gcdENABLE_VG + if (Kernel->core == gcvCORE_VG) + { + gcmkONERROR(gckVGMMU_Flush(Kernel->vg->mmu)); + } + else +#endif + { + gcmkONERROR(gckMMU_Flush(Kernel->mmu, node->Virtual.type)); + } +#endif + } + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Mapped virtual node 0x%x to 0x%08X", + node, + node->Virtual.addresses[Kernel->core]); + } + + /* Return hardware address. */ + *Address = node->Virtual.addresses[Kernel->core]; +#endif + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex)); + + *PhysicalAddress = (gctUINT64)physicalAddress; + + /* Success. */ + gcmkFOOTER_ARG("*Address=%08x", *Address); + return gcvSTATUS_OK; + +OnError: + if (locked) + { + if (node->Virtual.pageTables[Kernel->core] != gcvNULL) + { +#if gcdENABLE_VG + if (Kernel->vg != gcvNULL) + { + /* Free the pages from the MMU. */ + gcmkVERIFY_OK( + gckVGMMU_FreePages(Kernel->vg->mmu, + node->Virtual.pageTables[Kernel->core], + node->Virtual.pageCount)); + } + else +#endif + { + /* Free the pages from the MMU. */ + gcmkVERIFY_OK( + gckMMU_FreePages(Kernel->mmu, + node->Virtual.pageTables[Kernel->core], + node->Virtual.pageCount)); + } + node->Virtual.pageTables[Kernel->core] = gcvNULL; + node->Virtual.lockKernels[Kernel->core] = gcvNULL; + } + + /* Unlock the pages. */ + gcmkVERIFY_OK( + gckOS_UnlockPages(os, + node->Virtual.physical, + node->Virtual.bytes, + node->Virtual.logical + )); + + node->Virtual.lockeds[Kernel->core]--; + } + + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVIDMEM_Unlock +** +** Unlock a video memory node. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a locked gcuVIDMEM_NODE union. +** +** gceSURF_TYPE Type +** Type of surface to unlock. +** +** gctBOOL * Asynchroneous +** Pointer to a variable specifying whether the surface should be +** unlocked asynchroneously or not. +** +** OUTPUT: +** +** gctBOOL * Asynchroneous +** Pointer to a variable receiving the number of bytes used in the +** command buffer specified by 'Commands'. If gcvNULL, there is no +** command buffer. +*/ +gceSTATUS +gckVIDMEM_Unlock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + IN gceSURF_TYPE Type, + IN OUT gctBOOL * Asynchroneous + ) +{ + gceSTATUS status; + gckOS os = gcvNULL; + gctBOOL acquired = gcvFALSE; + gcuVIDMEM_NODE_PTR node = Node->node; + + gcmkHEADER_ARG("Node=0x%x Type=%d *Asynchroneous=%d", + Node, Type, gcmOPT_VALUE(Asynchroneous)); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + + /* Get the gckOS object pointer. */ + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Verify the arguments. */ + if ((node == gcvNULL) + || (node->VidMem.memory == gcvNULL) + ) + { + /* Invalid object. */ + gcmkONERROR(gcvSTATUS_INVALID_OBJECT); + } + + /* Grab the mutex. */ + gcmkONERROR(gckOS_AcquireMutex(os, Node->mutex, gcvINFINITE)); + acquired = gcvTRUE; + + /**************************** Video Memory ********************************/ + + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + if (node->VidMem.locked <= 0) + { + /* The surface was not locked. */ + status = gcvSTATUS_MEMORY_UNLOCKED; + goto OnError; + } + + if (Asynchroneous != gcvNULL) + { + /* Schedule an event to sync with GPU. */ + *Asynchroneous = gcvTRUE; + } + else + { + /* Decrement the lock count. */ + node->VidMem.locked --; + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Unlocked node 0x%x (%d)", + node, + node->VidMem.locked); + } + + /*************************** Virtual Memory *******************************/ + + else + { + + + if (Asynchroneous == gcvNULL) + { +#if !gcdPROCESS_ADDRESS_SPACE + if (node->Virtual.lockeds[Kernel->core] == 0) + { + status = gcvSTATUS_MEMORY_UNLOCKED; + goto OnError; + } + + /* Decrement lock count. */ + -- node->Virtual.lockeds[Kernel->core]; + + /* See if we can unlock the resources. */ + if (node->Virtual.lockeds[Kernel->core] == 0) + { +#if gcdSECURITY + if (node->Virtual.addresses[Kernel->core] > 0x80000000) + { + gcmkONERROR(gckKERNEL_SecurityUnmapMemory( + Kernel, + node->Virtual.addresses[Kernel->core], + node->Virtual.pageCount + )); + } +#else + /* Free the page table. */ + if (node->Virtual.pageTables[Kernel->core] != gcvNULL) + { +#if gcdENABLE_VG + if (Kernel->vg != gcvNULL) + { + gcmkONERROR( + gckVGMMU_FreePages(Kernel->vg->mmu, + node->Virtual.pageTables[Kernel->core], + node->Virtual.pageCount)); + } + else +#endif + { + gcmkONERROR( + gckMMU_FreePages(Kernel->mmu, + node->Virtual.pageTables[Kernel->core], + node->Virtual.pageCount)); + } + + gcmkONERROR(gckOS_UnmapPages( + Kernel->os, + node->Virtual.pageCount, + node->Virtual.addresses[Kernel->core] + )); + + /* Mark page table as freed. */ + node->Virtual.pageTables[Kernel->core] = gcvNULL; + node->Virtual.lockKernels[Kernel->core] = gcvNULL; + } +#endif + } + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Unmapped virtual node 0x%x from 0x%08X", + node, node->Virtual.addresses[Kernel->core]); +#endif + + } + + else + { + gcmkONERROR( + gckOS_UnlockPages(os, + node->Virtual.physical, + node->Virtual.bytes, + node->Virtual.logical)); + + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "Scheduled unlock for virtual node 0x%x", + node); + + /* Schedule the surface to be unlocked. */ + *Asynchroneous = gcvTRUE; + } + } + + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex)); + acquired = gcvFALSE; + + /* Success. */ + gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous)); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + /* Release the mutex. */ + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex)); + } + + /* Return the status. */ + gcmkFOOTER(); + return status; +} + +#if gcdPROCESS_ADDRESS_SPACE +gceSTATUS +gckVIDMEM_Node_Lock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + OUT gctUINT32 *Address + ) +{ + gceSTATUS status; + gckOS os; + gcuVIDMEM_NODE_PTR node = Node->node; + gcsGPU_MAP_PTR gpuMap; + gctPHYS_ADDR physical = gcvNULL; + gctUINT32 phys = gcvINVALID_ADDRESS; + gctUINT32 processID; + gcsLOCK_INFO_PTR lockInfo; + gctUINT32 pageCount; + gckMMU mmu; + gctUINT32 i; + gctUINT32_PTR pageTableEntry; + gctUINT32 offset = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Node = %x", Node); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Node != gcvNULL); + gcmkVERIFY_ARGUMENT(Address != gcvNULL); + + os = Kernel->os; + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + gcmkONERROR(gckOS_GetProcessID(&processID)); + + gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu)); + + gcmkONERROR(gckOS_AcquireMutex(os, Node->mapMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Get map information for current process. */ + gpuMap = _FindGPUMap(Node->mapHead, processID); + + if (gpuMap == gcvNULL) + { + gpuMap = _CreateGPUMap(os, &Node->mapHead, &Node->mapTail, processID); + + if (gpuMap == gcvNULL) + { + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + } + + lockInfo = &gpuMap->lockInfo; + + if (lockInfo->lockeds[Kernel->core] ++ == 0) + { + /* Get necessary information. */ + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + phys = node->VidMem.memory->baseAddress + + node->VidMem.offset + + node->VidMem.alignment; + + /* GPU page table use 4K page. */ + pageCount = ((phys + node->VidMem.bytes + 4096 - 1) >> 12) + - (phys >> 12); + + offset = phys & 0xFFF; + } + else + { + pageCount = node->Virtual.pageCount; + physical = node->Virtual.physical; + } + + /* Allocate pages inside the MMU. */ + gcmkONERROR(gckMMU_AllocatePages( + mmu, + pageCount, + &lockInfo->pageTables[Kernel->core], + &lockInfo->GPUAddresses[Kernel->core])); + + /* Record MMU from which pages are allocated. */ + lockInfo->lockMmus[Kernel->core] = mmu; + + pageTableEntry = lockInfo->pageTables[Kernel->core]; + + /* Fill page table entries. */ + if (phys != gcvINVALID_ADDRESS) + { + gctUINT32 address = lockInfo->GPUAddresses[Kernel->core]; + for (i = 0; i < pageCount; i++) + { + gckMMU_GetPageEntry(mmu, address, &pageTableEntry); + gckMMU_SetPage(mmu, phys & 0xFFFFF000, pageTableEntry); + phys += 4096; + address += 4096; + pageTableEntry += 1; + } + } + else + { + gctUINT32 address = lockInfo->GPUAddresses[Kernel->core]; + gcmkASSERT(physical != gcvNULL); + gcmkONERROR(gckOS_MapPagesEx(os, + Kernel->core, + physical, + pageCount, + address, + pageTableEntry)); + } + + gcmkONERROR(gckMMU_Flush(mmu)); + } + + *Address = lockInfo->GPUAddresses[Kernel->core] + offset; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mapMutex)); + acquired = gcvFALSE; + + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mapMutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckVIDMEM_NODE_Unlock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + IN gctUINT32 ProcessID + ) +{ + gceSTATUS status; + gcsGPU_MAP_PTR gpuMap; + gcsLOCK_INFO_PTR lockInfo; + gckMMU mmu; + gcuVIDMEM_NODE_PTR node; + gctUINT32 pageCount; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%08X, Node = %x, ProcessID=%d", + Kernel, Node, ProcessID); + + gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); + gcmkVERIFY_ARGUMENT(Node != gcvNULL); + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Node->mapMutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Get map information for current process. */ + gpuMap = _FindGPUMap(Node->mapHead, ProcessID); + + if (gpuMap == gcvNULL) + { + /* No mapping for this process. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } + + lockInfo = &gpuMap->lockInfo; + + if (--lockInfo->lockeds[Kernel->core] == 0) + { + node = Node->node; + + /* Get necessary information. */ + if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + { + gctUINT32 phys = node->VidMem.memory->baseAddress + + node->VidMem.offset + + node->VidMem.alignment; + + /* GPU page table use 4K page. */ + pageCount = ((phys + node->VidMem.bytes + 4096 - 1) >> 12) + - (phys >> 12); + } + else + { + pageCount = node->Virtual.pageCount; + } + + /* Get MMU which allocates pages. */ + mmu = lockInfo->lockMmus[Kernel->core]; + + /* Free virtual spaces in page table. */ + gcmkVERIFY_OK(gckMMU_FreePagesEx( + mmu, + lockInfo->GPUAddresses[Kernel->core], + pageCount + )); + + _DestroyGPUMap(Kernel->os, &Node->mapHead, &Node->mapTail, gpuMap); + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Node->mapMutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Node->mapMutex)); + } + + gcmkFOOTER(); + return status; +} +#endif + +/******************************************************************************* +** +** gckVIDMEM_HANDLE_Allocate +** +** Allocate a handle for a gckVIDMEM_NODE object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gckVIDMEM_NODE Node +** Pointer to a gckVIDMEM_NODE object. +** +** OUTPUT: +** +** gctUINT32 * Handle +** Pointer to a variable receiving a handle represent this +** gckVIDMEM_NODE in userspace. +*/ +static gceSTATUS +gckVIDMEM_HANDLE_Allocate( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + OUT gctUINT32 * Handle + ) +{ + gceSTATUS status; + gctUINT32 processID = 0; + gctPOINTER pointer = gcvNULL; + gctPOINTER handleDatabase = gcvNULL; + gctPOINTER mutex = gcvNULL; + gctUINT32 handle = 0; + gckVIDMEM_HANDLE handleObject = gcvNULL; + gckOS os = Kernel->os; + + gcmkHEADER_ARG("Kernel=0x%X, Node=0x%X", Kernel, Node); + + gcmkVERIFY_OBJECT(os, gcvOBJ_OS); + + /* Allocate a gckVIDMEM_HANDLE object. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_HANDLE), &pointer)); + + gcmkVERIFY_OK(gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsVIDMEM_HANDLE))); + + handleObject = pointer; + + gcmkONERROR(gckOS_AtomConstruct(os, &handleObject->reference)); + + /* Set default reference count to 1. */ + gckOS_AtomSet(os, handleObject->reference, 1); + + gcmkVERIFY_OK(gckOS_GetProcessID(&processID)); + + gcmkONERROR( + gckKERNEL_FindHandleDatbase(Kernel, + processID, + &handleDatabase, + &mutex)); + + /* Allocate a handle for this object. */ + gcmkONERROR( + gckKERNEL_AllocateIntegerId(handleDatabase, handleObject, &handle)); + + handleObject->node = Node; + handleObject->handle = handle; + + *Handle = handle; + + gcmkFOOTER_ARG("*Handle=%d", *Handle); + return gcvSTATUS_OK; + +OnError: + if (handleObject != gcvNULL) + { + if (handleObject->reference != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, handleObject->reference)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, handleObject)); + } + + gcmkFOOTER(); + return status; +} + +static gceSTATUS +gckVIDMEM_NODE_Reference( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node + ) +{ + gctINT32 oldValue; + gcmkHEADER_ARG("Kernel=0x%X Node=0x%X", Kernel, Node); + + gckOS_AtomIncrement(Kernel->os, Node->reference, &oldValue); + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +gceSTATUS +gckVIDMEM_HANDLE_Reference( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ) +{ + gceSTATUS status; + gckVIDMEM_HANDLE handleObject = gcvNULL; + gctPOINTER database = gcvNULL; + gctPOINTER mutex = gcvNULL; + gctINT32 oldValue = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID); + + gcmkONERROR( + gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Translate handle to gckVIDMEM_HANDLE object. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject)); + + /* Increase the reference count. */ + gckOS_AtomIncrement(Kernel->os, handleObject->reference, &oldValue); + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + acquired = gcvFALSE; + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckVIDMEM_HANDLE_Dereference( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ) +{ + gceSTATUS status; + gctPOINTER handleDatabase = gcvNULL; + gctPOINTER mutex = gcvNULL; + gctINT32 oldValue = 0; + gckVIDMEM_HANDLE handleObject = gcvNULL; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID); + + gcmkONERROR( + gckKERNEL_FindHandleDatbase(Kernel, + ProcessID, + &handleDatabase, + &mutex)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Translate handle to gckVIDMEM_HANDLE. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(handleDatabase, Handle, (gctPOINTER *)&handleObject)); + + gckOS_AtomDecrement(Kernel->os, handleObject->reference, &oldValue); + + if (oldValue == 1) + { + /* Remove handle from database if this is the last reference. */ + gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(handleDatabase, Handle)); + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + acquired = gcvFALSE; + + if (oldValue == 1) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, handleObject->reference)); + gcmkOS_SAFE_FREE(Kernel->os, handleObject); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckVIDMEM_HANDLE_LookupAndReference( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + OUT gckVIDMEM_NODE * Node + ) +{ + gceSTATUS status; + gckVIDMEM_HANDLE handleObject = gcvNULL; + gckVIDMEM_NODE node = gcvNULL; + gctPOINTER database = gcvNULL; + gctPOINTER mutex = gcvNULL; + gctUINT32 processID = 0; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X Handle=%d", Kernel, Handle); + + gckOS_GetProcessID(&processID); + + gcmkONERROR( + gckKERNEL_FindHandleDatbase(Kernel, processID, &database, &mutex)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Translate handle to gckVIDMEM_HANDLE object. */ + gcmkONERROR( + gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject)); + + /* Get gckVIDMEM_NODE object. */ + node = handleObject->node; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + acquired = gcvFALSE; + + /* Reference this gckVIDMEM_NODE object. */ + gcmkVERIFY_OK(gckVIDMEM_NODE_Reference(Kernel, node)); + + /* Return result. */ + *Node = node; + + gcmkFOOTER_ARG("*Node=%d", *Node); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckVIDMEM_HANDLE_Lookup( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle, + OUT gckVIDMEM_NODE * Node + ) +{ + gceSTATUS status; + gckVIDMEM_HANDLE handleObject = gcvNULL; + gckVIDMEM_NODE node = gcvNULL; + gctPOINTER database = gcvNULL; + gctPOINTER mutex = gcvNULL; + gctBOOL acquired = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X ProcessID=%d Handle=%d", + Kernel, ProcessID, Handle); + + gcmkONERROR( + gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex)); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + acquired = gcvTRUE; + + gcmkONERROR( + gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject)); + + node = handleObject->node; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + acquired = gcvFALSE; + + *Node = node; + + gcmkFOOTER_ARG("*Node=%d", *Node); + return gcvSTATUS_OK; + +OnError: + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVIDMEM_NODE_Allocate +** +** Allocate a gckVIDMEM_NODE object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gcuVIDMEM_NODE_PTR Node +** Pointer to a gcuVIDMEM_NODE union. +** +** OUTPUT: +** +** gctUINT32 * Handle +** Pointer to a variable receiving a handle represent this +** gckVIDMEM_NODE in userspace. +*/ +gceSTATUS +gckVIDMEM_NODE_Allocate( + IN gckKERNEL Kernel, + IN gcuVIDMEM_NODE_PTR VideoNode, + IN gceSURF_TYPE Type, + IN gcePOOL Pool, + IN gctUINT32 * Handle + ) +{ + gceSTATUS status; + gckVIDMEM_NODE node = gcvNULL; + gctPOINTER pointer = gcvNULL; + gctUINT32 handle = 0; + gckOS os = Kernel->os; + + gcmkHEADER_ARG("Kernel=0x%X VideoNode=0x%X", Kernel, VideoNode); + + /* Construct a node. */ + gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_NODE), &pointer)); + + gcmkVERIFY_OK(gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsVIDMEM_NODE))); + + node = pointer; + + node->node = VideoNode; + node->type = Type; + node->pool = Pool; + +#if gcdPROCESS_ADDRESS_SPACE + gcmkONERROR(gckOS_CreateMutex(os, &node->mapMutex)); +#endif + + gcmkONERROR(gckOS_AtomConstruct(os, &node->reference)); + + gcmkONERROR(gckOS_CreateMutex(os, &node->mutex)); + + /* Reference is 1 by default . */ + gckVIDMEM_NODE_Reference(Kernel, node); + + /* Create a handle to represent this node. */ + gcmkONERROR(gckVIDMEM_HANDLE_Allocate(Kernel, node, &handle)); + + *Handle = handle; + + gcmkFOOTER_ARG("*Handle=%d", *Handle); + return gcvSTATUS_OK; + +OnError: + if (node != gcvNULL) + { +#if gcdPROCESS_ADDRESS_SPACE + if (node->mapMutex != gcvNULL) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->mapMutex)); + } +#endif + + if (node->mutex) + { + gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->mutex)); + } + + if (node->reference != gcvNULL) + { + gcmkVERIFY_OK(gckOS_AtomDestroy(os, node->reference)); + } + + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node)); + } + + gcmkFOOTER(); + return status; +} + +gceSTATUS +gckVIDMEM_NODE_Dereference( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node + ) +{ + gctINT32 oldValue = 0; + gctPOINTER database = Kernel->db->nameDatabase; + gctPOINTER mutex = Kernel->db->nameDatabaseMutex; + + gcmkHEADER_ARG("Kernel=0x%X Node=0x%X", Kernel, Node); + + gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + + gcmkVERIFY_OK(gckOS_AtomDecrement(Kernel->os, Node->reference, &oldValue)); + + if (oldValue == 1 && Node->name) + { + /* Free name if exists. */ + gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(database, Node->name)); + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + + if (oldValue == 1) + { + /* Free gcuVIDMEM_NODE. */ + gcmkVERIFY_OK(gckVIDMEM_Free(Kernel, Node->node)); + gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, Node->reference)); +#if gcdPROCESS_ADDRESS_SPACE + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Node->mapMutex)); +#endif + gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Node->mutex)); + gcmkOS_SAFE_FREE(Kernel->os, Node); + } + + gcmkFOOTER_NO(); + return gcvSTATUS_OK; +} + +/******************************************************************************* +** +** gckVIDMEM_NODE_Name +** +** Naming a gckVIDMEM_NODE object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 Handle +** Handle to a gckVIDMEM_NODE object. +** +** OUTPUT: +** +** gctUINT32 * Name +** Pointer to a variable receiving a name which can be pass to another +** process. +*/ +gceSTATUS +gckVIDMEM_NODE_Name( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + IN gctUINT32 * Name + ) +{ + gceSTATUS status; + gckVIDMEM_NODE node = gcvNULL; + gctUINT32 name = 0; + gctUINT32 processID = 0; + gctPOINTER database = Kernel->db->nameDatabase; + gctPOINTER mutex = Kernel->db->nameDatabaseMutex; + gctBOOL acquired = gcvFALSE; + gctBOOL referenced = gcvFALSE; + gcmkHEADER_ARG("Kernel=0x%X Handle=%d", Kernel, Handle); + + gcmkONERROR(gckOS_GetProcessID(&processID)); + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + acquired = gcvTRUE; + + gcmkONERROR(gckVIDMEM_HANDLE_LookupAndReference(Kernel, Handle, &node)); + referenced = gcvTRUE; + + if (node->name == 0) + { + /* Name this node. */ + gcmkONERROR(gckKERNEL_AllocateIntegerId(database, node, &name)); + node->name = name; + } + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + acquired = gcvFALSE; + + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node)); + + if(node) + { + *Name = node->name; + } + + gcmkFOOTER_ARG("*Name=%d", *Name); + return gcvSTATUS_OK; + +OnError: + if (referenced) + { + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node)); + } + + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + } + + gcmkFOOTER(); + return status; +} + +/******************************************************************************* +** +** gckVIDMEM_NODE_Import +** +** Import a gckVIDMEM_NODE object. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 Name +** Name of a gckVIDMEM_NODE object. +** +** OUTPUT: +** +** gctUINT32 * Handle +** Pointer to a variable receiving a handle represent this +** gckVIDMEM_NODE in userspace. +*/ +gceSTATUS +gckVIDMEM_NODE_Import( + IN gckKERNEL Kernel, + IN gctUINT32 Name, + IN gctUINT32 * Handle + ) +{ + gceSTATUS status; + gckVIDMEM_NODE node = gcvNULL; + gctPOINTER database = Kernel->db->nameDatabase; + gctPOINTER mutex = Kernel->db->nameDatabaseMutex; + gctBOOL acquired = gcvFALSE; + gctBOOL referenced = gcvFALSE; + + gcmkHEADER_ARG("Kernel=0x%X Name=%d", Kernel, Name); + + gcmkONERROR(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE)); + acquired = gcvTRUE; + + /* Lookup in database to get the node. */ + gcmkONERROR(gckKERNEL_QueryIntegerId(database, Name, (gctPOINTER *)&node)); + + /* Reference the node. */ + gcmkONERROR(gckVIDMEM_NODE_Reference(Kernel, node)); + referenced = gcvTRUE; + + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + acquired = gcvFALSE; + + /* Allocate a handle for current process. */ + gcmkONERROR(gckVIDMEM_HANDLE_Allocate(Kernel, node, Handle)); + + gcmkFOOTER_ARG("*Handle=%d", *Handle); + return gcvSTATUS_OK; + +OnError: + if (referenced) + { + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node)); + } + + if (acquired) + { + gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex)); + } + + gcmkFOOTER(); + return status; +} + + +typedef struct _gcsVIDMEM_NODE_FDPRIVATE +{ + gcsFDPRIVATE base; + gckKERNEL kernel; + gckVIDMEM_NODE node; +} +gcsVIDMEM_NODE_FDPRIVATE; + + +static gctINT +_ReleaseFdPrivate( + gcsFDPRIVATE_PTR FdPrivate + ) +{ + /* Cast private info. */ + gcsVIDMEM_NODE_FDPRIVATE * private = (gcsVIDMEM_NODE_FDPRIVATE *) FdPrivate; + + gckVIDMEM_NODE_Dereference(private->kernel, private->node); + gckOS_Free(private->kernel->os, private); + + return 0; +} + +/******************************************************************************* +** +** gckVIDMEM_NODE_GetFd +** +** Attach a gckVIDMEM_NODE object to a native fd. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** gctUINT32 Handle +** Handle to a gckVIDMEM_NODE object. +** +** OUTPUT: +** +** gctUINT32 * Fd +** Pointer to a variable receiving a native fd from os. +*/ +gceSTATUS +gckVIDMEM_NODE_GetFd( + IN gckKERNEL Kernel, + IN gctUINT32 Handle, + OUT gctINT * Fd + ) +{ + gceSTATUS status; + gckVIDMEM_NODE node = gcvNULL; + gctBOOL referenced = gcvFALSE; + gcsVIDMEM_NODE_FDPRIVATE * fdPrivate = gcvNULL; + gcmkHEADER_ARG("Kernel=0x%X Handle=%d", Kernel, Handle); + + /* Query and reference handle. */ + gcmkONERROR(gckVIDMEM_HANDLE_LookupAndReference(Kernel, Handle, &node)); + referenced = gcvTRUE; + + /* Allocate memory for private info. */ + gcmkONERROR(gckOS_Allocate( + Kernel->os, + gcmSIZEOF(gcsVIDMEM_NODE_FDPRIVATE), + (gctPOINTER *)&fdPrivate + )); + + fdPrivate->base.release = _ReleaseFdPrivate; + fdPrivate->kernel = Kernel; + fdPrivate->node = node; + + /* Allocated fd owns a reference. */ + gcmkONERROR(gckOS_GetFd("vidmem", &fdPrivate->base, Fd)); + + gcmkFOOTER_ARG("*Fd=%d", *Fd); + return gcvSTATUS_OK; + +OnError: + if (referenced) + { + gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node)); + } + + if (fdPrivate) + { + gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, fdPrivate)); + } + + gcmkFOOTER(); + return status; +} + diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h new file mode 100644 index 00000000000000..f4b7d991128294 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -0,0 +1,2859 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_h_ +#define __gc_hal_h_ + +#include "gc_hal_rename.h" +#include "gc_hal_types.h" +#include "gc_hal_enum.h" +#include "gc_hal_base.h" +#include "gc_hal_profiler.h" +#include "gc_hal_driver.h" +#if gcdENABLE_3D +#include "gc_hal_statistics.h" +#endif + +#if gcdSECURITY +#include "gc_hal_security_interface.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +******************************* Alignment Macros ******************************* +\******************************************************************************/ + +/* Alignment with a non-power of two value. */ +#define gcmALIGN_NP2(n, align) \ +( \ + ((n) + (align) - 1) - (((n) + (align) - 1) % (align)) \ +) + +/* Alignment with a power of two value. */ +#define gcmALIGN(n, align) \ +( \ + ((n) + ((align) - 1)) & ~((align) - 1) \ +) + +#define gcmALIGN_BASE(n, align) \ +( \ + ((n) & ~((align) - 1)) \ +) + +/******************************************************************************\ +***************************** Element Count Macro ***************************** +\******************************************************************************/ + +#define gcmSIZEOF(a) \ +( \ + (gctSIZE_T) (sizeof(a)) \ +) + +#define gcmCOUNTOF(a) \ +( \ + sizeof(a) / sizeof(a[0]) \ +) + +/******************************************************************************\ +********************************* Cast Macro ********************************** +\******************************************************************************/ +#define gcmNAME_TO_PTR(na) \ + gckKERNEL_QueryPointerFromName(kernel, gcmALL_TO_UINT32(na)) + +#define gcmPTR_TO_NAME(ptr) \ + gckKERNEL_AllocateNameFromPointer(kernel, ptr) + +#define gcmRELEASE_NAME(na) \ + gckKERNEL_DeleteName(kernel, gcmALL_TO_UINT32(na)) + +#define gcmALL_TO_UINT32(t) \ +( \ + (gctUINT32) (gctUINTPTR_T) (t)\ +) + +#define gcmPTR_TO_UINT64(p) \ +( \ + (gctUINT64) (gctUINTPTR_T) (p)\ +) + +#define gcmUINT64_TO_PTR(u) \ +( \ + (gctPOINTER) (gctUINTPTR_T) (u)\ +) + +#define gcmUINT64_TO_TYPE(u, t) \ +( \ + (t) (gctUINTPTR_T) (u)\ +) + +/******************************************************************************\ +******************************** Useful Macro ********************************* +\******************************************************************************/ + +#define gcvINVALID_ADDRESS ~0U + +#define gcmGET_PRE_ROTATION(rotate) \ + ((rotate) & (~(gcvSURF_POST_FLIP_X | gcvSURF_POST_FLIP_Y))) + +#define gcmGET_POST_ROTATION(rotate) \ + ((rotate) & (gcvSURF_POST_FLIP_X | gcvSURF_POST_FLIP_Y)) + +/******************************************************************************\ +******************************** gcsOBJECT Object ******************************* +\******************************************************************************/ + +/* Type of objects. */ +typedef enum _gceOBJECT_TYPE +{ + gcvOBJ_UNKNOWN = 0, + gcvOBJ_2D = gcmCC('2','D',' ',' '), + gcvOBJ_3D = gcmCC('3','D',' ',' '), + gcvOBJ_ATTRIBUTE = gcmCC('A','T','T','R'), + gcvOBJ_BRUSHCACHE = gcmCC('B','R','U','$'), + gcvOBJ_BRUSHNODE = gcmCC('B','R','U','n'), + gcvOBJ_BRUSH = gcmCC('B','R','U','o'), + gcvOBJ_BUFFER = gcmCC('B','U','F','R'), + gcvOBJ_COMMAND = gcmCC('C','M','D',' '), + gcvOBJ_COMMANDBUFFER = gcmCC('C','M','D','B'), + gcvOBJ_CONTEXT = gcmCC('C','T','X','T'), + gcvOBJ_DEVICE = gcmCC('D','E','V',' '), + gcvOBJ_DUMP = gcmCC('D','U','M','P'), + gcvOBJ_EVENT = gcmCC('E','V','N','T'), + gcvOBJ_FUNCTION = gcmCC('F','U','N','C'), + gcvOBJ_HAL = gcmCC('H','A','L',' '), + gcvOBJ_HARDWARE = gcmCC('H','A','R','D'), + gcvOBJ_HEAP = gcmCC('H','E','A','P'), + gcvOBJ_INDEX = gcmCC('I','N','D','X'), + gcvOBJ_INTERRUPT = gcmCC('I','N','T','R'), + gcvOBJ_KERNEL = gcmCC('K','E','R','N'), + gcvOBJ_KERNEL_FUNCTION = gcmCC('K','F','C','N'), + gcvOBJ_MEMORYBUFFER = gcmCC('M','E','M','B'), + gcvOBJ_MMU = gcmCC('M','M','U',' '), + gcvOBJ_OS = gcmCC('O','S',' ',' '), + gcvOBJ_OUTPUT = gcmCC('O','U','T','P'), + gcvOBJ_PAINT = gcmCC('P','N','T',' '), + gcvOBJ_PATH = gcmCC('P','A','T','H'), + gcvOBJ_QUEUE = gcmCC('Q','U','E',' '), + gcvOBJ_SAMPLER = gcmCC('S','A','M','P'), + gcvOBJ_SHADER = gcmCC('S','H','D','R'), + gcvOBJ_STREAM = gcmCC('S','T','R','M'), + gcvOBJ_SURF = gcmCC('S','U','R','F'), + gcvOBJ_TEXTURE = gcmCC('T','X','T','R'), + gcvOBJ_UNIFORM = gcmCC('U','N','I','F'), + gcvOBJ_VARIABLE = gcmCC('V','A','R','I'), + gcvOBJ_VERTEX = gcmCC('V','R','T','X'), + gcvOBJ_VIDMEM = gcmCC('V','M','E','M'), + gcvOBJ_VG = gcmCC('V','G',' ',' '), + gcvOBJ_BUFOBJ = gcmCC('B','U','F','O'), + gcvOBJ_UNIFORM_BLOCK = gcmCC('U','B','L','K'), + gcvOBJ_CL = gcmCC('C','L',' ',' '), +} +gceOBJECT_TYPE; + +/* gcsOBJECT object defintinon. */ +typedef struct _gcsOBJECT +{ + /* Type of an object. */ + gceOBJECT_TYPE type; +} +gcsOBJECT; + +typedef struct _gckHARDWARE * gckHARDWARE; + +/* CORE flags. */ +typedef enum _gceCORE +{ + gcvCORE_MAJOR = 0x0, + gcvCORE_2D = 0x1, + gcvCORE_VG = 0x2, +#if gcdMULTI_GPU_AFFINITY + gcvCORE_OCL = 0x3, +#endif +} +gceCORE; + +#if gcdMULTI_GPU_AFFINITY +#define gcdMAX_GPU_COUNT 4 +#else +#define gcdMAX_GPU_COUNT 3 +#endif + +#define gcdMAX_SURF_LAYER 4 + +#define gcdMAX_DRAW_BUFFERS 4 + +/******************************************************************************* +** +** gcmVERIFY_OBJECT +** +** Assert if an object is invalid or is not of the specified type. If the +** object is invalid or not of the specified type, gcvSTATUS_INVALID_OBJECT +** will be returned from the current function. In retail mode this macro +** does nothing. +** +** ARGUMENTS: +** +** obj Object to test. +** t Expected type of the object. +*/ +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +#define _gcmVERIFY_OBJECT(prefix, obj, t) \ + if ((obj) == gcvNULL) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "VERIFY_OBJECT failed: NULL"); \ + prefix##TRACE(gcvLEVEL_ERROR, " expected: %c%c%c%c", \ + gcmCC_PRINT(t)); \ + prefix##ASSERT((obj) != gcvNULL); \ + prefix##FOOTER_ARG("status=%d", gcvSTATUS_INVALID_OBJECT); \ + return gcvSTATUS_INVALID_OBJECT; \ + } \ + else if (((gcsOBJECT*) (obj))->type != t) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "VERIFY_OBJECT failed: %c%c%c%c", \ + gcmCC_PRINT(((gcsOBJECT*) (obj))->type)); \ + prefix##TRACE(gcvLEVEL_ERROR, " expected: %c%c%c%c", \ + gcmCC_PRINT(t)); \ + prefix##ASSERT(((gcsOBJECT*)(obj))->type == t); \ + prefix##FOOTER_ARG("status=%d", gcvSTATUS_INVALID_OBJECT); \ + return gcvSTATUS_INVALID_OBJECT; \ + } + +# define gcmVERIFY_OBJECT(obj, t) _gcmVERIFY_OBJECT(gcm, obj, t) +# define gcmkVERIFY_OBJECT(obj, t) _gcmVERIFY_OBJECT(gcmk, obj, t) +#else +# define gcmVERIFY_OBJECT(obj, t) do {} while (gcvFALSE) +# define gcmkVERIFY_OBJECT(obj, t) do {} while (gcvFALSE) +#endif + +/******************************************************************************/ +/*VERIFY_OBJECT if special return expected*/ +/******************************************************************************/ +#ifndef EGL_API_ANDROID +# define _gcmVERIFY_OBJECT_RETURN(prefix, obj, t, retVal) \ + do \ + { \ + if ((obj) == gcvNULL) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "VERIFY_OBJECT_RETURN failed: NULL"); \ + prefix##TRACE(gcvLEVEL_ERROR, " expected: %c%c%c%c", \ + gcmCC_PRINT(t)); \ + prefix##ASSERT((obj) != gcvNULL); \ + prefix##FOOTER_ARG("retVal=%d", retVal); \ + return retVal; \ + } \ + else if (((gcsOBJECT*) (obj))->type != t) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "VERIFY_OBJECT_RETURN failed: %c%c%c%c", \ + gcmCC_PRINT(((gcsOBJECT*) (obj))->type)); \ + prefix##TRACE(gcvLEVEL_ERROR, " expected: %c%c%c%c", \ + gcmCC_PRINT(t)); \ + prefix##ASSERT(((gcsOBJECT*)(obj))->type == t); \ + prefix##FOOTER_ARG("retVal=%d", retVal); \ + return retVal; \ + } \ + } \ + while (gcvFALSE) +# define gcmVERIFY_OBJECT_RETURN(obj, t, retVal) \ + _gcmVERIFY_OBJECT_RETURN(gcm, obj, t, retVal) +# define gcmkVERIFY_OBJECT_RETURN(obj, t, retVal) \ + _gcmVERIFY_OBJECT_RETURN(gcmk, obj, t, retVal) +#else +# define gcmVERIFY_OBJECT_RETURN(obj, t) do {} while (gcvFALSE) +# define gcmVERIFY_OBJECT_RETURN(obj, t) do {} while (gcvFALSE) +#endif + +/******************************************************************************\ +********************************** gckOS Object ********************************* +\******************************************************************************/ + +/* Construct a new gckOS object. */ +gceSTATUS +gckOS_Construct( + IN gctPOINTER Context, + OUT gckOS * Os + ); + +/* Destroy an gckOS object. */ +gceSTATUS +gckOS_Destroy( + IN gckOS Os + ); + +/* Query the video memory. */ +gceSTATUS +gckOS_QueryVideoMemory( + IN gckOS Os, + OUT gctPHYS_ADDR * InternalAddress, + OUT gctSIZE_T * InternalSize, + OUT gctPHYS_ADDR * ExternalAddress, + OUT gctSIZE_T * ExternalSize, + OUT gctPHYS_ADDR * ContiguousAddress, + OUT gctSIZE_T * ContiguousSize + ); + +/* Allocate memory from the heap. */ +gceSTATUS +gckOS_Allocate( + IN gckOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ); + +/* Free allocated memory. */ +gceSTATUS +gckOS_Free( + IN gckOS Os, + IN gctPOINTER Memory + ); + +/* Wrapper for allocation memory.. */ +gceSTATUS +gckOS_AllocateMemory( + IN gckOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ); + +/* Wrapper for freeing memory. */ +gceSTATUS +gckOS_FreeMemory( + IN gckOS Os, + IN gctPOINTER Memory + ); + +/* Allocate paged memory. */ +gceSTATUS +gckOS_AllocatePagedMemory( + IN gckOS Os, + IN gctSIZE_T Bytes, + OUT gctPHYS_ADDR * Physical + ); + +/* Allocate paged memory. */ +gceSTATUS +gckOS_AllocatePagedMemoryEx( + IN gckOS Os, + IN gctUINT32 Flag, + IN gctSIZE_T Bytes, + OUT gctUINT32 * Gid, + OUT gctPHYS_ADDR * Physical + ); + +/* Lock pages. */ +gceSTATUS +gckOS_LockPages( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctBOOL Cacheable, + OUT gctPOINTER * Logical, + OUT gctSIZE_T * PageCount + ); + +/* Map pages. */ +gceSTATUS +gckOS_MapPages( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T PageCount, + IN gctPOINTER PageTable + ); + +/* Map pages. */ +gceSTATUS +gckOS_MapPagesEx( + IN gckOS Os, + IN gceCORE Core, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T PageCount, + IN gctUINT32 Address, + IN gctPOINTER PageTable + ); + +gceSTATUS +gckOS_UnmapPages( + IN gckOS Os, + IN gctSIZE_T PageCount, + IN gctUINT32 Address + ); + +/* Unlock pages. */ +gceSTATUS +gckOS_UnlockPages( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +/* Free paged memory. */ +gceSTATUS +gckOS_FreePagedMemory( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes + ); + +/* Allocate non-paged memory. */ +gceSTATUS +gckOS_AllocateNonPagedMemory( + IN gckOS Os, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ); + +/* Free non-paged memory. */ +gceSTATUS +gckOS_FreeNonPagedMemory( + IN gckOS Os, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical + ); + +/* Allocate contiguous memory. */ +gceSTATUS +gckOS_AllocateContiguous( + IN gckOS Os, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ); + +/* Free contiguous memory. */ +gceSTATUS +gckOS_FreeContiguous( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); + +/* Get the number fo bytes per page. */ +gceSTATUS +gckOS_GetPageSize( + IN gckOS Os, + OUT gctSIZE_T * PageSize + ); + +/* Get the physical address of a corresponding logical address. */ +gceSTATUS +gckOS_GetPhysicalAddress( + IN gckOS Os, + IN gctPOINTER Logical, + OUT gctUINT32 * Address + ); + +/* Get the physical address of a corresponding user logical address. */ +gceSTATUS +gckOS_UserLogicalToPhysical( + IN gckOS Os, + IN gctPOINTER Logical, + OUT gctUINT32 * Address + ); + +/* Get the physical address of a corresponding logical address. */ +gceSTATUS +gckOS_GetPhysicalAddressProcess( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctUINT32 ProcessID, + OUT gctUINT32 * Address + ); + +/* Map physical memory. */ +gceSTATUS +gckOS_MapPhysical( + IN gckOS Os, + IN gctUINT32 Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical + ); + +/* Unmap previously mapped physical memory. */ +gceSTATUS +gckOS_UnmapPhysical( + IN gckOS Os, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); + +/* Get real physical address from descriptor. */ +gceSTATUS +gckOS_PhysicalToPhysicalAddress( + IN gckOS Os, + IN gctPOINTER Physical, + OUT gctUINT32 * PhysicalAddress + ); + +/* Read data from a hardware register. */ +gceSTATUS +gckOS_ReadRegister( + IN gckOS Os, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ); + +/* Read data from a hardware register. */ +gceSTATUS +gckOS_ReadRegisterEx( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ); + +/* Write data to a hardware register. */ +gceSTATUS +gckOS_WriteRegister( + IN gckOS Os, + IN gctUINT32 Address, + IN gctUINT32 Data + ); + +/* Write data to a hardware register. */ +gceSTATUS +gckOS_WriteRegisterEx( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address, + IN gctUINT32 Data + ); + +#if gcdMULTI_GPU +gceSTATUS +gckOS_ReadRegisterByCoreId( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 CoreId, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ); + +gceSTATUS +gckOS_WriteRegisterByCoreId( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 CoreId, + IN gctUINT32 Address, + IN gctUINT32 Data + ); +#endif + +/* Write data to a 32-bit memory location. */ +gceSTATUS +gckOS_WriteMemory( + IN gckOS Os, + IN gctPOINTER Address, + IN gctUINT32 Data + ); + +/* Map physical memory into the process space. */ +gceSTATUS +gckOS_MapMemory( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical + ); + +/* Unmap physical memory from the specified process space. */ +gceSTATUS +gckOS_UnmapMemoryEx( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical, + IN gctUINT32 PID + ); + +/* Unmap physical memory from the process space. */ +gceSTATUS +gckOS_UnmapMemory( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +/* Unmap user logical memory out of physical memory. + * This function is only supported in Linux currently. + */ +gceSTATUS +gckOS_UnmapUserLogical( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +/* Create a new mutex. */ +gceSTATUS +gckOS_CreateMutex( + IN gckOS Os, + OUT gctPOINTER * Mutex + ); + +/* Delete a mutex. */ +gceSTATUS +gckOS_DeleteMutex( + IN gckOS Os, + IN gctPOINTER Mutex + ); + +/* Acquire a mutex. */ +gceSTATUS +gckOS_AcquireMutex( + IN gckOS Os, + IN gctPOINTER Mutex, + IN gctUINT32 Timeout + ); + +/* Release a mutex. */ +gceSTATUS +gckOS_ReleaseMutex( + IN gckOS Os, + IN gctPOINTER Mutex + ); + +/* Atomically exchange a pair of 32-bit values. */ +gceSTATUS +gckOS_AtomicExchange( + IN gckOS Os, + IN OUT gctUINT32_PTR Target, + IN gctUINT32 NewValue, + OUT gctUINT32_PTR OldValue + ); + +/* Atomically exchange a pair of pointers. */ +gceSTATUS +gckOS_AtomicExchangePtr( + IN gckOS Os, + IN OUT gctPOINTER * Target, + IN gctPOINTER NewValue, + OUT gctPOINTER * OldValue + ); + +gceSTATUS +gckOS_AtomSetMask( + IN gctPOINTER Atom, + IN gctUINT32 Mask + ); + +gceSTATUS +gckOS_AtomClearMask( + IN gctPOINTER Atom, + IN gctUINT32 Mask + ); + +gceSTATUS +gckOS_DumpCallStack( + IN gckOS Os + ); + +gceSTATUS +gckOS_GetProcessNameByPid( + IN gctINT Pid, + IN gctSIZE_T Length, + OUT gctUINT8_PTR String + ); + +/******************************************************************************* +** +** gckOS_AtomConstruct +** +** Create an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** OUTPUT: +** +** gctPOINTER * Atom +** Pointer to a variable receiving the constructed atom. +*/ +gceSTATUS +gckOS_AtomConstruct( + IN gckOS Os, + OUT gctPOINTER * Atom + ); + +/******************************************************************************* +** +** gckOS_AtomDestroy +** +** Destroy an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom to destroy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomDestroy( + IN gckOS Os, + OUT gctPOINTER Atom + ); + +/******************************************************************************* +** +** gckOS_AtomGet +** +** Get the 32-bit value protected by an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** OUTPUT: +** +** gctINT32_PTR Value +** Pointer to a variable the receives the value of the atom. +*/ +gceSTATUS +gckOS_AtomGet( + IN gckOS Os, + IN gctPOINTER Atom, + OUT gctINT32_PTR Value + ); + +/******************************************************************************* +** +** gckOS_AtomSet +** +** Set the 32-bit value protected by an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** gctINT32 Value +** The value of the atom. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_AtomSet( + IN gckOS Os, + IN gctPOINTER Atom, + IN gctINT32 Value + ); + +/******************************************************************************* +** +** gckOS_AtomIncrement +** +** Atomically increment the 32-bit integer value inside an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** OUTPUT: +** +** gctINT32_PTR Value +** Pointer to a variable the receives the original value of the atom. +*/ +gceSTATUS +gckOS_AtomIncrement( + IN gckOS Os, + IN gctPOINTER Atom, + OUT gctINT32_PTR Value + ); + +/******************************************************************************* +** +** gckOS_AtomDecrement +** +** Atomically decrement the 32-bit integer value inside an atom. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gctPOINTER Atom +** Pointer to the atom. +** +** OUTPUT: +** +** gctINT32_PTR Value +** Pointer to a variable the receives the original value of the atom. +*/ +gceSTATUS +gckOS_AtomDecrement( + IN gckOS Os, + IN gctPOINTER Atom, + OUT gctINT32_PTR Value + ); + +/* Delay a number of microseconds. */ +gceSTATUS +gckOS_Delay( + IN gckOS Os, + IN gctUINT32 Delay + ); + +/* Get time in milliseconds. */ +gceSTATUS +gckOS_GetTicks( + OUT gctUINT32_PTR Time + ); + +/* Compare time value. */ +gceSTATUS +gckOS_TicksAfter( + IN gctUINT32 Time1, + IN gctUINT32 Time2, + OUT gctBOOL_PTR IsAfter + ); + +/* Get time in microseconds. */ +gceSTATUS +gckOS_GetTime( + OUT gctUINT64_PTR Time + ); + +/* Memory barrier. */ +gceSTATUS +gckOS_MemoryBarrier( + IN gckOS Os, + IN gctPOINTER Address + ); + +/* Map user pointer. */ +gceSTATUS +gckOS_MapUserPointer( + IN gckOS Os, + IN gctPOINTER Pointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ); + +/* Unmap user pointer. */ +gceSTATUS +gckOS_UnmapUserPointer( + IN gckOS Os, + IN gctPOINTER Pointer, + IN gctSIZE_T Size, + IN gctPOINTER KernelPointer + ); + +/******************************************************************************* +** +** gckOS_QueryNeedCopy +** +** Query whether the memory can be accessed or mapped directly or it has to be +** copied. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctUINT32 ProcessID +** Process ID of the current process. +** +** OUTPUT: +** +** gctBOOL_PTR NeedCopy +** Pointer to a boolean receiving gcvTRUE if the memory needs a copy or +** gcvFALSE if the memory can be accessed or mapped dircetly. +*/ +gceSTATUS +gckOS_QueryNeedCopy( + IN gckOS Os, + IN gctUINT32 ProcessID, + OUT gctBOOL_PTR NeedCopy + ); + +/******************************************************************************* +** +** gckOS_CopyFromUserData +** +** Copy data from user to kernel memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER KernelPointer +** Pointer to kernel memory. +** +** gctPOINTER Pointer +** Pointer to user memory. +** +** gctSIZE_T Size +** Number of bytes to copy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_CopyFromUserData( + IN gckOS Os, + IN gctPOINTER KernelPointer, + IN gctPOINTER Pointer, + IN gctSIZE_T Size + ); + +/******************************************************************************* +** +** gckOS_CopyToUserData +** +** Copy data from kernel to user memory. +** +** INPUT: +** +** gckOS Os +** Pointer to an gckOS object. +** +** gctPOINTER KernelPointer +** Pointer to kernel memory. +** +** gctPOINTER Pointer +** Pointer to user memory. +** +** gctSIZE_T Size +** Number of bytes to copy. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_CopyToUserData( + IN gckOS Os, + IN gctPOINTER KernelPointer, + IN gctPOINTER Pointer, + IN gctSIZE_T Size + ); + +gceSTATUS +gckOS_SuspendInterrupt( + IN gckOS Os + ); + +gceSTATUS +gckOS_SuspendInterruptEx( + IN gckOS Os, + IN gceCORE Core + ); + +gceSTATUS +gckOS_ResumeInterrupt( + IN gckOS Os + ); + +gceSTATUS +gckOS_ResumeInterruptEx( + IN gckOS Os, + IN gceCORE Core + ); + +/* Get the base address for the physical memory. */ +gceSTATUS +gckOS_GetBaseAddress( + IN gckOS Os, + OUT gctUINT32_PTR BaseAddress + ); + +/* Perform a memory copy. */ +gceSTATUS +gckOS_MemCopy( + IN gctPOINTER Destination, + IN gctCONST_POINTER Source, + IN gctSIZE_T Bytes + ); + +/* Zero memory. */ +gceSTATUS +gckOS_ZeroMemory( + IN gctPOINTER Memory, + IN gctSIZE_T Bytes + ); + +/* Device I/O control to the kernel HAL layer. */ +gceSTATUS +gckOS_DeviceControl( + IN gckOS Os, + IN gctBOOL FromUser, + IN gctUINT32 IoControlCode, + IN gctPOINTER InputBuffer, + IN gctSIZE_T InputBufferSize, + OUT gctPOINTER OutputBuffer, + IN gctSIZE_T OutputBufferSize + ); + +/******************************************************************************* +** +** gckOS_GetProcessID +** +** Get current process ID. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** gctUINT32_PTR ProcessID +** Pointer to the variable that receives the process ID. +*/ +gceSTATUS +gckOS_GetProcessID( + OUT gctUINT32_PTR ProcessID + ); + +gceSTATUS +gckOS_GetCurrentProcessID( + OUT gctUINT32_PTR ProcessID + ); + +/******************************************************************************* +** +** gckOS_GetThreadID +** +** Get current thread ID. +** +** INPUT: +** +** Nothing. +** +** OUTPUT: +** +** gctUINT32_PTR ThreadID +** Pointer to the variable that receives the thread ID. +*/ +gceSTATUS +gckOS_GetThreadID( + OUT gctUINT32_PTR ThreadID + ); + +#if gcdSECURITY +gceSTATUS +gckOS_OpenSecurityChannel( + IN gckOS Os, + IN gceCORE Core, + OUT gctUINT32 *Channel + ); + +gceSTATUS +gckOS_CloseSecurityChannel( + IN gctUINT32 Channel + ); + +gceSTATUS +gckOS_CallSecurityService( + IN gctUINT32 Channel, + IN gcsTA_INTERFACE * Interface + ); + +gceSTATUS +gckOS_InitSecurityChannel( + OUT gctUINT32 Channel + ); + +gceSTATUS +gckOS_AllocatePageArray( + IN gckOS Os, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T PageCount, + OUT gctPOINTER * PageArrayLogical, + OUT gctPHYS_ADDR * PageArrayPhysical + ); +#endif + +/******************************************************************************\ +********************************** Signal Object ********************************* +\******************************************************************************/ + +/* Create a signal. */ +gceSTATUS +gckOS_CreateSignal( + IN gckOS Os, + IN gctBOOL ManualReset, + OUT gctSIGNAL * Signal + ); + +/* Destroy a signal. */ +gceSTATUS +gckOS_DestroySignal( + IN gckOS Os, + IN gctSIGNAL Signal + ); + +/* Signal a signal. */ +gceSTATUS +gckOS_Signal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctBOOL State + ); + +/* Wait for a signal. */ +gceSTATUS +gckOS_WaitSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctUINT32 Wait + ); + +/* Map a user signal to the kernel space. */ +gceSTATUS +gckOS_MapSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctHANDLE Process, + OUT gctSIGNAL * MappedSignal + ); + +/* Unmap a user signal */ +gceSTATUS +gckOS_UnmapSignal( + IN gckOS Os, + IN gctSIGNAL Signal + ); + +/* Map user memory. */ +gceSTATUS +gckOS_MapUserMemory( + IN gckOS Os, + IN gceCORE Core, + IN gctPOINTER Memory, + IN gctUINT32 Physical, + IN gctSIZE_T Size, + OUT gctPOINTER * Info, + OUT gctUINT32_PTR Address + ); + +/* Unmap user memory. */ +gceSTATUS +gckOS_UnmapUserMemory( + IN gckOS Os, + IN gceCORE Core, + IN gctPOINTER Memory, + IN gctSIZE_T Size, + IN gctPOINTER Info, + IN gctUINT32 Address + ); + +/******************************************************************************\ +************************** Android Native Fence Sync *************************** +\******************************************************************************/ +gceSTATUS +gckOS_CreateSyncTimeline( + IN gckOS Os, + OUT gctHANDLE * Timeline + ); + +gceSTATUS +gckOS_DestroySyncTimeline( + IN gckOS Os, + IN gctHANDLE Timeline + ); + +gceSTATUS +gckOS_CreateSyncPoint( + IN gckOS Os, + OUT gctSYNC_POINT * SyncPoint + ); + +gceSTATUS +gckOS_ReferenceSyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint + ); + +gceSTATUS +gckOS_DestroySyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint + ); + +gceSTATUS +gckOS_SignalSyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint + ); + +gceSTATUS +gckOS_QuerySyncPoint( + IN gckOS Os, + IN gctSYNC_POINT SyncPoint, + OUT gctBOOL_PTR State + ); + +gceSTATUS +gckOS_CreateNativeFence( + IN gckOS Os, + IN gctHANDLE Timeline, + IN gctSYNC_POINT SyncPoint, + OUT gctINT * FenceFD + ); + +#if !USE_NEW_LINUX_SIGNAL +/* Create signal to be used in the user space. */ +gceSTATUS +gckOS_CreateUserSignal( + IN gckOS Os, + IN gctBOOL ManualReset, + OUT gctINT * SignalID + ); + +/* Destroy signal used in the user space. */ +gceSTATUS +gckOS_DestroyUserSignal( + IN gckOS Os, + IN gctINT SignalID + ); + +/* Wait for signal used in the user space. */ +gceSTATUS +gckOS_WaitUserSignal( + IN gckOS Os, + IN gctINT SignalID, + IN gctUINT32 Wait + ); + +/* Signal a signal used in the user space. */ +gceSTATUS +gckOS_SignalUserSignal( + IN gckOS Os, + IN gctINT SignalID, + IN gctBOOL State + ); +#endif /* USE_NEW_LINUX_SIGNAL */ + +/* Set a signal owned by a process. */ +#if defined(__QNXNTO__) +gceSTATUS +gckOS_UserSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctINT Recvid, + IN gctINT Coid + ); +#else +gceSTATUS +gckOS_UserSignal( + IN gckOS Os, + IN gctSIGNAL Signal, + IN gctHANDLE Process + ); +#endif + +/******************************************************************************\ +** Cache Support +*/ + +gceSTATUS +gckOS_CacheClean( + gckOS Os, + gctUINT32 ProcessID, + gctPHYS_ADDR Handle, + gctUINT32 Physical, + gctPOINTER Logical, + gctSIZE_T Bytes + ); + +gceSTATUS +gckOS_CacheFlush( + gckOS Os, + gctUINT32 ProcessID, + gctPHYS_ADDR Handle, + gctUINT32 Physical, + gctPOINTER Logical, + gctSIZE_T Bytes + ); + +gceSTATUS +gckOS_CacheInvalidate( + gckOS Os, + gctUINT32 ProcessID, + gctPHYS_ADDR Handle, + gctUINT32 Physical, + gctPOINTER Logical, + gctSIZE_T Bytes + ); + +gceSTATUS +gckOS_CPUPhysicalToGPUPhysical( + IN gckOS Os, + IN gctUINT32 CPUPhysical, + IN gctUINT32_PTR GPUPhysical + ); + +gceSTATUS +gckOS_GPUPhysicalToCPUPhysical( + IN gckOS Os, + IN gctUINT32 GPUPhysical, + IN gctUINT32_PTR CPUPhysical + ); + +gceSTATUS +gckOS_QueryOption( + IN gckOS Os, + IN gctCONST_STRING Option, + OUT gctUINT32 * Value + ); + +/******************************************************************************\ +** Debug Support +*/ + +void +gckOS_SetDebugLevel( + IN gctUINT32 Level + ); + +void +gckOS_SetDebugZone( + IN gctUINT32 Zone + ); + +void +gckOS_SetDebugLevelZone( + IN gctUINT32 Level, + IN gctUINT32 Zone + ); + +void +gckOS_SetDebugZones( + IN gctUINT32 Zones, + IN gctBOOL Enable + ); + +void +gckOS_SetDebugFile( + IN gctCONST_STRING FileName + ); + +/******************************************************************************* +** Broadcast interface. +*/ + +typedef enum _gceBROADCAST +{ + /* GPU might be idle. */ + gcvBROADCAST_GPU_IDLE, + + /* A commit is going to happen. */ + gcvBROADCAST_GPU_COMMIT, + + /* GPU seems to be stuck. */ + gcvBROADCAST_GPU_STUCK, + + /* First process gets attached. */ + gcvBROADCAST_FIRST_PROCESS, + + /* Last process gets detached. */ + gcvBROADCAST_LAST_PROCESS, + + /* AXI bus error. */ + gcvBROADCAST_AXI_BUS_ERROR, + + /* Out of memory. */ + gcvBROADCAST_OUT_OF_MEMORY, +} +gceBROADCAST; + +gceSTATUS +gckOS_Broadcast( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gceBROADCAST Reason + ); + +gceSTATUS +gckOS_BroadcastHurry( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gctUINT Urgency + ); + +gceSTATUS +gckOS_BroadcastCalibrateSpeed( + IN gckOS Os, + IN gckHARDWARE Hardware, + IN gctUINT Idle, + IN gctUINT Time + ); + +/******************************************************************************* +** +** gckOS_SetGPUPower +** +** Set the power of the GPU on or off. +** +** INPUT: +** +** gckOS Os +** Pointer to a gckOS object. +** +** gceCORE Core +** GPU whose power is set. +** +** gctBOOL Clock +** gcvTRUE to turn on the clock, or gcvFALSE to turn off the clock. +** +** gctBOOL Power +** gcvTRUE to turn on the power, or gcvFALSE to turn off the power. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckOS_SetGPUPower( + IN gckOS Os, + IN gceCORE Core, + IN gctBOOL Clock, + IN gctBOOL Power + ); + +gceSTATUS +gckOS_ResetGPU( + IN gckOS Os, + IN gceCORE Core + ); + +gceSTATUS +gckOS_PrepareGPUFrequency( + IN gckOS Os, + IN gceCORE Core + ); + +gceSTATUS +gckOS_FinishGPUFrequency( + IN gckOS Os, + IN gceCORE Core + ); + +gceSTATUS +gckOS_QueryGPUFrequency( + IN gckOS Os, + IN gceCORE Core, + OUT gctUINT32 * Frequency, + OUT gctUINT8 * Scale + ); + +gceSTATUS +gckOS_SetGPUFrequency( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT8 Scale + ); + +/******************************************************************************* +** Semaphores. +*/ + +/* Create a new semaphore. */ +gceSTATUS +gckOS_CreateSemaphore( + IN gckOS Os, + OUT gctPOINTER * Semaphore + ); + +#if gcdENABLE_VG +gceSTATUS +gckOS_CreateSemaphoreVG( + IN gckOS Os, + OUT gctPOINTER * Semaphore + ); +#endif + +/* Delete a semahore. */ +gceSTATUS +gckOS_DestroySemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ); + +/* Acquire a semahore. */ +gceSTATUS +gckOS_AcquireSemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ); + +/* Try to acquire a semahore. */ +gceSTATUS +gckOS_TryAcquireSemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ); + +/* Release a semahore. */ +gceSTATUS +gckOS_ReleaseSemaphore( + IN gckOS Os, + IN gctPOINTER Semaphore + ); + +/******************************************************************************* +** Timer API. +*/ + +typedef void (*gctTIMERFUNCTION)(gctPOINTER); + +/* Create a timer. */ +gceSTATUS +gckOS_CreateTimer( + IN gckOS Os, + IN gctTIMERFUNCTION Function, + IN gctPOINTER Data, + OUT gctPOINTER * Timer + ); + +/* Destory a timer. */ +gceSTATUS +gckOS_DestroyTimer( + IN gckOS Os, + IN gctPOINTER Timer + ); + +/* Start a timer. */ +gceSTATUS +gckOS_StartTimer( + IN gckOS Os, + IN gctPOINTER Timer, + IN gctUINT32 Delay + ); + +/* Stop a timer. */ +gceSTATUS +gckOS_StopTimer( + IN gckOS Os, + IN gctPOINTER Timer + ); + +/******************************************************************************\ +********************************* gckHEAP Object ******************************** +\******************************************************************************/ + +typedef struct _gckHEAP * gckHEAP; + +/* Construct a new gckHEAP object. */ +gceSTATUS +gckHEAP_Construct( + IN gckOS Os, + IN gctSIZE_T AllocationSize, + OUT gckHEAP * Heap + ); + +/* Destroy an gckHEAP object. */ +gceSTATUS +gckHEAP_Destroy( + IN gckHEAP Heap + ); + +/* Allocate memory. */ +gceSTATUS +gckHEAP_Allocate( + IN gckHEAP Heap, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Node + ); + +/* Free memory. */ +gceSTATUS +gckHEAP_Free( + IN gckHEAP Heap, + IN gctPOINTER Node + ); + +/* Profile the heap. */ +gceSTATUS +gckHEAP_ProfileStart( + IN gckHEAP Heap + ); + +gceSTATUS +gckHEAP_ProfileEnd( + IN gckHEAP Heap, + IN gctCONST_STRING Title + ); + + +/******************************************************************************\ +******************************** gckVIDMEM Object ****************************** +\******************************************************************************/ + +typedef struct _gckVIDMEM * gckVIDMEM; +typedef struct _gckKERNEL * gckKERNEL; +typedef struct _gckDB * gckDB; +typedef struct _gckDVFS * gckDVFS; + +/* Construct a new gckVIDMEM object. */ +gceSTATUS +gckVIDMEM_Construct( + IN gckOS Os, + IN gctUINT32 BaseAddress, + IN gctSIZE_T Bytes, + IN gctSIZE_T Threshold, + IN gctSIZE_T Banking, + OUT gckVIDMEM * Memory + ); + +/* Destroy an gckVDIMEM object. */ +gceSTATUS +gckVIDMEM_Destroy( + IN gckVIDMEM Memory + ); + +/* Allocate linear memory. */ +gceSTATUS +gckVIDMEM_AllocateLinear( + IN gckKERNEL Kernel, + IN gckVIDMEM Memory, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + IN gctBOOL Specified, + OUT gcuVIDMEM_NODE_PTR * Node + ); + +/* Free memory. */ +gceSTATUS +gckVIDMEM_Free( + IN gckKERNEL Kernel, + IN gcuVIDMEM_NODE_PTR Node + ); + +/* Lock memory. */ +gceSTATUS +gckVIDMEM_Lock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + IN gctBOOL Cacheable, + OUT gctUINT32 * Address, + OUT gctUINT32 * Gid, + OUT gctUINT64 * PhysicalAddress + ); + +/* Unlock memory. */ +gceSTATUS +gckVIDMEM_Unlock( + IN gckKERNEL Kernel, + IN gckVIDMEM_NODE Node, + IN gceSURF_TYPE Type, + IN OUT gctBOOL * Asynchroneous + ); + +/* Construct a gcuVIDMEM_NODE union for virtual memory. */ +gceSTATUS +gckVIDMEM_ConstructVirtual( + IN gckKERNEL Kernel, + IN gctUINT32 Flag, + IN gctSIZE_T Bytes, + OUT gcuVIDMEM_NODE_PTR * Node + ); + +/* Destroy a gcuVIDMEM_NODE union for virtual memory. */ +gceSTATUS +gckVIDMEM_DestroyVirtual( + IN gcuVIDMEM_NODE_PTR Node + ); + +/******************************************************************************\ +******************************** gckKERNEL Object ****************************** +\******************************************************************************/ + +struct _gcsHAL_INTERFACE; + +/* Notifications. */ +typedef enum _gceNOTIFY +{ + gcvNOTIFY_INTERRUPT, + gcvNOTIFY_COMMAND_QUEUE, +} +gceNOTIFY; + +/* Flush flags. */ +typedef enum _gceKERNEL_FLUSH +{ + gcvFLUSH_COLOR = 0x01, + gcvFLUSH_DEPTH = 0x02, + gcvFLUSH_TEXTURE = 0x04, + gcvFLUSH_2D = 0x08, +#if gcdMULTI_GPU + gcvFLUSH_L2 = 0x10, +#endif + gcvFLUSH_TILE_STATUS = 0x20, + gcvFLUSH_ALL = gcvFLUSH_COLOR + | gcvFLUSH_DEPTH + | gcvFLUSH_TEXTURE + | gcvFLUSH_2D +#if gcdMULTI_GPU + | gcvFLUSH_L2 +#endif + | gcvFLUSH_TILE_STATUS +} +gceKERNEL_FLUSH; + +/* Construct a new gckKERNEL object. */ +gceSTATUS +gckKERNEL_Construct( + IN gckOS Os, + IN gceCORE Core, + IN gctPOINTER Context, + IN gckDB SharedDB, + OUT gckKERNEL * Kernel + ); + +/* Destroy an gckKERNEL object. */ +gceSTATUS +gckKERNEL_Destroy( + IN gckKERNEL Kernel + ); + +/* Dispatch a user-level command. */ +gceSTATUS +gckKERNEL_Dispatch( + IN gckKERNEL Kernel, + IN gctBOOL FromUser, + IN OUT struct _gcsHAL_INTERFACE * Interface + ); + +/* Query Database requirements. */ +gceSTATUS + gckKERNEL_QueryDatabase( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcsHAL_INTERFACE * Interface + ); + +/* Query the video memory. */ +gceSTATUS +gckKERNEL_QueryVideoMemory( + IN gckKERNEL Kernel, + OUT struct _gcsHAL_INTERFACE * Interface + ); + +/* Lookup the gckVIDMEM object for a pool. */ +gceSTATUS +gckKERNEL_GetVideoMemoryPool( + IN gckKERNEL Kernel, + IN gcePOOL Pool, + OUT gckVIDMEM * VideoMemory + ); + +gceSTATUS +gckKERNEL_AllocateLinearMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcePOOL * Pool, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + IN gctUINT32 Flag, + OUT gctUINT32 * Node + ); + +gceSTATUS +gckKERNEL_ReleaseVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN gctUINT32 Handle + ); + +gceSTATUS +gckKERNEL_LockVideoMemory( + IN gckKERNEL Kernel, + IN gceCORE Core, + IN gctUINT32 ProcessID, + IN gctBOOL FromUser, + IN OUT gcsHAL_INTERFACE * Interface + ); + +gceSTATUS +gckKERNEL_UnlockVideoMemory( + IN gckKERNEL Kernel, + IN gctUINT32 ProcessID, + IN OUT gcsHAL_INTERFACE * Interface + ); + +/* Map video memory. */ +gceSTATUS +gckKERNEL_MapVideoMemory( + IN gckKERNEL Kernel, + IN gctBOOL InUserSpace, + IN gctUINT32 Address, +#ifdef __QNXNTO__ + IN gctUINT32 Pid, + IN gctUINT32 Bytes, +#endif + OUT gctPOINTER * Logical + ); + +/* Map video memory. */ +gceSTATUS +gckKERNEL_MapVideoMemoryEx( + IN gckKERNEL Kernel, + IN gceCORE Core, + IN gctBOOL InUserSpace, + IN gctUINT32 Address, +#ifdef __QNXNTO__ + IN gctUINT32 Pid, + IN gctUINT32 Bytes, +#endif + OUT gctPOINTER * Logical + ); + +#ifdef __QNXNTO__ +/* Unmap video memory. */ +gceSTATUS +gckKERNEL_UnmapVideoMemory( + IN gckKERNEL Kernel, + IN gctPOINTER Logical, + IN gctUINT32 Pid, + IN gctUINT32 Bytes + ); +#endif + +/* Map memory. */ +gceSTATUS +gckKERNEL_MapMemory( + IN gckKERNEL Kernel, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Logical + ); + +/* Unmap memory. */ +gceSTATUS +gckKERNEL_UnmapMemory( + IN gckKERNEL Kernel, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +/* Notification of events. */ +gceSTATUS +gckKERNEL_Notify( + IN gckKERNEL Kernel, +#if gcdMULTI_GPU + IN gctUINT CoreId, +#endif + IN gceNOTIFY Notifcation, + IN gctBOOL Data + ); + +gceSTATUS +gckKERNEL_QuerySettings( + IN gckKERNEL Kernel, + OUT gcsKERNEL_SETTINGS * Settings + ); + +/******************************************************************************* +** +** gckKERNEL_Recovery +** +** Try to recover the GPU from a fatal error. +** +** INPUT: +** +** gckKERNEL Kernel +** Pointer to an gckKERNEL object. +** +** OUTPUT: +** +** Nothing. +*/ +gceSTATUS +gckKERNEL_Recovery( + IN gckKERNEL Kernel + ); + +/* Set the value of timeout on HW operation. */ +void +gckKERNEL_SetTimeOut( + IN gckKERNEL Kernel, + IN gctUINT32 timeOut + ); + +/* Get access to the user data. */ +gceSTATUS +gckKERNEL_OpenUserData( + IN gckKERNEL Kernel, + IN gctBOOL NeedCopy, + IN gctPOINTER StaticStorage, + IN gctPOINTER UserPointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ); + +/* Release resources associated with the user data connection. */ +gceSTATUS +gckKERNEL_CloseUserData( + IN gckKERNEL Kernel, + IN gctBOOL NeedCopy, + IN gctBOOL FlushData, + IN gctPOINTER UserPointer, + IN gctSIZE_T Size, + OUT gctPOINTER * KernelPointer + ); + +gceSTATUS +gckDVFS_Construct( + IN gckHARDWARE Hardware, + OUT gckDVFS * Frequency + ); + +gceSTATUS +gckDVFS_Destroy( + IN gckDVFS Dvfs + ); + +gceSTATUS +gckDVFS_Start( + IN gckDVFS Dvfs + ); + +gceSTATUS +gckDVFS_Stop( + IN gckDVFS Dvfs + ); + +/******************************************************************************\ +******************************* gckHARDWARE Object ***************************** +\******************************************************************************/ + +/* Construct a new gckHARDWARE object. */ +gceSTATUS +gckHARDWARE_Construct( + IN gckOS Os, + IN gceCORE Core, + OUT gckHARDWARE * Hardware + ); + +/* Destroy an gckHARDWARE object. */ +gceSTATUS +gckHARDWARE_Destroy( + IN gckHARDWARE Hardware + ); + +/* Get hardware type. */ +gceSTATUS +gckHARDWARE_GetType( + IN gckHARDWARE Hardware, + OUT gceHARDWARE_TYPE * Type + ); + +/* Query system memory requirements. */ +gceSTATUS +gckHARDWARE_QuerySystemMemory( + IN gckHARDWARE Hardware, + OUT gctSIZE_T * SystemSize, + OUT gctUINT32 * SystemBaseAddress + ); + +/* Build virtual address. */ +gceSTATUS +gckHARDWARE_BuildVirtualAddress( + IN gckHARDWARE Hardware, + IN gctUINT32 Index, + IN gctUINT32 Offset, + OUT gctUINT32 * Address + ); + +/* Query command buffer requirements. */ +gceSTATUS +gckHARDWARE_QueryCommandBuffer( + IN gckHARDWARE Hardware, + OUT gctUINT32 * Alignment, + OUT gctUINT32 * ReservedHead, + OUT gctUINT32 * ReservedTail + ); + +/* Add a WAIT/LINK pair in the command queue. */ +gceSTATUS +gckHARDWARE_WaitLink( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT32 Offset, + IN OUT gctUINT32 * Bytes, + OUT gctUINT32 * WaitOffset, + OUT gctUINT32 * WaitBytes + ); + +/* Kickstart the command processor. */ +gceSTATUS +gckHARDWARE_Execute( + IN gckHARDWARE Hardware, + IN gctUINT32 Address, + IN gctSIZE_T Bytes + ); + +/* Add an END command in the command queue. */ +gceSTATUS +gckHARDWARE_End( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ); + +#if gcdMULTI_GPU +gceSTATUS +gckHARDWARE_ChipEnable( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gceCORE_3D_MASK ChipEnable, + IN OUT gctSIZE_T * Bytes + ); +#endif + +/* Add a NOP command in the command queue. */ +gceSTATUS +gckHARDWARE_Nop( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN OUT gctSIZE_T * Bytes + ); + +/* Add a PIPESELECT command in the command queue. */ +gceSTATUS +gckHARDWARE_PipeSelect( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gcePIPE_SELECT Pipe, + IN OUT gctUINT32 * Bytes + ); + +/* Add a LINK command in the command queue. */ +gceSTATUS +gckHARDWARE_Link( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT32 FetchAddress, + IN gctUINT32 FetchSize, + IN OUT gctUINT32 * Bytes + ); + +/* Add an EVENT command in the command queue. */ +gceSTATUS +gckHARDWARE_Event( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT8 Event, + IN gceKERNEL_WHERE FromWhere, + IN OUT gctUINT32 * Bytes + ); + +/* Query the available memory. */ +gceSTATUS +gckHARDWARE_QueryMemory( + IN gckHARDWARE Hardware, + OUT gctSIZE_T * InternalSize, + OUT gctUINT32 * InternalBaseAddress, + OUT gctUINT32 * InternalAlignment, + OUT gctSIZE_T * ExternalSize, + OUT gctUINT32 * ExternalBaseAddress, + OUT gctUINT32 * ExternalAlignment, + OUT gctUINT32 * HorizontalTileSize, + OUT gctUINT32 * VerticalTileSize + ); + +/* Query the identity of the hardware. */ +gceSTATUS +gckHARDWARE_QueryChipIdentity( + IN gckHARDWARE Hardware, + OUT gcsHAL_QUERY_CHIP_IDENTITY_PTR Identity + ); + +/* Query the shader uniforms support. */ +gceSTATUS +gckHARDWARE_QueryShaderCaps( + IN gckHARDWARE Hardware, + OUT gctUINT * VertexUniforms, + OUT gctUINT * FragmentUniforms, + OUT gctBOOL * UnifiedUnforms + ); + +/* Split a harwdare specific address into API stuff. */ +gceSTATUS +gckHARDWARE_SplitMemory( + IN gckHARDWARE Hardware, + IN gctUINT32 Address, + OUT gcePOOL * Pool, + OUT gctUINT32 * Offset + ); + +/* Update command queue tail pointer. */ +gceSTATUS +gckHARDWARE_UpdateQueueTail( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctUINT32 Offset + ); + +/* Convert logical address to hardware specific address. */ +gceSTATUS +gckHARDWARE_ConvertLogical( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + OUT gctUINT32 * Address + ); + +/* Interrupt manager. */ +gceSTATUS +gckHARDWARE_Interrupt( + IN gckHARDWARE Hardware, +#if gcdMULTI_GPU + IN gctUINT CoreId, +#endif + IN gctBOOL InterruptValid + ); + +/* Program MMU. */ +gceSTATUS +gckHARDWARE_SetMMU( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical + ); + +/* Flush the MMU. */ +gceSTATUS +gckHARDWARE_FlushMMU( + IN gckHARDWARE Hardware + ); + +/* Set the page table base address. */ +gceSTATUS +gckHARDWARE_SetMMUv2( + IN gckHARDWARE Hardware, + IN gctBOOL Enable, + IN gctPOINTER MtlbAddress, + IN gceMMU_MODE Mode, + IN gctPOINTER SafeAddress, + IN gctBOOL FromPower + ); + +#if gcdPROCESS_ADDRESS_SPACE +/* Configure mmu configuration. */ +gceSTATUS +gckHARDWARE_ConfigMMU( + IN gckHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctPOINTER MtlbLogical, + IN gctUINT32 Offset, + IN OUT gctSIZE_T * Bytes, + OUT gctSIZE_T * WaitLinkOffset, + OUT gctSIZE_T * WaitLinkBytes + ); +#endif + +/* Get idle register. */ +gceSTATUS +gckHARDWARE_GetIdle( + IN gckHARDWARE Hardware, + IN gctBOOL Wait, + OUT gctUINT32 * Data + ); + +/* Flush the caches. */ +gceSTATUS +gckHARDWARE_Flush( + IN gckHARDWARE Hardware, + IN gceKERNEL_FLUSH Flush, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ); + +/* Enable/disable fast clear. */ +gceSTATUS +gckHARDWARE_SetFastClear( + IN gckHARDWARE Hardware, + IN gctINT Enable, + IN gctINT Compression + ); + +gceSTATUS +gckHARDWARE_ReadInterrupt( + IN gckHARDWARE Hardware, + OUT gctUINT32_PTR IDs + ); + +/* Power management. */ +gceSTATUS +gckHARDWARE_SetPowerManagementState( + IN gckHARDWARE Hardware, + IN gceCHIPPOWERSTATE State + ); + +gceSTATUS +gckHARDWARE_QueryPowerManagementState( + IN gckHARDWARE Hardware, + OUT gceCHIPPOWERSTATE* State + ); + +gceSTATUS +gckHARDWARE_SetPowerManagement( + IN gckHARDWARE Hardware, + IN gctBOOL PowerManagement + ); + +gceSTATUS +gckHARDWARE_SetPowerManagementLock( + IN gckHARDWARE Hardware, + IN gctBOOL Lock + ); + +gceSTATUS +gckHARDWARE_SetGpuProfiler( + IN gckHARDWARE Hardware, + IN gctBOOL GpuProfiler + ); + +#if gcdENABLE_FSCALE_VAL_ADJUST +gceSTATUS +gckHARDWARE_SetFscaleValue( + IN gckHARDWARE Hardware, + IN gctUINT32 FscaleValue + ); + +gceSTATUS +gckHARDWARE_GetFscaleValue( + IN gckHARDWARE Hardware, + IN gctUINT * FscaleValue, + IN gctUINT * MinFscaleValue, + IN gctUINT * MaxFscaleValue + ); + +gceSTATUS +gckHARDWARE_SetMinFscaleValue( + IN gckHARDWARE Hardware, + IN gctUINT MinFscaleValue + ); +#endif + +#if gcdPOWEROFF_TIMEOUT +gceSTATUS +gckHARDWARE_SetPowerOffTimeout( + IN gckHARDWARE Hardware, + IN gctUINT32 Timeout +); + +gceSTATUS +gckHARDWARE_QueryPowerOffTimeout( + IN gckHARDWARE Hardware, + OUT gctUINT32* Timeout +); +#endif + +/* Profile 2D Engine. */ +gceSTATUS +gckHARDWARE_ProfileEngine2D( + IN gckHARDWARE Hardware, + OUT gcs2D_PROFILE_PTR Profile + ); + +gceSTATUS +gckHARDWARE_InitializeHardware( + IN gckHARDWARE Hardware + ); + +gceSTATUS +gckHARDWARE_Reset( + IN gckHARDWARE Hardware + ); + +typedef gceSTATUS (*gctISRMANAGERFUNC)(gctPOINTER Context); + +gceSTATUS +gckHARDWARE_SetIsrManager( + IN gckHARDWARE Hardware, + IN gctISRMANAGERFUNC StartIsr, + IN gctISRMANAGERFUNC StopIsr, + IN gctPOINTER Context + ); + +/* Start a composition. */ +gceSTATUS +gckHARDWARE_Compose( + IN gckHARDWARE Hardware, + IN gctUINT32 ProcessID, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Offset, + IN gctSIZE_T Size, + IN gctUINT8 EventID + ); + +/* Check for Hardware features. */ +gceSTATUS +gckHARDWARE_IsFeatureAvailable( + IN gckHARDWARE Hardware, + IN gceFEATURE Feature + ); + +gceSTATUS +gckHARDWARE_DumpMMUException( + IN gckHARDWARE Hardware + ); + +gceSTATUS +gckHARDWARE_DumpGPUState( + IN gckHARDWARE Hardware + ); + +gceSTATUS +gckHARDWARE_InitDVFS( + IN gckHARDWARE Hardware + ); + +gceSTATUS +gckHARDWARE_QueryLoad( + IN gckHARDWARE Hardware, + OUT gctUINT32 * Load + ); + +gceSTATUS +gckHARDWARE_SetDVFSPeroid( + IN gckHARDWARE Hardware, + IN gctUINT32 Frequency + ); + +gceSTATUS +gckHARDWARE_PrepareFunctions( + gckHARDWARE Hardware + ); + +gceSTATUS +gckHARDWARE_SetMMUStates( + IN gckHARDWARE Hardware, + IN gctPOINTER MtlbAddress, + IN gceMMU_MODE Mode, + IN gctPOINTER SafeAddress, + IN gctPOINTER Logical, + IN OUT gctUINT32 * Bytes + ); + +#if !gcdENABLE_VG +/******************************************************************************\ +***************************** gckINTERRUPT Object ****************************** +\******************************************************************************/ + +typedef struct _gckINTERRUPT * gckINTERRUPT; + +typedef gceSTATUS (* gctINTERRUPT_HANDLER)( + IN gckKERNEL Kernel + ); + +gceSTATUS +gckINTERRUPT_Construct( + IN gckKERNEL Kernel, + OUT gckINTERRUPT * Interrupt + ); + +gceSTATUS +gckINTERRUPT_Destroy( + IN gckINTERRUPT Interrupt + ); + +gceSTATUS +gckINTERRUPT_SetHandler( + IN gckINTERRUPT Interrupt, + IN OUT gctINT32_PTR Id, + IN gctINTERRUPT_HANDLER Handler + ); + +gceSTATUS +gckINTERRUPT_Notify( + IN gckINTERRUPT Interrupt, + IN gctBOOL Valid + ); +#endif +/******************************************************************************\ +******************************** gckEVENT Object ******************************* +\******************************************************************************/ + +typedef struct _gckEVENT * gckEVENT; + +/* Construct a new gckEVENT object. */ +gceSTATUS +gckEVENT_Construct( + IN gckKERNEL Kernel, + OUT gckEVENT * Event + ); + +/* Destroy an gckEVENT object. */ +gceSTATUS +gckEVENT_Destroy( + IN gckEVENT Event + ); + +/* Reserve the next available hardware event. */ +#if gcdMULTI_GPU +gceSTATUS +gckEVENT_GetEvent( + IN gckEVENT Event, + IN gctBOOL Wait, + OUT gctUINT8 * EventID, + IN gceKERNEL_WHERE Source, + IN gceCORE_3D_MASK ChipEnable + ); +#else +gceSTATUS +gckEVENT_GetEvent( + IN gckEVENT Event, + IN gctBOOL Wait, + OUT gctUINT8 * EventID, + IN gceKERNEL_WHERE Source + ); +#endif + +/* Add a new event to the list of events. */ +gceSTATUS +gckEVENT_AddList( + IN gckEVENT Event, + IN gcsHAL_INTERFACE_PTR Interface, + IN gceKERNEL_WHERE FromWhere, + IN gctBOOL AllocateAllowed, + IN gctBOOL FromKernel + ); + +/* Schedule a FreeNonPagedMemory event. */ +gceSTATUS +gckEVENT_FreeNonPagedMemory( + IN gckEVENT Event, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gceKERNEL_WHERE FromWhere + ); + +/* Schedule a FreeContiguousMemory event. */ +gceSTATUS +gckEVENT_FreeContiguousMemory( + IN gckEVENT Event, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gceKERNEL_WHERE FromWhere + ); + +/* Schedule a FreeVideoMemory event. */ +gceSTATUS +gckEVENT_FreeVideoMemory( + IN gckEVENT Event, + IN gcuVIDMEM_NODE_PTR VideoMemory, + IN gceKERNEL_WHERE FromWhere + ); + +/* Schedule a signal event. */ +gceSTATUS +gckEVENT_Signal( + IN gckEVENT Event, + IN gctSIGNAL Signal, + IN gceKERNEL_WHERE FromWhere + ); + +/* Schedule an Unlock event. */ +gceSTATUS +gckEVENT_Unlock( + IN gckEVENT Event, + IN gceKERNEL_WHERE FromWhere, + IN gctPOINTER Node, + IN gceSURF_TYPE Type + ); + +gceSTATUS +gckEVENT_CommitDone( + IN gckEVENT Event, + IN gceKERNEL_WHERE FromWhere + ); + +/* Schedule a FreeVirtualCommandBuffer event. */ +gceSTATUS +gckEVENT_DestroyVirtualCommandBuffer( + IN gckEVENT Event, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gceKERNEL_WHERE FromWhere + ); + +#if gcdMULTI_GPU +gceSTATUS +gckEVENT_Submit( + IN gckEVENT Event, + IN gctBOOL Wait, + IN gctBOOL FromPower, + IN gceCORE_3D_MASK ChipEnable + ); +#else +gceSTATUS +gckEVENT_Submit( + IN gckEVENT Event, + IN gctBOOL Wait, + IN gctBOOL FromPower + ); +#endif + +#if gcdMULTI_GPU +gceSTATUS +gckEVENT_Commit( + IN gckEVENT Event, + IN gcsQUEUE_PTR Queue, + IN gceCORE_3D_MASK ChipEnable + ); +#else +gceSTATUS +gckEVENT_Commit( + IN gckEVENT Event, + IN gcsQUEUE_PTR Queue + ); +#endif + +/* Schedule a composition event. */ +gceSTATUS +gckEVENT_Compose( + IN gckEVENT Event, + IN gcsHAL_COMPOSE_PTR Info + ); + +/* Event callback routine. */ +gceSTATUS +gckEVENT_Notify( + IN gckEVENT Event, + IN gctUINT32 IDs + ); + +/* Event callback routine. */ +gceSTATUS +gckEVENT_Interrupt( + IN gckEVENT Event, +#if gcdMULTI_GPU + IN gctUINT CoreId, +#endif + IN gctUINT32 IDs + ); + +gceSTATUS +gckEVENT_Dump( + IN gckEVENT Event + ); +/******************************************************************************\ +******************************* gckCOMMAND Object ****************************** +\******************************************************************************/ + +typedef struct _gckCOMMAND * gckCOMMAND; + +/* Construct a new gckCOMMAND object. */ +gceSTATUS +gckCOMMAND_Construct( + IN gckKERNEL Kernel, + OUT gckCOMMAND * Command + ); + +/* Destroy an gckCOMMAND object. */ +gceSTATUS +gckCOMMAND_Destroy( + IN gckCOMMAND Command + ); + +/* Acquire command queue synchronization objects. */ +gceSTATUS +gckCOMMAND_EnterCommit( + IN gckCOMMAND Command, + IN gctBOOL FromPower + ); + +/* Release command queue synchronization objects. */ +gceSTATUS +gckCOMMAND_ExitCommit( + IN gckCOMMAND Command, + IN gctBOOL FromPower + ); + +/* Start the command queue. */ +gceSTATUS +gckCOMMAND_Start( + IN gckCOMMAND Command + ); + +/* Stop the command queue. */ +gceSTATUS +gckCOMMAND_Stop( + IN gckCOMMAND Command, + IN gctBOOL FromRecovery + ); + +#if gcdMULTI_GPU +/* Commit a buffer to the command queue. */ +gceSTATUS +gckCOMMAND_Commit( + IN gckCOMMAND Command, + IN gckCONTEXT Context, + IN gcoCMDBUF CommandBuffer, + IN gcsSTATE_DELTA_PTR StateDelta, + IN gcsQUEUE_PTR EventQueue, + IN gctUINT32 ProcessID, + IN gceCORE_3D_MASK ChipEnable + ); +#else +gceSTATUS +gckCOMMAND_Commit( + IN gckCOMMAND Command, + IN gckCONTEXT Context, + IN gcoCMDBUF CommandBuffer, + IN gcsSTATE_DELTA_PTR StateDelta, + IN gcsQUEUE_PTR EventQueue, + IN gctUINT32 ProcessID + ); +#endif + +/* Reserve space in the command buffer. */ +gceSTATUS +gckCOMMAND_Reserve( + IN gckCOMMAND Command, + IN gctUINT32 RequestedBytes, + OUT gctPOINTER * Buffer, + OUT gctUINT32 * BufferSize + ); + +/* Execute reserved space in the command buffer. */ +gceSTATUS +gckCOMMAND_Execute( + IN gckCOMMAND Command, + IN gctUINT32 RequstedBytes + ); + +/* Stall the command queue. */ +#if gcdMULTI_GPU +gceSTATUS +gckCOMMAND_Stall( + IN gckCOMMAND Command, + IN gctBOOL FromPower, + IN gceCORE_3D_MASK ChipEnable + ); +#else +gceSTATUS +gckCOMMAND_Stall( + IN gckCOMMAND Command, + IN gctBOOL FromPower + ); +#endif + +/* Attach user process. */ +gceSTATUS +gckCOMMAND_Attach( + IN gckCOMMAND Command, + OUT gckCONTEXT * Context, + OUT gctSIZE_T * StateCount, + IN gctUINT32 ProcessID + ); + +/* Detach user process. */ +gceSTATUS +gckCOMMAND_Detach( + IN gckCOMMAND Command, + IN gckCONTEXT Context + ); + +/* Dump command buffer being executed by GPU. */ +gceSTATUS +gckCOMMAND_DumpExecutingBuffer( + IN gckCOMMAND Command + ); + +/* Whether a kernel command buffer address. */ +gceSTATUS +gckCOMMAND_AddressInKernelCommandBuffer( + IN gckCOMMAND Command, + IN gctUINT32 Address, + OUT gctBOOL *In + ); + +/******************************************************************************\ +********************************* gckMMU Object ******************************** +\******************************************************************************/ + +typedef struct _gckMMU * gckMMU; + +/* Construct a new gckMMU object. */ +gceSTATUS +gckMMU_Construct( + IN gckKERNEL Kernel, + IN gctSIZE_T MmuSize, + OUT gckMMU * Mmu + ); + +/* Destroy an gckMMU object. */ +gceSTATUS +gckMMU_Destroy( + IN gckMMU Mmu + ); + +/* Allocate pages inside the MMU. */ +gceSTATUS +gckMMU_AllocatePages( + IN gckMMU Mmu, + IN gctSIZE_T PageCount, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ); + +gceSTATUS +gckMMU_AllocatePagesEx( + IN gckMMU Mmu, + IN gctSIZE_T PageCount, + IN gceSURF_TYPE Type, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ); + +/* Remove a page table from the MMU. */ +gceSTATUS +gckMMU_FreePages( + IN gckMMU Mmu, + IN gctPOINTER PageTable, + IN gctSIZE_T PageCount + ); + +/* Set the MMU page with info. */ +gceSTATUS +gckMMU_SetPage( + IN gckMMU Mmu, + IN gctUINT32 PageAddress, + IN gctUINT32 *PageEntry + ); + +gceSTATUS +gckMMU_Flush( + IN gckMMU Mmu, + IN gceSURF_TYPE Type + ); + +gceSTATUS +gckMMU_DumpPageTableEntry( + IN gckMMU Mmu, + IN gctUINT32 Address + ); + + +#if VIVANTE_PROFILER +gceSTATUS +gckHARDWARE_QueryProfileRegisters( + IN gckHARDWARE Hardware, + IN gctBOOL Reset, + OUT gcsPROFILER_COUNTERS * Counters + ); +#endif + +#if VIVANTE_PROFILER_CONTEXT +gceSTATUS +gckHARDWARE_QueryContextProfile( + IN gckHARDWARE Hardware, + IN gctBOOL Reset, + IN gckCONTEXT Context, + OUT gcsPROFILER_COUNTERS * Counters + ); + +gceSTATUS +gckHARDWARE_UpdateContextProfile( + IN gckHARDWARE Hardware, + IN gckCONTEXT Context + ); +#endif + +#if VIVANTE_PROFILER_NEW +gceSTATUS +gckHARDWARE_InitProfiler( + IN gckHARDWARE Hardware + ); +#endif + +gceSTATUS +gckOS_SignalQueryHardware( + IN gckOS Os, + IN gctSIGNAL Signal, + OUT gckHARDWARE * Hardware + ); + +gceSTATUS +gckOS_SignalSetHardware( + IN gckOS Os, + IN gctSIGNAL Signal, + gckHARDWARE Hardware + ); + +gceSTATUS +gckOS_DetectProcessByName( + IN gctCONST_POINTER Name + ); + +void +gckOS_DumpParam( + void + ); + +#ifdef __cplusplus +} +#endif + +#if gcdENABLE_VG +#include "gc_hal_vg.h" +#endif + +#endif /* __gc_hal_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_base.h b/drivers/gpu/galcore/inc/gc_hal_base.h new file mode 100644 index 00000000000000..b798152523af33 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_base.h @@ -0,0 +1,5520 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +#ifndef __gc_hal_base_h_ +#define __gc_hal_base_h_ + +#include "gc_hal_enum.h" +#include "gc_hal_types.h" +#include "gc_hal_dump.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +****************************** Object Declarations ***************************** +\******************************************************************************/ + +typedef struct _gckOS * gckOS; +typedef struct _gcoHAL * gcoHAL; +typedef struct _gcoOS * gcoOS; +typedef struct _gco2D * gco2D; +typedef struct gcsATOM * gcsATOM_PTR; + +#if gcdENABLE_3D +typedef struct _gco3D * gco3D; +typedef struct _gcoCL * gcoCL; +typedef struct _gcsFAST_FLUSH * gcsFAST_FLUSH_PTR; +#endif + +typedef struct _gcoSURF * gcoSURF; +typedef struct _gcsSURF_INFO * gcsSURF_INFO_PTR; +typedef struct _gcsSURF_NODE * gcsSURF_NODE_PTR; +typedef struct _gcsSURF_FORMAT_INFO * gcsSURF_FORMAT_INFO_PTR; +typedef struct _gcsPOINT * gcsPOINT_PTR; +typedef struct _gcsSIZE * gcsSIZE_PTR; +typedef struct _gcsRECT * gcsRECT_PTR; +typedef struct _gcsBOUNDARY * gcsBOUNDARY_PTR; +typedef struct _gcoDUMP * gcoDUMP; +typedef struct _gcoHARDWARE * gcoHARDWARE; +typedef union _gcuVIDMEM_NODE * gcuVIDMEM_NODE_PTR; +typedef struct _gcsVIDMEM_NODE * gckVIDMEM_NODE; + +#if gcdENABLE_VG +typedef struct _gcoVG * gcoVG; +typedef struct _gcsCOMPLETION_SIGNAL * gcsCOMPLETION_SIGNAL_PTR; +typedef struct _gcsCONTEXT_MAP * gcsCONTEXT_MAP_PTR; +#else +typedef void * gcoVG; +#endif + +#if gcdSYNC +typedef struct _gcoFENCE * gcoFENCE; +typedef struct _gcsSYNC_CONTEXT * gcsSYNC_CONTEXT_PTR; +#endif + +#if defined(ANDROID) +typedef struct _gcoOS_SymbolsList gcoOS_SymbolsList; +#endif + +/******************************************************************************\ +******************************* Process local storage ************************* +\******************************************************************************/ + +typedef struct _gcsPLS * gcsPLS_PTR; + +#if gcdENABLE_3D +/****************************************************************************** +** +** Patch defines which should be moved to dedicate file later +** +** !!! ALWAYS ADD new ID in the TAIL, otherwise will break exising TRACE FILE +*******************************************************************************/ +typedef enum _gcePATCH_ID +{ + gcvPATCH_NOTINIT = -1, + gcvPATCH_INVALID = 0, + +#if gcdDEBUG_OPTION + gcvPATCH_DEBUG, +#endif + + gcvPATCH_GTFES30, + gcvPATCH_CTGL11, + gcvPATCH_CTGL20, + gcvPATCH_GLBM11, + gcvPATCH_GLBM21, + gcvPATCH_GLBM25, + gcvPATCH_GLBM27, + gcvPATCH_GLBMGUI, + gcvPATCH_GFXBENCH, + gcvPATCH_ANTUTU, /* Antutu 3.x */ + gcvPATCH_ANTUTU4X, /* Antutu 4.x */ + gcvPATCH_QUADRANT, + gcvPATCH_GPUBENCH, + gcvPATCH_DUOKAN, + gcvPATCH_GLOFTSXHM, + gcvPATCH_XRUNNER, + gcvPATCH_BUSPARKING3D, + gcvPATCH_SIEGECRAFT, + gcvPATCH_PREMIUM, + gcvPATCH_RACEILLEGAL, + gcvPATCH_MEGARUN, + gcvPATCH_BMGUI, + gcvPATCH_NENAMARK, + gcvPATCH_NENAMARK2, + gcvPATCH_FISHNOODLE, + gcvPATCH_MM06, + gcvPATCH_MM07, + gcvPATCH_BM21, + gcvPATCH_SMARTBENCH, + gcvPATCH_JPCT, + gcvPATCH_NEOCORE, + gcvPATCH_RTESTVA, + gcvPATCH_NBA2013, + gcvPATCH_BARDTALE, + gcvPATCH_F18, + gcvPATCH_CARPARK, + gcvPATCH_CARCHALLENGE, + gcvPATCH_HEROESCALL, + gcvPATCH_GLOFTF3HM, + gcvPATCH_CRAZYRACING, + gcvPATCH_FIREFOX, + gcvPATCH_CHROME, + gcvPATCH_MONOPOLY, + gcvPATCH_SNOWCOLD, + gcvPATCH_BM3, + gcvPATCH_BASEMARKX, + gcvPATCH_DEQP, + gcvPATCH_SF4, + gcePATCH_MGOHEAVEN2, + gcePATCH_SILIBILI, + gcePATCH_ELEMENTSDEF, + gcePATCH_GLOFTKRHM, + gcvPATCH_OCLCTS, + gcvPATCH_A8HP, + gcvPATCH_A8CN, + gcvPATCH_WISTONESG, + gcvPATCH_SPEEDRACE, + gcvPATCH_FSBHAWAIIF, + gcvPATCH_AIRNAVY, + gcvPATCH_F18NEW, + gcvPATCH_CKZOMBIES2, + gcvPATCH_EADGKEEPER, + gcvPATCH_BASEMARK2V2, + gcvPATCH_RIPTIDEGP2, + gcvPATCH_OESCTS, + gcvPATCH_GANGSTAR, + gcvPATCH_WHRKYZIXOVAN, + gcvPATCH_NAMESGAS, + gcvPATCH_AFTERBURNER, + gcvPATCH_UIMARK, + gcvPATCH_FM_OES_PLAYER, + gcvPATCH_SUMSUNG_BENCH, + gcvPATCH_ROCKSTAR_MAXPAYNE, + gcvPATCH_TITANPACKING, + gcvPATCH_BASEMARKOSIICN, + gcvPATCH_FRUITNINJA, +#if defined(ANDROID) + gcePATCH_ANDROID_CTS_MEDIA_PRESENTATIONTIME, +#endif + gcvPATCH_ANDROID_COMPOSITOR, + gcvPATCH_CTS_TEXTUREVIEW, + gcvPATCH_WATER2_CHUKONG, + + gcvPATCH_COUNT +} gcePATCH_ID; +#endif /* gcdENABLE_3D */ + +typedef void (* gctPLS_DESTRUCTOR) ( + gcsPLS_PTR + ); + +typedef struct _gcsPLS +{ + /* Global objects. */ + gcoOS os; + gcoHAL hal; + + /* Internal memory pool. */ + gctSIZE_T internalSize; + gctPHYS_ADDR internalPhysical; + gctPOINTER internalLogical; + + /* External memory pool. */ + gctSIZE_T externalSize; + gctPHYS_ADDR externalPhysical; + gctPOINTER externalLogical; + + /* Contiguous memory pool. */ + gctSIZE_T contiguousSize; + gctPHYS_ADDR contiguousPhysical; + gctPOINTER contiguousLogical; + + /* EGL-specific process-wide objects. */ + gctPOINTER eglDisplayInfo; + gctPOINTER eglSurfaceInfo; + gceSURF_FORMAT eglConfigFormat; + + /* PLS reference count */ + gcsATOM_PTR reference; + + /* PorcessID of the constrcutor process */ + gctUINT32 processID; + + /* ThreadID of the constrcutor process. */ + gctSIZE_T threadID; + /* Flag for calling module destructor. */ + gctBOOL exiting; + + gctBOOL bNeedSupportNP2Texture; + + gctPLS_DESTRUCTOR destructor; + /* Mutex to guard PLS access. currently it's for EGL. + ** We can use this mutex for every PLS access. + */ + gctPOINTER accessLock; +#if gcdENABLE_3D + /* Global patchID to overwrite the detection */ + gcePATCH_ID patchID; +#endif +} +gcsPLS; + +extern gcsPLS gcPLS; + +#if gcdENABLE_3D +#define gcPLS_INITIALIZER \ +{ \ + gcvNULL, /* gcoOS object. */ \ + gcvNULL, /* gcoHAL object. */ \ + 0, /* internalSize */ \ + gcvNULL, /* internalPhysical */ \ + gcvNULL, /* internalLogical */ \ + 0, /* externalSize */ \ + gcvNULL, /* externalPhysical */ \ + gcvNULL, /* externalLogical */ \ + 0, /* contiguousSize */ \ + gcvNULL, /* contiguousPhysical */ \ + gcvNULL, /* contiguousLogical */ \ + gcvNULL, /* eglDisplayInfo */ \ + gcvNULL, /* eglSurfaceInfo */ \ + gcvSURF_A8R8G8B8,/* eglConfigFormat */ \ + gcvNULL, /* reference */ \ + 0, /* processID */ \ + 0, /* threadID */ \ + gcvFALSE, /* exiting */ \ + gcvFALSE, /* Special flag for NP2 texture. */ \ + gcvNULL, /* destructor */ \ + gcvNULL, /* accessLock */ \ + gcvPATCH_NOTINIT,/* global patchID */ \ +} +#else +#define gcPLS_INITIALIZER \ +{ \ + gcvNULL, /* gcoOS object. */ \ + gcvNULL, /* gcoHAL object. */ \ + 0, /* internalSize */ \ + gcvNULL, /* internalPhysical */ \ + gcvNULL, /* internalLogical */ \ + 0, /* externalSize */ \ + gcvNULL, /* externalPhysical */ \ + gcvNULL, /* externalLogical */ \ + 0, /* contiguousSize */ \ + gcvNULL, /* contiguousPhysical */ \ + gcvNULL, /* contiguousLogical */ \ + gcvNULL, /* eglDisplayInfo */ \ + gcvNULL, /* eglSurfaceInfo */ \ + gcvSURF_A8R8G8B8,/* eglConfigFormat */ \ + gcvNULL, /* reference */ \ + 0, /* processID */ \ + 0, /* threadID */ \ + gcvFALSE, /* exiting */ \ + gcvFALSE, /* Special flag for NP2 texture. */ \ + gcvNULL, /* destructor */ \ + gcvNULL, /* accessLock */ \ +} +#endif + +/******************************************************************************\ +******************************* Thread local storage ************************* +\******************************************************************************/ + +typedef struct _gcsTLS * gcsTLS_PTR; + +typedef void (* gctTLS_DESTRUCTOR) ( + gcsTLS_PTR + ); + +typedef struct _gcsTLS +{ + gceHARDWARE_TYPE currentType; + + /* Current 3D hardwre of this thread */ + gcoHARDWARE currentHardware; + + /* Default 3D hardware of this thread */ + gcoHARDWARE defaultHardware; + + /* Only for separated 3D and 2D */ + gcoHARDWARE hardware2D; +#if gcdENABLE_VG + gcoVGHARDWARE vg; + gcoVG engineVG; +#endif /* gcdENABLE_VG */ +#if gcdENABLE_3D + gco3D engine3D; +#endif +#if gcdENABLE_2D + gco2D engine2D; +#endif + + /*thread data */ + gctPOINTER context; + /* ES(including es1 and es2) client driver context which is current state */ + gctPOINTER esClientCtx; + gctTLS_DESTRUCTOR destructor; + + gctBOOL copied; + + /* libGAL.so handle */ + gctHANDLE handle; + + /* If true, do not releas 2d engine and hardware in hal layer */ + gctBOOL release2DUpper; +} +gcsTLS; + +/******************************************************************************\ +********************************* Enumerations ********************************* +\******************************************************************************/ + +typedef enum _gcePLS_VALUE +{ + gcePLS_VALUE_EGL_DISPLAY_INFO, + gcePLS_VALUE_EGL_SURFACE_INFO, + gcePLS_VALUE_EGL_CONFIG_FORMAT_INFO, + gcePLS_VALUE_EGL_DESTRUCTOR_INFO, +} +gcePLS_VALUE; + +/* Video memory pool type. */ +typedef enum _gcePOOL +{ + gcvPOOL_UNKNOWN = 0, + gcvPOOL_DEFAULT, + gcvPOOL_LOCAL, + gcvPOOL_LOCAL_INTERNAL, + gcvPOOL_LOCAL_EXTERNAL, + gcvPOOL_UNIFIED, + gcvPOOL_SYSTEM, + gcvPOOL_VIRTUAL, + gcvPOOL_USER, + gcvPOOL_CONTIGUOUS, + + gcvPOOL_NUMBER_OF_POOLS +} +gcePOOL; + +#if gcdENABLE_3D +/* Blending functions. */ +typedef enum _gceBLEND_FUNCTION +{ + gcvBLEND_ZERO, + gcvBLEND_ONE, + gcvBLEND_SOURCE_COLOR, + gcvBLEND_INV_SOURCE_COLOR, + gcvBLEND_SOURCE_ALPHA, + gcvBLEND_INV_SOURCE_ALPHA, + gcvBLEND_TARGET_COLOR, + gcvBLEND_INV_TARGET_COLOR, + gcvBLEND_TARGET_ALPHA, + gcvBLEND_INV_TARGET_ALPHA, + gcvBLEND_SOURCE_ALPHA_SATURATE, + gcvBLEND_CONST_COLOR, + gcvBLEND_INV_CONST_COLOR, + gcvBLEND_CONST_ALPHA, + gcvBLEND_INV_CONST_ALPHA, +} +gceBLEND_FUNCTION; + +/* Blending modes. */ +typedef enum _gceBLEND_MODE +{ + gcvBLEND_ADD, + gcvBLEND_SUBTRACT, + gcvBLEND_REVERSE_SUBTRACT, + gcvBLEND_MIN, + gcvBLEND_MAX, +} +gceBLEND_MODE; + +/* Depth modes. */ +typedef enum _gceDEPTH_MODE +{ + gcvDEPTH_NONE, + gcvDEPTH_Z, + gcvDEPTH_W, +} +gceDEPTH_MODE; +#endif /* gcdENABLE_3D */ + +#if (gcdENABLE_3D || gcdENABLE_VG) +/* API flags. */ +typedef enum _gceAPI +{ + gcvAPI_D3D = 1, + gcvAPI_OPENGL_ES11, + gcvAPI_OPENGL_ES20, + gcvAPI_OPENGL_ES30, + gcvAPI_OPENGL, + gcvAPI_OPENVG, + gcvAPI_OPENCL, +} +gceAPI; +#endif + + +typedef enum _gceWHERE +{ + gcvWHERE_COMMAND, + gcvWHERE_RASTER, + gcvWHERE_PIXEL, +} +gceWHERE; + +typedef enum _gceHOW +{ + gcvHOW_SEMAPHORE = 0x1, + gcvHOW_STALL = 0x2, + gcvHOW_SEMAPHORE_STALL = 0x3, +} +gceHOW; + +typedef enum _gceSignalHandlerType +{ + gcvHANDLE_SIGFPE_WHEN_SIGNAL_CODE_IS_0 = 0x1, +} +gceSignalHandlerType; + +/* gcsHAL_Limits*/ +typedef struct _gcsHAL_LIMITS +{ + /* chip info */ + gceCHIPMODEL chipModel; + gctUINT32 chipRevision; + gctUINT32 featureCount; + gctUINT32 *chipFeatures; + + /* target caps */ + gctUINT32 maxWidth; + gctUINT32 maxHeight; + gctUINT32 multiTargetCount; + gctUINT32 maxSamples; + +}gcsHAL_LIMITS; + +/******************************************************************************\ +*********** Generic Memory Allocation Optimization Using Containers ************ +\******************************************************************************/ + +/* Generic container definition. */ +typedef struct _gcsCONTAINER_LINK * gcsCONTAINER_LINK_PTR; +typedef struct _gcsCONTAINER_LINK +{ + /* Points to the next container. */ + gcsCONTAINER_LINK_PTR next; +} +gcsCONTAINER_LINK; + +typedef struct _gcsCONTAINER_RECORD * gcsCONTAINER_RECORD_PTR; +typedef struct _gcsCONTAINER_RECORD +{ + gcsCONTAINER_RECORD_PTR prev; + gcsCONTAINER_RECORD_PTR next; +} +gcsCONTAINER_RECORD; + +typedef struct _gcsCONTAINER * gcsCONTAINER_PTR; +typedef struct _gcsCONTAINER +{ + gctUINT containerSize; + gctUINT recordSize; + gctUINT recordCount; + gcsCONTAINER_LINK_PTR containers; + gcsCONTAINER_RECORD freeList; + gcsCONTAINER_RECORD allocList; +} +gcsCONTAINER; + +gceSTATUS +gcsCONTAINER_Construct( + IN gcsCONTAINER_PTR Container, + gctUINT RecordsPerContainer, + gctUINT RecordSize + ); + +gceSTATUS +gcsCONTAINER_Destroy( + IN gcsCONTAINER_PTR Container + ); + +gceSTATUS +gcsCONTAINER_AllocateRecord( + IN gcsCONTAINER_PTR Container, + OUT gctPOINTER * Record + ); + +gceSTATUS +gcsCONTAINER_FreeRecord( + IN gcsCONTAINER_PTR Container, + IN gctPOINTER Record + ); + +gceSTATUS +gcsCONTAINER_FreeAll( + IN gcsCONTAINER_PTR Container + ); + +/******************************************************************************\ +********************************* gcoHAL Object ********************************* +\******************************************************************************/ + +/* Construct a new gcoHAL object. */ +gceSTATUS +gcoHAL_ConstructEx( + IN gctPOINTER Context, + IN gcoOS Os, + OUT gcoHAL * Hal + ); + +/* Destroy an gcoHAL object. */ +gceSTATUS +gcoHAL_DestroyEx( + IN gcoHAL Hal + ); + +/* Empty function for compatibility. */ +gceSTATUS +gcoHAL_Construct( + IN gctPOINTER Context, + IN gcoOS Os, + OUT gcoHAL * Hal + ); + +/* Empty function for compatibility. */ +gceSTATUS +gcoHAL_Destroy( + IN gcoHAL Hal + ); + +/* Get HAL options */ +gceSTATUS +gcoHAL_GetOption( + IN gcoHAL Hal, + IN gceOPTION Option + ); + +gceSTATUS +gcoHAL_FrameInfoOps( + IN gcoHAL Hal, + IN gceFRAMEINFO FrameInfo, + IN gceFRAMEINFO_OP Op, + IN OUT gctUINT * Val + ); + + +gceSTATUS +gcoHAL_GetHardware( + IN gcoHAL Hal, + OUT gcoHARDWARE* Hw + ); + +#if gcdENABLE_2D +/* Get pointer to gco2D object. */ +gceSTATUS +gcoHAL_Get2DEngine( + IN gcoHAL Hal, + OUT gco2D * Engine + ); +#endif + +#if gcdENABLE_3D +gceSTATUS +gcoHAL_GetSpecialHintData( + IN gcoHAL Hal, + OUT gctINT * Hint + ); +/* +** Deprecated(Don't use it), keep it here for external library(libgcu.so) +*/ +gceSTATUS +gcoHAL_Get3DEngine( + IN gcoHAL Hal, + OUT gco3D * Engine + ); +#endif /* gcdEANBLE_3D */ + + +gceSTATUS +gcoHAL_GetProductName( + IN gcoHAL Hal, + OUT gctSTRING *ProductName + ); + +gceSTATUS +gcoHAL_SetFscaleValue( + IN gctUINT FscaleValue + ); + +gceSTATUS +gcoHAL_GetFscaleValue( + OUT gctUINT * FscaleValue, + OUT gctUINT * MinFscaleValue, + OUT gctUINT * MaxFscaleValue + ); + +gceSTATUS +gcoHAL_SetBltNP2Texture( + gctBOOL enable + ); + +gceSTATUS +gcoHAL_NameVideoMemory( + IN gctUINT32 Handle, + OUT gctUINT32 * Name + ); + +gceSTATUS +gcoHAL_ImportVideoMemory( + IN gctUINT32 Name, + OUT gctUINT32 * Handle + ); + +gceSTATUS +gcoHAL_GetVideoMemoryFd( + IN gctUINT32 Handle, + OUT gctINT * Fd + ); + +/* Verify whether the specified feature is available in hardware. */ +gceSTATUS +gcoHAL_IsFeatureAvailable( + IN gcoHAL Hal, + IN gceFEATURE Feature + ); + +gceSTATUS +gcoHAL_IsSwwaNeeded( + IN gcoHAL Hal, + IN gceSWWA Swwa + ); + +gceSTATUS +gcoHAL_IsFeatureAvailable1( + IN gcoHAL Hal, + IN gceFEATURE Feature + ); + +/* Query the identity of the hardware. */ +gceSTATUS +gcoHAL_QueryChipIdentity( + IN gcoHAL Hal, + OUT gceCHIPMODEL* ChipModel, + OUT gctUINT32* ChipRevision, + OUT gctUINT32* ChipFeatures, + OUT gctUINT32* ChipMinorFeatures + ); + +/* Query the minor features of the hardware. */ +gceSTATUS gcoHAL_QueryChipMinorFeatures( + IN gcoHAL Hal, + OUT gctUINT32* NumFeatures, + OUT gctUINT32* ChipMinorFeatures + ); + +gctINT32 +gcoOS_EndRecordAllocation(void); +void +gcoOS_RecordAllocation(void); +void +gcoOS_AddRecordAllocation(gctSIZE_T Size); + +/* Query the amount of video memory. */ +gceSTATUS +gcoHAL_QueryVideoMemory( + IN gcoHAL Hal, + OUT gctPHYS_ADDR * InternalAddress, + OUT gctSIZE_T * InternalSize, + OUT gctPHYS_ADDR * ExternalAddress, + OUT gctSIZE_T * ExternalSize, + OUT gctPHYS_ADDR * ContiguousAddress, + OUT gctSIZE_T * ContiguousSize + ); + +/* Map video memory. */ +gceSTATUS +gcoHAL_MapMemory( + IN gcoHAL Hal, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T NumberOfBytes, + OUT gctPOINTER * Logical + ); + +/* Unmap video memory. */ +gceSTATUS +gcoHAL_UnmapMemory( + IN gcoHAL Hal, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T NumberOfBytes, + IN gctPOINTER Logical + ); + +/* Schedule an unmap of a buffer mapped through its physical address. */ +gceSTATUS +gcoHAL_ScheduleUnmapMemory( + IN gcoHAL Hal, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T NumberOfBytes, + IN gctPOINTER Logical + ); + +/* Allocate video memory. */ +gceSTATUS +gcoOS_AllocateVideoMemory( + IN gcoOS Os, + IN gctBOOL InUserSpace, + IN gctBOOL InCacheable, + IN OUT gctSIZE_T * Bytes, + OUT gctUINT32 * Physical, + OUT gctPOINTER * Logical, + OUT gctPOINTER * Handle + ); + +/* Free video memory. */ +gceSTATUS +gcoOS_FreeVideoMemory( + IN gcoOS Os, + IN gctPOINTER Handle + ); + +/* Lock video memory. */ +gceSTATUS +gcoOS_LockVideoMemory( + IN gcoOS Os, + IN gctPOINTER Handle, + IN gctBOOL InUserSpace, + IN gctBOOL InCacheable, + OUT gctUINT32 * Physical, + OUT gctPOINTER * Logical + ); + +/* Map user memory. */ +gceSTATUS +gcoHAL_MapUserMemory( + IN gctPOINTER Logical, + IN gctUINT32 Physical, + IN gctSIZE_T Size, + OUT gctPOINTER * Info, + OUT gctUINT32_PTR GPUAddress + ); + +/* Unmap user memory. */ +gceSTATUS +gcoHAL_UnmapUserMemory( + IN gctPOINTER Logical, + IN gctSIZE_T Size, + IN gctPOINTER Info, + IN gctUINT32 GPUAddress + ); + +/* Schedule an unmap of a user buffer using event mechanism. */ +gceSTATUS +gcoHAL_ScheduleUnmapUserMemory( + IN gcoHAL Hal, + IN gctPOINTER Info, + IN gctSIZE_T Size, + IN gctUINT32 Address, + IN gctPOINTER Memory + ); + +/* Commit the current command buffer. */ +gceSTATUS +gcoHAL_Commit( + IN gcoHAL Hal, + IN gctBOOL Stall + ); + +#if gcdENABLE_3D +/* Sencd fence command. */ +gceSTATUS +gcoHAL_SendFence( + IN gcoHAL Hal + ); +#endif /* gcdENABLE_3D */ + +/* Query the tile capabilities. */ +gceSTATUS +gcoHAL_QueryTiled( + IN gcoHAL Hal, + OUT gctINT32 * TileWidth2D, + OUT gctINT32 * TileHeight2D, + OUT gctINT32 * TileWidth3D, + OUT gctINT32 * TileHeight3D + ); + +gceSTATUS +gcoHAL_Compact( + IN gcoHAL Hal + ); + +#if VIVANTE_PROFILER +gceSTATUS +gcoHAL_ProfileStart( + IN gcoHAL Hal + ); + +gceSTATUS +gcoHAL_ProfileEnd( + IN gcoHAL Hal, + IN gctCONST_STRING Title + ); +#endif + +/* Power Management */ +gceSTATUS +gcoHAL_SetPowerManagementState( + IN gcoHAL Hal, + IN gceCHIPPOWERSTATE State + ); + +gceSTATUS +gcoHAL_QueryPowerManagementState( + IN gcoHAL Hal, + OUT gceCHIPPOWERSTATE *State + ); + +/* Set the filter type for filter blit. */ +gceSTATUS +gcoHAL_SetFilterType( + IN gcoHAL Hal, + IN gceFILTER_TYPE FilterType + ); + +gceSTATUS +gcoHAL_GetDump( + IN gcoHAL Hal, + OUT gcoDUMP * Dump + ); + +#if gcdENABLE_3D +gceSTATUS +gcoHAL_SetPatchID( + IN gcoHAL Hal, + IN gcePATCH_ID PatchID + ); + +/* Get Patch ID based on process name */ +gceSTATUS +gcoHAL_GetPatchID( + IN gcoHAL Hal, + OUT gcePATCH_ID * PatchID + ); + +gceSTATUS +gcoHAL_SetGlobalPatchID( + IN gcoHAL Hal, + IN gcePATCH_ID PatchID + ); +#endif /* gcdENABLE_3D */ +/* Call the kernel HAL layer. */ +gceSTATUS +gcoHAL_Call( + IN gcoHAL Hal, + IN OUT gcsHAL_INTERFACE_PTR Interface + ); + +/* Schedule an event. */ +gceSTATUS +gcoHAL_ScheduleEvent( + IN gcoHAL Hal, + IN OUT gcsHAL_INTERFACE_PTR Interface + ); + +/* Destroy a surface. */ +gceSTATUS +gcoHAL_DestroySurface( + IN gcoHAL Hal, + IN gcoSURF Surface + ); + +/* Request a start/stop timestamp. */ +gceSTATUS +gcoHAL_SetTimer( + IN gcoHAL Hal, + IN gctUINT32 Index, + IN gctBOOL Start + ); + +/* Get Time delta from a Timer in microseconds. */ +gceSTATUS +gcoHAL_GetTimerTime( + IN gcoHAL Hal, + IN gctUINT32 Timer, + OUT gctINT32_PTR TimeDelta + ); + +/* set timeout value. */ +gceSTATUS +gcoHAL_SetTimeOut( + IN gcoHAL Hal, + IN gctUINT32 timeOut + ); + +gceSTATUS +gcoHAL_SetHardwareType( + IN gcoHAL Hal, + IN gceHARDWARE_TYPE HardwardType + ); + +gceSTATUS +gcoHAL_GetHardwareType( + IN gcoHAL Hal, + OUT gceHARDWARE_TYPE * HardwardType + ); + +gceSTATUS +gcoHAL_QueryChipCount( + IN gcoHAL Hal, + OUT gctINT32 * Count + ); + +gceSTATUS +gcoHAL_Query3DCoreCount( + IN gcoHAL Hal, + OUT gctUINT32 *Count + ); + +gceSTATUS +gcoHAL_QuerySeparated2D( + IN gcoHAL Hal + ); + +gceSTATUS +gcoHAL_Is3DAvailable( + IN gcoHAL Hal + ); + +/* Get pointer to gcoVG object. */ +gceSTATUS +gcoHAL_GetVGEngine( + IN gcoHAL Hal, + OUT gcoVG * Engine + ); + +gceSTATUS +gcoHAL_QueryChipLimits( + IN gcoHAL Hal, + IN gctINT32 Chip, + IN gctINT32 Mask, + OUT gcsHAL_LIMITS *Limits); + +gceSTATUS +gcoHAL_QueryChipFeature( + IN gcoHAL Hal, + IN gctINT32 Chip, + IN gctINT32 Mask, + IN gceFEATURE Feature); + +/*----------------------------------------------------------------------------*/ +/*----- Shared Buffer --------------------------------------------------------*/ + +/* Create shared buffer. */ +gceSTATUS +gcoHAL_CreateShBuffer( + IN gctUINT32 Size, + OUT gctSHBUF * ShBuf + ); + +/* Destroy shared buffer. */ +gceSTATUS +gcoHAL_DestroyShBuffer( + IN gctSHBUF ShBuf + ); + +/* Map shared buffer to current process. */ +gceSTATUS +gcoHAL_MapShBuffer( + IN gctSHBUF ShBuf + ); + +/* Write user data to shared buffer. */ +gceSTATUS +gcoHAL_WriteShBuffer( + IN gctSHBUF ShBuf, + IN gctCONST_POINTER Data, + IN gctUINT32 ByteCount + ); + +/* Read user data from shared buffer. */ +gceSTATUS +gcoHAL_ReadShBuffer( + IN gctSHBUF ShBuf, + IN gctPOINTER Data, + IN gctUINT32 BytesCount, + OUT gctUINT32 * BytesRead + ); + +/* Config power management to be enabled or disabled. */ +gceSTATUS +gcoHAL_ConfigPowerManagement( + IN gctBOOL Enable + ); + +#if gcdENABLE_3D || gcdENABLE_VG +/* Query the target capabilities. */ +gceSTATUS +gcoHAL_QueryTargetCaps( + IN gcoHAL Hal, + OUT gctUINT * MaxWidth, + OUT gctUINT * MaxHeight, + OUT gctUINT * MultiTargetCount, + OUT gctUINT * MaxSamples + ); +#endif + +/******************************************************************************\ +********************************** gcoOS Object ********************************* +\******************************************************************************/ +/* Lock PLS access */ +gceSTATUS +gcoOS_LockPLS( + void + ); + +/* Unlock PLS access */ +gceSTATUS +gcoOS_UnLockPLS( + void + ); + +/* Get PLS value for given key */ +gctPOINTER +gcoOS_GetPLSValue( + IN gcePLS_VALUE key + ); + +/* Set PLS value of a given key */ +void +gcoOS_SetPLSValue( + IN gcePLS_VALUE key, + OUT gctPOINTER value + ); + +/* Get access to the thread local storage. */ +gceSTATUS +gcoOS_GetTLS( + OUT gcsTLS_PTR * TLS + ); + + /* Copy the TLS from a source thread. */ + gceSTATUS gcoOS_CopyTLS(IN gcsTLS_PTR Source); + +/* Destroy the objects associated with the current thread. */ +void +gcoOS_FreeThreadData( + void + ); + +/* Empty function for compatibility. */ +gceSTATUS +gcoOS_Construct( + IN gctPOINTER Context, + OUT gcoOS * Os + ); + +/* Empty function for compatibility. */ +gceSTATUS +gcoOS_Destroy( + IN gcoOS Os + ); + +/* Get the base address for the physical memory. */ +gceSTATUS +gcoOS_GetBaseAddress( + IN gcoOS Os, + OUT gctUINT32_PTR BaseAddress + ); + +/* Allocate memory from the heap. */ +gceSTATUS +gcoOS_Allocate( + IN gcoOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ); + +/* Get allocated memory size. */ +gceSTATUS +gcoOS_GetMemorySize( + IN gcoOS Os, + IN gctPOINTER Memory, + OUT gctSIZE_T_PTR MemorySize + ); + +/* Free allocated memory. */ +gceSTATUS +gcoOS_Free( + IN gcoOS Os, + IN gctPOINTER Memory + ); + +/* Allocate memory. */ +gceSTATUS +gcoOS_AllocateSharedMemory( + IN gcoOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ); + +/* Free memory. */ +gceSTATUS +gcoOS_FreeSharedMemory( + IN gcoOS Os, + IN gctPOINTER Memory + ); + +/* Allocate memory. */ +gceSTATUS +gcoOS_AllocateMemory( + IN gcoOS Os, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Memory + ); + +/* Free memory. */ +gceSTATUS +gcoOS_FreeMemory( + IN gcoOS Os, + IN gctPOINTER Memory + ); + +/* Allocate contiguous memory. */ +gceSTATUS +gcoOS_AllocateContiguous( + IN gcoOS Os, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ); + +/* Free contiguous memory. */ +gceSTATUS +gcoOS_FreeContiguous( + IN gcoOS Os, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); + +/* Map user memory. */ +gceSTATUS +gcoOS_MapUserMemory( + IN gcoOS Os, + IN gctPOINTER Memory, + IN gctSIZE_T Size, + OUT gctPOINTER * Info, + OUT gctUINT32_PTR Address + ); + +/* Map user memory. */ +gceSTATUS +gcoOS_MapUserMemoryEx( + IN gcoOS Os, + IN gctPOINTER Memory, + IN gctUINT32 Physical, + IN gctSIZE_T Size, + OUT gctPOINTER * Info, + OUT gctUINT32_PTR Address + ); + +/* Unmap user memory. */ +gceSTATUS +gcoOS_UnmapUserMemory( + IN gcoOS Os, + IN gctPOINTER Memory, + IN gctSIZE_T Size, + IN gctPOINTER Info, + IN gctUINT32 Address + ); + +/* Device I/O Control call to the kernel HAL layer. */ +gceSTATUS +gcoOS_DeviceControl( + IN gcoOS Os, + IN gctUINT32 IoControlCode, + IN gctPOINTER InputBuffer, + IN gctSIZE_T InputBufferSize, + IN gctPOINTER OutputBuffer, + IN gctSIZE_T OutputBufferSize + ); + +/* Allocate non paged memory. */ +gceSTATUS +gcoOS_AllocateNonPagedMemory( + IN gcoOS Os, + IN gctBOOL InUserSpace, + IN OUT gctSIZE_T * Bytes, + OUT gctPHYS_ADDR * Physical, + OUT gctPOINTER * Logical + ); + +/* Free non paged memory. */ +gceSTATUS +gcoOS_FreeNonPagedMemory( + IN gcoOS Os, + IN gctSIZE_T Bytes, + IN gctPHYS_ADDR Physical, + IN gctPOINTER Logical + ); + +#define gcmOS_SAFE_FREE(os, mem) \ + gcoOS_Free(os, mem); \ + mem = gcvNULL + +#define gcmOS_SAFE_FREE_SHARED_MEMORY(os, mem) \ + gcoOS_FreeSharedMemory(os, mem); \ + mem = gcvNULL + +#define gcmkOS_SAFE_FREE(os, mem) \ + gckOS_Free(os, mem); \ + mem = gcvNULL + +typedef enum _gceFILE_MODE +{ + gcvFILE_CREATE = 0, + gcvFILE_APPEND, + gcvFILE_READ, + gcvFILE_CREATETEXT, + gcvFILE_APPENDTEXT, + gcvFILE_READTEXT, +} +gceFILE_MODE; + +/* Open a file. */ +gceSTATUS +gcoOS_Open( + IN gcoOS Os, + IN gctCONST_STRING FileName, + IN gceFILE_MODE Mode, + OUT gctFILE * File + ); + +/* Close a file. */ +gceSTATUS +gcoOS_Close( + IN gcoOS Os, + IN gctFILE File + ); + +/* Read data from a file. */ +gceSTATUS +gcoOS_Read( + IN gcoOS Os, + IN gctFILE File, + IN gctSIZE_T ByteCount, + IN gctPOINTER Data, + OUT gctSIZE_T * ByteRead + ); + +/* Write data to a file. */ +gceSTATUS +gcoOS_Write( + IN gcoOS Os, + IN gctFILE File, + IN gctSIZE_T ByteCount, + IN gctCONST_POINTER Data + ); + +/* Flush data to a file. */ +gceSTATUS +gcoOS_Flush( + IN gcoOS Os, + IN gctFILE File + ); + +/* Close a file descriptor. */ +gceSTATUS +gcoOS_CloseFD( + IN gcoOS Os, + IN gctINT FD + ); + +/* Dup file descriptor to another. */ +gceSTATUS +gcoOS_DupFD( + IN gcoOS Os, + IN gctINT FD, + OUT gctINT * FD2 + ); + +/* Create an endpoint for communication. */ +gceSTATUS +gcoOS_Socket( + IN gcoOS Os, + IN gctINT Domain, + IN gctINT Type, + IN gctINT Protocol, + OUT gctINT *SockFd + ); + +/* Close a socket. */ +gceSTATUS +gcoOS_CloseSocket( + IN gcoOS Os, + IN gctINT SockFd + ); + +/* Initiate a connection on a socket. */ +gceSTATUS +gcoOS_Connect( + IN gcoOS Os, + IN gctINT SockFd, + IN gctCONST_POINTER HostName, + IN gctUINT Port); + +/* Shut down part of connection on a socket. */ +gceSTATUS +gcoOS_Shutdown( + IN gcoOS Os, + IN gctINT SockFd, + IN gctINT How + ); + +/* Send a message on a socket. */ +gceSTATUS +gcoOS_Send( + IN gcoOS Os, + IN gctINT SockFd, + IN gctSIZE_T ByteCount, + IN gctCONST_POINTER Data, + IN gctINT Flags + ); + +/* Initiate a connection on a socket. */ +gceSTATUS +gcoOS_WaitForSend( + IN gcoOS Os, + IN gctINT SockFd, + IN gctINT Seconds, + IN gctINT MicroSeconds); + +/* Get environment variable value. */ +gceSTATUS +gcoOS_GetEnv( + IN gcoOS Os, + IN gctCONST_STRING VarName, + OUT gctSTRING * Value + ); + +/* Set environment variable value. */ +gceSTATUS +gcoOS_SetEnv( + IN gcoOS Os, + IN gctCONST_STRING VarName, + IN gctSTRING Value + ); + +/* Get current working directory. */ +gceSTATUS +gcoOS_GetCwd( + IN gcoOS Os, + IN gctINT SizeInBytes, + OUT gctSTRING Buffer + ); + +/* Get file status info. */ +gceSTATUS +gcoOS_Stat( + IN gcoOS Os, + IN gctCONST_STRING FileName, + OUT gctPOINTER Buffer + ); + +typedef enum _gceFILE_WHENCE +{ + gcvFILE_SEEK_SET, + gcvFILE_SEEK_CUR, + gcvFILE_SEEK_END +} +gceFILE_WHENCE; + +/* Set the current position of a file. */ +gceSTATUS +gcoOS_Seek( + IN gcoOS Os, + IN gctFILE File, + IN gctUINT32 Offset, + IN gceFILE_WHENCE Whence + ); + +/* Set the current position of a file. */ +gceSTATUS +gcoOS_SetPos( + IN gcoOS Os, + IN gctFILE File, + IN gctUINT32 Position + ); + +/* Get the current position of a file. */ +gceSTATUS +gcoOS_GetPos( + IN gcoOS Os, + IN gctFILE File, + OUT gctUINT32 * Position + ); + +/* Same as strstr. */ +gceSTATUS +gcoOS_StrStr( + IN gctCONST_STRING String, + IN gctCONST_STRING SubString, + OUT gctSTRING * Output + ); + +/* Find the last occurance of a character inside a string. */ +gceSTATUS +gcoOS_StrFindReverse( + IN gctCONST_STRING String, + IN gctINT8 Character, + OUT gctSTRING * Output + ); + +gceSTATUS +gcoOS_StrDup( + IN gcoOS Os, + IN gctCONST_STRING String, + OUT gctSTRING * Target + ); + +/* Copy a string. */ +gceSTATUS +gcoOS_StrCopySafe( + IN gctSTRING Destination, + IN gctSIZE_T DestinationSize, + IN gctCONST_STRING Source + ); + +/* Append a string. */ +gceSTATUS +gcoOS_StrCatSafe( + IN gctSTRING Destination, + IN gctSIZE_T DestinationSize, + IN gctCONST_STRING Source + ); + +/* Compare two strings. */ +gceSTATUS +gcoOS_StrCmp( + IN gctCONST_STRING String1, + IN gctCONST_STRING String2 + ); + +/* Compare characters of two strings. */ +gceSTATUS +gcoOS_StrNCmp( + IN gctCONST_STRING String1, + IN gctCONST_STRING String2, + IN gctSIZE_T Count + ); + +/* Convert string to float. */ +gceSTATUS +gcoOS_StrToFloat( + IN gctCONST_STRING String, + OUT gctFLOAT * Float + ); + +/* Convert hex string to integer. */ +gceSTATUS gcoOS_HexStrToInt( + IN gctCONST_STRING String, + OUT gctINT * Int + ); + +/* Convert hex string to float. */ +gceSTATUS +gcoOS_HexStrToFloat( + IN gctCONST_STRING String, + OUT gctFLOAT * Float + ); + +/* Convert string to integer. */ +gceSTATUS +gcoOS_StrToInt( + IN gctCONST_STRING String, + OUT gctINT * Int + ); + +gceSTATUS +gcoOS_MemCmp( + IN gctCONST_POINTER Memory1, + IN gctCONST_POINTER Memory2, + IN gctSIZE_T Bytes + ); + +gceSTATUS +gcoOS_PrintStrSafe( + OUT gctSTRING String, + IN gctSIZE_T StringSize, + IN OUT gctUINT * Offset, + IN gctCONST_STRING Format, + ... + ); + +gceSTATUS +gcoOS_LoadLibrary( + IN gcoOS Os, + IN gctCONST_STRING Library, + OUT gctHANDLE * Handle + ); + +gceSTATUS +gcoOS_FreeLibrary( + IN gcoOS Os, + IN gctHANDLE Handle + ); + +gceSTATUS +gcoOS_GetProcAddress( + IN gcoOS Os, + IN gctHANDLE Handle, + IN gctCONST_STRING Name, + OUT gctPOINTER * Function + ); + +gceSTATUS +gcoOS_Compact( + IN gcoOS Os + ); + +gceSTATUS +gcoOS_AddSignalHandler ( + IN gceSignalHandlerType SignalHandlerType + ); + +#if VIVANTE_PROFILER +gceSTATUS +gcoOS_ProfileStart( + IN gcoOS Os + ); + +gceSTATUS +gcoOS_ProfileEnd( + IN gcoOS Os, + IN gctCONST_STRING Title + ); + +gceSTATUS +gcoOS_SetProfileSetting( + IN gcoOS Os, + IN gctBOOL Enable, + IN gctCONST_STRING FileName + ); +#endif + +/* Query the video memory. */ +gceSTATUS +gcoOS_QueryVideoMemory( + IN gcoOS Os, + OUT gctPHYS_ADDR * InternalAddress, + OUT gctSIZE_T * InternalSize, + OUT gctPHYS_ADDR * ExternalAddress, + OUT gctSIZE_T * ExternalSize, + OUT gctPHYS_ADDR * ContiguousAddress, + OUT gctSIZE_T * ContiguousSize + ); + +/* Detect if the process is the executable specified. */ +gceSTATUS +gcoOS_DetectProcessByNamePid( + IN gctCONST_STRING Name, + IN gctHANDLE Pid + ); + +/* Detect if the current process is the executable specified. */ +gceSTATUS +gcoOS_DetectProcessByName( + IN gctCONST_STRING Name + ); + +gceSTATUS +gcoOS_DetectProcessByEncryptedName( + IN gctCONST_STRING Name + ); + +#if defined(ANDROID) +gceSTATUS +gcoOS_DetectProgrameByEncryptedSymbols( + IN gcoOS_SymbolsList Symbols + ); +#endif + +/*----------------------------------------------------------------------------*/ +/*----- Atoms ----------------------------------------------------------------*/ + +/* Construct an atom. */ +gceSTATUS +gcoOS_AtomConstruct( + IN gcoOS Os, + OUT gcsATOM_PTR * Atom + ); + +/* Destroy an atom. */ +gceSTATUS +gcoOS_AtomDestroy( + IN gcoOS Os, + IN gcsATOM_PTR Atom + ); + +/* Get the 32-bit value protected by an atom. */ +gceSTATUS +gcoOS_AtomGet( + IN gcoOS Os, + IN gcsATOM_PTR Atom, + OUT gctINT32_PTR Value + ); + +/* Set the 32-bit value protected by an atom. */ +gceSTATUS +gcoOS_AtomSet( + IN gcoOS Os, + IN gcsATOM_PTR Atom, + IN gctINT32 Value + ); + +/* Increment an atom. */ +gceSTATUS +gcoOS_AtomIncrement( + IN gcoOS Os, + IN gcsATOM_PTR Atom, + OUT gctINT32_PTR OldValue + ); + +/* Decrement an atom. */ +gceSTATUS +gcoOS_AtomDecrement( + IN gcoOS Os, + IN gcsATOM_PTR Atom, + OUT gctINT32_PTR OldValue + ); + +gctHANDLE +gcoOS_GetCurrentProcessID( + void + ); + +gctHANDLE +gcoOS_GetCurrentThreadID( + void + ); + +/*----------------------------------------------------------------------------*/ +/*----- Time -----------------------------------------------------------------*/ + +/* Get the number of milliseconds since the system started. */ +gctUINT32 +gcoOS_GetTicks( + void + ); + +/* Get time in microseconds. */ +gceSTATUS +gcoOS_GetTime( + gctUINT64_PTR Time + ); + +/* Get CPU usage in microseconds. */ +gceSTATUS +gcoOS_GetCPUTime( + gctUINT64_PTR CPUTime + ); + +/* Get memory usage. */ +gceSTATUS +gcoOS_GetMemoryUsage( + gctUINT32_PTR MaxRSS, + gctUINT32_PTR IxRSS, + gctUINT32_PTR IdRSS, + gctUINT32_PTR IsRSS + ); + +/* Delay a number of microseconds. */ +gceSTATUS +gcoOS_Delay( + IN gcoOS Os, + IN gctUINT32 Delay + ); + +/*----------------------------------------------------------------------------*/ +/*----- Threads --------------------------------------------------------------*/ + +#ifdef _WIN32 +/* Cannot include windows.h here becuase "near" and "far" + * which are used in gcsDEPTH_INFO, are defined to nothing in WinDef.h. + * So, use the real value of DWORD and WINAPI, instead. + * DWORD is unsigned long, and WINAPI is __stdcall. + * If these two are change in WinDef.h, the following two typdefs + * need to be changed, too. + */ +typedef unsigned long gctTHREAD_RETURN; +typedef unsigned long (__stdcall * gcTHREAD_ROUTINE)(void * Argument); +#else +typedef void * gctTHREAD_RETURN; +typedef void * (* gcTHREAD_ROUTINE)(void *); +#endif + +/* Create a new thread. */ +gceSTATUS +gcoOS_CreateThread( + IN gcoOS Os, + IN gcTHREAD_ROUTINE Worker, + IN gctPOINTER Argument, + OUT gctPOINTER * Thread + ); + +/* Close a thread. */ +gceSTATUS +gcoOS_CloseThread( + IN gcoOS Os, + IN gctPOINTER Thread + ); + +/*----------------------------------------------------------------------------*/ +/*----- Mutexes --------------------------------------------------------------*/ + +/* Create a new mutex. */ +gceSTATUS +gcoOS_CreateMutex( + IN gcoOS Os, + OUT gctPOINTER * Mutex + ); + +/* Delete a mutex. */ +gceSTATUS +gcoOS_DeleteMutex( + IN gcoOS Os, + IN gctPOINTER Mutex + ); + +/* Acquire a mutex. */ +gceSTATUS +gcoOS_AcquireMutex( + IN gcoOS Os, + IN gctPOINTER Mutex, + IN gctUINT32 Timeout + ); + +/* Release a mutex. */ +gceSTATUS +gcoOS_ReleaseMutex( + IN gcoOS Os, + IN gctPOINTER Mutex + ); + +/*----------------------------------------------------------------------------*/ +/*----- Signals --------------------------------------------------------------*/ + +/* Create a signal. */ +gceSTATUS +gcoOS_CreateSignal( + IN gcoOS Os, + IN gctBOOL ManualReset, + OUT gctSIGNAL * Signal + ); + +/* Destroy a signal. */ +gceSTATUS +gcoOS_DestroySignal( + IN gcoOS Os, + IN gctSIGNAL Signal + ); + +/* Signal a signal. */ +gceSTATUS +gcoOS_Signal( + IN gcoOS Os, + IN gctSIGNAL Signal, + IN gctBOOL State + ); + +/* Wait for a signal. */ +gceSTATUS +gcoOS_WaitSignal( + IN gcoOS Os, + IN gctSIGNAL Signal, + IN gctUINT32 Wait + ); + +/* Map a signal from another process */ +gceSTATUS +gcoOS_MapSignal( + IN gctSIGNAL RemoteSignal, + OUT gctSIGNAL * LocalSignal + ); + +/* Unmap a signal mapped from another process */ +gceSTATUS +gcoOS_UnmapSignal( + IN gctSIGNAL Signal + ); + +/*----------------------------------------------------------------------------*/ +/*----- Android Native Fence -------------------------------------------------*/ + +/* Create sync point. */ +gceSTATUS +gcoOS_CreateSyncPoint( + IN gcoOS Os, + OUT gctSYNC_POINT * SyncPoint + ); + +/* Destroy sync point. */ +gceSTATUS +gcoOS_DestroySyncPoint( + IN gcoOS Os, + IN gctSYNC_POINT SyncPoint + ); + +/* Create native fence. */ +gceSTATUS +gcoOS_CreateNativeFence( + IN gcoOS Os, + IN gctSYNC_POINT SyncPoint, + OUT gctINT * FenceFD + ); + +/* Wait on native fence. */ +gceSTATUS +gcoOS_WaitNativeFence( + IN gcoOS Os, + IN gctINT FenceFD, + IN gctUINT32 Timeout + ); + +/*----------------------------------------------------------------------------*/ +/*----- Memory Access and Cache ----------------------------------------------*/ + +/* Write a register. */ +gceSTATUS +gcoOS_WriteRegister( + IN gcoOS Os, + IN gctUINT32 Address, + IN gctUINT32 Data + ); + +/* Read a register. */ +gceSTATUS +gcoOS_ReadRegister( + IN gcoOS Os, + IN gctUINT32 Address, + OUT gctUINT32 * Data + ); + +gceSTATUS +gcoOS_CacheClean( + IN gcoOS Os, + IN gctUINT32 Node, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); + +gceSTATUS +gcoOS_CacheFlush( + IN gcoOS Os, + IN gctUINT32 Node, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); + +gceSTATUS +gcoOS_CacheInvalidate( + IN gcoOS Os, + IN gctUINT32 Node, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); + +gceSTATUS +gcoOS_MemoryBarrier( + IN gcoOS Os, + IN gctPOINTER Logical + ); + +gceSTATUS +gcoOS_CPUPhysicalToGPUPhysical( + IN gctUINT32 CPUPhysical, + OUT gctUINT32_PTR GPUPhysical + ); + +/*----------------------------------------------------------------------------*/ +/*----- Profile --------------------------------------------------------------*/ + +gceSTATUS +gckOS_GetProfileTick( + OUT gctUINT64_PTR Tick + ); + +gceSTATUS +gckOS_QueryProfileTickRate( + OUT gctUINT64_PTR TickRate + ); + +gctUINT32 +gckOS_ProfileToMS( + IN gctUINT64 Ticks + ); + +gceSTATUS +gcoOS_GetProfileTick( + OUT gctUINT64_PTR Tick + ); + +gceSTATUS +gcoOS_QueryProfileTickRate( + OUT gctUINT64_PTR TickRate + ); + +#define _gcmPROFILE_INIT(prefix, freq, start) \ + do { \ + prefix ## OS_QueryProfileTickRate(&(freq)); \ + prefix ## OS_GetProfileTick(&(start)); \ + } while (gcvFALSE) + +#define _gcmPROFILE_QUERY(prefix, start, ticks) \ + do { \ + prefix ## OS_GetProfileTick(&(ticks)); \ + (ticks) = ((ticks) > (start)) ? ((ticks) - (start)) \ + : (~0ull - (start) + (ticks) + 1); \ + } while (gcvFALSE) + +#if gcdENABLE_PROFILING +# define gcmkPROFILE_INIT(freq, start) _gcmPROFILE_INIT(gck, freq, start) +# define gcmkPROFILE_QUERY(start, ticks) _gcmPROFILE_QUERY(gck, start, ticks) +# define gcmPROFILE_INIT(freq, start) _gcmPROFILE_INIT(gco, freq, start) +# define gcmPROFILE_QUERY(start, ticks) _gcmPROFILE_QUERY(gco, start, ticks) +# define gcmPROFILE_ONLY(x) x +# define gcmPROFILE_ELSE(x) do { } while (gcvFALSE) +# define gcmPROFILE_DECLARE_ONLY(x) x +# define gcmPROFILE_DECLARE_ELSE(x) typedef x +#else +# define gcmkPROFILE_INIT(start, freq) do { } while (gcvFALSE) +# define gcmkPROFILE_QUERY(start, ticks) do { } while (gcvFALSE) +# define gcmPROFILE_INIT(start, freq) do { } while (gcvFALSE) +# define gcmPROFILE_QUERY(start, ticks) do { } while (gcvFALSE) +# define gcmPROFILE_ONLY(x) do { } while (gcvFALSE) +# define gcmPROFILE_ELSE(x) x +# define gcmPROFILE_DECLARE_ONLY(x) do { } while (gcvFALSE) +# define gcmPROFILE_DECLARE_ELSE(x) x +#endif + +/******************************************************************************* +** gcoMATH object +*/ + +#define gcdPI 3.14159265358979323846f + +/* Kernel. */ +gctINT +gckMATH_ModuloInt( + IN gctINT X, + IN gctINT Y + ); + +/* User. */ +gctUINT32 +gcoMATH_Log2in5dot5( + IN gctINT X + ); + + +gctFLOAT +gcoMATH_UIntAsFloat( + IN gctUINT32 X + ); + +gctUINT32 +gcoMATH_FloatAsUInt( + IN gctFLOAT X + ); + +gctBOOL +gcoMATH_CompareEqualF( + IN gctFLOAT X, + IN gctFLOAT Y + ); + +gctUINT16 +gcoMATH_UInt8AsFloat16( + IN gctUINT8 X + ); + +gctUINT32 +gcoMATH_Float16ToFloat( + IN gctUINT16 In + ); + +gctUINT16 +gcoMATH_FloatToFloat16( + IN gctUINT32 In + ); + +gctUINT32 +gcoMATH_Float11ToFloat( + IN gctUINT32 In + ); + +gctUINT16 +gcoMATH_FloatToFloat11( + IN gctUINT32 In + ); + +gctUINT32 +gcoMATH_Float10ToFloat( + IN gctUINT32 In + ); + +gctUINT16 +gcoMATH_FloatToFloat10( + IN gctUINT32 In + ); + +gctUINT32 +gcoMATH_Float14ToFloat( + IN gctUINT16 In + ); + +/******************************************************************************\ +**************************** Coordinate Structures ***************************** +\******************************************************************************/ + +typedef struct _gcsPOINT +{ + gctINT32 x; + gctINT32 y; +} +gcsPOINT; + +typedef struct _gcsSIZE +{ + gctINT32 width; + gctINT32 height; +} +gcsSIZE; + +typedef struct _gcsRECT +{ + gctINT32 left; + gctINT32 top; + gctINT32 right; + gctINT32 bottom; +} +gcsRECT; + +typedef union _gcsPIXEL +{ + struct + { + gctFLOAT r, g, b, a; + gctFLOAT d, s; + } pf; + + struct + { + gctINT32 r, g, b, a; + gctINT32 d, s; + } pi; + + struct + { + gctUINT32 r, g, b, a; + gctUINT32 d, s; + } pui; + +} gcsPIXEL; + +/******************************************************************************\ +********************************* gcoSURF Object ******************************** +\******************************************************************************/ + +/*----------------------------------------------------------------------------*/ +/*------------------------------- gcoSURF Common ------------------------------*/ + +/* Color format classes. */ +typedef enum _gceFORMAT_CLASS +{ + gcvFORMAT_CLASS_RGBA = 4500, + gcvFORMAT_CLASS_YUV, + gcvFORMAT_CLASS_INDEX, + gcvFORMAT_CLASS_LUMINANCE, + gcvFORMAT_CLASS_BUMP, + gcvFORMAT_CLASS_DEPTH, + gcvFORMAT_CLASS_ASTC, + gcvFORMAT_CLASS_OTHER +} +gceFORMAT_CLASS; + +/* Color format data type */ +typedef enum _gceFORMAT_DATATYPE +{ + gcvFORMAT_DATATYPE_UNSIGNED_NORMALIZED, + gcvFORMAT_DATATYPE_SIGNED_NORMALIZED, + gcvFORMAT_DATATYPE_UNSIGNED_INTEGER, + gcvFORMAT_DATATYPE_SIGNED_INTEGER, + gcvFORMAT_DATATYPE_FLOAT16, + gcvFORMAT_DATATYPE_FLOAT32, + gcvFORMAT_DATATYPE_FLOAT_E5B9G9R9, + gcvFORMAT_DATATYPE_FLOAT_B10G11R11F, + gcvFORMAT_DATATYPE_INDEX, + gcvFORMAT_DATATYPE_SRGB, + gcvFORMAT_DATATYPE_FLOAT32_UINT, +} +gceFORMAT_DATATYPE; + +/* Special enums for width field in gcsFORMAT_COMPONENT. */ +typedef enum _gceCOMPONENT_CONTROL +{ + gcvCOMPONENT_NOTPRESENT = 0x00, + gcvCOMPONENT_DONTCARE = 0x80, + gcvCOMPONENT_WIDTHMASK = 0x7F, + gcvCOMPONENT_ODD = 0x80 +} +gceCOMPONENT_CONTROL; + +/* Color format component parameters. */ +typedef struct _gcsFORMAT_COMPONENT +{ + gctUINT8 start; + gctUINT8 width; +} +gcsFORMAT_COMPONENT; + +/* RGBA color format class. */ +typedef struct _gcsFORMAT_CLASS_TYPE_RGBA +{ + gcsFORMAT_COMPONENT alpha; + gcsFORMAT_COMPONENT red; + gcsFORMAT_COMPONENT green; + gcsFORMAT_COMPONENT blue; +} +gcsFORMAT_CLASS_TYPE_RGBA; + +/* YUV color format class. */ +typedef struct _gcsFORMAT_CLASS_TYPE_YUV +{ + gcsFORMAT_COMPONENT y; + gcsFORMAT_COMPONENT u; + gcsFORMAT_COMPONENT v; +} +gcsFORMAT_CLASS_TYPE_YUV; + +/* Index color format class. */ +typedef struct _gcsFORMAT_CLASS_TYPE_INDEX +{ + gcsFORMAT_COMPONENT value; +} +gcsFORMAT_CLASS_TYPE_INDEX; + +/* Luminance color format class. */ +typedef struct _gcsFORMAT_CLASS_TYPE_LUMINANCE +{ + gcsFORMAT_COMPONENT alpha; + gcsFORMAT_COMPONENT value; +} +gcsFORMAT_CLASS_TYPE_LUMINANCE; + +/* Bump map color format class. */ +typedef struct _gcsFORMAT_CLASS_TYPE_BUMP +{ + gcsFORMAT_COMPONENT alpha; + gcsFORMAT_COMPONENT l; + gcsFORMAT_COMPONENT v; + gcsFORMAT_COMPONENT u; + gcsFORMAT_COMPONENT q; + gcsFORMAT_COMPONENT w; +} +gcsFORMAT_CLASS_TYPE_BUMP; + +/* Depth and stencil format class. */ +typedef struct _gcsFORMAT_CLASS_TYPE_DEPTH +{ + gcsFORMAT_COMPONENT depth; + gcsFORMAT_COMPONENT stencil; +} +gcsFORMAT_CLASS_TYPE_DEPTH; + +typedef union _gcuPIXEL_FORMAT_CLASS +{ + gcsFORMAT_CLASS_TYPE_BUMP bump; + gcsFORMAT_CLASS_TYPE_RGBA rgba; + gcsFORMAT_CLASS_TYPE_YUV yuv; + gcsFORMAT_CLASS_TYPE_LUMINANCE lum; + gcsFORMAT_CLASS_TYPE_INDEX index; + gcsFORMAT_CLASS_TYPE_DEPTH depth; +} +gcuPIXEL_FORMAT_CLASS; + +/* Format parameters. */ +typedef struct _gcsSURF_FORMAT_INFO +{ + /* Name of the format */ + gctCONST_STRING formatName; + + /* Format code and class. */ + gceSURF_FORMAT format; + gceFORMAT_CLASS fmtClass; + + /* Format data type */ + gceFORMAT_DATATYPE fmtDataType; + + /* The size of one pixel in bits. */ + gctUINT8 bitsPerPixel; + + /* Pixel block dimensions. */ + gctUINT blockWidth; + gctUINT blockHeight; + + /* Pixel block size in bits. */ + gctUINT blockSize; + + /* Some formats are larger than what the GPU can support. */ + /* These formats are read in the number of layers specified. */ + gctUINT8 layers; + + /* The format is faked and software will interpret it differently + ** with HW. Most of them can't be blendable(PE) or filterable(TX). + */ + gctBOOL fakedFormat; + + /* Some formats have two neighbour pixels interleaved together. */ + /* To describe such format, set the flag to 1 and add another */ + /* like this one describing the odd pixel format. */ + gctBOOL interleaved; + + /* sRGB format. */ + gctBOOL sRGB; + + /* Format components. */ + gcuPIXEL_FORMAT_CLASS u; + + /* Format components. */ + gcuPIXEL_FORMAT_CLASS uOdd; + + /* Render format. */ + gceSURF_FORMAT closestRenderFormat; + /*gctCLOSEST_FORMAT dynamicClosestRenderFormat;*/ + gctUINT renderFormat; + const gceTEXTURE_SWIZZLE * pixelSwizzle; + + /* Texture format. */ + gceSURF_FORMAT closestTXFormat; + gctUINT txFormat; + const gceTEXTURE_SWIZZLE * txSwizzle; + gctBOOL txIntFilter; +} +gcsSURF_FORMAT_INFO; + +/* Frame buffer information. */ +typedef struct _gcsSURF_FRAMEBUFFER +{ + gctPOINTER logical; + gctUINT width, height; + gctINT stride; + gceSURF_FORMAT format; +} +gcsSURF_FRAMEBUFFER; + +/* Generic pixel component descriptors. */ +extern gcsFORMAT_COMPONENT gcvPIXEL_COMP_XXX8; +extern gcsFORMAT_COMPONENT gcvPIXEL_COMP_XX8X; +extern gcsFORMAT_COMPONENT gcvPIXEL_COMP_X8XX; +extern gcsFORMAT_COMPONENT gcvPIXEL_COMP_8XXX; + +typedef enum _gceORIENTATION +{ + gcvORIENTATION_TOP_BOTTOM, + gcvORIENTATION_BOTTOM_TOP, +} +gceORIENTATION; + + +/* Construct a new gcoSURF object. */ +gceSTATUS +gcoSURF_Construct( + IN gcoHAL Hal, + IN gctUINT Width, + IN gctUINT Height, + IN gctUINT Depth, + IN gceSURF_TYPE Type, + IN gceSURF_FORMAT Format, + IN gcePOOL Pool, + OUT gcoSURF * Surface + ); + +/* Destroy an gcoSURF object. */ +gceSTATUS +gcoSURF_Destroy( + IN gcoSURF Surface + ); + +/* Map user-allocated surface. */ +gceSTATUS +gcoSURF_MapUserSurface( + IN gcoSURF Surface, + IN gctUINT Alignment, + IN gctPOINTER Logical, + IN gctUINT32 Physical + ); + +/* Wrapp surface with known logical/GPU address */ +gceSTATUS +gcoSURF_WrapSurface( + IN gcoSURF Surface, + IN gctUINT Alignment, + IN gctPOINTER Logical, + IN gctUINT32 Physical + ); + + +/* Query vid mem node info. */ +gceSTATUS +gcoSURF_QueryVidMemNode( + IN gcoSURF Surface, + OUT gctUINT32 * Node, + OUT gcePOOL * Pool, + OUT gctSIZE_T_PTR Bytes + ); + +/* Set the color type of the surface. */ +gceSTATUS +gcoSURF_SetColorType( + IN gcoSURF Surface, + IN gceSURF_COLOR_TYPE ColorType + ); + +/* Get the color type of the surface. */ +gceSTATUS +gcoSURF_GetColorType( + IN gcoSURF Surface, + OUT gceSURF_COLOR_TYPE *ColorType + ); + +/* Set the color space of the surface. */ +gceSTATUS +gcoSURF_SetColorSpace( + IN gcoSURF Surface, + IN gceSURF_COLOR_SPACE ColorSpace + ); + +/* Get the color space of the surface. */ +gceSTATUS +gcoSURF_GetColorSpace( + IN gcoSURF Surface, + OUT gceSURF_COLOR_SPACE *ColorSpace + ); + + +/* Set the surface ration angle. */ +gceSTATUS +gcoSURF_SetRotation( + IN gcoSURF Surface, + IN gceSURF_ROTATION Rotation + ); + +gceSTATUS +gcoSURF_IsValid( + IN gcoSURF Surface + ); + +#if gcdENABLE_3D +/* Verify and return the state of the tile status mechanism. */ +gceSTATUS +gcoSURF_IsTileStatusSupported( + IN gcoSURF Surface + ); + +/* Verify if surface has tile status enabled. */ +gceSTATUS +gcoSURF_IsTileStatusEnabled( + IN gcoSURF Surface + ); + +/* Verify if surface is compressed. */ +gceSTATUS +gcoSURF_IsCompressed( + IN gcoSURF Surface + ); + +/* Enable tile status for the specified surface on zero slot. */ +gceSTATUS +gcoSURF_EnableTileStatus( + IN gcoSURF Surface + ); + +/* Enable tile status for the specified surface on specified slot. */ +gceSTATUS +gcoSURF_EnableTileStatusEx( + IN gcoSURF Surface, + IN gctUINT RtIndex + ); + +/* Disable tile status for the specified surface. */ +gceSTATUS +gcoSURF_DisableTileStatus( + IN gcoSURF Surface, + IN gctBOOL Decompress + ); + +/* Flush tile status cache for the specified surface. */ +gceSTATUS +gcoSURF_FlushTileStatus( + IN gcoSURF Surface, + IN gctBOOL Decompress + ); +#endif /* gcdENABLE_3D */ + +/* Get surface size. */ +gceSTATUS +gcoSURF_GetSize( + IN gcoSURF Surface, + OUT gctUINT * Width, + OUT gctUINT * Height, + OUT gctUINT * Depth + ); + +/* Get surface aligned sizes. */ +gceSTATUS +gcoSURF_GetAlignedSize( + IN gcoSURF Surface, + OUT gctUINT * Width, + OUT gctUINT * Height, + OUT gctINT * Stride + ); + +/* Get alignments. */ +gceSTATUS +gcoSURF_GetAlignment( + IN gceSURF_TYPE Type, + IN gceSURF_FORMAT Format, + OUT gctUINT * AddressAlignment, + OUT gctUINT * XAlignment, + OUT gctUINT * YAlignment + ); + +gceSTATUS +gcoSURF_AlignResolveRect( + IN gcoSURF Surf, + IN gcsPOINT_PTR RectOrigin, + IN gcsPOINT_PTR RectSize, + OUT gcsPOINT_PTR AlignedOrigin, + OUT gcsPOINT_PTR AlignedSize + ); + +/* Get surface type and format. */ +gceSTATUS +gcoSURF_GetFormat( + IN gcoSURF Surface, + OUT OPTIONAL gceSURF_TYPE * Type, + OUT OPTIONAL gceSURF_FORMAT * Format + ); + +/* Get surface information */ +gceSTATUS +gcoSURF_GetFormatInfo( + IN gcoSURF Surface, + OUT gcsSURF_FORMAT_INFO_PTR * formatInfo + ); + +/* Get Surface pack format */ +gceSTATUS +gcoSURF_GetPackedFormat( + IN gcoSURF Surface, + OUT gceSURF_FORMAT * Format + ); + +/* Get surface tiling. */ +gceSTATUS +gcoSURF_GetTiling( + IN gcoSURF Surface, + OUT gceTILING * Tiling + ); + +/* Get flip bitmap offset bytes. */ +gceSTATUS +gcoSURF_GetFlipBitmapOffset( + IN gcoSURF Surface, + OUT gctUINT_PTR FlipBitmapOffset + ); + +/* Get bottom buffer offset bytes. */ +gceSTATUS +gcoSURF_GetBottomBufferOffset( + IN gcoSURF Surface, + OUT gctUINT_PTR BottomBufferOffset + ); + +/* Lock the surface. */ +gceSTATUS +gcoSURF_Lock( + IN gcoSURF Surface, + IN OUT gctUINT32 * Address, + IN OUT gctPOINTER * Memory + ); + +/* Unlock the surface. */ +gceSTATUS +gcoSURF_Unlock( + IN gcoSURF Surface, + IN gctPOINTER Memory + ); + +/*. Query surface flags.*/ +gceSTATUS +gcoSURF_QueryFlags( + IN gcoSURF Surface, + IN gceSURF_FLAG Flag + ); + +/* Return pixel format parameters; Info is required to be a pointer to an + * array of at least two items because some formats have up to two records + * of description. */ +gceSTATUS +gcoSURF_QueryFormat( + IN gceSURF_FORMAT Format, + OUT gcsSURF_FORMAT_INFO_PTR * Info + ); + +/* Compute the color pixel mask. */ +gceSTATUS +gcoSURF_ComputeColorMask( + IN gcsSURF_FORMAT_INFO_PTR Format, + OUT gctUINT32_PTR ColorMask + ); + +/* Flush the surface. */ +gceSTATUS +gcoSURF_Flush( + IN gcoSURF Surface + ); + +/* Fill surface from it's tile status buffer. */ +gceSTATUS +gcoSURF_FillFromTile( + IN gcoSURF Surface + ); + +/* Fill surface with a value. */ +gceSTATUS +gcoSURF_Fill( + IN gcoSURF Surface, + IN gcsPOINT_PTR Origin, + IN gcsSIZE_PTR Size, + IN gctUINT32 Value, + IN gctUINT32 Mask + ); + +/* Alpha blend two surfaces together. */ +gceSTATUS +gcoSURF_Blend( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gcsPOINT_PTR SrcOrig, + IN gcsPOINT_PTR DestOrigin, + IN gcsSIZE_PTR Size, + IN gceSURF_BLEND_MODE Mode + ); + +/* Create a new gcoSURF wrapper object. */ +gceSTATUS +gcoSURF_ConstructWrapper( + IN gcoHAL Hal, + OUT gcoSURF * Surface + ); + +/* Set surface flags.*/ +gceSTATUS +gcoSURF_SetFlags( + IN gcoSURF Surface, + IN gceSURF_FLAG Flag, + IN gctBOOL Value + ); + +/* Set the underlying buffer for the surface wrapper. */ +gceSTATUS +gcoSURF_SetBuffer( + IN gcoSURF Surface, + IN gceSURF_TYPE Type, + IN gceSURF_FORMAT Format, + IN gctUINT Stride, + IN gctPOINTER Logical, + IN gctUINT32 Physical + ); + +/* Set the underlying video buffer for the surface wrapper. */ +gceSTATUS +gcoSURF_SetVideoBuffer( + IN gcoSURF Surface, + IN gceSURF_TYPE Type, + IN gceSURF_FORMAT Format, + IN gctUINT Width, + IN gctUINT Height, + IN gctUINT Stride, + IN gctPOINTER *LogicalPlane1, + IN gctUINT32 *PhysicalPlane1 + ); + +/* Set the size of the surface in pixels and map the underlying buffer. */ +gceSTATUS +gcoSURF_SetWindow( + IN gcoSURF Surface, + IN gctUINT X, + IN gctUINT Y, + IN gctUINT Width, + IN gctUINT Height + ); + +/* Set width/height alignment of the surface directly and calculate stride/size. This is only for dri backend now. Please be careful before use. */ +gceSTATUS +gcoSURF_SetAlignment( + IN gcoSURF Surface, + IN gctUINT Width, + IN gctUINT Height + ); + +/* Increase reference count of the surface. */ +gceSTATUS +gcoSURF_ReferenceSurface( + IN gcoSURF Surface + ); + +/* Get surface reference count. */ +gceSTATUS +gcoSURF_QueryReferenceCount( + IN gcoSURF Surface, + OUT gctINT32 * ReferenceCount + ); + +/* Set surface orientation. */ +gceSTATUS +gcoSURF_SetOrientation( + IN gcoSURF Surface, + IN gceORIENTATION Orientation + ); + +/* Query surface orientation. */ +gceSTATUS +gcoSURF_QueryOrientation( + IN gcoSURF Surface, + OUT gceORIENTATION * Orientation + ); + +gceSTATUS +gcoSURF_SetOffset( + IN gcoSURF Surface, + IN gctSIZE_T Offset + ); + +gceSTATUS +gcoSURF_NODE_Cache( + IN gcsSURF_NODE_PTR Node, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes, + IN gceCACHEOPERATION Operation + ); + +/* Lock and unlock surface node */ +gceSTATUS +gcoSURF_LockNode( + IN gcsSURF_NODE_PTR Node, + OUT gctUINT32 * Address, + OUT gctPOINTER * Memory + ); + +gceSTATUS +gcoSURF_UnLockNode( + IN gcsSURF_NODE_PTR Node, + IN gceSURF_TYPE Type + ); + +/* Perform CPU cache operation on surface node */ +gceSTATUS +gcoSURF_NODE_CPUCacheOperation( + IN gcsSURF_NODE_PTR Node, + IN gceSURF_TYPE Type, + IN gctSIZE_T Offset, + IN gctSIZE_T Length, + IN gceCACHEOPERATION Operation + ); + +/* Perform CPU cache operation on surface */ +gceSTATUS +gcoSURF_CPUCacheOperation( + IN gcoSURF Surface, + IN gceCACHEOPERATION Operation + ); + + +gceSTATUS +gcoSURF_Swap( + IN gcoSURF Surface1, + IN gcoSURF Surface2 + ); + +gceSTATUS +gcoSURF_ResetSurWH( + IN gcoSURF Surface, + IN gctUINT oriw, + IN gctUINT orih, + IN gctUINT alignw, + IN gctUINT alignh, + IN gceSURF_FORMAT fmt +); + +/* Update surface timestamp. */ +gceSTATUS +gcoSURF_UpdateTimeStamp( + IN gcoSURF Surface + ); + +/* Query surface current timestamp. */ +gceSTATUS +gcoSURF_QueryTimeStamp( + IN gcoSURF Surface, + OUT gctUINT64 * TimeStamp + ); + +/* + * Allocate shared buffer for this surface, so that + * surface states can be shared across processes. + */ +gceSTATUS +gcoSURF_AllocShBuffer( + IN gcoSURF Surface, + OUT gctSHBUF * ShBuf + ); + +/* Bind shared buffer to this surface */ +gceSTATUS +gcoSURF_BindShBuffer( + IN gcoSURF Surface, + IN gctSHBUF ShBuf + ); + +/* Push surface shared states to shared buffer. */ +gceSTATUS +gcoSURF_PushSharedInfo( + IN gcoSURF Surface + ); + +/* Pop shared states from shared buffer. */ +gceSTATUS +gcoSURF_PopSharedInfo( + IN gcoSURF Surface + ); + +#if (gcdENABLE_3D || gcdENABLE_VG) +/* Copy surface. */ +gceSTATUS +gcoSURF_Copy( + IN gcoSURF Surface, + IN gcoSURF Source + ); + +/* Set number of samples for a gcoSURF object. */ +gceSTATUS +gcoSURF_SetSamples( + IN gcoSURF Surface, + IN gctUINT Samples + ); + +/* Get the number of samples per pixel. */ +gceSTATUS +gcoSURF_GetSamples( + IN gcoSURF Surface, + OUT gctUINT_PTR Samples + ); +#endif + +/******************************************************************************\ +********************************* gcoDUMP Object ******************************** +\******************************************************************************/ + +/* Construct a new gcoDUMP object. */ +gceSTATUS +gcoDUMP_Construct( + IN gcoOS Os, + IN gcoHAL Hal, + OUT gcoDUMP * Dump + ); + +/* Destroy a gcoDUMP object. */ +gceSTATUS +gcoDUMP_Destroy( + IN gcoDUMP Dump + ); + +/* Enable/disable dumping. */ +gceSTATUS +gcoDUMP_Control( + IN gcoDUMP Dump, + IN gctSTRING FileName + ); + +gceSTATUS +gcoDUMP_IsEnabled( + IN gcoDUMP Dump, + OUT gctBOOL * Enabled + ); + +/* Add surface. */ +gceSTATUS +gcoDUMP_AddSurface( + IN gcoDUMP Dump, + IN gctINT32 Width, + IN gctINT32 Height, + IN gceSURF_FORMAT PixelFormat, + IN gctUINT32 Address, + IN gctSIZE_T ByteCount + ); + +/* Mark the beginning of a frame. */ +gceSTATUS +gcoDUMP_FrameBegin( + IN gcoDUMP Dump + ); + +/* Mark the end of a frame. */ +gceSTATUS +gcoDUMP_FrameEnd( + IN gcoDUMP Dump + ); + +/* Dump data. */ +gceSTATUS +gcoDUMP_DumpData( + IN gcoDUMP Dump, + IN gceDUMP_TAG Type, + IN gctUINT32 Address, + IN gctSIZE_T ByteCount, + IN gctCONST_POINTER Data + ); + +/* Delete an address. */ +gceSTATUS +gcoDUMP_Delete( + IN gcoDUMP Dump, + IN gctUINT32 Address + ); + +/* Enable dump or not. */ +gceSTATUS +gcoDUMP_SetDumpFlag( + IN gctBOOL DumpState + ); + +/******************************************************************************\ +******************************* gcsRECT Structure ****************************** +\******************************************************************************/ + +/* Initialize rectangle structure. */ +gceSTATUS +gcsRECT_Set( + OUT gcsRECT_PTR Rect, + IN gctINT32 Left, + IN gctINT32 Top, + IN gctINT32 Right, + IN gctINT32 Bottom + ); + +/* Return the width of the rectangle. */ +gceSTATUS +gcsRECT_Width( + IN gcsRECT_PTR Rect, + OUT gctINT32 * Width + ); + +/* Return the height of the rectangle. */ +gceSTATUS +gcsRECT_Height( + IN gcsRECT_PTR Rect, + OUT gctINT32 * Height + ); + +/* Ensure that top left corner is to the left and above the right bottom. */ +gceSTATUS +gcsRECT_Normalize( + IN OUT gcsRECT_PTR Rect + ); + +/* Compare two rectangles. */ +gceSTATUS +gcsRECT_IsEqual( + IN gcsRECT_PTR Rect1, + IN gcsRECT_PTR Rect2, + OUT gctBOOL * Equal + ); + +/* Compare the sizes of two rectangles. */ +gceSTATUS +gcsRECT_IsOfEqualSize( + IN gcsRECT_PTR Rect1, + IN gcsRECT_PTR Rect2, + OUT gctBOOL * EqualSize + ); + +gceSTATUS +gcsRECT_RelativeRotation( + IN gceSURF_ROTATION Orientation, + IN OUT gceSURF_ROTATION *Relation); + +gceSTATUS + +gcsRECT_Rotate( + + IN OUT gcsRECT_PTR Rect, + + IN gceSURF_ROTATION Rotation, + + IN gceSURF_ROTATION toRotation, + + IN gctINT32 SurfaceWidth, + + IN gctINT32 SurfaceHeight + + ); + +/******************************************************************************\ +**************************** gcsBOUNDARY Structure ***************************** +\******************************************************************************/ + +typedef struct _gcsBOUNDARY +{ + gctINT x; + gctINT y; + gctINT width; + gctINT height; +} +gcsBOUNDARY; + +/******************************************************************************\ +********************************* gcoHEAP Object ******************************** +\******************************************************************************/ + +typedef struct _gcoHEAP * gcoHEAP; + +/* Construct a new gcoHEAP object. */ +gceSTATUS +gcoHEAP_Construct( + IN gcoOS Os, + IN gctSIZE_T AllocationSize, + OUT gcoHEAP * Heap + ); + +/* Destroy an gcoHEAP object. */ +gceSTATUS +gcoHEAP_Destroy( + IN gcoHEAP Heap + ); + +/* Allocate memory. */ +gceSTATUS +gcoHEAP_Allocate( + IN gcoHEAP Heap, + IN gctSIZE_T Bytes, + OUT gctPOINTER * Node + ); + +gceSTATUS +gcoHEAP_GetMemorySize( + IN gcoHEAP Heap, + IN gctPOINTER Memory, + OUT gctSIZE_T_PTR MemorySize + ); + +/* Free memory. */ +gceSTATUS +gcoHEAP_Free( + IN gcoHEAP Heap, + IN gctPOINTER Node + ); + +#if (VIVANTE_PROFILER || gcdDEBUG) +/* Profile the heap. */ +gceSTATUS +gcoHEAP_ProfileStart( + IN gcoHEAP Heap + ); + +gceSTATUS +gcoHEAP_ProfileEnd( + IN gcoHEAP Heap, + IN gctCONST_STRING Title + ); +#endif + + +/******************************************************************************\ +******************************* Debugging Macros ******************************* +\******************************************************************************/ + +void +gcoOS_SetDebugLevel( + IN gctUINT32 Level + ); + +void +gcoOS_GetDebugLevel( + OUT gctUINT32_PTR DebugLevel + ); + +void +gcoOS_SetDebugZone( + IN gctUINT32 Zone + ); + +void +gcoOS_GetDebugZone( + IN gctUINT32 Zone, + OUT gctUINT32_PTR DebugZone + ); + +void +gcoOS_SetDebugLevelZone( + IN gctUINT32 Level, + IN gctUINT32 Zone + ); + +void +gcoOS_SetDebugZones( + IN gctUINT32 Zones, + IN gctBOOL Enable + ); + +void +gcoOS_SetDebugFile( + IN gctCONST_STRING FileName + ); + +gctFILE +gcoOS_ReplaceDebugFile( + IN gctFILE fp + ); + +void +gcoOS_SysTraceBegin( + IN gctCONST_STRING FuncName + ); + +void +gcoOS_SysTraceEnd( + IN void); + +/******************************************************************************* +** +** gcmFATAL +** +** Print a message to the debugger and execute a break point. +** +** ARGUMENTS: +** +** message Message. +** ... Optional arguments. +*/ + +void +gckOS_DebugFatal( + IN gctCONST_STRING Message, + ... + ); + +void +gcoOS_DebugFatal( + IN gctCONST_STRING Message, + ... + ); + +#if gcmIS_DEBUG(gcdDEBUG_FATAL) +# define gcmFATAL gcoOS_DebugFatal +# define gcmkFATAL gckOS_DebugFatal +#elif gcdHAS_ELLIPSIS +# define gcmFATAL(...) +# define gcmkFATAL(...) +#else + gcmINLINE static void + __dummy_fatal( + IN gctCONST_STRING Message, + ... + ) + { + } +# define gcmFATAL __dummy_fatal +# define gcmkFATAL __dummy_fatal +#endif + +#define gcmENUM2TEXT(e) case e: return #e + +/******************************************************************************* +** +** gcmTRACE +** +** Print a message to the debugfer if the correct level has been set. In +** retail mode this macro does nothing. +** +** ARGUMENTS: +** +** level Level of message. +** message Message. +** ... Optional arguments. +*/ +#define gcvLEVEL_NONE -1 +#define gcvLEVEL_ERROR 0 +#define gcvLEVEL_WARNING 1 +#define gcvLEVEL_INFO 2 +#define gcvLEVEL_VERBOSE 3 + +void +gckOS_DebugTrace( + IN gctUINT32 Level, + IN gctCONST_STRING Message, + ... + ); + +void +gckOS_DebugTraceN( + IN gctUINT32 Level, + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ); + +void +gcoOS_DebugTrace( + IN gctUINT32 Level, + IN gctCONST_STRING Message, + ... + ); + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +# define gcmTRACE gcoOS_DebugTrace +# define gcmkTRACE gckOS_DebugTrace +# define gcmkTRACE_N gckOS_DebugTraceN +#elif gcdHAS_ELLIPSIS +# define gcmTRACE(...) +# define gcmkTRACE(...) +# define gcmkTRACE_N(...) +#else + gcmINLINE static void + __dummy_trace( + IN gctUINT32 Level, + IN gctCONST_STRING Message, + ... + ) + { + } + + gcmINLINE static void + __dummy_trace_n( + IN gctUINT32 Level, + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ) + { + } + +# define gcmTRACE __dummy_trace +# define gcmkTRACE __dummy_trace +# define gcmkTRACE_N __dummy_trace_n +#endif + +/* Zones common for kernel and user. */ +#define gcvZONE_OS (1 << 0) +#define gcvZONE_HARDWARE (1 << 1) +#define gcvZONE_HEAP (1 << 2) +#define gcvZONE_SIGNAL (1 << 27) + +/* Kernel zones. */ +#define gcvZONE_KERNEL (1 << 3) +#define gcvZONE_VIDMEM (1 << 4) +#define gcvZONE_COMMAND (1 << 5) +#define gcvZONE_DRIVER (1 << 6) +#define gcvZONE_CMODEL (1 << 7) +#define gcvZONE_MMU (1 << 8) +#define gcvZONE_EVENT (1 << 9) +#define gcvZONE_DEVICE (1 << 10) +#define gcvZONE_DATABASE (1 << 11) +#define gcvZONE_INTERRUPT (1 << 12) +#define gcvZONE_POWER (1 << 13) + +/* User zones. */ +#define gcvZONE_HAL (1 << 3) +#define gcvZONE_BUFFER (1 << 4) +#define gcvZONE_CONTEXT (1 << 5) +#define gcvZONE_SURFACE (1 << 6) +#define gcvZONE_INDEX (1 << 7) +#define gcvZONE_STREAM (1 << 8) +#define gcvZONE_TEXTURE (1 << 9) +#define gcvZONE_2D (1 << 10) +#define gcvZONE_3D (1 << 11) +#define gcvZONE_COMPILER (1 << 12) +#define gcvZONE_MEMORY (1 << 13) +#define gcvZONE_STATE (1 << 14) +#define gcvZONE_AUX (1 << 15) +#define gcvZONE_VERTEX (1 << 16) +#define gcvZONE_CL (1 << 17) +#define gcvZONE_COMPOSITION (1 << 17) +#define gcvZONE_VG (1 << 18) +#define gcvZONE_IMAGE (1 << 19) +#define gcvZONE_UTILITY (1 << 20) +#define gcvZONE_PARAMETERS (1 << 21) +#define gcvZONE_BUFOBJ (1 << 22) +#define gcvZONE_SHADER (1 << 23) +#define gcvZONE_STREAM_OUT (1 << 24) + +/* API definitions. */ +#define gcvZONE_API_HAL (1 << 28) +#define gcvZONE_API_EGL (2 << 28) +#define gcvZONE_API_ES11 (3 << 28) +#define gcvZONE_API_ES20 (4 << 28) +#define gcvZONE_API_VG11 (5 << 28) +#define gcvZONE_API_GL (6 << 28) +#define gcvZONE_API_DFB (7 << 28) +#define gcvZONE_API_GDI ((gctUINT32)8 << 28) +#define gcvZONE_API_D3D ((gctUINT32)9 << 28) +#define gcvZONE_API_ES30 ((gctUINT32)10 << 28) + + +#define gcmZONE_GET_API(zone) ((zone) >> 28) +/*Set gcdZONE_MASE like 0x0 | gcvZONE_API_EGL +will enable print EGL module debug info*/ +#define gcdZONE_MASK 0x0FFFFFFF + +/* Handy zones. */ +#define gcvZONE_NONE 0 +#define gcvZONE_ALL 0x0FFFFFFF + +/*Dump API depth set 1 for API, 2 for API and API behavior*/ +#define gcvDUMP_API_DEPTH 1 + +/******************************************************************************* +** +** gcmTRACE_ZONE +** +** Print a message to the debugger if the correct level and zone has been +** set. In retail mode this macro does nothing. +** +** ARGUMENTS: +** +** Level Level of message. +** Zone Zone of message. +** Message Message. +** ... Optional arguments. +*/ + +void +gckOS_DebugTraceZone( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctCONST_STRING Message, + ... + ); + +void +gckOS_DebugTraceZoneN( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ); + +void +gcoOS_DebugTraceZone( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctCONST_STRING Message, + ... + ); + +#if gcmIS_DEBUG(gcdDEBUG_TRACE) +# define gcmTRACE_ZONE gcoOS_DebugTraceZone +# define gcmkTRACE_ZONE gckOS_DebugTraceZone +# define gcmkTRACE_ZONE_N gckOS_DebugTraceZoneN +#elif gcdHAS_ELLIPSIS +# define gcmTRACE_ZONE(...) +# define gcmkTRACE_ZONE(...) +# define gcmkTRACE_ZONE_N(...) +#else + gcmINLINE static void + __dummy_trace_zone( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctCONST_STRING Message, + ... + ) + { + } + + gcmINLINE static void + __dummy_trace_zone_n( + IN gctUINT32 Level, + IN gctUINT32 Zone, + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ) + { + } + +# define gcmTRACE_ZONE __dummy_trace_zone +# define gcmkTRACE_ZONE __dummy_trace_zone +# define gcmkTRACE_ZONE_N __dummy_trace_zone_n +#endif + +/******************************************************************************* +** +** gcmDEBUG_ONLY +** +** Execute a statement or function only in DEBUG mode. +** +** ARGUMENTS: +** +** f Statement or function to execute. +*/ +#if gcmIS_DEBUG(gcdDEBUG_CODE) +# define gcmDEBUG_ONLY(f) f +#else +# define gcmDEBUG_ONLY(f) +#endif + +/******************************************************************************* +** +** gcmSTACK_PUSH +** gcmSTACK_POP +** gcmSTACK_DUMP +** +** Push or pop a function with entry arguments on the trace stack. +** +** ARGUMENTS: +** +** Function Name of function. +** Line Line number. +** Text Optional text. +** ... Optional arguments for text. +*/ +#if gcmIS_DEBUG(gcdDEBUG_STACK) + void gcoOS_StackPush(IN gctINT8_PTR Identity, IN gctCONST_STRING Function, IN gctINT Line, IN gctCONST_STRING Text, ...); + void gcoOS_StackPop(IN gctINT8_PTR Identity, IN gctCONST_STRING Function); + void gcoOS_StackDump(void); + void gcoOS_StackRemove(IN gctHANDLE Thread); + +# define gcmSTACK_PUSH gcoOS_StackPush +# define gcmSTACK_POP gcoOS_StackPop +# define gcmSTACK_DUMP gcoOS_StackDump +# define gcmSTACK_REMOVE gcoOS_StackRemove +#elif gcdHAS_ELLIPSIS +# define gcmSTACK_PUSH(...) do { } while (0) +# define gcmSTACK_POP(...) do { } while (0) +# define gcmSTACK_DUMP() do { } while (0) +# define gcmSTACK_REMOVE(...) do { } while (0) +#else + gcmINLINE static void + __dummy_stack_push( + IN gctCONST_STRING Function, + IN gctINT Line, + IN gctCONST_STRING Text, ... + ) + { + } +# define gcmSTACK_PUSH __dummy_stack_push +# define gcmSTACK_POP(a,b) do { } while (0) +# define gcmSTACK_DUMP() do { } while (0) +# define gcmSTACK_REMOVE(a) do { } while (0) +#endif + +/******************************************************************************\ +******************************** Binary Trace ********************************** +\******************************************************************************/ +typedef struct _gcsBINARY_TRACE_MESSAGE * gcsBINARY_TRACE_MESSAGE_PTR; +typedef struct _gcsBINARY_TRACE_MESSAGE +{ + gctUINT32 signature; + gctUINT32 pid; + gctUINT32 tid; + gctUINT32 line; + gctUINT32 numArguments; + gctUINT8 payload; +} +gcsBINARY_TRACE_MESSAGE; + +#define gcdBINARY_TRACE_MESSAGE_SIZE 240 + +#if gcdBINARY_TRACE + void + gcoOS_BinaryTrace( + IN gctCONST_STRING Function, + IN gctINT Line, + IN gctCONST_STRING Text OPTIONAL, + ... + ); + + void + gckOS_BinaryTrace( + IN gctCONST_STRING Function, + IN gctINT Line, + IN gctCONST_STRING Text OPTIONAL, + ... + ); + +# define gcmBINARY_TRACE gcoOS_BinaryTrace +# define gcmkBINARY_TRACE gckOS_BinaryTrace +#elif gcdHAS_ELLIPSIS +# define gcmBINARY_TRACE(Function, Line, Text, ...) +# define gcmkBINARY_TRACE(Function, Line, Text, ...) +#else + gcmINLINE static void + __dummy_binary_trace( + IN gctCONST_STRING Function, + IN gctINT Line, + IN gctCONST_STRING Text, + ) + { + } + +# define gcmBINARY_TRACE __dummy_binary_trace +# define gcmkBINARY_TRACE __dummy_binary_trace +#endif + +/******************************************************************************\ +******************************** Logging Macros ******************************** +\******************************************************************************/ + +#define gcdHEADER_LEVEL gcvLEVEL_VERBOSE + +#ifndef gcdEMPTY_HEADER_FOOTER +#define gcdEMPTY_HEADER_FOOTER 0 +#endif + +#if gcdENABLE_PROFILING +void +gcoOS_ProfileDB( + IN gctCONST_STRING Function, + IN OUT gctBOOL_PTR Initialized + ); + +#define gcmHEADER() \ + gctINT8 __user__ = 1; \ + static gctBOOL __profile__initialized__ = gcvFALSE; \ + gcmSTACK_PUSH(&__user__, __FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcoOS_ProfileDB(__FUNCTION__, &__profile__initialized__) +#define gcmHEADER_ARG(...) \ + gctINT8 __user__ = 1; \ + static gctBOOL __profile__initialized__ = gcvFALSE; \ + gcmSTACK_PUSH(&__user__, __FUNCTION__, __LINE__, Text, __VA_ARGS__); \ + gcoOS_ProfileDB(__FUNCTION__, &__profile__initialized__) +#define gcmFOOTER() \ + gcmSTACK_POP(&__user__, __FUNCTION__); \ + gcoOS_ProfileDB(__FUNCTION__, gcvNULL) +#define gcmFOOTER_NO() \ + gcmSTACK_POP(&__user__, __FUNCTION__); \ + gcoOS_ProfileDB(__FUNCTION__, gcvNULL) +#define gcmFOOTER_ARG(...) \ + gcmSTACK_POP(&__user__, __FUNCTION__); \ + gcoOS_ProfileDB(__FUNCTION__, gcvNULL) +#define gcmFOOTER_KILL() \ + gcmSTACK_POP(&__user__, __FUNCTION__); \ + gcoOS_ProfileDB(gcvNULL, gcvNULL) + +#else /* gcdENABLE_PROFILING */ + +#ifdef gcdFSL_REL_BUILD +#define gcmHEADER() +#elif gcdEMPTY_HEADER_FOOTER +# define gcmHEADER() +#elif gcdHAS_ELLIPSIS +#define gcmHEADER() \ + gctINT8 __user__ = 1; \ + gctINT8_PTR __user_ptr__ = &__user__; \ + gcmSTACK_PUSH(__user_ptr__, __FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "++%s(%d)", __FUNCTION__, __LINE__) +#else + gcmINLINE static void + __dummy_header(void) + { + } +# define gcmHEADER __dummy_header +#endif + +#ifdef gcdFSL_REL_BUILD +#define gcmHEADER_ARG(Text, ...) +#elif gcdHAS_ELLIPSIS +#if gcdEMPTY_HEADER_FOOTER +# define gcmHEADER_ARG(Text, ...) +#else +# define gcmHEADER_ARG(Text, ...) \ + gctINT8 __user__ = 1; \ + gctINT8_PTR __user_ptr__ = &__user__; \ + gcmSTACK_PUSH(__user_ptr__, __FUNCTION__, __LINE__, Text, __VA_ARGS__); \ + gcmBINARY_TRACE(__FUNCTION__, __LINE__, Text, __VA_ARGS__); \ + gcmTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "++%s(%d): " Text, __FUNCTION__, __LINE__, __VA_ARGS__) +#endif +#else + gcmINLINE static void + __dummy_header_arg( + IN gctCONST_STRING Text, + ... + ) + { + } +# define gcmHEADER_ARG __dummy_header_arg +#endif + +#ifdef gcdFSL_REL_BUILD +# define gcmFOOTER() +#elif gcdEMPTY_HEADER_FOOTER +# define gcmFOOTER() +#elif gcdHAS_ELLIPSIS +# define gcmFOOTER() \ + gcmSTACK_POP(__user_ptr__, __FUNCTION__); \ + gcmBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d): status=%d(%s)", \ + __FUNCTION__, __LINE__, \ + status, gcoOS_DebugStatus2Name(status)); \ + *__user_ptr__ -= 1 +#else + gcmINLINE static void + __dummy_footer(void) + { + } +# define gcmFOOTER __dummy_footer +#endif + +#ifdef gcdFSL_REL_BUILD +#define gcmFOOTER_NO() +#elif gcdEMPTY_HEADER_FOOTER +# define gcmFOOTER_NO() +#elif gcdHAS_ELLIPSIS +#define gcmFOOTER_NO() \ + gcmSTACK_POP(__user_ptr__, __FUNCTION__); \ + gcmBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d)", __FUNCTION__, __LINE__); \ + *__user_ptr__ -= 1 +#else + gcmINLINE static void + __dummy_footer_no(void) + { + } +# define gcmFOOTER_NO __dummy_footer_no +#endif + +#ifdef gcdFSL_REL_BUILD +#define gcmFOOTER_KILL() +#elif gcdEMPTY_HEADER_FOOTER +# define gcmFOOTER_KILL() +#elif gcdHAS_ELLIPSIS +#define gcmFOOTER_KILL() \ + gcmSTACK_POP(__user_ptr__, __FUNCTION__); \ + gcmBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d)", __FUNCTION__, __LINE__); \ + *__user_ptr__ -= 1 +#else + gcmINLINE static void + __dummy_footer_kill(void) + { + } +# define gcmFOOTER_KILL __dummy_footer_kill +#endif + +#ifdef gcdFSL_REL_BUILD +# define gcmFOOTER_ARG(Text, ...) +#elif gcdHAS_ELLIPSIS +#if gcdEMPTY_HEADER_FOOTER +# define gcmFOOTER_ARG(Text, ...) +#else +# define gcmFOOTER_ARG(Text, ...) \ + gcmSTACK_POP(__user_ptr__, __FUNCTION__); \ + gcmBINARY_TRACE(__FUNCTION__, __LINE__, Text, __VA_ARGS__); \ + gcmTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d): " Text, __FUNCTION__, __LINE__, __VA_ARGS__); \ + *__user_ptr__ -= 1 +#endif +#else + gcmINLINE static void + __dummy_footer_arg( + IN gctCONST_STRING Text, + ... + ) + { + } +# define gcmFOOTER_ARG __dummy_footer_arg +#endif + +#endif /* gcdENABLE_PROFILING */ + +#ifdef gcdFSL_REL_BUILD +#define gcmkHEADER() +#elif gcdHAS_ELLIPSIS +#define gcmkHEADER() \ + gctINT8 __kernel__ = 1; \ + gctINT8_PTR __kernel_ptr__ = &__kernel__; \ + gcmkBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "++%s(%d)", __FUNCTION__, __LINE__) +#else + gcmINLINE static void + __dummy_kheader(void) + { + } +# define gcmkHEADER __dummy_kheader +#endif + +#ifdef gcdFSL_REL_BUILD +# define gcmkHEADER_ARG(Text, ...) +#elif gcdHAS_ELLIPSIS +# define gcmkHEADER_ARG(Text, ...) \ + gctINT8 __kernel__ = 1; \ + gctINT8_PTR __kernel_ptr__ = &__kernel__; \ + gcmkBINARY_TRACE(__FUNCTION__, __LINE__, Text, __VA_ARGS__); \ + gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "++%s(%d): " Text, __FUNCTION__, __LINE__, __VA_ARGS__) +#else + gcmINLINE static void + __dummy_kheader_arg( + IN gctCONST_STRING Text, + ... + ) + { + } +# define gcmkHEADER_ARG __dummy_kheader_arg +#endif + +#ifdef gcdFSL_REL_BUILD +#define gcmkFOOTER() +#elif gcdHAS_ELLIPSIS +#define gcmkFOOTER() \ + gcmkBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, status); \ + gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d): status=%d(%s)", \ + __FUNCTION__, __LINE__, status, gckOS_DebugStatus2Name(status)); \ + *__kernel_ptr__ -= 1 +#else + gcmINLINE static void + __dummy_kfooter(void) + { + } +# define gcmkFOOTER __dummy_kfooter +#endif + +#ifdef gcdFSL_REL_BUILD +#define gcmkFOOTER_NO() +#elif gcdHAS_ELLIPSIS +#define gcmkFOOTER_NO() \ + gcmkBINARY_TRACE(__FUNCTION__, __LINE__, gcvNULL, gcvNULL); \ + gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d)", __FUNCTION__, __LINE__); \ + *__kernel_ptr__ -= 1 +#else + gcmINLINE static void + __dummy_kfooter_no(void) + { + } +# define gcmkFOOTER_NO __dummy_kfooter_no +#endif + +#ifdef gcdFSL_REL_BUILD +# define gcmkFOOTER_ARG(Text, ...) +#elif gcdHAS_ELLIPSIS +# define gcmkFOOTER_ARG(Text, ...) \ + gcmkBINARY_TRACE(__FUNCTION__, __LINE__, Text, __VA_ARGS__); \ + gcmkTRACE_ZONE(gcdHEADER_LEVEL, _GC_OBJ_ZONE, \ + "--%s(%d): " Text, \ + __FUNCTION__, __LINE__, __VA_ARGS__); \ + *__kernel_ptr__ -= 1 +#else + gcmINLINE static void + __dummy_kfooter_arg( + IN gctCONST_STRING Text, + ... + ) + { + } +# define gcmkFOOTER_ARG __dummy_kfooter_arg +#endif + +#define gcmOPT_VALUE(ptr) (((ptr) == gcvNULL) ? 0 : *(ptr)) +#define gcmOPT_VALUE_INDEX(ptr, index) (((ptr) == gcvNULL) ? 0 : ptr[index]) +#define gcmOPT_POINTER(ptr) (((ptr) == gcvNULL) ? gcvNULL : *(ptr)) +#define gcmOPT_STRING(ptr) (((ptr) == gcvNULL) ? "(nil)" : (ptr)) + +void +gckOS_Print( + IN gctCONST_STRING Message, + ... + ); + +void +gckOS_PrintN( + IN gctUINT ArgumentSize, + IN gctCONST_STRING Message, + ... + ); + +void +gckOS_CopyPrint( + IN gctCONST_STRING Message, + ... + ); + +void +gcoOS_Print( + IN gctCONST_STRING Message, + ... + ); + +#define gcmPRINT gcoOS_Print +#define gcmkPRINT gckOS_Print +#define gcmkPRINT_N gckOS_PrintN + +#if gcdPRINT_VERSION +# define gcmPRINT_VERSION() do { \ + _gcmPRINT_VERSION(gcm); \ + gcmSTACK_DUMP(); \ + } while (0) +# define gcmkPRINT_VERSION() _gcmPRINT_VERSION(gcmk) +# define _gcmPRINT_VERSION(prefix) \ + prefix##TRACE(gcvLEVEL_ERROR, \ + "Vivante HAL version %d.%d.%d build %d %s %s", \ + gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, \ + gcvVERSION_BUILD, gcvVERSION_DATE, gcvVERSION_TIME ) +#else +# define gcmPRINT_VERSION() do { gcmSTACK_DUMP(); } while (gcvFALSE) +# define gcmkPRINT_VERSION() do { } while (gcvFALSE) +#endif + +typedef enum _gceDUMP_BUFFER +{ + gceDUMP_BUFFER_CONTEXT, + gceDUMP_BUFFER_USER, + gceDUMP_BUFFER_KERNEL, + gceDUMP_BUFFER_LINK, + gceDUMP_BUFFER_WAITLINK, + gceDUMP_BUFFER_FROM_USER, +} +gceDUMP_BUFFER; + +void +gckOS_DumpBuffer( + IN gckOS Os, + IN gctPOINTER Buffer, + IN gctUINT Size, + IN gceDUMP_BUFFER Type, + IN gctBOOL CopyMessage + ); + +#define gcmkDUMPBUFFER gckOS_DumpBuffer + +#if gcdDUMP_COMMAND +# define gcmkDUMPCOMMAND(Os, Buffer, Size, Type, CopyMessage) \ + gcmkDUMPBUFFER(Os, Buffer, Size, Type, CopyMessage) +#else +# define gcmkDUMPCOMMAND(Os, Buffer, Size, Type, CopyMessage) +#endif + +#if gcmIS_DEBUG(gcdDEBUG_CODE) + +void +gckOS_DebugFlush( + gctCONST_STRING CallerName, + gctUINT LineNumber, + gctUINT32 DmaAddress + ); + +# define gcmkDEBUGFLUSH(DmaAddress) \ + gckOS_DebugFlush(__FUNCTION__, __LINE__, DmaAddress) +#else +# define gcmkDEBUGFLUSH(DmaAddress) +#endif + +/******************************************************************************* +** +** gcmDUMP_FRAMERATE +** +** Print average frame rate +** +*/ +#if gcdDUMP_FRAMERATE + gceSTATUS + gcfDumpFrameRate( + void + ); +# define gcmDUMP_FRAMERATE gcfDumpFrameRate +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_FRAMERATE(...) +#else + gcmINLINE static void + __dummy_dump_frame_rate( + void + ) + { + } +# define gcmDUMP_FRAMERATE __dummy_dump_frame_rate +#endif + + +/******************************************************************************* +** +** gcmDUMP +** +** Print a dump message. +** +** ARGUMENTS: +** +** gctSTRING Message. +** +** ... Optional arguments. +*/ +#if gcdDUMP + gceSTATUS + gcfDump( + IN gcoOS Os, + IN gctCONST_STRING String, + ... + ); +# define gcmDUMP gcfDump +#elif gcdHAS_ELLIPSIS +# define gcmDUMP(...) +#else + gcmINLINE static void + __dummy_dump( + IN gcoOS Os, + IN gctCONST_STRING Message, + ... + ) + { + } +# define gcmDUMP __dummy_dump +#endif + +/******************************************************************************* +** +** gcmDUMP_DATA +** +** Add data to the dump. +** +** ARGUMENTS: +** +** gctSTRING Tag +** Tag for dump. +** +** gctPOINTER Logical +** Logical address of buffer. +** +** gctSIZE_T Bytes +** Number of bytes. +*/ + +#if gcdDUMP || gcdDUMP_COMMAND + gceSTATUS + gcfDumpData( + IN gcoOS Os, + IN gctSTRING Tag, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ); +# define gcmDUMP_DATA gcfDumpData +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_DATA(...) +#else + gcmINLINE static void + __dummy_dump_data( + IN gcoOS Os, + IN gctSTRING Tag, + IN gctPOINTER Logical, + IN gctSIZE_T Bytes + ) + { + } +# define gcmDUMP_DATA __dummy_dump_data +#endif + +/******************************************************************************* +** +** gcmDUMP_BUFFER +** +** Print a buffer to the dump. +** +** ARGUMENTS: +** +** gctSTRING Tag +** Tag for dump. +** +** gctUINT32 Physical +** Physical address of buffer. +** +** gctPOINTER Logical +** Logical address of buffer. +** +** gctUINT32 Offset +** Offset into buffer. +** +** gctSIZE_T Bytes +** Number of bytes. +*/ + +#if gcdDUMP || gcdDUMP_COMMAND +gceSTATUS +gcfDumpBuffer( + IN gcoOS Os, + IN gctSTRING Tag, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctUINT32 Offset, + IN gctSIZE_T Bytes + ); +# define gcmDUMP_BUFFER gcfDumpBuffer +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_BUFFER(...) +#else + gcmINLINE static void + __dummy_dump_buffer( + IN gcoOS Os, + IN gctSTRING Tag, + IN gctUINT32 Physical, + IN gctPOINTER Logical, + IN gctUINT32 Offset, + IN gctSIZE_T Bytes + ) + { + } +# define gcmDUMP_BUFFER __dummy_dump_buffer +#endif + +/******************************************************************************* +** +** gcmDUMP_API +** +** Print a dump message for a high level API prefixed by the function name. +** +** ARGUMENTS: +** +** gctSTRING Message. +** +** ... Optional arguments. +*/ +gceSTATUS gcfDumpApi(IN gctCONST_STRING String, ...); +#if gcdDUMP_API +# define gcmDUMP_API gcfDumpApi +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_API(...) +#else + gcmINLINE static void + __dummy_dump_api( + IN gctCONST_STRING Message, + ... + ) + { + } +# define gcmDUMP_API __dummy_dump_api +#endif + +/******************************************************************************* +** +** gcmDUMP_API_ARRAY +** +** Print an array of data. +** +** ARGUMENTS: +** +** gctUINT32_PTR Pointer to array. +** gctUINT32 Size. +*/ +gceSTATUS gcfDumpArray(IN gctCONST_POINTER Data, IN gctUINT32 Size); +#if gcdDUMP_API +# define gcmDUMP_API_ARRAY gcfDumpArray +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_API_ARRAY(...) +#else + gcmINLINE static void + __dummy_dump_api_array( + IN gctCONST_POINTER Data, + IN gctUINT32 Size + ) + { + } +# define gcmDUMP_API_ARRAY __dummy_dump_api_array +#endif + +/******************************************************************************* +** +** gcmDUMP_API_ARRAY_TOKEN +** +** Print an array of data terminated by a token. +** +** ARGUMENTS: +** +** gctUINT32_PTR Pointer to array. +** gctUINT32 Termination. +*/ +gceSTATUS gcfDumpArrayToken(IN gctCONST_POINTER Data, IN gctUINT32 Termination); +#if gcdDUMP_API +# define gcmDUMP_API_ARRAY_TOKEN gcfDumpArrayToken +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_API_ARRAY_TOKEN(...) +#else + gcmINLINE static void + __dummy_dump_api_array_token( + IN gctCONST_POINTER Data, + IN gctUINT32 Termination + ) + { + } +# define gcmDUMP_API_ARRAY_TOKEN __dummy_dump_api_array_token +#endif + +/******************************************************************************* +** +** gcmDUMP_API_DATA +** +** Print an array of bytes. +** +** ARGUMENTS: +** +** gctCONST_POINTER Pointer to array. +** gctSIZE_T Size. +*/ +gceSTATUS gcfDumpApiData(IN gctCONST_POINTER Data, IN gctSIZE_T Size); +#if gcdDUMP_API +# define gcmDUMP_API_DATA gcfDumpApiData +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_API_DATA(...) +#else + gcmINLINE static void + __dummy_dump_api_data( + IN gctCONST_POINTER Data, + IN gctSIZE_T Size + ) + { + } +# define gcmDUMP_API_DATA __dummy_dump_api_data +#endif + +/******************************************************************************* +** gcmDUMP_2D_COMMAND +** +** Print the 2D command buffer. +** +** ARGUMENTS: +** +** gctUINT32_PTR Pointer to the command buffer. +** gctUINT32 Command buffer size. +*/ +gceSTATUS gcfDump2DCommand(IN gctUINT32_PTR Command, IN gctUINT32 Size); +#if gcdDUMP_2D +# define gcmDUMP_2D_COMMAND gcfDump2DCommand +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_2D_COMMAND(...) +#else + gcmINLINE static void + __dummy_dump_2d_command( + IN gctUINT32_PTR Command, + IN gctUINT32 Size + ) + { + } +# define gcmDUMP_2D_COMMAND __dummy_dump_2d_command +#endif + +/******************************************************************************* +** gcmDUMP_2D_SURFACE +** +** Print the 2D surface memory. +** +** ARGUMENTS: +** +** gctBOOL Src. +** gctUINT32 Address. +*/ +gceSTATUS gcfDump2DSurface(IN gctBOOL Src, IN gctUINT32 Address); +#if gcdDUMP_2D +# define gcmDUMP_2D_SURFACE gcfDump2DSurface +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_2D_SURFACE(...) +#else + gcmINLINE static void + __dummy_dump_2d_surface( + IN gctBOOL Src, + IN gctUINT32 Address + ) + { + } +# define gcmDUMP_2D_SURFACE __dummy_dump_2d_surface +#endif + +/******************************************************************************* +** gcmDUMP_ADD_MEMORY_INFO +** +** Record the memory info. +** +** ARGUMENTS: +** +** gctUINT32 Address. +** gctSIZE_T Size. +*/ +gceSTATUS gcfAddMemoryInfo(IN gctUINT32 GPUAddress, IN gctPOINTER Logical, IN gctUINT32 Physical, IN gctUINT32 Size); +#if gcdDUMP_2D +# define gcmDUMP_ADD_MEMORY_INFO gcfAddMemoryInfo +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_ADD_MEMORY_INFO(...) +#else + gcmINLINE static void + __dummy_dump_add_memory_info( + IN gctUINT32 GPUAddress, + IN gctPOINTER Logical, + IN gctUINT32 Physical, + IN gctUINT32 Size + ) + { + } +# define gcmDUMP_ADD_MEMORY_INFO __dummy_dump_add_memory_info +#endif + +/******************************************************************************* +** gcmDUMP_DEL_MEMORY_INFO +** +** Record the memory info. +** +** ARGUMENTS: +** +** gctUINT32 Address. +*/ +gceSTATUS gcfDelMemoryInfo(IN gctUINT32 Address); +#if gcdDUMP_2D +# define gcmDUMP_DEL_MEMORY_INFO gcfDelMemoryInfo +#elif gcdHAS_ELLIPSIS +# define gcmDUMP_DEL_MEMORY_INFO(...) +#else + gcmINLINE static void + __dummy_dump_del_memory_info( + IN gctUINT32 Address + ) + { + } +# define gcmDUMP_DEL_MEMORY_INFO __dummy_dump_del_memory_info +#endif + +#if gcdDUMP_2D +extern gctPOINTER dumpMemInfoListMutex; +extern gctBOOL dump2DFlag; +#endif + +/******************************************************************************* +** +** gcmTRACE_RELEASE +** +** Print a message to the shader debugger. +** +** ARGUMENTS: +** +** message Message. +** ... Optional arguments. +*/ + +#define gcmTRACE_RELEASE gcoOS_DebugShaderTrace + +void +gcoOS_DebugShaderTrace( + IN gctCONST_STRING Message, + ... + ); + +void +gcoOS_SetDebugShaderFiles( + IN gctCONST_STRING VSFileName, + IN gctCONST_STRING FSFileName + ); + +void +gcoOS_SetDebugShaderFileType( + IN gctUINT32 ShaderType + ); + +void +gcoOS_EnableDebugBuffer( + IN gctBOOL Enable + ); + +/******************************************************************************* +** +** gcmBREAK +** +** Break into the debugger. In retail mode this macro does nothing. +** +** ARGUMENTS: +** +** None. +*/ + +void +gcoOS_DebugBreak( + void + ); + +void +gckOS_DebugBreak( + void + ); + +#if gcmIS_DEBUG(gcdDEBUG_BREAK) +# define gcmBREAK gcoOS_DebugBreak +# define gcmkBREAK gckOS_DebugBreak +#else +# define gcmBREAK() +# define gcmkBREAK() +#endif + +/******************************************************************************* +** +** gcmASSERT +** +** Evaluate an expression and break into the debugger if the expression +** evaluates to false. In retail mode this macro does nothing. +** +** ARGUMENTS: +** +** exp Expression to evaluate. +*/ +#if gcmIS_DEBUG(gcdDEBUG_ASSERT) +# define _gcmASSERT(prefix, exp) \ + do \ + { \ + if (!(exp)) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ASSERT at %s(%d)", \ + __FUNCTION__, __LINE__); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + "(%s)", #exp); \ + prefix##BREAK(); \ + } \ + } \ + while (gcvFALSE) +# define gcmASSERT(exp) _gcmASSERT(gcm, exp) +# define gcmkASSERT(exp) _gcmASSERT(gcmk, exp) +#else +# define gcmASSERT(exp) +# define gcmkASSERT(exp) +#endif + +/******************************************************************************* +** +** gcmVERIFY +** +** Verify if an expression returns true. If the expression does not +** evaluates to true, an assertion will happen in debug mode. +** +** ARGUMENTS: +** +** exp Expression to evaluate. +*/ +#if gcmIS_DEBUG(gcdDEBUG_ASSERT) +# define gcmVERIFY(exp) gcmASSERT(exp) +# define gcmkVERIFY(exp) gcmkASSERT(exp) +#else +# define gcmVERIFY(exp) exp +# define gcmkVERIFY(exp) exp +#endif + +/******************************************************************************* +** +** gcmVERIFY_OK +** +** Verify a fucntion returns gcvSTATUS_OK. If the function does not return +** gcvSTATUS_OK, an assertion will happen in debug mode. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ + +void +gcoOS_Verify( + IN gceSTATUS status + ); + +void +gckOS_Verify( + IN gceSTATUS status + ); + +#if gcmIS_DEBUG(gcdDEBUG_ASSERT) +# define gcmVERIFY_OK(func) \ + do \ + { \ + gceSTATUS verifyStatus = func; \ + gcoOS_Verify(verifyStatus); \ + if (verifyStatus != gcvSTATUS_OK) \ + { \ + gcmTRACE( \ + gcvLEVEL_ERROR, \ + "gcmVERIFY_OK(%d): function returned %d", \ + __LINE__, verifyStatus \ + ); \ + } \ + gcmASSERT(verifyStatus == gcvSTATUS_OK); \ + } \ + while (gcvFALSE) +# define gcmkVERIFY_OK(func) \ + do \ + { \ + gceSTATUS verifyStatus = func; \ + if (verifyStatus != gcvSTATUS_OK) \ + { \ + gcmkTRACE( \ + gcvLEVEL_ERROR, \ + "gcmkVERIFY_OK(%d): function returned %d", \ + __LINE__, verifyStatus \ + ); \ + } \ + gckOS_Verify(verifyStatus); \ + gcmkASSERT(verifyStatus == gcvSTATUS_OK); \ + } \ + while (gcvFALSE) +#else +# define gcmVERIFY_OK(func) func +# define gcmkVERIFY_OK(func) func +#endif + +gctCONST_STRING +gcoOS_DebugStatus2Name( + gceSTATUS status + ); + +gctCONST_STRING +gckOS_DebugStatus2Name( + gceSTATUS status + ); + +/******************************************************************************* +** +** gcmERR_BREAK +** +** Executes a break statement on error. +** +** ASSUMPTIONS: +** +** 'status' variable of gceSTATUS type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _gcmERR_BREAK(prefix, func) \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ERR_BREAK: status=%d(%s) @ %s(%d)", \ + status, gcoOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + break; \ + } \ + do { } while (gcvFALSE) +#define _gcmkERR_BREAK(prefix, func) \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ERR_BREAK: status=%d(%s) @ %s(%d)", \ + status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + break; \ + } \ + do { } while (gcvFALSE) +#define gcmERR_BREAK(func) _gcmERR_BREAK(gcm, func) +#define gcmkERR_BREAK(func) _gcmkERR_BREAK(gcmk, func) + +/******************************************************************************* +** +** gcmERR_RETURN +** +** Executes a return on error. +** +** ASSUMPTIONS: +** +** 'status' variable of gceSTATUS type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _gcmERR_RETURN(prefix, func) \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ERR_RETURN: status=%d(%s) @ %s(%d)", \ + status, gcoOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + prefix##FOOTER(); \ + return status; \ + } \ + do { } while (gcvFALSE) +#define _gcmkERR_RETURN(prefix, func) \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ERR_RETURN: status=%d(%s) @ %s(%d)", \ + status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + prefix##FOOTER(); \ + return status; \ + } \ + do { } while (gcvFALSE) +#define gcmERR_RETURN(func) _gcmERR_RETURN(gcm, func) +#define gcmkERR_RETURN(func) _gcmkERR_RETURN(gcmk, func) + + +/******************************************************************************* +** +** gcmONERROR +** +** Jump to the error handler in case there is an error. +** +** ASSUMPTIONS: +** +** 'status' variable of gceSTATUS type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _gcmONERROR(prefix, func) \ + do \ + { \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ONERROR: status=%d(%s) @ %s(%d)", \ + status, gcoOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + goto OnError; \ + } \ + } \ + while (gcvFALSE) +#define _gcmkONERROR(prefix, func) \ + do \ + { \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ONERROR: status=%d(%s) @ %s(%d)", \ + status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + goto OnError; \ + } \ + } \ + while (gcvFALSE) +#define gcmONERROR(func) _gcmONERROR(gcm, func) +#define gcmkONERROR(func) _gcmkONERROR(gcmk, func) + +/******************************************************************************* +** +** gcmkSAFECASTSIZET +** +** Check wether value of a gctSIZE_T varible beyond the capability +** of 32bits GPU hardware. +** +** ASSUMPTIONS: +** +** +** +** ARGUMENTS: +** +** x A gctUINT32 variable +** y A gctSIZE_T variable +*/ +#define gcmkSAFECASTSIZET(x, y) \ + do \ + { \ + gctUINT32 tmp = (gctUINT32)(y); \ + if (gcmSIZEOF(gctSIZE_T) > gcmSIZEOF(gctUINT32)) \ + { \ + gcmkASSERT(tmp <= gcvMAXUINT32); \ + } \ + (x) = tmp; \ + } \ + while (gcvFALSE) + +#define gcmSAFECASTSIZET(x, y) \ + do \ + { \ + gctUINT32 tmp = (gctUINT32)(y); \ + if (gcmSIZEOF(gctSIZE_T) > gcmSIZEOF(gctUINT32)) \ + { \ + gcmASSERT(tmp <= gcvMAXUINT32); \ + } \ + (x) = tmp; \ + } \ + while (gcvFALSE) + +/******************************************************************************* +** +** gcmVERIFY_LOCK +** +** Verifies whether the surface is locked. +** +** ARGUMENTS: +** +** surfaceInfo Pointer to the surface iniformational structure. +*/ +#define gcmVERIFY_LOCK(surfaceInfo) \ + if (!surfaceInfo->node.valid) \ + { \ + gcmONERROR(gcvSTATUS_MEMORY_UNLOCKED); \ + } \ + +/******************************************************************************* +** +** gcmVERIFY_NODE_LOCK +** +** Verifies whether the surface node is locked. +** +** ARGUMENTS: +** +** surfaceInfo Pointer to the surface iniformational structure. +*/ +#define gcmVERIFY_NODE_LOCK(surfaceNode) \ + if (!(surfaceNode)->valid) \ + { \ + status = gcvSTATUS_MEMORY_UNLOCKED; \ + break; \ + } \ + do { } while (gcvFALSE) + +/******************************************************************************* +** +** gcmBADOBJECT_BREAK +** +** Executes a break statement on bad object. +** +** ARGUMENTS: +** +** obj Object to test. +** t Expected type of the object. +*/ +#define gcmBADOBJECT_BREAK(obj, t) \ + if ((obj == gcvNULL) \ + || (((gcsOBJECT *)(obj))->type != t) \ + ) \ + { \ + status = gcvSTATUS_INVALID_OBJECT; \ + break; \ + } \ + do { } while (gcvFALSE) + +/******************************************************************************* +** +** gcmCHECK_STATUS +** +** Executes a break statement on error. +** +** ASSUMPTIONS: +** +** 'status' variable of gceSTATUS type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _gcmCHECK_STATUS(prefix, func) \ + do \ + { \ + last = func; \ + if (gcmIS_ERROR(last)) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "CHECK_STATUS: status=%d(%s) @ %s(%d)", \ + last, gcoOS_DebugStatus2Name(last), __FUNCTION__, __LINE__); \ + status = last; \ + } \ + } \ + while (gcvFALSE) +#define _gcmkCHECK_STATUS(prefix, func) \ + do \ + { \ + last = func; \ + if (gcmIS_ERROR(last)) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "CHECK_STATUS: status=%d(%s) @ %s(%d)", \ + last, gckOS_DebugStatus2Name(last), __FUNCTION__, __LINE__); \ + status = last; \ + } \ + } \ + while (gcvFALSE) +#define gcmCHECK_STATUS(func) _gcmCHECK_STATUS(gcm, func) +#define gcmkCHECK_STATUS(func) _gcmkCHECK_STATUS(gcmk, func) + +/******************************************************************************* +** +** gcmVERIFY_ARGUMENT +** +** Assert if an argument does not apply to the specified expression. If +** the argument evaluates to false, gcvSTATUS_INVALID_ARGUMENT will be +** returned from the current function. In retail mode this macro does +** nothing. +** +** ARGUMENTS: +** +** arg Argument to evaluate. +*/ +# define _gcmVERIFY_ARGUMENT(prefix, arg) \ + do \ + { \ + if (!(arg)) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, #prefix "VERIFY_ARGUMENT failed:"); \ + prefix##ASSERT(arg); \ + prefix##FOOTER_ARG("status=%d", gcvSTATUS_INVALID_ARGUMENT); \ + return gcvSTATUS_INVALID_ARGUMENT; \ + } \ + } \ + while (gcvFALSE) +# define gcmVERIFY_ARGUMENT(arg) _gcmVERIFY_ARGUMENT(gcm, arg) +# define gcmkVERIFY_ARGUMENT(arg) _gcmVERIFY_ARGUMENT(gcmk, arg) + +/******************************************************************************* +** +** gcmDEBUG_VERIFY_ARGUMENT +** +** Works just like gcmVERIFY_ARGUMENT, but is only valid in debug mode. +** Use this to verify arguments inside non-public API functions. +*/ +#if gcdDEBUG +# define gcmDEBUG_VERIFY_ARGUMENT(arg) _gcmVERIFY_ARGUMENT(gcm, arg) +# define gcmkDEBUG_VERIFY_ARGUMENT(arg) _gcmkVERIFY_ARGUMENT(gcm, arg) +#else +# define gcmDEBUG_VERIFY_ARGUMENT(arg) +# define gcmkDEBUG_VERIFY_ARGUMENT(arg) +#endif + +/******************************************************************************* +** +** gcmVERIFY_ARGUMENT_RETURN +** +** Assert if an argument does not apply to the specified expression. If +** the argument evaluates to false, gcvSTATUS_INVALID_ARGUMENT will be +** returned from the current function. In retail mode this macro does +** nothing. +** +** ARGUMENTS: +** +** arg Argument to evaluate. +*/ +# define _gcmVERIFY_ARGUMENT_RETURN(prefix, arg, value) \ + do \ + { \ + if (!(arg)) \ + { \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "gcmVERIFY_ARGUMENT_RETURN failed:"); \ + prefix##ASSERT(arg); \ + prefix##FOOTER_ARG("value=%d", value); \ + return value; \ + } \ + } \ + while (gcvFALSE) +# define gcmVERIFY_ARGUMENT_RETURN(arg, value) \ + _gcmVERIFY_ARGUMENT_RETURN(gcm, arg, value) +# define gcmkVERIFY_ARGUMENT_RETURN(arg, value) \ + _gcmVERIFY_ARGUMENT_RETURN(gcmk, arg, value) + +#define MAX_LOOP_COUNT 0x7FFFFFFF + +/******************************************************************************\ +****************************** User Debug Option ****************************** +\******************************************************************************/ + +/* User option. */ +typedef enum _gceDEBUG_MSG +{ + gcvDEBUG_MSG_NONE, + gcvDEBUG_MSG_ERROR, + gcvDEBUG_MSG_WARNING +} +gceDEBUG_MSG; + +typedef struct _gcsUSER_DEBUG_OPTION +{ + gceDEBUG_MSG debugMsg; +} +gcsUSER_DEBUG_OPTION; + +gcsUSER_DEBUG_OPTION * +gcGetUserDebugOption( + void + ); + +#if defined(ANDROID) +struct _gcoOS_SymbolsList +{ +#if gcdENABLE_3D + gcePATCH_ID patchId; +#endif + const char * symList[10]; +}; +#endif + +#if gcdHAS_ELLIPSIS +#define gcmUSER_DEBUG_MSG(level, ...) \ + do \ + { \ + if (level <= gcGetUserDebugOption()->debugMsg) \ + { \ + gcoOS_Print(__VA_ARGS__); \ + } \ + } while (gcvFALSE) + +#define gcmUSER_DEBUG_ERROR_MSG(...) gcmUSER_DEBUG_MSG(gcvDEBUG_MSG_ERROR, "Error: " __VA_ARGS__) +#define gcmUSER_DEBUG_WARNING_MSG(...) gcmUSER_DEBUG_MSG(gcvDEBUG_MSG_WARNING, "Warring: " __VA_ARGS__) +#else +#define gcmUSER_DEBUG_MSG +#define gcmUSER_DEBUG_ERROR_MSG +#define gcmUSER_DEBUG_WARNING_MSG +#endif + +/******************************************************************************* +** +** A set of macros to aid state loading. +** +** ARGUMENTS: +** +** CommandBuffer Pointer to a gcoCMDBUF object. +** StateDelta Pointer to a gcsSTATE_DELTA state delta structure. +** Memory Destination memory pointer of gctUINT32_PTR type. +** PartOfContext Whether or not the state is a part of the context. +** FixedPoint Whether or not the state is of the fixed point format. +** Count Number of consecutive states to be loaded. +** Address State address. +** Data Data to be set to the state. +*/ + +/*----------------------------------------------------------------------------*/ + +#if gcmIS_DEBUG(gcdDEBUG_CODE) + +# define gcmSTORELOADSTATE(CommandBuffer, Memory, Address, Count) \ + CommandBuffer->lastLoadStatePtr = gcmPTR_TO_UINT64(Memory); \ + CommandBuffer->lastLoadStateAddress = Address; \ + CommandBuffer->lastLoadStateCount = Count + +# define gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address) \ + gcmASSERT( \ + (gctUINT) (Memory - gcmUINT64_TO_TYPE(CommandBuffer->lastLoadStatePtr, gctUINT32_PTR) - 1) \ + == \ + (gctUINT) (Address - CommandBuffer->lastLoadStateAddress) \ + ); \ + \ + gcmASSERT(CommandBuffer->lastLoadStateCount > 0); \ + \ + CommandBuffer->lastLoadStateCount -= 1 + +# define gcmVERIFYLOADSTATEDONE(CommandBuffer) \ + gcmASSERT(CommandBuffer->lastLoadStateCount == 0); + +# define gcmDEFINELOADSTATEBASE() \ + gctUINT32_PTR LoadStateBase; + +# define gcmSETLOADSTATEBASE(CommandBuffer, OutSide) \ + if (OutSide) \ + {\ + LoadStateBase = (gctUINT32_PTR)*OutSide; \ + }\ + else\ + {\ + LoadStateBase = (gctUINT_PTR)CommandBuffer->buffer;\ + } + + +# define gcmVERIFYLOADSTATEALIGNED(CommandBuffer, Memory) \ + gcmASSERT(((Memory - LoadStateBase) & 1) == 0); + +# define gcmUNSETLOADSTATEBASE() \ + LoadStateBase = LoadStateBase; + +#else + +# define gcmSTORELOADSTATE(CommandBuffer, Memory, Address, Count) +# define gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address) +# define gcmVERIFYLOADSTATEDONE(CommandBuffer) + +# define gcmDEFINELOADSTATEBASE() +# define gcmSETLOADSTATEBASE(CommandBuffer, OutSide) +# define gcmVERIFYLOADSTATEALIGNED(CommandBuffer, Memory) +# define gcmUNSETLOADSTATEBASE() + +#endif + +#if gcdSECURE_USER + +# define gcmDEFINESECUREUSER() \ + gctUINT __secure_user_offset__; \ + gctUINT32_PTR __secure_user_hintArray__; + +# define gcmBEGINSECUREUSER() \ + __secure_user_offset__ = reserve->lastOffset; \ + \ + __secure_user_hintArray__ = gcmUINT64_TO_PTR(reserve->hintArrayTail) + +# define gcmENDSECUREUSER() \ + reserve->hintArrayTail = gcmPTR_TO_UINT64(__secure_user_hintArray__) + +# define gcmSKIPSECUREUSER() \ + __secure_user_offset__ += gcmSIZEOF(gctUINT32) + +# define gcmUPDATESECUREUSER() \ + *__secure_user_hintArray__ = __secure_user_offset__; \ + \ + __secure_user_offset__ += gcmSIZEOF(gctUINT32); \ + __secure_user_hintArray__ += 1 + +#else + +# define gcmDEFINESECUREUSER() +# define gcmBEGINSECUREUSER() +# define gcmENDSECUREUSER() +# define gcmSKIPSECUREUSER() +# define gcmUPDATESECUREUSER() + +#endif + +/*----------------------------------------------------------------------------*/ + +#if gcdDUMP +# define gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, Data) \ + if (FixedPoint) \ + { \ + gcmDUMP(gcvNULL, "#[state.x 0x%04X 0x%08X]", \ + Address, Data \ + ); \ + } \ + else \ + { \ + gcmDUMP(gcvNULL, "#[state 0x%04X 0x%08X]", \ + Address, Data \ + ); \ + } +#else +# define gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, Data) +#endif + +#define gcmDEFINESTATEBUFFER(CommandBuffer, StateDelta, Memory, ReserveSize) \ + gcmDEFINESECUREUSER() \ + gctSIZE_T ReserveSize; \ + gcoCMDBUF CommandBuffer; \ + gctUINT32_PTR Memory; \ + gcsSTATE_DELTA_PTR StateDelta + +#define gcmBEGINSTATEBUFFER(Hardware, CommandBuffer, StateDelta, Memory, ReserveSize) \ +{ \ + gcmONERROR(gcoBUFFER_Reserve( \ + Hardware->buffer, ReserveSize, gcvTRUE, gcvCOMMAND_3D, &CommandBuffer \ + )); \ + \ + Memory = (gctUINT32_PTR) gcmUINT64_TO_PTR(CommandBuffer->lastReserve); \ + \ + StateDelta = Hardware->delta; \ + \ + gcmBEGINSECUREUSER(); \ +} + +#define gcmENDSTATEBUFFER(Hardware, CommandBuffer, Memory, ReserveSize) \ +{ \ + gcmENDSECUREUSER(); \ + \ + gcmASSERT( \ + gcmUINT64_TO_TYPE(CommandBuffer->lastReserve, gctUINT8_PTR) + ReserveSize \ + == \ + (gctUINT8_PTR) Memory \ + ); \ +} + +/*----------------------------------------------------------------------------*/ + +#define gcmBEGINSTATEBATCH(CommandBuffer, Memory, FixedPoint, Address, Count) \ +{ \ + gcmASSERT(((Memory - gcmUINT64_TO_TYPE(CommandBuffer->lastReserve, gctUINT32_PTR)) & 1) == 0); \ + gcmASSERT((gctUINT32)Count <= 1024); \ + \ + gcmVERIFYLOADSTATEDONE(CommandBuffer); \ + \ + gcmSTORELOADSTATE(CommandBuffer, Memory, Address, Count); \ + \ + *Memory++ \ + = gcmSETFIELDVALUE(0, AQ_COMMAND_LOAD_STATE_COMMAND, OPCODE, LOAD_STATE) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, FLOAT, FixedPoint) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, COUNT, Count) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, ADDRESS, Address); \ + \ + gcmSKIPSECUREUSER(); \ +} + +#define gcmENDSTATEBATCH(CommandBuffer, Memory) \ +{ \ + gcmVERIFYLOADSTATEDONE(CommandBuffer); \ + \ + gcmASSERT(((Memory - gcmUINT64_TO_TYPE(CommandBuffer->lastReserve, gctUINT32_PTR)) & 1) == 0); \ +} + +/*----------------------------------------------------------------------------*/ + +#define gcmSETSTATEDATA(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address); \ + \ + gcmSAFECASTSIZET(__temp_data32__, Data); \ + \ + *Memory++ = __temp_data32__; \ + \ + gcoHARDWARE_UpdateDelta( \ + StateDelta, Address, 0, __temp_data32__ \ + ); \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + +#define gcmSETSTATEDATAWITHMASK(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address); \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcoHARDWARE_UpdateDelta( \ + StateDelta, Address, Mask, __temp_data32__ \ + ); \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + + +#define gcmSETCTRLSTATE(StateDelta, CommandBuffer, Memory, Address, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address); \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcmDUMPSTATEDATA(StateDelta, gcvFALSE, Address, __temp_data32__); \ + \ + gcmSKIPSECUREUSER(); \ +} + +#define gcmSETFILLER(CommandBuffer, Memory) \ +{ \ + gcmVERIFYLOADSTATEDONE(CommandBuffer); \ + \ + Memory += 1; \ + \ + gcmSKIPSECUREUSER(); \ +} + +/*----------------------------------------------------------------------------*/ + +#define gcmSETSINGLESTATE(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gcmBEGINSTATEBATCH(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATA(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data); \ + gcmENDSTATEBATCH(CommandBuffer, Memory); \ +} + +#define gcmSETSINGLESTATEWITHMASK(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gcmBEGINSTATEBATCH(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATAWITHMASK(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data); \ + gcmENDSTATEBATCH(CommandBuffer, Memory); \ +} + + +#define gcmSETSINGLECTRLSTATE(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gcmBEGINSTATEBATCH(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETCTRLSTATE(StateDelta, CommandBuffer, Memory, Address, Data); \ + gcmENDSTATEBATCH(CommandBuffer, Memory); \ +} + + + +#define gcmSETSEMASTALLPIPE(StateDelta, CommandBuffer, Memory, Data) \ +{ \ + gcmSETSINGLESTATE(StateDelta, CommandBuffer, Memory, gcvFALSE, AQSemaphoreRegAddrs, Data); \ + \ + *Memory++ = gcmSETFIELDVALUE(0, STALL_COMMAND, OPCODE, STALL); \ + \ + *Memory++ = Data; \ + \ + gcmDUMP(gcvNULL, "#[stall 0x%08X 0x%08X]", \ + gcmSETFIELDVALUE(0, AQ_SEMAPHORE, SOURCE, FRONT_END), \ + gcmSETFIELDVALUE(0, AQ_SEMAPHORE, DESTINATION, PIXEL_ENGINE)); \ + \ + gcmSKIPSECUREUSER(); \ +} + +/******************************************************************************* +** +** gcmSETSTARTDECOMMAND +** +** Form a START_DE command. +** +** ARGUMENTS: +** +** Memory Destination memory pointer of gctUINT32_PTR type. +** Count Number of the rectangles. +*/ + +#define gcmSETSTARTDECOMMAND(Memory, Count) \ +{ \ + *Memory++ \ + = gcmSETFIELDVALUE(0, AQ_COMMAND_START_DE_COMMAND, OPCODE, START_DE) \ + | gcmSETFIELD (0, AQ_COMMAND_START_DE_COMMAND, COUNT, Count) \ + | gcmSETFIELD (0, AQ_COMMAND_START_DE_COMMAND, DATA_COUNT, 0); \ + \ + *Memory++ = 0xDEADDEED; \ +} + +/***************************************** +** Temp command buffer macro +*/ +#define gcmDEFINESTATEBUFFER_NEW(CommandBuffer, StateDelta, Memory) \ + gcmDEFINESECUREUSER() \ + gcmDEFINELOADSTATEBASE() \ + gcsTEMPCMDBUF CommandBuffer = gcvNULL; \ + gctUINT32_PTR Memory; \ + gcsSTATE_DELTA_PTR StateDelta + + +#define gcmBEGINSTATEBUFFER_NEW(Hardware, CommandBuffer, StateDelta, Memory, OutSide) \ +{ \ + if (OutSide) \ + {\ + Memory = (gctUINT32_PTR)*OutSide; \ + }\ + else \ + {\ + gcmONERROR(gcoBUFFER_StartTEMPCMDBUF( \ + Hardware->buffer, &CommandBuffer \ + ));\ + \ + Memory = (gctUINT32_PTR)(CommandBuffer->buffer); \ + \ + }\ + StateDelta = Hardware->delta; \ + \ + gcmBEGINSECUREUSER(); \ + gcmSETLOADSTATEBASE(CommandBuffer,OutSide);\ +} + +#define gcmENDSTATEBUFFER_NEW(Hardware, CommandBuffer, Memory, OutSide) \ +{ \ + gcmENDSECUREUSER(); \ + \ + if (OutSide) \ + {\ + *OutSide = Memory; \ + }\ + else \ + {\ + CommandBuffer->currentByteSize = (gctUINT32)((gctUINT8_PTR)Memory - \ + (gctUINT8_PTR)CommandBuffer->buffer); \ + \ + gcmONERROR(gcoBUFFER_EndTEMPCMDBUF(Hardware->buffer));\ + }\ + gcmUNSETLOADSTATEBASE()\ +} + +/*----------------------------------------------------------------------------*/ + +#define gcmBEGINSTATEBATCH_NEW(CommandBuffer, Memory, FixedPoint, Address, Count) \ +{ \ + gcmVERIFYLOADSTATEALIGNED(CommandBuffer,Memory);\ + gcmASSERT((gctUINT32)Count <= 1024); \ + \ + *Memory++ \ + = gcmSETFIELDVALUE(0, AQ_COMMAND_LOAD_STATE_COMMAND, OPCODE, LOAD_STATE) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, FLOAT, FixedPoint) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, COUNT, Count) \ + | gcmSETFIELD (0, AQ_COMMAND_LOAD_STATE_COMMAND, ADDRESS, Address); \ + \ + gcmSKIPSECUREUSER(); \ +} + +#define gcmENDSTATEBATCH_NEW(CommandBuffer, Memory) \ + gcmVERIFYLOADSTATEALIGNED(CommandBuffer,Memory); + +/*----------------------------------------------------------------------------*/ + +#define gcmSETSTATEDATA_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcoHARDWARE_UpdateDelta( \ + StateDelta, Address, 0, __temp_data32__ \ + ); \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + +#define gcmSETSTATEDATAWITHMASK_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcoHARDWARE_UpdateDelta( \ + StateDelta, Address, Mask, __temp_data32__ \ + ); \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + + +#define gcmSETCTRLSTATE_NEW(StateDelta, CommandBuffer, Memory, Address, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcmDUMPSTATEDATA(StateDelta, gcvFALSE, Address, __temp_data32__); \ + \ + gcmSKIPSECUREUSER(); \ +} + +#define gcmSETFILLER_NEW(CommandBuffer, Memory) \ +{ \ + Memory += 1; \ + \ + gcmSKIPSECUREUSER(); \ +} + +/*----------------------------------------------------------------------------*/ + +#define gcmSETSINGLESTATE_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gcmBEGINSTATEBATCH_NEW(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATA_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data); \ + gcmENDSTATEBATCH_NEW(CommandBuffer, Memory); \ +} + +#define gcmSETSINGLESTATEWITHMASK_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gcmBEGINSTATEBATCH_NEW(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATAWITHMASK_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data); \ + gcmENDSTATEBATCH_NEW(CommandBuffer, Memory); \ +} + + +#define gcmSETSINGLECTRLSTATE_NEW(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gcmBEGINSTATEBATCH_NEW(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETCTRLSTATE_NEW(StateDelta, CommandBuffer, Memory, Address, Data); \ + gcmENDSTATEBATCH_NEW(CommandBuffer, Memory); \ +} + + + +#define gcmSETSEMASTALLPIPE_NEW(StateDelta, CommandBuffer, Memory, Data) \ +{ \ + gcmSETSINGLESTATE_NEW(StateDelta, CommandBuffer, Memory, gcvFALSE, AQSemaphoreRegAddrs, Data); \ + \ + *Memory++ = gcmSETFIELDVALUE(0, STALL_COMMAND, OPCODE, STALL); \ + \ + *Memory++ = Data; \ + \ + gcmDUMP(gcvNULL, "#[stall 0x%08X 0x%08X]", \ + gcmSETFIELDVALUE(0, AQ_SEMAPHORE, SOURCE, FRONT_END), \ + gcmSETFIELDVALUE(0, AQ_SEMAPHORE, DESTINATION, PIXEL_ENGINE)); \ + \ + gcmSKIPSECUREUSER(); \ +} + +#define gcmSETSTARTDECOMMAND_NEW(CommandBuffer, Memory, Count) \ +{ \ + *Memory++ \ + = gcmSETFIELDVALUE(0, AQ_COMMAND_START_DE_COMMAND, OPCODE, START_DE) \ + | gcmSETFIELD (0, AQ_COMMAND_START_DE_COMMAND, COUNT, Count) \ + | gcmSETFIELD (0, AQ_COMMAND_START_DE_COMMAND, DATA_COUNT, 0); \ + \ + *Memory++ = 0xDEADDEED; \ + \ +} + +#define gcmSETSTATEDATA_NEW_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + +#define gcmSETSTATEDATAWITHMASK_NEW_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + +#define gcmSETSINGLESTATE_NEW_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gcmBEGINSTATEBATCH_NEW(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATA_NEW_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data); \ + gcmENDSTATEBATCH_NEW(CommandBuffer, Memory); \ +} + +#define gcmSETSINGLESTATEWITHMASK_NEW_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gcmBEGINSTATEBATCH_NEW(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATAWITHMASK_NEW_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data); \ + gcmENDSTATEBATCH_NEW(CommandBuffer, Memory); \ +} + +#define gcmSETSTATEDATA_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address); \ + \ + gcmSAFECASTSIZET(__temp_data32__, Data); \ + \ + *Memory++ = __temp_data32__; \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + +#define gcmSETSTATEDATAWITHMASK_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gctUINT32 __temp_data32__; \ + \ + gcmVERIFYLOADSTATE(CommandBuffer, Memory, Address); \ + \ + __temp_data32__ = Data; \ + \ + *Memory++ = __temp_data32__; \ + \ + gcmDUMPSTATEDATA(StateDelta, FixedPoint, Address, __temp_data32__); \ + \ + gcmUPDATESECUREUSER(); \ +} + +#define gcmSETSINGLESTATE_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data) \ +{ \ + gcmBEGINSTATEBATCH(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATA_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Data); \ + gcmENDSTATEBATCH(CommandBuffer, Memory); \ +} + +#define gcmSETSINGLESTATEWITHMASK_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data) \ +{ \ + gcmBEGINSTATEBATCH(CommandBuffer, Memory, FixedPoint, Address, 1); \ + gcmSETSTATEDATAWITHMASK_FAST(StateDelta, CommandBuffer, Memory, FixedPoint, \ + Address, Mask, Data); \ + gcmENDSTATEBATCH(CommandBuffer, Memory); \ +} + +#define gcmDEFINESTATEBUFFER_NEW_FAST(CommandBuffer, Memory) \ + gcmDEFINESECUREUSER() \ + gcmDEFINELOADSTATEBASE() \ + gcsTEMPCMDBUF CommandBuffer = gcvNULL; \ + gctUINT32_PTR Memory; + +#define gcmDEFINESTATEBUFFER_FAST(CommandBuffer, Memory, ReserveSize) \ + gcmDEFINESECUREUSER() \ + gctSIZE_T ReserveSize; \ + gcoCMDBUF CommandBuffer; \ + gctUINT32_PTR Memory; + +#define gcmBEGINSTATEBUFFER_FAST(Hardware, CommandBuffer, Memory, ReserveSize) \ +{ \ + gcmONERROR(gcoBUFFER_Reserve( \ + Hardware->buffer, ReserveSize, gcvTRUE, &CommandBuffer \ + )); \ + \ + Memory = (gctUINT32_PTR) gcmUINT64_TO_PTR(CommandBuffer->lastReserve); \ + \ + gcmBEGINSECUREUSER(); \ +} + +#define gcmBEGINSTATEBUFFER_NEW_FAST(Hardware, CommandBuffer, Memory, OutSide) \ +{ \ + if (OutSide) \ + {\ + Memory = (gctUINT32_PTR)*OutSide; \ + }\ + else \ + {\ + gcmONERROR(gcoBUFFER_StartTEMPCMDBUF( \ + Hardware->buffer, &CommandBuffer \ + ));\ + \ + Memory = (gctUINT32_PTR)(CommandBuffer->buffer); \ + \ + }\ + \ + gcmBEGINSECUREUSER(); \ + gcmSETLOADSTATEBASE(CommandBuffer,OutSide);\ +} +/******************************************************************************* +** +** gcmCONFIGUREUNIFORMS +** +** Configure uniforms according to chip and numConstants. +*/ +#if !gcdENABLE_UNIFIED_CONSTANT +#define gcmCONFIGUREUNIFORMS(ChipModel, ChipRevision, NumConstants, \ + UnifiedConst, VsConstBase, PsConstBase, VsConstMax, PsConstMax, ConstMax) \ +{ \ + if (ChipModel == gcv2000 && ChipRevision == 0x5118) \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 64; \ + ConstMax = 320; \ + } \ + else if (NumConstants == 320) \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 64; \ + ConstMax = 320; \ + } \ + /* All GC1000 series chips can only support 64 uniforms for ps on non-unified const mode. */ \ + else if (NumConstants > 256 && ChipModel == gcv1000) \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 64; \ + ConstMax = 320; \ + } \ + else if (NumConstants > 256) \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 256; \ + ConstMax = 512; \ + } \ + else if (NumConstants == 256) \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 256; \ + ConstMax = 512; \ + } \ + else \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 168; \ + PsConstMax = 64; \ + ConstMax = 232; \ + } \ +} +#else +#define gcmCONFIGUREUNIFORMS(ChipModel, ChipRevision, NumConstants, \ + UnifiedConst, VsConstBase, PsConstBase, VsConstMax, PsConstMax, ConstMax) \ +{ \ + if (NumConstants > 256) \ + { \ + UnifiedConst = gcvTRUE; \ + VsConstBase = gcregSHUniformsRegAddrs; \ + PsConstBase = gcregSHUniformsRegAddrs; \ + ConstMax = NumConstants; \ + VsConstMax = 256; \ + PsConstMax = ConstMax - VsConstMax; \ + } \ + else if (NumConstants == 256) \ + { \ + if (ChipModel == gcv2000 && ChipRevision == 0x5118) \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 64; \ + ConstMax = 320; \ + } \ + else \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 256; \ + PsConstMax = 256; \ + ConstMax = 512; \ + } \ + } \ + else \ + { \ + UnifiedConst = gcvFALSE; \ + VsConstBase = AQVertexShaderConstRegAddrs; \ + PsConstBase = AQPixelShaderConstRegAddrs; \ + VsConstMax = 168; \ + PsConstMax = 64; \ + ConstMax = 232; \ + } \ +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_base_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_driver.h b/drivers/gpu/galcore/inc/gc_hal_driver.h new file mode 100644 index 00000000000000..345b4eaca17b7f --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_driver.h @@ -0,0 +1,1136 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +#ifndef __gc_hal_driver_h_ +#define __gc_hal_driver_h_ + +#include "gc_hal_enum.h" +#include "gc_hal_types.h" + +#if gcdENABLE_VG +#include "gc_hal_driver_vg.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +******************************* I/O Control Codes ****************************** +\******************************************************************************/ + +#define gcvHAL_CLASS "galcore" +#define IOCTL_GCHAL_INTERFACE 30000 +#define IOCTL_GCHAL_KERNEL_INTERFACE 30001 +#define IOCTL_GCHAL_TERMINATE 30002 + +#undef CONFIG_ANDROID_RESERVED_MEMORY_ACCOUNT +/******************************************************************************\ +********************************* Command Codes ******************************** +\******************************************************************************/ + +typedef enum _gceHAL_COMMAND_CODES +{ + /* Generic query. */ + gcvHAL_QUERY_VIDEO_MEMORY, + gcvHAL_QUERY_CHIP_IDENTITY, + + /* Contiguous memory. */ + gcvHAL_ALLOCATE_NON_PAGED_MEMORY, + gcvHAL_FREE_NON_PAGED_MEMORY, + gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY, + gcvHAL_FREE_CONTIGUOUS_MEMORY, + + /* Video memory allocation. */ + gcvHAL_ALLOCATE_VIDEO_MEMORY, /* Enforced alignment. */ + gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY, /* No alignment. */ + gcvHAL_RELEASE_VIDEO_MEMORY, + + /* Physical-to-logical mapping. */ + gcvHAL_MAP_MEMORY, + gcvHAL_UNMAP_MEMORY, + + /* Logical-to-physical mapping. */ + gcvHAL_MAP_USER_MEMORY, + gcvHAL_UNMAP_USER_MEMORY, + + /* Surface lock/unlock. */ + gcvHAL_LOCK_VIDEO_MEMORY, + gcvHAL_UNLOCK_VIDEO_MEMORY, + + /* Event queue. */ + gcvHAL_EVENT_COMMIT, + + gcvHAL_USER_SIGNAL, + gcvHAL_SIGNAL, + gcvHAL_WRITE_DATA, + + gcvHAL_COMMIT, + gcvHAL_STALL, + + gcvHAL_READ_REGISTER, + gcvHAL_WRITE_REGISTER, + + gcvHAL_GET_PROFILE_SETTING, + gcvHAL_SET_PROFILE_SETTING, + + gcvHAL_READ_ALL_PROFILE_REGISTERS, + gcvHAL_PROFILE_REGISTERS_2D, +#if VIVANTE_PROFILER_PERDRAW + gcvHAL_READ_PROFILER_REGISTER_SETTING, +#endif + + /* Power management. */ + gcvHAL_SET_POWER_MANAGEMENT_STATE, + gcvHAL_QUERY_POWER_MANAGEMENT_STATE, + + gcvHAL_GET_BASE_ADDRESS, + + gcvHAL_SET_IDLE, /* reserved */ + + /* Queries. */ + gcvHAL_QUERY_KERNEL_SETTINGS, + + /* Reset. */ + gcvHAL_RESET, + + /* Map physical address into handle. */ + gcvHAL_MAP_PHYSICAL, + + /* Debugger stuff. */ + gcvHAL_DEBUG, + + /* Cache stuff. */ + gcvHAL_CACHE, + + /* TimeStamp */ + gcvHAL_TIMESTAMP, + + /* Database. */ + gcvHAL_DATABASE, + + /* Version. */ + gcvHAL_VERSION, + + /* Chip info */ + gcvHAL_CHIP_INFO, + + /* Process attaching/detaching. */ + gcvHAL_ATTACH, + gcvHAL_DETACH, + + /* Composition. */ + gcvHAL_COMPOSE, + + /* Set timeOut value */ + gcvHAL_SET_TIMEOUT, + + /* Frame database. */ + gcvHAL_GET_FRAME_INFO, + + gcvHAL_QUERY_COMMAND_BUFFER, + + gcvHAL_COMMIT_DONE, + + /* GPU and event dump */ + gcvHAL_DUMP_GPU_STATE, + gcvHAL_DUMP_EVENT, + + /* Virtual command buffer. */ + gcvHAL_ALLOCATE_VIRTUAL_COMMAND_BUFFER, + gcvHAL_FREE_VIRTUAL_COMMAND_BUFFER, + + /* FSCALE_VAL. */ + gcvHAL_SET_FSCALE_VALUE, + gcvHAL_GET_FSCALE_VALUE, + + gcvHAL_NAME_VIDEO_MEMORY, + gcvHAL_IMPORT_VIDEO_MEMORY, + + /* Reset time stamp. */ + gcvHAL_QUERY_RESET_TIME_STAMP, + + /* Multi-GPU read/write. */ + gcvHAL_READ_REGISTER_EX, + gcvHAL_WRITE_REGISTER_EX, + + /* Sync point operations. */ + gcvHAL_SYNC_POINT, + + /* Create native fence and return its fd. */ + gcvHAL_CREATE_NATIVE_FENCE, + + /* Destory MMU. */ + gcvHAL_DESTROY_MMU, + + /* Shared buffer. */ + gcvHAL_SHBUF, + + /* Config power management. */ + gcvHAL_CONFIG_POWER_MANAGEMENT, + + /* Connect a video node to an OS native fd. */ + gcvHAL_GET_VIDEO_MEMORY_FD, +} +gceHAL_COMMAND_CODES; + +/******************************************************************************\ +****************************** Interface Structure ***************************** +\******************************************************************************/ + +#define gcdMAX_PROFILE_FILE_NAME 128 + +/* Kernel settings. */ +typedef struct _gcsKERNEL_SETTINGS +{ + /* Used RealTime signal between kernel and user. */ + gctINT signal; +} +gcsKERNEL_SETTINGS; + + +/* gcvHAL_QUERY_CHIP_IDENTITY */ +typedef struct _gcsHAL_QUERY_CHIP_IDENTITY * gcsHAL_QUERY_CHIP_IDENTITY_PTR; +typedef struct _gcsHAL_QUERY_CHIP_IDENTITY +{ + + /* Chip model. */ + gceCHIPMODEL chipModel; + + /* Revision value.*/ + gctUINT32 chipRevision; + + /* Supported feature fields. */ + gctUINT32 chipFeatures; + + /* Supported minor feature fields. */ + gctUINT32 chipMinorFeatures; + + /* Supported minor feature 1 fields. */ + gctUINT32 chipMinorFeatures1; + + /* Supported minor feature 2 fields. */ + gctUINT32 chipMinorFeatures2; + + /* Supported minor feature 3 fields. */ + gctUINT32 chipMinorFeatures3; + + /* Supported minor feature 4 fields. */ + gctUINT32 chipMinorFeatures4; + + /* Supported minor feature 5 fields. */ + gctUINT32 chipMinorFeatures5; + + /* Number of streams supported. */ + gctUINT32 streamCount; + + /* Total number of temporary registers per thread. */ + gctUINT32 registerMax; + + /* Maximum number of threads. */ + gctUINT32 threadCount; + + /* Number of shader cores. */ + gctUINT32 shaderCoreCount; + + /* Size of the vertex cache. */ + gctUINT32 vertexCacheSize; + + /* Number of entries in the vertex output buffer. */ + gctUINT32 vertexOutputBufferSize; + + /* Number of pixel pipes. */ + gctUINT32 pixelPipes; + + /* Number of instructions. */ + gctUINT32 instructionCount; + + /* Number of constants. */ + gctUINT32 numConstants; + + /* Buffer size */ + gctUINT32 bufferSize; + + /* Number of varyings */ + gctUINT32 varyingsCount; + + /* Supertile layout style in hardware */ + gctUINT32 superTileMode; + +#if gcdMULTI_GPU + /* Number of 3D GPUs */ + gctUINT32 gpuCoreCount; +#endif + + /* Special control bits for 2D chip. */ + gctUINT32 chip2DControl; + + /* Product ID */ + gctUINT32 productID; +} +gcsHAL_QUERY_CHIP_IDENTITY; + +/* gcvHAL_COMPOSE. */ +typedef struct _gcsHAL_COMPOSE * gcsHAL_COMPOSE_PTR; +typedef struct _gcsHAL_COMPOSE +{ + /* Composition state buffer. */ + gctUINT64 physical; + gctUINT64 logical; + gctUINT offset; + gctUINT size; + + /* Composition end signal. */ + gctUINT64 process; + gctUINT64 signal; + + /* User signals. */ + gctUINT64 userProcess; + gctUINT64 userSignal1; + gctUINT64 userSignal2; + +#if defined(__QNXNTO__) + /* Client pulse side-channel connection ID. */ + gctINT32 coid; + + /* Set by server. */ + gctINT32 rcvid; +#endif +} +gcsHAL_COMPOSE; + + +typedef struct _gcsHAL_INTERFACE +{ + /* Command code. */ + gceHAL_COMMAND_CODES command; + + /* Hardware type. */ + gceHARDWARE_TYPE hardwareType; + + /* Status value. */ + gceSTATUS status; + + /* Handle to this interface channel. */ + gctUINT64 handle; + + /* Pid of the client. */ + gctUINT32 pid; + + /* Union of command structures. */ + union _u + { + /* gcvHAL_GET_BASE_ADDRESS */ + struct _gcsHAL_GET_BASE_ADDRESS + { + /* Physical memory address of internal memory. */ + OUT gctUINT32 baseAddress; + } + GetBaseAddress; + + /* gcvHAL_QUERY_VIDEO_MEMORY */ + struct _gcsHAL_QUERY_VIDEO_MEMORY + { + /* Physical memory address of internal memory. Just a name. */ + OUT gctUINT32 internalPhysical; + + /* Size in bytes of internal memory. */ + OUT gctUINT64 internalSize; + + /* Physical memory address of external memory. Just a name. */ + OUT gctUINT32 externalPhysical; + + /* Size in bytes of external memory.*/ + OUT gctUINT64 externalSize; + + /* Physical memory address of contiguous memory. Just a name. */ + OUT gctUINT32 contiguousPhysical; + + /* Size in bytes of contiguous memory.*/ + OUT gctUINT64 contiguousSize; + } + QueryVideoMemory; + + /* gcvHAL_QUERY_CHIP_IDENTITY */ + gcsHAL_QUERY_CHIP_IDENTITY QueryChipIdentity; + + /* gcvHAL_MAP_MEMORY */ + struct _gcsHAL_MAP_MEMORY + { + /* Physical memory address to map. Just a name on Linux/Qnx. */ + IN gctUINT32 physical; + + /* Number of bytes in physical memory to map. */ + IN gctUINT64 bytes; + + /* Address of mapped memory. */ + OUT gctUINT64 logical; + } + MapMemory; + + /* gcvHAL_UNMAP_MEMORY */ + struct _gcsHAL_UNMAP_MEMORY + { + /* Physical memory address to unmap. Just a name on Linux/Qnx. */ + IN gctUINT32 physical; + + /* Number of bytes in physical memory to unmap. */ + IN gctUINT64 bytes; + + /* Address of mapped memory to unmap. */ + IN gctUINT64 logical; + } + UnmapMemory; + + /* gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY */ + struct _gcsHAL_ALLOCATE_LINEAR_VIDEO_MEMORY + { + /* Number of bytes to allocate. */ + IN OUT gctUINT bytes; + + /* Buffer alignment. */ + IN gctUINT alignment; + + /* Type of allocation. */ + IN gceSURF_TYPE type; + + /* Flag of allocation. */ + IN gctUINT32 flag; + + /* Memory pool to allocate from. */ + IN OUT gcePOOL pool; + + /* Allocated video memory. */ + OUT gctUINT32 node; + } + AllocateLinearVideoMemory; + + /* gcvHAL_ALLOCATE_VIDEO_MEMORY */ + struct _gcsHAL_ALLOCATE_VIDEO_MEMORY + { + /* Width of rectangle to allocate. */ + IN OUT gctUINT width; + + /* Height of rectangle to allocate. */ + IN OUT gctUINT height; + + /* Depth of rectangle to allocate. */ + IN gctUINT depth; + + /* Format rectangle to allocate in gceSURF_FORMAT. */ + IN gceSURF_FORMAT format; + + /* Type of allocation. */ + IN gceSURF_TYPE type; + + /* Memory pool to allocate from. */ + IN OUT gcePOOL pool; + + /* Allocated video memory. */ + OUT gctUINT32 node; + } + AllocateVideoMemory; + + /* gcvHAL_RELEASE_VIDEO_MEMORY */ + struct _gcsHAL_RELEASE_VIDEO_MEMORY + { + /* Allocated video memory. */ + IN gctUINT32 node; + +#ifdef __QNXNTO__ +/* TODO: This is part of the unlock - why is it here? */ + /* Mapped logical address to unmap in user space. */ + OUT gctUINT64 memory; + + /* Number of bytes to allocated. */ + OUT gctUINT64 bytes; +#endif + } + ReleaseVideoMemory; + + /* gcvHAL_LOCK_VIDEO_MEMORY */ + struct _gcsHAL_LOCK_VIDEO_MEMORY + { + /* Allocated video memory. */ + IN gctUINT32 node; + + /* Cache configuration. */ + /* Only gcvPOOL_CONTIGUOUS and gcvPOOL_VIRUTAL + ** can be configured */ + IN gctBOOL cacheable; + + /* Hardware specific address. */ + OUT gctUINT32 address; + + /* Mapped logical address. */ + OUT gctUINT64 memory; + + /* Customer priviate handle*/ + OUT gctUINT32 gid; + + /* Bus address of a contiguous video node. */ + OUT gctUINT64 physicalAddress; + } + LockVideoMemory; + + /* gcvHAL_UNLOCK_VIDEO_MEMORY */ + struct _gcsHAL_UNLOCK_VIDEO_MEMORY + { + /* Allocated video memory. */ + IN gctUINT64 node; + + /* Type of surface. */ + IN gceSURF_TYPE type; + + /* Flag to unlock surface asynchroneously. */ + IN OUT gctBOOL asynchroneous; + } + UnlockVideoMemory; + + /* gcvHAL_ALLOCATE_NON_PAGED_MEMORY */ + struct _gcsHAL_ALLOCATE_NON_PAGED_MEMORY + { + /* Number of bytes to allocate. */ + IN OUT gctUINT64 bytes; + + /* Physical address of allocation. Just a name. */ + OUT gctUINT32 physical; + + /* Logical address of allocation. */ + OUT gctUINT64 logical; + } + AllocateNonPagedMemory; + + /* gcvHAL_FREE_NON_PAGED_MEMORY */ + struct _gcsHAL_FREE_NON_PAGED_MEMORY + { + /* Number of bytes allocated. */ + IN gctUINT64 bytes; + + /* Physical address of allocation. Just a name. */ + IN gctUINT32 physical; + + /* Logical address of allocation. */ + IN gctUINT64 logical; + } + FreeNonPagedMemory; + + /* gcvHAL_ALLOCATE_NON_PAGED_MEMORY */ + struct _gcsHAL_ALLOCATE_VIRTUAL_COMMAND_BUFFER + { + /* Number of bytes to allocate. */ + IN OUT gctUINT64 bytes; + + /* Physical address of allocation. Just a name. */ + OUT gctUINT32 physical; + + /* Logical address of allocation. */ + OUT gctUINT64 logical; + } + AllocateVirtualCommandBuffer; + + /* gcvHAL_FREE_NON_PAGED_MEMORY */ + struct _gcsHAL_FREE_VIRTUAL_COMMAND_BUFFER + { + /* Number of bytes allocated. */ + IN gctUINT64 bytes; + + /* Physical address of allocation. Just a name. */ + IN gctUINT32 physical; + + /* Logical address of allocation. */ + IN gctUINT64 logical; + } + FreeVirtualCommandBuffer; + + /* gcvHAL_EVENT_COMMIT. */ + struct _gcsHAL_EVENT_COMMIT + { + /* Event queue in gcsQUEUE. */ + IN gctUINT64 queue; + +#if gcdMULTI_GPU + IN gceCORE_3D_MASK chipEnable; + + IN gceMULTI_GPU_MODE gpuMode; +#endif + } + Event; + + /* gcvHAL_COMMIT */ + struct _gcsHAL_COMMIT + { + /* Context buffer object gckCONTEXT. */ + IN gctUINT64 context; + + /* Command buffer gcoCMDBUF. */ + IN gctUINT64 commandBuffer; + + /* State delta buffer in gcsSTATE_DELTA. */ + gctUINT64 delta; + + /* Event queue in gcsQUEUE. */ + IN gctUINT64 queue; + +#if gcdMULTI_GPU + IN gceCORE_3D_MASK chipEnable; + + IN gceMULTI_GPU_MODE gpuMode; +#endif + } + Commit; + + /* gcvHAL_MAP_USER_MEMORY */ + struct _gcsHAL_MAP_USER_MEMORY + { + /* Base address of user memory to map. */ + IN gctUINT64 memory; + + /* Physical address of user memory to map. */ + IN gctUINT32 physical; + + /* Size of user memory in bytes to map. */ + IN gctUINT64 size; + + /* Info record required by gcvHAL_UNMAP_USER_MEMORY. Just a name. */ + OUT gctUINT32 info; + + /* Physical address of mapped memory. */ + OUT gctUINT32 address; + } + MapUserMemory; + + /* gcvHAL_UNMAP_USER_MEMORY */ + struct _gcsHAL_UNMAP_USER_MEMORY + { + /* Base address of user memory to unmap. */ + IN gctUINT64 memory; + + /* Size of user memory in bytes to unmap. */ + IN gctUINT64 size; + + /* Info record returned by gcvHAL_MAP_USER_MEMORY. Just a name. */ + IN gctUINT32 info; + + /* Physical address of mapped memory as returned by + gcvHAL_MAP_USER_MEMORY. */ + IN gctUINT32 address; + } + UnmapUserMemory; +#if !USE_NEW_LINUX_SIGNAL + /* gcsHAL_USER_SIGNAL */ + struct _gcsHAL_USER_SIGNAL + { + /* Command. */ + gceUSER_SIGNAL_COMMAND_CODES command; + + /* Signal ID. */ + IN OUT gctINT id; + + /* Reset mode. */ + IN gctBOOL manualReset; + + /* Wait timedout. */ + IN gctUINT32 wait; + + /* State. */ + IN gctBOOL state; + } + UserSignal; +#endif + + /* gcvHAL_SIGNAL. */ + struct _gcsHAL_SIGNAL + { + /* Signal handle to signal gctSIGNAL. */ + IN gctUINT64 signal; + + /* Reserved gctSIGNAL. */ + IN gctUINT64 auxSignal; + + /* Process owning the signal gctHANDLE. */ + IN gctUINT64 process; + +#if defined(__QNXNTO__) + /* Client pulse side-channel connection ID. Set by client in gcoOS_CreateSignal. */ + IN gctINT32 coid; + + /* Set by server. */ + IN gctINT32 rcvid; +#endif + /* Event generated from where of pipeline */ + IN gceKERNEL_WHERE fromWhere; + } + Signal; + + /* gcvHAL_WRITE_DATA. */ + struct _gcsHAL_WRITE_DATA + { + /* Address to write data to. */ + IN gctUINT32 address; + + /* Data to write. */ + IN gctUINT32 data; + } + WriteData; + + /* gcvHAL_ALLOCATE_CONTIGUOUS_MEMORY */ + struct _gcsHAL_ALLOCATE_CONTIGUOUS_MEMORY + { + /* Number of bytes to allocate. */ + IN OUT gctUINT64 bytes; + + /* Hardware address of allocation. */ + OUT gctUINT32 address; + + /* Physical address of allocation. Just a name. */ + OUT gctUINT32 physical; + + /* Logical address of allocation. */ + OUT gctUINT64 logical; + } + AllocateContiguousMemory; + + /* gcvHAL_FREE_CONTIGUOUS_MEMORY */ + struct _gcsHAL_FREE_CONTIGUOUS_MEMORY + { + /* Number of bytes allocated. */ + IN gctUINT64 bytes; + + /* Physical address of allocation. Just a name. */ + IN gctUINT32 physical; + + /* Logical address of allocation. */ + IN gctUINT64 logical; + } + FreeContiguousMemory; + + /* gcvHAL_READ_REGISTER */ + struct _gcsHAL_READ_REGISTER + { + /* Logical address of memory to write data to. */ + IN gctUINT32 address; + + /* Data read. */ + OUT gctUINT32 data; + } + ReadRegisterData; + + /* gcvHAL_WRITE_REGISTER */ + struct _gcsHAL_WRITE_REGISTER + { + /* Logical address of memory to write data to. */ + IN gctUINT32 address; + + /* Data read. */ + IN gctUINT32 data; + } + WriteRegisterData; + +#if gcdMULTI_GPU + /* gcvHAL_READ_REGISTER_EX */ + struct _gcsHAL_READ_REGISTER_EX + { + /* Logical address of memory to write data to. */ + IN gctUINT32 address; + + IN gctUINT32 coreSelect; + + /* Data read. */ + OUT gctUINT32 data[gcdMULTI_GPU]; + } + ReadRegisterDataEx; + + /* gcvHAL_WRITE_REGISTER_EX */ + struct _gcsHAL_WRITE_REGISTER_EX + { + /* Logical address of memory to write data to. */ + IN gctUINT32 address; + + IN gctUINT32 coreSelect; + + /* Data read. */ + IN gctUINT32 data[gcdMULTI_GPU]; + } + WriteRegisterDataEx; +#endif + +#if VIVANTE_PROFILER + /* gcvHAL_GET_PROFILE_SETTING */ + struct _gcsHAL_GET_PROFILE_SETTING + { + /* Enable profiling */ + OUT gctBOOL enable; + } + GetProfileSetting; + + /* gcvHAL_SET_PROFILE_SETTING */ + struct _gcsHAL_SET_PROFILE_SETTING + { + /* Enable profiling */ + IN gctBOOL enable; + } + SetProfileSetting; + +#if VIVANTE_PROFILER_PERDRAW + /* gcvHAL_READ_PROFILER_REGISTER_SETTING */ + struct _gcsHAL_READ_PROFILER_REGISTER_SETTING + { + /*Should Clear Register*/ + IN gctBOOL bclear; + } + SetProfilerRegisterClear; +#endif + + /* gcvHAL_READ_ALL_PROFILE_REGISTERS */ + struct _gcsHAL_READ_ALL_PROFILE_REGISTERS + { +#if VIVANTE_PROFILER_CONTEXT + /* Context buffer object gckCONTEXT. Just a name. */ + IN gctUINT32 context; +#endif + + /* Data read. */ + OUT gcsPROFILER_COUNTERS counters; + } + RegisterProfileData; + + /* gcvHAL_PROFILE_REGISTERS_2D */ + struct _gcsHAL_PROFILE_REGISTERS_2D + { + /* Data read in gcs2D_PROFILE. */ + OUT gctUINT64 hwProfile2D; + } + RegisterProfileData2D; +#endif + + /* Power management. */ + /* gcvHAL_SET_POWER_MANAGEMENT_STATE */ + struct _gcsHAL_SET_POWER_MANAGEMENT + { + /* Data read. */ + IN gceCHIPPOWERSTATE state; + } + SetPowerManagement; + + /* gcvHAL_QUERY_POWER_MANAGEMENT_STATE */ + struct _gcsHAL_QUERY_POWER_MANAGEMENT + { + /* Data read. */ + OUT gceCHIPPOWERSTATE state; + + /* Idle query. */ + OUT gctBOOL isIdle; + } + QueryPowerManagement; + + /* gcvHAL_QUERY_KERNEL_SETTINGS */ + struct _gcsHAL_QUERY_KERNEL_SETTINGS + { + /* Settings.*/ + OUT gcsKERNEL_SETTINGS settings; + } + QueryKernelSettings; + + /* gcvHAL_MAP_PHYSICAL */ + struct _gcsHAL_MAP_PHYSICAL + { + /* gcvTRUE to map, gcvFALSE to unmap. */ + IN gctBOOL map; + + /* Physical address. */ + IN OUT gctUINT64 physical; + } + MapPhysical; + + /* gcvHAL_DEBUG */ + struct _gcsHAL_DEBUG + { + /* If gcvTRUE, set the debug information. */ + IN gctBOOL set; + IN gctUINT32 level; + IN gctUINT32 zones; + IN gctBOOL enable; + + IN gceDEBUG_MESSAGE_TYPE type; + IN gctUINT32 messageSize; + + /* Message to print if not empty. */ + IN gctCHAR message[80]; + } + Debug; + + /* gcvHAL_CACHE */ + struct _gcsHAL_CACHE + { + IN gceCACHEOPERATION operation; + IN gctUINT64 process; + IN gctUINT64 logical; + IN gctUINT64 bytes; + IN gctUINT32 node; + } + Cache; + + /* gcvHAL_TIMESTAMP */ + struct _gcsHAL_TIMESTAMP + { + /* Timer select. */ + IN gctUINT32 timer; + + /* Timer request type (0-stop, 1-start, 2-send delta). */ + IN gctUINT32 request; + + /* Result of delta time in microseconds. */ + OUT gctINT32 timeDelta; + } + TimeStamp; + + /* gcvHAL_DATABASE */ + struct _gcsHAL_DATABASE + { + /* Set to gcvTRUE if you want to query a particular process ID. + ** Set to gcvFALSE to query the last detached process. */ + IN gctBOOL validProcessID; + + /* Process ID to query. */ + IN gctUINT32 processID; + + /* Information. */ + OUT gcuDATABASE_INFO vidMem; + OUT gcuDATABASE_INFO nonPaged; + OUT gcuDATABASE_INFO contiguous; + OUT gcuDATABASE_INFO gpuIdle; + + /* Detail information about video memory. */ + OUT gcuDATABASE_INFO vidMemPool[3]; + } + Database; + + /* gcvHAL_VERSION */ + struct _gcsHAL_VERSION + { + /* Major version: N.n.n. */ + OUT gctINT32 major; + + /* Minor version: n.N.n. */ + OUT gctINT32 minor; + + /* Patch version: n.n.N. */ + OUT gctINT32 patch; + + /* Build version. */ + OUT gctUINT32 build; + } + Version; + + /* gcvHAL_CHIP_INFO */ + struct _gcsHAL_CHIP_INFO + { + /* Chip count. */ + OUT gctINT32 count; + + /* Chip types. */ + OUT gceHARDWARE_TYPE types[gcdCHIP_COUNT]; + } + ChipInfo; + + /* gcvHAL_ATTACH */ + struct _gcsHAL_ATTACH + { + /* Handle of context buffer object. */ + OUT gctUINT32 context; + + /* Number of states in the buffer. */ + OUT gctUINT64 stateCount; + + /* Map context buffer to user or not. */ + IN gctBOOL map; + + /* Physical of context buffer. */ + OUT gctUINT32 physicals[2]; + + /* Physical of context buffer. */ + OUT gctUINT64 logicals[2]; + + /* Bytes of context buffer. */ + OUT gctUINT32 bytes; + } + Attach; + + /* gcvHAL_DETACH */ + struct _gcsHAL_DETACH + { + /* Context buffer object gckCONTEXT. Just a name. */ + IN gctUINT32 context; + } + Detach; + + /* gcvHAL_COMPOSE. */ + gcsHAL_COMPOSE Compose; + + /* gcvHAL_GET_FRAME_INFO. */ + struct _gcsHAL_GET_FRAME_INFO + { + /* gcsHAL_FRAME_INFO* */ + OUT gctUINT64 frameInfo; + } + GetFrameInfo; + + /* gcvHAL_SET_TIME_OUT. */ + struct _gcsHAL_SET_TIMEOUT + { + gctUINT32 timeOut; + } + SetTimeOut; + +#if gcdENABLE_VG + /* gcvHAL_COMMIT */ + struct _gcsHAL_VGCOMMIT + { + /* Context buffer. gcsVGCONTEXT_PTR */ + IN gctUINT64 context; + + /* Command queue. gcsVGCMDQUEUE_PTR */ + IN gctUINT64 queue; + + /* Number of entries in the queue. */ + IN gctUINT entryCount; + + /* Task table. gcsTASK_MASTER_TABLE_PTR */ + IN gctUINT64 taskTable; + } + VGCommit; + + /* gcvHAL_QUERY_COMMAND_BUFFER */ + struct _gcsHAL_QUERY_COMMAND_BUFFER + { + /* Command buffer attributes. */ + OUT gcsCOMMAND_BUFFER_INFO information; + } + QueryCommandBuffer; + +#endif + + struct _gcsHAL_SET_FSCALE_VALUE + { + IN gctUINT value; + } + SetFscaleValue; + + struct _gcsHAL_GET_FSCALE_VALUE + { + OUT gctUINT value; + OUT gctUINT minValue; + OUT gctUINT maxValue; + } + GetFscaleValue; + + struct _gcsHAL_NAME_VIDEO_MEMORY + { + IN gctUINT32 handle; + OUT gctUINT32 name; + } + NameVideoMemory; + + struct _gcsHAL_IMPORT_VIDEO_MEMORY + { + IN gctUINT32 name; + OUT gctUINT32 handle; + } + ImportVideoMemory; + + struct _gcsHAL_QUERY_RESET_TIME_STAMP + { + OUT gctUINT64 timeStamp; + } + QueryResetTimeStamp; + + struct _gcsHAL_SYNC_POINT + { + /* Command. */ + gceSYNC_POINT_COMMAND_CODES command; + + /* Sync point. */ + IN OUT gctUINT64 syncPoint; + + /* From where. */ + IN gceKERNEL_WHERE fromWhere; + + /* Signaled state. */ + OUT gctBOOL state; + } + SyncPoint; + + struct _gcsHAL_CREATE_NATIVE_FENCE + { + /* Signal id to dup. */ + IN gctUINT64 syncPoint; + + /* Native fence file descriptor. */ + OUT gctINT fenceFD; + + } + CreateNativeFence; + + struct _gcsHAL_DESTROY_MMU + { + /* Mmu object. */ + IN gctUINT64 mmu; + } + DestroyMmu; + + struct _gcsHAL_SHBUF + { + gceSHBUF_COMMAND_CODES command; + + /* Shared buffer. */ + IN OUT gctUINT64 id; + + /* User data to be shared. */ + IN gctUINT64 data; + + /* Data size. */ + IN OUT gctUINT32 bytes; + } + ShBuf; + + struct _gcsHAL_CONFIG_POWER_MANAGEMENT + { + IN gctBOOL enable; + } + ConfigPowerManagement; + + struct _gcsHAL_GET_VIDEO_MEMORY_FD + { + IN gctUINT32 handle; + OUT gctINT fd; + } + GetVideoMemoryFd; + } + u; +} +gcsHAL_INTERFACE; + + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_driver_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_driver_vg.h b/drivers/gpu/galcore/inc/gc_hal_driver_vg.h new file mode 100644 index 00000000000000..d4e4b3eb67c7af --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_driver_vg.h @@ -0,0 +1,270 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_driver_vg_h_ +#define __gc_hal_driver_vg_h_ + + + +#include "gc_hal_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +******************************* I/O Control Codes ****************************** +\******************************************************************************/ + +#define gcvHAL_CLASS "galcore" +#define IOCTL_GCHAL_INTERFACE 30000 + +/******************************************************************************\ +********************************* Command Codes ******************************** +\******************************************************************************/ + +/******************************************************************************\ +********************* Command buffer information structure. ******************** +\******************************************************************************/ + +typedef struct _gcsCOMMAND_BUFFER_INFO * gcsCOMMAND_BUFFER_INFO_PTR; +typedef struct _gcsCOMMAND_BUFFER_INFO +{ + /* FE command buffer interrupt ID. */ + gctINT32 feBufferInt; + + /* TS overflow interrupt ID. */ + gctINT32 tsOverflowInt; + + /* Alignment and mask for the buffer address. */ + gctUINT addressMask; + gctUINT32 addressAlignment; + + /* Alignment for each command. */ + gctUINT32 commandAlignment; + + /* Number of bytes required by the STATE command. */ + gctUINT32 stateCommandSize; + + /* Number of bytes required by the RESTART command. */ + gctUINT32 restartCommandSize; + + /* Number of bytes required by the FETCH command. */ + gctUINT32 fetchCommandSize; + + /* Number of bytes required by the CALL command. */ + gctUINT32 callCommandSize; + + /* Number of bytes required by the RETURN command. */ + gctUINT32 returnCommandSize; + + /* Number of bytes required by the EVENT command. */ + gctUINT32 eventCommandSize; + + /* Number of bytes required by the END command. */ + gctUINT32 endCommandSize; + + /* Number of bytes reserved at the tail of a static command buffer. */ + gctUINT32 staticTailSize; + + /* Number of bytes reserved at the tail of a dynamic command buffer. */ + gctUINT32 dynamicTailSize; +} +gcsCOMMAND_BUFFER_INFO; + +/******************************************************************************\ +******************************** Task Structures ******************************* +\******************************************************************************/ + +typedef enum _gceTASK +{ + gcvTASK_LINK, + gcvTASK_CLUSTER, + gcvTASK_INCREMENT, + gcvTASK_DECREMENT, + gcvTASK_SIGNAL, + gcvTASK_LOCKDOWN, + gcvTASK_UNLOCK_VIDEO_MEMORY, + gcvTASK_FREE_VIDEO_MEMORY, + gcvTASK_FREE_CONTIGUOUS_MEMORY, + gcvTASK_UNMAP_USER_MEMORY +} +gceTASK; + +typedef struct _gcsTASK_HEADER * gcsTASK_HEADER_PTR; +typedef struct _gcsTASK_HEADER +{ + /* Task ID. */ + IN gceTASK id; +} +gcsTASK_HEADER; + +typedef struct _gcsTASK_LINK * gcsTASK_LINK_PTR; +typedef struct _gcsTASK_LINK +{ + /* Task ID (gcvTASK_LINK). */ + IN gceTASK id; + + /* Pointer to the next task container. */ + IN gctPOINTER cotainer; + + /* Pointer to the next task from the next task container. */ + IN gcsTASK_HEADER_PTR task; +} +gcsTASK_LINK; + +typedef struct _gcsTASK_CLUSTER * gcsTASK_CLUSTER_PTR; +typedef struct _gcsTASK_CLUSTER +{ + /* Task ID (gcvTASK_CLUSTER). */ + IN gceTASK id; + + /* Number of tasks in the cluster. */ + IN gctUINT taskCount; +} +gcsTASK_CLUSTER; + +typedef struct _gcsTASK_INCREMENT * gcsTASK_INCREMENT_PTR; +typedef struct _gcsTASK_INCREMENT +{ + /* Task ID (gcvTASK_INCREMENT). */ + IN gceTASK id; + + /* Address of the variable to increment. */ + IN gctUINT32 address; +} +gcsTASK_INCREMENT; + +typedef struct _gcsTASK_DECREMENT * gcsTASK_DECREMENT_PTR; +typedef struct _gcsTASK_DECREMENT +{ + /* Task ID (gcvTASK_DECREMENT). */ + IN gceTASK id; + + /* Address of the variable to decrement. */ + IN gctUINT32 address; +} +gcsTASK_DECREMENT; + +typedef struct _gcsTASK_SIGNAL * gcsTASK_SIGNAL_PTR; +typedef struct _gcsTASK_SIGNAL +{ + /* Task ID (gcvTASK_SIGNAL). */ + IN gceTASK id; + + /* Process owning the signal. */ + IN gctHANDLE process; + + /* Signal handle to signal. */ + IN gctSIGNAL signal; + +#if defined(__QNXNTO__) + IN gctINT32 coid; + IN gctINT32 rcvid; +#endif +} +gcsTASK_SIGNAL; + +typedef struct _gcsTASK_LOCKDOWN * gcsTASK_LOCKDOWN_PTR; +typedef struct _gcsTASK_LOCKDOWN +{ + /* Task ID (gcvTASK_LOCKDOWN). */ + IN gceTASK id; + + /* Address of the user space counter. */ + IN gctUINT32 userCounter; + + /* Address of the kernel space counter. */ + IN gctUINT32 kernelCounter; + + /* Process owning the signal. */ + IN gctHANDLE process; + + /* Signal handle to signal. */ + IN gctSIGNAL signal; +} +gcsTASK_LOCKDOWN; + +typedef struct _gcsTASK_UNLOCK_VIDEO_MEMORY * gcsTASK_UNLOCK_VIDEO_MEMORY_PTR; +typedef struct _gcsTASK_UNLOCK_VIDEO_MEMORY +{ + /* Task ID (gcvTASK_UNLOCK_VIDEO_MEMORY). */ + IN gceTASK id; + + /* Allocated video memory. */ + IN gctUINT64 node; +} +gcsTASK_UNLOCK_VIDEO_MEMORY; + +typedef struct _gcsTASK_FREE_VIDEO_MEMORY * gcsTASK_FREE_VIDEO_MEMORY_PTR; +typedef struct _gcsTASK_FREE_VIDEO_MEMORY +{ + /* Task ID (gcvTASK_FREE_VIDEO_MEMORY). */ + IN gceTASK id; + + /* Allocated video memory. */ + IN gctUINT32 node; +} +gcsTASK_FREE_VIDEO_MEMORY; + +typedef struct _gcsTASK_FREE_CONTIGUOUS_MEMORY * gcsTASK_FREE_CONTIGUOUS_MEMORY_PTR; +typedef struct _gcsTASK_FREE_CONTIGUOUS_MEMORY +{ + /* Task ID (gcvTASK_FREE_CONTIGUOUS_MEMORY). */ + IN gceTASK id; + + /* Number of bytes allocated. */ + IN gctSIZE_T bytes; + + /* Physical address of allocation. */ + IN gctPHYS_ADDR physical; + + /* Logical address of allocation. */ + IN gctPOINTER logical; +} +gcsTASK_FREE_CONTIGUOUS_MEMORY; + +typedef struct _gcsTASK_UNMAP_USER_MEMORY * gcsTASK_UNMAP_USER_MEMORY_PTR; +typedef struct _gcsTASK_UNMAP_USER_MEMORY +{ + /* Task ID (gcvTASK_UNMAP_USER_MEMORY). */ + IN gceTASK id; + + /* Base address of user memory to unmap. */ + IN gctPOINTER memory; + + /* Size of user memory in bytes to unmap. */ + IN gctSIZE_T size; + + /* Info record returned by gcvHAL_MAP_USER_MEMORY. */ + IN gctPOINTER info; + + /* Physical address of mapped memory as returned by + gcvHAL_MAP_USER_MEMORY. */ + IN gctUINT32 address; +} +gcsTASK_UNMAP_USER_MEMORY; + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_driver_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_dump.h b/drivers/gpu/galcore/inc/gc_hal_dump.h new file mode 100644 index 00000000000000..3f5dfac5301206 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_dump.h @@ -0,0 +1,89 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_dump_h_ +#define __gc_hal_dump_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** FILE LAYOUT: +** +** gcsDUMP_FILE structure +** +** gcsDUMP_DATA frame +** gcsDUMP_DATA or gcDUMP_DATA_SIZE records rendingring the frame +** gctUINT8 data[length] +*/ + +#define gcvDUMP_FILE_SIGNATURE gcmCC('g','c','D','B') + +typedef struct _gcsDUMP_FILE +{ + gctUINT32 signature; /* File signature */ + gctSIZE_T length; /* Length of file */ + gctUINT32 frames; /* Number of frames in file */ +} +gcsDUMP_FILE; + +typedef enum _gceDUMP_TAG +{ + gcvTAG_SURFACE = gcmCC('s','u','r','f'), + gcvTAG_FRAME = gcmCC('f','r','m',' '), + gcvTAG_COMMAND = gcmCC('c','m','d',' '), + gcvTAG_INDEX = gcmCC('i','n','d','x'), + gcvTAG_STREAM = gcmCC('s','t','r','m'), + gcvTAG_TEXTURE = gcmCC('t','e','x','t'), + gcvTAG_RENDER_TARGET = gcmCC('r','n','d','r'), + gcvTAG_DEPTH = gcmCC('z','b','u','f'), + gcvTAG_RESOLVE = gcmCC('r','s','l','v'), + gcvTAG_DELETE = gcmCC('d','e','l',' '), + gcvTAG_BUFOBJ = gcmCC('b','u','f','o'), +} +gceDUMP_TAG; + +typedef struct _gcsDUMP_SURFACE +{ + gceDUMP_TAG type; /* Type of record. */ + gctUINT32 address; /* Address of the surface. */ + gctINT16 width; /* Width of surface. */ + gctINT16 height; /* Height of surface. */ + gceSURF_FORMAT format; /* Surface pixel format. */ + gctSIZE_T length; /* Number of bytes inside the surface. */ +} +gcsDUMP_SURFACE; + +typedef struct _gcsDUMP_DATA +{ + gceDUMP_TAG type; /* Type of record. */ + gctSIZE_T length; /* Number of bytes of data. */ + gctUINT32 address; /* Address for the data. */ +} +gcsDUMP_DATA; + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_dump_h_ */ + diff --git a/drivers/gpu/galcore/inc/gc_hal_eglplatform.h b/drivers/gpu/galcore/inc/gc_hal_eglplatform.h new file mode 100644 index 00000000000000..1f35873601d234 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_eglplatform.h @@ -0,0 +1,672 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_eglplatform_h_ +#define __gc_hal_eglplatform_h_ + +/* Include VDK types. */ +#include "gc_hal_types.h" +#include "gc_hal_base.h" +#include "gc_hal_eglplatform_type.h" +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +/* Win32 and Windows CE platforms. */ +#include +typedef HDC HALNativeDisplayType; +typedef HWND HALNativeWindowType; +typedef HBITMAP HALNativePixmapType; + +typedef struct __BITFIELDINFO{ + BITMAPINFO bmi; + RGBQUAD bmiColors[2]; +} BITFIELDINFO; + +#elif defined(LINUX) && defined(EGL_API_DFB) && !defined(__APPLE__) +#include +typedef struct _DFBDisplay * HALNativeDisplayType; +typedef struct _DFBWindow * HALNativeWindowType; +typedef struct _DFBPixmap * HALNativePixmapType; + +#elif defined(LINUX) && defined(EGL_API_FB) && !defined(__APPLE__) + +#if defined(EGL_API_WL) + +#if defined(__GNUC__) +# define inline __inline__ /* GNU keyword. */ +#endif + +/* Wayland platform. */ +#include + +#define WL_EGL_NUM_BACKBUFFERS 3 + +typedef struct _gcsWL_VIV_BUFFER +{ + struct wl_resource *wl_buffer; + gcoSURF surface; + gctINT32 width, height; +} gcsWL_VIV_BUFFER; + +typedef struct _gcsWL_EGL_DISPLAY +{ + struct wl_display* wl_display; + struct wl_viv* wl_viv; + struct wl_registry *registry; + struct wl_event_queue *wl_queue; + gctINT swapInterval; +} gcsWL_EGL_DISPLAY; + +typedef struct _gcsWL_EGL_BUFFER_INFO +{ + gctINT32 width; + gctINT32 height; + gctINT32 stride; + gceSURF_FORMAT format; + gcuVIDMEM_NODE_PTR node; + gcePOOL pool; + gctUINT bytes; + gcoSURF surface; + gcoSURF attached_surface; + gctINT32 invalidate; + gctBOOL locked; +} gcsWL_EGL_BUFFER_INFO; + +typedef struct _gcsWL_EGL_BUFFER +{ + struct wl_buffer* wl_buffer; + gcsWL_EGL_BUFFER_INFO info; +} gcsWL_EGL_BUFFER; + +typedef struct _gcsWL_EGL_WINDOW_INFO +{ + gctINT32 dx; + gctINT32 dy; + gctUINT width; + gctUINT height; + gctINT32 attached_width; + gctINT32 attached_height; + gceSURF_FORMAT format; + gctUINT bpp; +} gcsWL_EGL_WINDOW_INFO; + +struct wl_egl_window +{ + gcsWL_EGL_DISPLAY* display; + gcsWL_EGL_BUFFER backbuffers[WL_EGL_NUM_BACKBUFFERS]; + gcsWL_EGL_WINDOW_INFO info; + gctUINT current; + struct wl_surface* surface; + struct wl_callback* frame_callback; +}; + +typedef void* HALNativeDisplayType; +typedef void* HALNativeWindowType; +typedef void* HALNativePixmapType; +#else +/* Linux platform for FBDEV. */ +typedef struct _FBDisplay * HALNativeDisplayType; +typedef struct _FBWindow * HALNativeWindowType; +typedef struct _FBPixmap * HALNativePixmapType; +#endif +#elif defined(__ANDROID__) || defined(ANDROID) + +struct egl_native_pixmap_t; + +#if ANDROID_SDK_VERSION >= 9 + #include + + typedef struct ANativeWindow* HALNativeWindowType; + typedef struct egl_native_pixmap_t* HALNativePixmapType; + typedef void* HALNativeDisplayType; +#else + struct android_native_window_t; + typedef struct android_native_window_t* HALNativeWindowType; + typedef struct egl_native_pixmap_t * HALNativePixmapType; + typedef void* HALNativeDisplayType; +#endif + +#elif defined(LINUX) || defined(__APPLE__) +/* X11 platform. */ +#include +#include + +typedef Display * HALNativeDisplayType; +typedef Window HALNativeWindowType; + +#ifdef CUSTOM_PIXMAP +typedef void * HALNativePixmapType; +#else +typedef Pixmap HALNativePixmapType; +#endif /* CUSTOM_PIXMAP */ + +/* Rename some badly named X defines. */ +#ifdef Status +# define XStatus int +# undef Status +#endif +#ifdef Always +# define XAlways 2 +# undef Always +#endif +#ifdef CurrentTime +# undef CurrentTime +# define XCurrentTime 0 +#endif + +#elif defined(__QNXNTO__) +#include + +/* VOID */ +typedef int HALNativeDisplayType; +typedef screen_window_t HALNativeWindowType; +typedef screen_pixmap_t HALNativePixmapType; + +#else + +#error "Platform not recognized" + +/* VOID */ +typedef void * HALNativeDisplayType; +typedef void * HALNativeWindowType; +typedef void * HALNativePixmapType; + +#endif + +/* define DUMMY according to the system */ +#if defined(EGL_API_WL) +# define WL_DUMMY (31415926) +# define EGL_DUMMY WL_DUMMY +#elif defined(__ANDROID__) || defined(ANDROID) +# define ANDROID_DUMMY (31415926) +# define EGL_DUMMY ANDROID_DUMMY +#else +# define EGL_DUMMY (31415926) +#endif + +/******************************************************************************* +** Display. ******************************************************************** +*/ + +gceSTATUS +gcoOS_GetDisplay( + OUT HALNativeDisplayType * Display, + IN gctPOINTER Context + ); + +gceSTATUS +gcoOS_GetDisplayByIndex( + IN gctINT DisplayIndex, + OUT HALNativeDisplayType * Display, + IN gctPOINTER Context + ); + +gceSTATUS +gcoOS_GetDisplayInfo( + IN HALNativeDisplayType Display, + OUT gctINT * Width, + OUT gctINT * Height, + OUT gctSIZE_T * Physical, + OUT gctINT * Stride, + OUT gctINT * BitsPerPixel + ); + + + +gceSTATUS +gcoOS_GetDisplayInfoEx( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctUINT DisplayInfoSize, + OUT halDISPLAY_INFO * DisplayInfo + ); + +gceSTATUS +gcoOS_GetNextDisplayInfoExByIndex( + IN gctINT Index, + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctUINT DisplayInfoSize, + OUT halDISPLAY_INFO * DisplayInfo + ); + +gceSTATUS +gcoOS_GetDisplayVirtual( + IN HALNativeDisplayType Display, + OUT gctINT * Width, + OUT gctINT * Height + ); + +gceSTATUS +gcoOS_GetDisplayBackbuffer( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + OUT gctPOINTER * context, + OUT gcoSURF * surface, + OUT gctUINT * Offset, + OUT gctINT * X, + OUT gctINT * Y + ); + +gceSTATUS +gcoOS_SetDisplayVirtual( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctUINT Offset, + IN gctINT X, + IN gctINT Y + ); + +gceSTATUS +gcoOS_SetDisplayVirtualEx( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctPOINTER Context, + IN gcoSURF Surface, + IN gctUINT Offset, + IN gctINT X, + IN gctINT Y + ); + +gceSTATUS +gcoOS_SetSwapInterval( + IN HALNativeDisplayType Display, + IN gctINT Interval +); + +gceSTATUS +gcoOS_SetSwapIntervalEx( + IN HALNativeDisplayType Display, + IN gctINT Interval, + IN gctPOINTER localDisplay); + +gceSTATUS +gcoOS_GetSwapInterval( + IN HALNativeDisplayType Display, + IN gctINT_PTR Min, + IN gctINT_PTR Max +); + +gceSTATUS +gcoOS_DisplayBufferRegions( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctINT NumRects, + IN gctINT_PTR Rects + ); + +gceSTATUS +gcoOS_DestroyDisplay( + IN HALNativeDisplayType Display + ); + +gceSTATUS +gcoOS_InitLocalDisplayInfo( + IN HALNativeDisplayType Display, + IN OUT gctPOINTER * localDisplay + ); + +gceSTATUS +gcoOS_DeinitLocalDisplayInfo( + IN HALNativeDisplayType Display, + IN OUT gctPOINTER * localDisplay + ); + +gceSTATUS +gcoOS_GetDisplayInfoEx2( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctPOINTER localDisplay, + IN gctUINT DisplayInfoSize, + OUT halDISPLAY_INFO * DisplayInfo + ); + +gceSTATUS +gcoOS_GetDisplayBackbufferEx( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctPOINTER localDisplay, + OUT gctPOINTER * context, + OUT gcoSURF * surface, + OUT gctUINT * Offset, + OUT gctINT * X, + OUT gctINT * Y + ); + +gceSTATUS +gcoOS_IsValidDisplay( + IN HALNativeDisplayType Display + ); + +gceSTATUS +gcoOS_GetNativeVisualId( + IN HALNativeDisplayType Display, + OUT gctINT* nativeVisualId + ); + +gctBOOL +gcoOS_SynchronousFlip( + IN HALNativeDisplayType Display + ); + +/******************************************************************************* +** Windows. ******************************************************************** +*/ + +gceSTATUS +gcoOS_CreateWindow( + IN HALNativeDisplayType Display, + IN gctINT X, + IN gctINT Y, + IN gctINT Width, + IN gctINT Height, + OUT HALNativeWindowType * Window + ); + +gceSTATUS +gcoOS_GetWindowInfo( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + OUT gctINT * X, + OUT gctINT * Y, + OUT gctINT * Width, + OUT gctINT * Height, + OUT gctINT * BitsPerPixel, + OUT gctUINT * Offset + ); + +gceSTATUS +gcoOS_DestroyWindow( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window + ); + +gceSTATUS +gcoOS_DrawImage( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctINT Left, + IN gctINT Top, + IN gctINT Right, + IN gctINT Bottom, + IN gctINT Width, + IN gctINT Height, + IN gctINT BitsPerPixel, + IN gctPOINTER Bits + ); + +gceSTATUS +gcoOS_GetImage( + IN HALNativeWindowType Window, + IN gctINT Left, + IN gctINT Top, + IN gctINT Right, + IN gctINT Bottom, + OUT gctINT * BitsPerPixel, + OUT gctPOINTER * Bits + ); + +gceSTATUS +gcoOS_GetWindowInfoEx( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + OUT gctINT * X, + OUT gctINT * Y, + OUT gctINT * Width, + OUT gctINT * Height, + OUT gctINT * BitsPerPixel, + OUT gctUINT * Offset, + OUT gceSURF_FORMAT * Format + ); + +gceSTATUS +gcoOS_DrawImageEx( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctINT Left, + IN gctINT Top, + IN gctINT Right, + IN gctINT Bottom, + IN gctINT Width, + IN gctINT Height, + IN gctINT BitsPerPixel, + IN gctPOINTER Bits, + IN gceSURF_FORMAT Format + ); + +/******************************************************************************* +** Pixmaps. ******************************************************************** +*/ + +gceSTATUS +gcoOS_CreatePixmap( + IN HALNativeDisplayType Display, + IN gctINT Width, + IN gctINT Height, + IN gctINT BitsPerPixel, + OUT HALNativePixmapType * Pixmap + ); + +gceSTATUS +gcoOS_GetPixmapInfo( + IN HALNativeDisplayType Display, + IN HALNativePixmapType Pixmap, + OUT gctINT * Width, + OUT gctINT * Height, + OUT gctINT * BitsPerPixel, + OUT gctINT * Stride, + OUT gctPOINTER * Bits + ); + +gceSTATUS +gcoOS_DrawPixmap( + IN HALNativeDisplayType Display, + IN HALNativePixmapType Pixmap, + IN gctINT Left, + IN gctINT Top, + IN gctINT Right, + IN gctINT Bottom, + IN gctINT Width, + IN gctINT Height, + IN gctINT BitsPerPixel, + IN gctPOINTER Bits + ); + +gceSTATUS +gcoOS_DestroyPixmap( + IN HALNativeDisplayType Display, + IN HALNativePixmapType Pixmap + ); + +gceSTATUS +gcoOS_GetPixmapInfoEx( + IN HALNativeDisplayType Display, + IN HALNativePixmapType Pixmap, + OUT gctINT * Width, + OUT gctINT * Height, + OUT gctINT * BitsPerPixel, + OUT gctINT * Stride, + OUT gctPOINTER * Bits, + OUT gceSURF_FORMAT * Format + ); + +gceSTATUS +gcoOS_CopyPixmapBits( + IN HALNativeDisplayType Display, + IN HALNativePixmapType Pixmap, + IN gctUINT DstWidth, + IN gctUINT DstHeight, + IN gctINT DstStride, + IN gceSURF_FORMAT DstFormat, + OUT gctPOINTER DstBits + ); + +/******************************************************************************* +** OS relative. **************************************************************** +*/ +gceSTATUS +gcoOS_LoadEGLLibrary( + OUT gctHANDLE * Handle + ); + +gceSTATUS +gcoOS_FreeEGLLibrary( + IN gctHANDLE Handle + ); + +gceSTATUS +gcoOS_ShowWindow( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window + ); + +gceSTATUS +gcoOS_HideWindow( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window + ); + +gceSTATUS +gcoOS_SetWindowTitle( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + IN gctCONST_STRING Title + ); + +gceSTATUS +gcoOS_CapturePointer( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window + ); + +gceSTATUS +gcoOS_GetEvent( + IN HALNativeDisplayType Display, + IN HALNativeWindowType Window, + OUT halEvent * Event + ); + +gceSTATUS +gcoOS_CreateClientBuffer( + IN gctINT Width, + IN gctINT Height, + IN gctINT Format, + IN gctINT Type, + OUT gctPOINTER * ClientBuffer + ); + +gceSTATUS +gcoOS_GetClientBufferInfo( + IN gctPOINTER ClientBuffer, + OUT gctINT * Width, + OUT gctINT * Height, + OUT gctINT * Stride, + OUT gctPOINTER * Bits + ); + +gceSTATUS +gcoOS_DestroyClientBuffer( + IN gctPOINTER ClientBuffer + ); + +gceSTATUS +gcoOS_DestroyContext( + IN gctPOINTER Display, + IN gctPOINTER Context + ); + +gceSTATUS +gcoOS_CreateContext( + IN gctPOINTER LocalDisplay, + IN gctPOINTER Context + ); + +gceSTATUS +gcoOS_MakeCurrent( + IN gctPOINTER LocalDisplay, + IN HALNativeWindowType DrawDrawable, + IN HALNativeWindowType ReadDrawable, + IN gctPOINTER Context, + IN gcoSURF ResolveTarget + ); + +gceSTATUS +gcoOS_CreateDrawable( + IN gctPOINTER LocalDisplay, + IN HALNativeWindowType Drawable + ); + +gceSTATUS +gcoOS_DestroyDrawable( + IN gctPOINTER LocalDisplay, + IN HALNativeWindowType Drawable + ); +gceSTATUS +gcoOS_SwapBuffers( + IN gctPOINTER LocalDisplay, + IN HALNativeWindowType Drawable, + IN gcoSURF RenderTarget, + IN gcoSURF ResolveTarget, + IN gctPOINTER ResolveBits, + OUT gctUINT *Width, + OUT gctUINT *Height + ); + +#ifdef EGL_API_DRI +gceSTATUS +gcoOS_ResizeWindow( + IN gctPOINTER localDisplay, + IN HALNativeWindowType Drawable, + IN gctUINT Width, + IN gctUINT Height) + ; + +#ifdef USE_FREESCALE_EGL_ACCEL +gceSTATUS +gcoOS_SwapBuffersGeneric_Async( + IN gctPOINTER localDisplay, + IN HALNativeWindowType Drawable, + IN gcoSURF RenderTarget, + IN gcoSURF ResolveTarget, + IN gctPOINTER ResolveBits, + OUT gctUINT *Width, + OUT gctUINT *Height, + IN void * resolveRect + ); + +gceSTATUS +gcoOS_DrawSurface( + IN gctPOINTER localDisplay, + IN HALNativeWindowType Drawable + ); +#endif + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_eglplatform_h_ */ + diff --git a/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h b/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h new file mode 100644 index 00000000000000..10da3af527de20 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h @@ -0,0 +1,286 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_eglplatform_type_h_ +#define __gc_hal_eglplatform_type_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* +** Events. ********************************************************************* +*/ + +typedef enum _halEventType +{ + /* Keyboard event. */ + HAL_KEYBOARD, + + /* Mouse move event. */ + HAL_POINTER, + + /* Mouse button event. */ + HAL_BUTTON, + + /* Application close event. */ + HAL_CLOSE, + + /* Application window has been updated. */ + HAL_WINDOW_UPDATE +} +halEventType; + +/* Scancodes for keyboard. */ +typedef enum _halKeys +{ + HAL_UNKNOWN = -1, + + HAL_BACKSPACE = 0x08, + HAL_TAB, + HAL_ENTER = 0x0D, + HAL_ESCAPE = 0x1B, + + HAL_SPACE = 0x20, + HAL_SINGLEQUOTE = 0x27, + HAL_PAD_ASTERISK = 0x2A, + HAL_COMMA = 0x2C, + HAL_HYPHEN, + HAL_PERIOD, + HAL_SLASH, + HAL_0, + HAL_1, + HAL_2, + HAL_3, + HAL_4, + HAL_5, + HAL_6, + HAL_7, + HAL_8, + HAL_9, + HAL_SEMICOLON = 0x3B, + HAL_EQUAL = 0x3D, + HAL_A = 0x41, + HAL_B, + HAL_C, + HAL_D, + HAL_E, + HAL_F, + HAL_G, + HAL_H, + HAL_I, + HAL_J, + HAL_K, + HAL_L, + HAL_M, + HAL_N, + HAL_O, + HAL_P, + HAL_Q, + HAL_R, + HAL_S, + HAL_T, + HAL_U, + HAL_V, + HAL_W, + HAL_X, + HAL_Y, + HAL_Z, + HAL_LBRACKET, + HAL_BACKSLASH, + HAL_RBRACKET, + HAL_BACKQUOTE = 0x60, + + HAL_F1 = 0x80, + HAL_F2, + HAL_F3, + HAL_F4, + HAL_F5, + HAL_F6, + HAL_F7, + HAL_F8, + HAL_F9, + HAL_F10, + HAL_F11, + HAL_F12, + + HAL_LCTRL, + HAL_RCTRL, + HAL_LSHIFT, + HAL_RSHIFT, + HAL_LALT, + HAL_RALT, + HAL_CAPSLOCK, + HAL_NUMLOCK, + HAL_SCROLLLOCK, + HAL_PAD_0, + HAL_PAD_1, + HAL_PAD_2, + HAL_PAD_3, + HAL_PAD_4, + HAL_PAD_5, + HAL_PAD_6, + HAL_PAD_7, + HAL_PAD_8, + HAL_PAD_9, + HAL_PAD_HYPHEN, + HAL_PAD_PLUS, + HAL_PAD_SLASH, + HAL_PAD_PERIOD, + HAL_PAD_ENTER, + HAL_SYSRQ, + HAL_PRNTSCRN, + HAL_BREAK, + HAL_UP, + HAL_LEFT, + HAL_RIGHT, + HAL_DOWN, + HAL_HOME, + HAL_END, + HAL_PGUP, + HAL_PGDN, + HAL_INSERT, + HAL_DELETE, + HAL_LWINDOW, + HAL_RWINDOW, + HAL_MENU, + HAL_POWER, + HAL_SLEEP, + HAL_WAKE +} +halKeys; + +/* Structure that defined keyboard mapping. */ +typedef struct _halKeyMap +{ + /* Normal key. */ + halKeys normal; + + /* Extended key. */ + halKeys extended; +} +halKeyMap; + +/* Event structure. */ +typedef struct _halEvent +{ + /* Event type. */ + halEventType type; + + /* Event data union. */ + union _halEventData + { + /* Event data for keyboard. */ + struct _halKeyboard + { + /* Scancode. */ + halKeys scancode; + + /* ASCII characte of the key pressed. */ + char key; + + /* Flag whether the key was pressed (1) or released (0). */ + char pressed; + } + keyboard; + + /* Event data for pointer. */ + struct _halPointer + { + /* Current pointer coordinate. */ + int x; + int y; + } + pointer; + + /* Event data for mouse buttons. */ + struct _halButton + { + /* Left button state. */ + int left; + + /* Middle button state. */ + int middle; + + /* Right button state. */ + int right; + + /* Current pointer coordinate. */ + int x; + int y; + } + button; + } + data; +} +halEvent; + +/* VFK_DISPLAY_INFO structure defining information returned by + vdkGetDisplayInfoEx. */ +typedef struct _halDISPLAY_INFO +{ + /* The size of the display in pixels. */ + int width; + int height; + + /* The stride of the dispay. -1 is returned if the stride is not known + ** for the specified display.*/ + int stride; + + /* The color depth of the display in bits per pixel. */ + int bitsPerPixel; + + /* The logical pointer to the display memory buffer. NULL is returned + ** if the pointer is not known for the specified display. */ + void * logical; + + /* The physical address of the display memory buffer. ~0 is returned + ** if the address is not known for the specified display. */ + unsigned long physical; + + int wrapFB; /* true if compositor, false otherwise. */ + +#ifndef __QNXNTO__ + /* 355_FB_MULTI_BUFFER */ + int multiBuffer; + int backBufferY; +#endif + + /* The color info of the display. */ + unsigned int alphaLength; + unsigned int alphaOffset; + unsigned int redLength; + unsigned int redOffset; + unsigned int greenLength; + unsigned int greenOffset; + unsigned int blueLength; + unsigned int blueOffset; + + /* Display flip support. */ + int flip; +} +halDISPLAY_INFO; + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_eglplatform_type_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_engine.h b/drivers/gpu/galcore/inc/gc_hal_engine.h new file mode 100644 index 00000000000000..700f7eb6d76a91 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_engine.h @@ -0,0 +1,2587 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_engine_h_ +#define __gc_hal_engine_h_ + +#include "gc_hal_types.h" +#include "gc_hal_enum.h" + +#if gcdENABLE_3D +#if gcdENABLE_VG +#include "gc_hal_engine_vg.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +****************************** Object Declarations ***************************** +\******************************************************************************/ + +typedef struct _gcoSTREAM * gcoSTREAM; +typedef struct _gcoVERTEX * gcoVERTEX; +typedef struct _gcoTEXTURE * gcoTEXTURE; +typedef struct _gcoINDEX * gcoINDEX; +typedef struct _gcsVERTEX_ATTRIBUTES * gcsVERTEX_ATTRIBUTES_PTR; +typedef struct _gcoVERTEXARRAY * gcoVERTEXARRAY; +typedef struct _gcoBUFOBJ * gcoBUFOBJ; + +#define gcdATTRIBUTE_COUNT 16 + +typedef enum _gcePROGRAM_STAGE +{ + gcvPROGRAM_STAGE_VERTEX = 0x0, + gcvPROGRAM_STAGE_TES = 0x1, + gcvPROGRAM_STAGE_TCS = 0x2, + gcvPROGRAM_STAGE_GEOMETRY = 0x3, + gcvPROGRAM_STAGE_FRAGMENT = 0x4, + gcvPROGRAM_STAGE_COMPUTE = 0x5, + gcvPROGRAM_STAGE_OPENCL = 0x6, + gcvPROGRAM_STAGE_LAST +} +gcePROGRAM_STAGE; + +typedef enum _gcePROGRAM_STAGE_BIT +{ + gcvPROGRAM_STAGE_VERTEX_BIT = 1 << gcvPROGRAM_STAGE_VERTEX, + gcvPROGRAM_STAGE_TES_BIT = 1 << gcvPROGRAM_STAGE_TES, + gcvPROGRAM_STAGE_TCS_BIT = 1 << gcvPROGRAM_STAGE_TCS, + gcvPROGRAM_STAGE_GEOMETRY_BIT = 1 << gcvPROGRAM_STAGE_GEOMETRY, + gcvPROGRAM_STAGE_FRAGMENT_BIT = 1 << gcvPROGRAM_STAGE_FRAGMENT, + gcvPROGRAM_STAGE_COMPUTE_BIT = 1 << gcvPROGRAM_STAGE_COMPUTE, + gcvPROGRAM_STAGE_OPENCL_BIT = 1 << gcvPROGRAM_STAGE_OPENCL, +} +gcePROGRAM_STAGE_BIT; + + +/******************************************************************************\ +********************************* gcoHAL Object ********************************* +\******************************************************************************/ + +gceSTATUS +gcoHAL_QueryShaderCaps( + IN gcoHAL Hal, + OUT gctUINT * VertexUniforms, + OUT gctUINT * FragmentUniforms, + OUT gctUINT * Varyings + ); + +gceSTATUS +gcoHAL_QueryShaderCapsEx( + IN gcoHAL Hal, + OUT gctUINT * ShaderCoreCount, + OUT gctUINT * ThreadCount, + OUT gctUINT * VertexInstructionCount, + OUT gctUINT * FragmentInstructionCount + ); + +gceSTATUS +gcoHAL_QuerySamplerBase( + IN gcoHAL Hal, + OUT gctUINT32 * VertexCount, + OUT gctINT_PTR VertexBase, + OUT gctUINT32 * FragmentCount, + OUT gctINT_PTR FragmentBase + ); + +gceSTATUS +gcoHAL_QueryUniformBase( + IN gcoHAL Hal, + OUT gctUINT32 * VertexBase, + OUT gctUINT32 * FragmentBase + ); + +gceSTATUS +gcoHAL_QueryTextureCaps( + IN gcoHAL Hal, + OUT gctUINT * MaxWidth, + OUT gctUINT * MaxHeight, + OUT gctUINT * MaxDepth, + OUT gctBOOL * Cubic, + OUT gctBOOL * NonPowerOfTwo, + OUT gctUINT * VertexSamplers, + OUT gctUINT * PixelSamplers + ); + +gceSTATUS +gcoHAL_QueryTextureMaxAniso( + IN gcoHAL Hal, + OUT gctUINT * MaxAnisoValue + ); + +gceSTATUS +gcoHAL_QueryStreamCaps( + IN gcoHAL Hal, + OUT gctUINT32 * MaxAttributes, + OUT gctUINT32 * MaxStreamSize, + OUT gctUINT32 * NumberOfStreams, + OUT gctUINT32 * Alignment + ); + +/******************************************************************************\ +********************************* gcoSURF Object ******************************** +\******************************************************************************/ + +/*----------------------------------------------------------------------------*/ +/*--------------------------------- gcoSURF 3D --------------------------------*/ +typedef enum _gceBLIT_FLAG +{ + gcvBLIT_FLAG_SKIP_DEPTH_WRITE = 0x1, + gcvBLIT_FLAG_SKIP_STENCIL_WRITE = 0x2, +} gceBLIT_FLAG; + +typedef struct _gcsSURF_BLIT_ARGS +{ + gcoSURF srcSurface; + gctINT srcX, srcY, srcZ; + gctINT srcWidth, srcHeight, srcDepth; + gcoSURF dstSurface; + gctINT dstX, dstY, dstZ; + gctINT dstWidth, dstHeight, dstDepth; + gctBOOL xReverse; + gctBOOL yReverse; + gctBOOL scissorTest; + gcsRECT scissor; + gctUINT flags; +} +gcsSURF_BLIT_ARGS; + + + + +/* Clear flags. */ +typedef enum _gceCLEAR +{ + gcvCLEAR_COLOR = 0x1, + gcvCLEAR_DEPTH = 0x2, + gcvCLEAR_STENCIL = 0x4, + gcvCLEAR_HZ = 0x8, + gcvCLEAR_HAS_VAA = 0x10, + gcvCLEAR_WITH_GPU_ONLY = 0x100, + gcvCLEAR_WITH_CPU_ONLY = 0x200, +} +gceCLEAR; + +typedef struct _gcsSURF_CLEAR_ARGS +{ + /* + ** Color to fill the color portion of the framebuffer when clear + ** is called. + */ + struct { + gcuVALUE r; + gcuVALUE g; + gcuVALUE b; + gcuVALUE a; + /* + ** Color has multiple value type so we must specify it. + */ + gceVALUE_TYPE valueType; + } color; + + gcuVALUE depth; + + gctUINT stencil; + + + + /* + ** stencil bit-wise mask + */ + gctUINT8 stencilMask; + /* + ** Depth Write Mask + */ + gctBOOL depthMask; + /* + ** 4-bit channel Mask: ABGR:MSB->LSB + */ + gctUINT8 colorMask; + /* + ** If ClearRect is NULL, it means full clear + */ + gcsRECT_PTR clearRect; + /* + ** clear flags + */ + gceCLEAR flags; + + /* + ** Offset in surface to cube/array/3D + */ + gctUINT32 offset; + +} gcsSURF_CLEAR_ARGS; + + +typedef gcsSURF_CLEAR_ARGS* gcsSURF_CLEAR_ARGS_PTR; + +typedef struct _gscSURF_BLITDRAW_BLIT +{ + gcoSURF srcSurface; + gcoSURF dstSurface; + gcsRECT srcRect; + gcsRECT dstRect; + gceTEXTURE_FILTER filterMode; + gctBOOL xReverse; + gctBOOL yReverse; + gctBOOL scissorEnabled; + gcsRECT scissor; +}gscSURF_BLITDRAW_BLIT; + + +typedef enum _gceBLITDRAW_TYPE +{ + gcvBLITDRAW_CLEAR = 0, + gcvBLITDRAW_BLIT = 1, + + /* last number, not a real type */ + gcvBLITDRAW_NUM_TYPE + } +gceBLITDRAW_TYPE; + + +typedef struct _gscSURF_BLITDRAW_ARGS +{ + /* always the fist member */ + gceHAL_ARG_VERSION version; + + union _gcsSURF_BLITDRAW_ARGS_UNION + { + struct _gscSURF_BLITDRAW_ARG_v1 + { + /* Whether it's clear or blit operation, can be extended. */ + gceBLITDRAW_TYPE type; + + union _gscSURF_BLITDRAW_UNION + { + gscSURF_BLITDRAW_BLIT blit; + + struct _gscSURF_BLITDRAW_CLEAR + { + gcsSURF_CLEAR_ARGS clearArgs; + gcoSURF rtSurface; + gcoSURF dsSurface; + } clear; + } u; + } v1; + } uArgs; +} +gcsSURF_BLITDRAW_ARGS; + + +typedef struct _gcsSURF_RESOLVE_ARGS +{ + gceHAL_ARG_VERSION version; + union _gcsSURF_RESOLVE_ARGS_UNION + { + struct _gcsSURF_RESOLVE_ARG_v1 + { + gctBOOL yInverted; + }v1; + } uArgs; +} +gcsSURF_RESOLVE_ARGS; + + +/* CPU Blit with format (including linear <-> tile) conversion*/ +gceSTATUS +gcoSURF_BlitCPU( + gcsSURF_BLIT_ARGS* args + ); + + +gceSTATUS +gcoSURF_BlitDraw( + IN gcsSURF_BLITDRAW_ARGS *args + ); +#endif /* gcdENABLE_3D */ + + + +#if gcdENABLE_3D +/* Clear surface function. */ +gceSTATUS +gcoSURF_Clear( + IN gcoSURF Surface, + IN gcsSURF_CLEAR_ARGS_PTR clearArg + ); + +/* Preserve pixels from source. */ +gceSTATUS +gcoSURF_Preserve( + IN gcoSURF Source, + IN gcoSURF Dest, + IN gcsRECT_PTR MaskRect + ); + + +/* TO BE REMOVED */ + gceSTATUS + depr_gcoSURF_Resolve( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gctUINT32 DestAddress, + IN gctPOINTER DestBits, + IN gctINT DestStride, + IN gceSURF_TYPE DestType, + IN gceSURF_FORMAT DestFormat, + IN gctUINT DestWidth, + IN gctUINT DestHeight + ); + + gceSTATUS + depr_gcoSURF_ResolveRect( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gctUINT32 DestAddress, + IN gctPOINTER DestBits, + IN gctINT DestStride, + IN gceSURF_TYPE DestType, + IN gceSURF_FORMAT DestFormat, + IN gctUINT DestWidth, + IN gctUINT DestHeight, + IN gcsPOINT_PTR SrcOrigin, + IN gcsPOINT_PTR DestOrigin, + IN gcsPOINT_PTR RectSize + ); + +/* Resample surface. */ +gceSTATUS +gcoSURF_Resample( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface + ); + +/* Resolve surface. */ +gceSTATUS +gcoSURF_Resolve( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface + ); + +gceSTATUS +gcoSURF_ResolveEx( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gcsSURF_RESOLVE_ARGS *args + ); + + +/* Resolve rectangular area of a surface. */ +gceSTATUS +gcoSURF_ResolveRect( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gcsPOINT_PTR SrcOrigin, + IN gcsPOINT_PTR DestOrigin, + IN gcsPOINT_PTR RectSize + ); + +/* Resolve rectangular area of a surface. */ +gceSTATUS +gcoSURF_ResolveRectEx( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gcsPOINT_PTR SrcOrigin, + IN gcsPOINT_PTR DestOrigin, + IN gcsPOINT_PTR RectSize, + IN gcsSURF_RESOLVE_ARGS *args + ); + + +gceSTATUS +gcoSURF_GetResolveAlignment( + IN gcoSURF Surface, + OUT gctUINT *originX, + OUT gctUINT *originY, + OUT gctUINT *sizeX, + OUT gctUINT *sizeY + ); + +gceSTATUS +gcoSURF_IsHWResolveable( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gcsPOINT_PTR SrcOrigin, + IN gcsPOINT_PTR DestOrigin, + IN gcsPOINT_PTR RectSize + ); + +/* Set surface resolvability. */ +gceSTATUS +gcoSURF_SetResolvability( + IN gcoSURF Surface, + IN gctBOOL Resolvable + ); + +gceSTATUS +gcoSURF_IsRenderable( + IN gcoSURF Surface + ); + +gceSTATUS +gcoSURF_IsFormatRenderableAsRT( + IN gcoSURF Surface + ); + +gceSTATUS +gcoSURF_GetFence( + IN gcoSURF Surface + ); + +gceSTATUS +gcoBUFOBJ_GetFence( + IN gcoBUFOBJ bufObj + ); + +gceSTATUS +gcoBUFOBJ_WaitFence( + IN gcoBUFOBJ bufObj + ); + +gceSTATUS +gcoBUFOBJ_IsFenceEnabled( + IN gcoBUFOBJ bufObj + ); + +gceSTATUS +gcoSURF_WaitFence( + IN gcoSURF Surface + ); + +gceSTATUS +gcoSTREAM_GetFence( + IN gcoSTREAM stream + ); + +gceSTATUS +gcoSTREAM_WaitFence( + IN gcoSTREAM stream + ); + +gceSTATUS +gcoINDEX_GetFence( + IN gcoINDEX index + ); + +gceSTATUS +gcoINDEX_WaitFence( + IN gcoINDEX index + ); + +gceSTATUS +gcoSURF_3DBlitClearRect( + IN gcoSURF Surface, + IN gcsSURF_CLEAR_ARGS_PTR ClearArgs + ); + + +gceSTATUS +gcoSURF_3DBlitBltRect( + IN gcoSURF SrcSurf, + IN gcoSURF DestSurf, + IN gcsPOINT_PTR SrcOrigin, + IN gcsPOINT_PTR DestOrigin, + IN gcsPOINT_PTR RectSize + ); + +gceSTATUS +gcoSURF_3DBlitCopy( + IN gctUINT32 SrcAddress, + IN gctUINT32 DestAddress, + IN gctUINT32 Bytes + ); + + +/******************************************************************************\ +******************************** gcoINDEX Object ******************************* +\******************************************************************************/ + +/* Construct a new gcoINDEX object. */ +gceSTATUS +gcoINDEX_Construct( + IN gcoHAL Hal, + OUT gcoINDEX * Index + ); + +/* Destroy a gcoINDEX object. */ +gceSTATUS +gcoINDEX_Destroy( + IN gcoINDEX Index + ); + +/* Lock index in memory. */ +gceSTATUS +gcoINDEX_Lock( + IN gcoINDEX Index, + OUT gctUINT32 * Address, + OUT gctPOINTER * Memory + ); + +/* Unlock index that was previously locked with gcoINDEX_Lock. */ +gceSTATUS +gcoINDEX_Unlock( + IN gcoINDEX Index + ); + +/* Upload index data into the memory. */ +gceSTATUS +gcoINDEX_Load( + IN gcoINDEX Index, + IN gceINDEX_TYPE IndexType, + IN gctUINT32 IndexCount, + IN gctPOINTER IndexBuffer + ); + +/* Bind an index object to the hardware. */ +gceSTATUS +gcoINDEX_Bind( + IN gcoINDEX Index, + IN gceINDEX_TYPE Type + ); + +/* Bind an index object to the hardware. */ +gceSTATUS +gcoINDEX_BindOffset( + IN gcoINDEX Index, + IN gceINDEX_TYPE Type, + IN gctUINT32 Offset + ); + +/* Free existing index buffer. */ +gceSTATUS +gcoINDEX_Free( + IN gcoINDEX Index + ); + +/* Upload data into an index buffer. */ +gceSTATUS +gcoINDEX_Upload( + IN gcoINDEX Index, + IN gctCONST_POINTER Buffer, + IN gctSIZE_T Bytes + ); + +/* Upload data into an index buffer starting at an offset. */ +gceSTATUS +gcoINDEX_UploadOffset( + IN gcoINDEX Index, + IN gctSIZE_T Offset, + IN gctCONST_POINTER Buffer, + IN gctSIZE_T Bytes + ); + +/*Merge index2 to index1 from 0, index2 must subset of inex1*/ +gceSTATUS +gcoINDEX_Merge( + IN gcoINDEX Index1, + IN gcoINDEX Index2 + ); + +/*check if index buffer is enough for this draw*/ +gctBOOL +gcoINDEX_CheckRange( + IN gcoINDEX Index, + IN gceINDEX_TYPE Type, + IN gctINT Count, + IN gctUINT32 Indices + ); + +/* Query the index capabilities. */ +gceSTATUS +gcoINDEX_QueryCaps( + OUT gctBOOL * Index8, + OUT gctBOOL * Index16, + OUT gctBOOL * Index32, + OUT gctUINT * MaxIndex + ); + +/* Determine the index range in the current index buffer. */ +gceSTATUS +gcoINDEX_GetIndexRange( + IN gcoINDEX Index, + IN gceINDEX_TYPE Type, + IN gctUINT32 Offset, + IN gctUINT32 Count, + OUT gctUINT32 * MinimumIndex, + OUT gctUINT32 * MaximumIndex + ); + +/* Dynamic buffer management. */ +gceSTATUS +gcoINDEX_SetDynamic( + IN gcoINDEX Index, + IN gctSIZE_T Bytes, + IN gctUINT Buffers + ); + +/******************************************************************************\ +********************************** gco3D Object ********************************* +\******************************************************************************/ + +/* Blending targets. */ +typedef enum _gceBLEND_UNIT +{ + gcvBLEND_SOURCE, + gcvBLEND_TARGET, +} +gceBLEND_UNIT; + +/* Construct a new gco3D object. */ +gceSTATUS +gco3D_Construct( + IN gcoHAL Hal, + OUT gco3D * Engine + ); + +/* Destroy an gco3D object. */ +gceSTATUS +gco3D_Destroy( + IN gco3D Engine + ); + +/* Set 3D API type. */ +gceSTATUS +gco3D_SetAPI( + IN gco3D Engine, + IN gceAPI ApiType + ); + +/* Get 3D API type. */ +gceSTATUS +gco3D_GetAPI( + IN gco3D Engine, + OUT gceAPI * ApiType + ); + +/* Set render target. */ +gceSTATUS +gco3D_SetTarget( + IN gco3D Engine, + IN gcoSURF Surface + ); + +/* Unset render target. */ +gceSTATUS +gco3D_UnsetTarget( + IN gco3D Engine, + IN gcoSURF Surface + ); + +gceSTATUS +gco3D_SetTargetEx( + IN gco3D Engine, + IN gctUINT32 TargetIndex, + IN gcoSURF Surface, + IN gctUINT32 LayerIndex + ); + +gceSTATUS +gco3D_UnsetTargetEx( + IN gco3D Engine, + IN gctUINT32 TargetIndex, + IN gcoSURF Surface + ); + +gceSTATUS +gco3D_SetTargetOffsetEx( + IN gco3D Engine, + IN gctUINT32 TargetIndex, + IN gctSIZE_T Offset + ); + + +gceSTATUS +gco3D_SetPSOutputMapping( + IN gco3D Engine, + IN gctINT32 * psOutputMapping + ); + + +/* Set depth buffer. */ +gceSTATUS +gco3D_SetDepth( + IN gco3D Engine, + IN gcoSURF Surface + ); + +gceSTATUS +gco3D_SetDepthBufferOffset( + IN gco3D Engine, + IN gctSIZE_T Offset + ); + +/* Unset depth buffer. */ +gceSTATUS +gco3D_UnsetDepth( + IN gco3D Engine, + IN gcoSURF Surface + ); + +/* Set viewport. */ +gceSTATUS +gco3D_SetViewport( + IN gco3D Engine, + IN gctINT32 Left, + IN gctINT32 Top, + IN gctINT32 Right, + IN gctINT32 Bottom + ); + +/* Set scissors. */ +gceSTATUS +gco3D_SetScissors( + IN gco3D Engine, + IN gctINT32 Left, + IN gctINT32 Top, + IN gctINT32 Right, + IN gctINT32 Bottom + ); + +/* Set clear color. */ +gceSTATUS +gco3D_SetClearColor( + IN gco3D Engine, + IN gctUINT8 Red, + IN gctUINT8 Green, + IN gctUINT8 Blue, + IN gctUINT8 Alpha + ); + +/* Set fixed point clear color. */ +gceSTATUS +gco3D_SetClearColorX( + IN gco3D Engine, + IN gctFIXED_POINT Red, + IN gctFIXED_POINT Green, + IN gctFIXED_POINT Blue, + IN gctFIXED_POINT Alpha + ); + +/* Set floating point clear color. */ +gceSTATUS +gco3D_SetClearColorF( + IN gco3D Engine, + IN gctFLOAT Red, + IN gctFLOAT Green, + IN gctFLOAT Blue, + IN gctFLOAT Alpha + ); + +/* Set fixed point clear depth. */ +gceSTATUS +gco3D_SetClearDepthX( + IN gco3D Engine, + IN gctFIXED_POINT Depth + ); + +/* Set floating point clear depth. */ +gceSTATUS +gco3D_SetClearDepthF( + IN gco3D Engine, + IN gctFLOAT Depth + ); + +/* Set clear stencil. */ +gceSTATUS +gco3D_SetClearStencil( + IN gco3D Engine, + IN gctUINT32 Stencil + ); + +/* Set shading mode. */ +gceSTATUS +gco3D_SetShading( + IN gco3D Engine, + IN gceSHADING Shading + ); + +/* Set blending mode. */ +gceSTATUS +gco3D_EnableBlending( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set blending function. */ +gceSTATUS +gco3D_SetBlendFunction( + IN gco3D Engine, + IN gceBLEND_UNIT Unit, + IN gceBLEND_FUNCTION FunctionRGB, + IN gceBLEND_FUNCTION FunctionAlpha + ); + +/* Set blending mode. */ +gceSTATUS +gco3D_SetBlendMode( + IN gco3D Engine, + IN gceBLEND_MODE ModeRGB, + IN gceBLEND_MODE ModeAlpha + ); + +/* Set blending color. */ +gceSTATUS +gco3D_SetBlendColor( + IN gco3D Engine, + IN gctUINT Red, + IN gctUINT Green, + IN gctUINT Blue, + IN gctUINT Alpha + ); + +/* Set fixed point blending color. */ +gceSTATUS +gco3D_SetBlendColorX( + IN gco3D Engine, + IN gctFIXED_POINT Red, + IN gctFIXED_POINT Green, + IN gctFIXED_POINT Blue, + IN gctFIXED_POINT Alpha + ); + +/* Set floating point blending color. */ +gceSTATUS +gco3D_SetBlendColorF( + IN gco3D Engine, + IN gctFLOAT Red, + IN gctFLOAT Green, + IN gctFLOAT Blue, + IN gctFLOAT Alpha + ); + +/* Set culling mode. */ +gceSTATUS +gco3D_SetCulling( + IN gco3D Engine, + IN gceCULL Mode + ); + +/* Enable point size */ +gceSTATUS +gco3D_SetPointSizeEnable( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set point sprite */ +gceSTATUS +gco3D_SetPointSprite( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set fill mode. */ +gceSTATUS +gco3D_SetFill( + IN gco3D Engine, + IN gceFILL Mode + ); + +/* Set depth compare mode. */ +gceSTATUS +gco3D_SetDepthCompare( + IN gco3D Engine, + IN gceCOMPARE Compare + ); + +/* Enable depth writing. */ +gceSTATUS +gco3D_EnableDepthWrite( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set depth mode. */ +gceSTATUS +gco3D_SetDepthMode( + IN gco3D Engine, + IN gceDEPTH_MODE Mode + ); + +/* Set depth range. */ +gceSTATUS +gco3D_SetDepthRangeX( + IN gco3D Engine, + IN gceDEPTH_MODE Mode, + IN gctFIXED_POINT Near, + IN gctFIXED_POINT Far + ); + +/* Set depth range. */ +gceSTATUS +gco3D_SetDepthRangeF( + IN gco3D Engine, + IN gceDEPTH_MODE Mode, + IN gctFLOAT Near, + IN gctFLOAT Far + ); + +/* Set last pixel enable */ +gceSTATUS +gco3D_SetLastPixelEnable( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set depth Bias and Scale */ +gceSTATUS +gco3D_SetDepthScaleBiasX( + IN gco3D Engine, + IN gctFIXED_POINT DepthScale, + IN gctFIXED_POINT DepthBias + ); + +gceSTATUS +gco3D_SetDepthScaleBiasF( + IN gco3D Engine, + IN gctFLOAT DepthScale, + IN gctFLOAT DepthBias + ); + +/* Set depth near and far clipping plane. */ +gceSTATUS +gco3D_SetDepthPlaneF( + IN gco3D Engine, + IN gctFLOAT Near, + IN gctFLOAT Far + ); + +/* Enable or disable dithering. */ +gceSTATUS +gco3D_EnableDither( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set color write enable bits. */ +gceSTATUS +gco3D_SetColorWrite( + IN gco3D Engine, + IN gctUINT8 Enable + ); + +/* Enable or disable early depth. */ +gceSTATUS +gco3D_SetEarlyDepth( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Deprecated: Enable or disable all early depth operations. */ +gceSTATUS +gco3D_SetAllEarlyDepthModes( + IN gco3D Engine, + IN gctBOOL Disable + ); + +/* Enable or disable all early depth operations. */ +gceSTATUS +gco3D_SetAllEarlyDepthModesEx( + IN gco3D Engine, + IN gctBOOL Disable, + IN gctBOOL DisableModify, + IN gctBOOL DisablePassZ + ); + +/* Switch dynamic early mode */ +gceSTATUS +gco3D_SwitchDynamicEarlyDepthMode( + IN gco3D Engine + ); + +/* Set dynamic early mode */ +gceSTATUS +gco3D_DisableDynamicEarlyDepthMode( + IN gco3D Engine, + IN gctBOOL Disable + ); + +/* Enable or disable depth-only mode. */ +gceSTATUS +gco3D_SetDepthOnly( + IN gco3D Engine, + IN gctBOOL Enable + ); + +typedef struct _gcsSTENCIL_INFO * gcsSTENCIL_INFO_PTR; +typedef struct _gcsSTENCIL_INFO +{ + gceSTENCIL_MODE mode; + + gctUINT8 maskFront; + gctUINT8 maskBack; + gctUINT8 writeMaskFront; + gctUINT8 writeMaskBack; + + gctUINT8 referenceFront; + + gceCOMPARE compareFront; + gceSTENCIL_OPERATION passFront; + gceSTENCIL_OPERATION failFront; + gceSTENCIL_OPERATION depthFailFront; + + gctUINT8 referenceBack; + gceCOMPARE compareBack; + gceSTENCIL_OPERATION passBack; + gceSTENCIL_OPERATION failBack; + gceSTENCIL_OPERATION depthFailBack; +} +gcsSTENCIL_INFO; + +/* Set stencil mode. */ +gceSTATUS +gco3D_SetStencilMode( + IN gco3D Engine, + IN gceSTENCIL_MODE Mode + ); + +/* Set stencil mask. */ +gceSTATUS +gco3D_SetStencilMask( + IN gco3D Engine, + IN gctUINT8 Mask + ); + +/* Set stencil back mask. */ +gceSTATUS +gco3D_SetStencilMaskBack( + IN gco3D Engine, + IN gctUINT8 Mask + ); + +/* Set stencil write mask. */ +gceSTATUS +gco3D_SetStencilWriteMask( + IN gco3D Engine, + IN gctUINT8 Mask + ); + +/* Set stencil back write mask. */ +gceSTATUS +gco3D_SetStencilWriteMaskBack( + IN gco3D Engine, + IN gctUINT8 Mask + ); + +/* Set stencil reference. */ +gceSTATUS +gco3D_SetStencilReference( + IN gco3D Engine, + IN gctUINT8 Reference, + IN gctBOOL Front + ); + +/* Set stencil compare. */ +gceSTATUS +gco3D_SetStencilCompare( + IN gco3D Engine, + IN gceSTENCIL_WHERE Where, + IN gceCOMPARE Compare + ); + +/* Set stencil operation on pass. */ +gceSTATUS +gco3D_SetStencilPass( + IN gco3D Engine, + IN gceSTENCIL_WHERE Where, + IN gceSTENCIL_OPERATION Operation + ); + +/* Set stencil operation on fail. */ +gceSTATUS +gco3D_SetStencilFail( + IN gco3D Engine, + IN gceSTENCIL_WHERE Where, + IN gceSTENCIL_OPERATION Operation + ); + +/* Set stencil operation on depth fail. */ +gceSTATUS +gco3D_SetStencilDepthFail( + IN gco3D Engine, + IN gceSTENCIL_WHERE Where, + IN gceSTENCIL_OPERATION Operation + ); + +/* Set all stencil states in one blow. */ +gceSTATUS +gco3D_SetStencilAll( + IN gco3D Engine, + IN gcsSTENCIL_INFO_PTR Info + ); + +typedef struct _gcsALPHA_INFO * gcsALPHA_INFO_PTR; +typedef struct _gcsALPHA_INFO +{ + /* Alpha test states. */ + gctBOOL test; + gceCOMPARE compare; + gctUINT8 reference; + gctFLOAT floatReference; + + /* Alpha blending states. */ + gctBOOL blend; + + gceBLEND_FUNCTION srcFuncColor; + gceBLEND_FUNCTION srcFuncAlpha; + gceBLEND_FUNCTION trgFuncColor; + gceBLEND_FUNCTION trgFuncAlpha; + + gceBLEND_MODE modeColor; + gceBLEND_MODE modeAlpha; + + gctUINT32 color; +} +gcsALPHA_INFO; + +/* Enable or disable alpha test. */ +gceSTATUS +gco3D_SetAlphaTest( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set alpha test compare. */ +gceSTATUS +gco3D_SetAlphaCompare( + IN gco3D Engine, + IN gceCOMPARE Compare + ); + +/* Set alpha test reference in unsigned integer. */ +gceSTATUS +gco3D_SetAlphaReference( + IN gco3D Engine, + IN gctUINT8 Reference, + IN gctFLOAT FloatReference + ); + +/* Set alpha test reference in fixed point. */ +gceSTATUS +gco3D_SetAlphaReferenceX( + IN gco3D Engine, + IN gctFIXED_POINT Reference + ); + +/* Set alpha test reference in floating point. */ +gceSTATUS +gco3D_SetAlphaReferenceF( + IN gco3D Engine, + IN gctFLOAT Reference + ); + +/* Enable/Disable anti-alias line. */ +gceSTATUS +gco3D_SetAntiAliasLine( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Set texture slot for anti-alias line. */ +gceSTATUS +gco3D_SetAALineTexSlot( + IN gco3D Engine, + IN gctUINT TexSlot + ); + +/* Set anti-alias line width scale. */ +gceSTATUS +gco3D_SetAALineWidth( + IN gco3D Engine, + IN gctFLOAT Width + ); + +/* Draw a number of primitives. */ +gceSTATUS +gco3D_DrawPrimitives( + IN gco3D Engine, + IN gcePRIMITIVE Type, + IN gctSIZE_T StartVertex, + IN gctSIZE_T PrimitiveCount + ); + +gceSTATUS +gco3D_DrawInstancedPrimitives( + IN gco3D Engine, + IN gcePRIMITIVE Type, + IN gctBOOL DrawIndex, + IN gctSIZE_T StartVertex, + IN gctSIZE_T StartIndex, + IN gctSIZE_T PrimitiveCount, + IN gctSIZE_T VertexCount, + IN gctSIZE_T InstanceCount + ); + +gceSTATUS +gco3D_DrawPrimitivesCount( + IN gco3D Engine, + IN gcePRIMITIVE Type, + IN gctINT* StartVertex, + IN gctSIZE_T* VertexCount, + IN gctSIZE_T PrimitiveCount + ); + + +/* Draw a number of primitives using offsets. */ +gceSTATUS +gco3D_DrawPrimitivesOffset( + IN gco3D Engine, + IN gcePRIMITIVE Type, + IN gctINT32 StartOffset, + IN gctSIZE_T PrimitiveCount + ); + +/* Draw a number of indexed primitives. */ +gceSTATUS +gco3D_DrawIndexedPrimitives( + IN gco3D Engine, + IN gcePRIMITIVE Type, + IN gctSIZE_T BaseVertex, + IN gctSIZE_T StartIndex, + IN gctSIZE_T PrimitiveCount + ); + +/* Draw a number of indexed primitives using offsets. */ +gceSTATUS +gco3D_DrawIndexedPrimitivesOffset( + IN gco3D Engine, + IN gcePRIMITIVE Type, + IN gctINT32 BaseOffset, + IN gctINT32 StartOffset, + IN gctSIZE_T PrimitiveCount + ); + +/* Draw a element from pattern */ +gceSTATUS +gco3D_DrawPattern( + IN gco3D Engine, + IN gcsFAST_FLUSH_PTR FastFlushInfo + ); + +/* Enable or disable anti-aliasing. */ +gceSTATUS +gco3D_SetAntiAlias( + IN gco3D Engine, + IN gctBOOL Enable + ); + +/* Write data into the command buffer. */ +gceSTATUS +gco3D_WriteBuffer( + IN gco3D Engine, + IN gctCONST_POINTER Data, + IN gctSIZE_T Bytes, + IN gctBOOL Aligned + ); + +/* Send sempahore and stall until sempahore is signalled. */ +gceSTATUS +gco3D_Semaphore( + IN gco3D Engine, + IN gceWHERE From, + IN gceWHERE To, + IN gceHOW How); + +/* Explicitly flush shader L1 cache */ +gceSTATUS +gco3D_FlushSHL1Cache( + IN gco3D Engine + ); + +/* Set the subpixels center. */ +gceSTATUS +gco3D_SetCentroids( + IN gco3D Engine, + IN gctUINT32 Index, + IN gctPOINTER Centroids + ); + +gceSTATUS +gco3D_SetLogicOp( + IN gco3D Engine, + IN gctUINT8 Rop + ); + +gceSTATUS +gco3D_SetOQ( + IN gco3D Engine, + INOUT gctPOINTER * Result, + IN gctBOOL Enable + ); + +gceSTATUS +gco3D_GetOQ( + IN gco3D Engine, + IN gctPOINTER Result, + OUT gctINT64 * Logical + ); + +gceSTATUS +gco3D_DeleteOQ( + IN gco3D Engine, + INOUT gctPOINTER Result + ); + +gceSTATUS +gco3D_SetColorOutCount( + IN gco3D Engine, + IN gctUINT32 ColorOutCount + ); + +gceSTATUS +gco3D_Set3DEngine( + IN gco3D Engine + ); + +gceSTATUS +gco3D_UnSet3DEngine( + IN gco3D Engine + ); + +gceSTATUS +gco3D_Get3DEngine( + OUT gco3D * Engine + ); + + +/* OCL thread walker information. */ +typedef struct _gcsTHREAD_WALKER_INFO * gcsTHREAD_WALKER_INFO_PTR; +typedef struct _gcsTHREAD_WALKER_INFO +{ + gctUINT32 dimensions; + gctUINT32 traverseOrder; + gctUINT32 enableSwathX; + gctUINT32 enableSwathY; + gctUINT32 enableSwathZ; + gctUINT32 swathSizeX; + gctUINT32 swathSizeY; + gctUINT32 swathSizeZ; + gctUINT32 valueOrder; + + gctUINT32 globalSizeX; + gctUINT32 globalOffsetX; + gctUINT32 globalSizeY; + gctUINT32 globalOffsetY; + gctUINT32 globalSizeZ; + gctUINT32 globalOffsetZ; + + gctUINT32 workGroupSizeX; + gctUINT32 workGroupCountX; + gctUINT32 workGroupSizeY; + gctUINT32 workGroupCountY; + gctUINT32 workGroupSizeZ; + gctUINT32 workGroupCountZ; + + gctUINT32 threadAllocation; +} +gcsTHREAD_WALKER_INFO; + +/* Start OCL thread walker. */ +gceSTATUS +gco3D_InvokeThreadWalker( + IN gco3D Engine, + IN gcsTHREAD_WALKER_INFO_PTR Info + ); + +gceSTATUS +gco3D_GetClosestRenderFormat( + IN gco3D Engine, + IN gceSURF_FORMAT InFormat, + OUT gceSURF_FORMAT* OutFormat + ); + +/* Set w clip and w plane limit value. */ +gceSTATUS +gco3D_SetWClipEnable( + IN gco3D Engine, + IN gctBOOL Enable + ); + +gceSTATUS +gco3D_GetWClipEnable( + IN gco3D Engine, + OUT gctBOOL * Enable + ); + +gceSTATUS +gco3D_SetWPlaneLimitF( + IN gco3D Engine, + IN gctFLOAT Value + ); + +gceSTATUS +gco3D_SetWPlaneLimitX( + IN gco3D Engine, + IN gctFIXED_POINT Value + ); + +gceSTATUS +gco3D_SetWPlaneLimit( + IN gco3D Engine, + IN gctFLOAT Value + ); + +gceSTATUS +gco3D_PrimitiveRestart( + IN gco3D Engine, + IN gctBOOL PrimitiveRestart); + +#if gcdSTREAM_OUT_BUFFER + +gceSTATUS +gco3D_QueryStreamOut( + IN gco3D Engine, + IN gctUINT32 OriginalIndexAddress, + IN gctUINT32 OriginalIndexOffset, + IN gctUINT32 OriginalIndexCount, + OUT gctBOOL_PTR Found + ); + +gceSTATUS +gco3D_StartStreamOut( + IN gco3D Engine, + IN gctINT StreamOutStatus, + IN gctUINT32 IndexAddress, + IN gctUINT32 IndexOffset, + IN gctUINT32 IndexCount + ); + +gceSTATUS +gco3D_StopStreamOut( + IN gco3D Engine + ); + +gceSTATUS +gco3D_ReplayStreamOut( + IN gco3D Engine, + IN gctUINT32 IndexAddress, + IN gctUINT32 IndexOffset, + IN gctUINT32 IndexCount + ); + +gceSTATUS +gco3D_EndStreamOut( + IN gco3D Engine + ); + +#endif + +/*----------------------------------------------------------------------------*/ +/*-------------------------- gco3D Fragment Processor ------------------------*/ + +/* Set the fragment processor configuration. */ +gceSTATUS +gco3D_SetFragmentConfiguration( + IN gco3D Engine, + IN gctBOOL ColorFromStream, + IN gctBOOL EnableFog, + IN gctBOOL EnableSmoothPoint, + IN gctUINT32 ClipPlanes + ); + +/* Enable/disable texture stage operation. */ +gceSTATUS +gco3D_EnableTextureStage( + IN gco3D Engine, + IN gctINT Stage, + IN gctBOOL Enable + ); + +/* Program the channel enable masks for the color texture function. */ +gceSTATUS +gco3D_SetTextureColorMask( + IN gco3D Engine, + IN gctINT Stage, + IN gctBOOL ColorEnabled, + IN gctBOOL AlphaEnabled + ); + +/* Program the channel enable masks for the alpha texture function. */ +gceSTATUS +gco3D_SetTextureAlphaMask( + IN gco3D Engine, + IN gctINT Stage, + IN gctBOOL ColorEnabled, + IN gctBOOL AlphaEnabled + ); + +/* Program the constant fragment color. */ +gceSTATUS +gco3D_SetFragmentColorX( + IN gco3D Engine, + IN gctFIXED_POINT Red, + IN gctFIXED_POINT Green, + IN gctFIXED_POINT Blue, + IN gctFIXED_POINT Alpha + ); + +gceSTATUS +gco3D_SetFragmentColorF( + IN gco3D Engine, + IN gctFLOAT Red, + IN gctFLOAT Green, + IN gctFLOAT Blue, + IN gctFLOAT Alpha + ); + +/* Program the constant fog color. */ +gceSTATUS +gco3D_SetFogColorX( + IN gco3D Engine, + IN gctFIXED_POINT Red, + IN gctFIXED_POINT Green, + IN gctFIXED_POINT Blue, + IN gctFIXED_POINT Alpha + ); + +gceSTATUS +gco3D_SetFogColorF( + IN gco3D Engine, + IN gctFLOAT Red, + IN gctFLOAT Green, + IN gctFLOAT Blue, + IN gctFLOAT Alpha + ); + +/* Program the constant texture color. */ +gceSTATUS +gco3D_SetTetxureColorX( + IN gco3D Engine, + IN gctINT Stage, + IN gctFIXED_POINT Red, + IN gctFIXED_POINT Green, + IN gctFIXED_POINT Blue, + IN gctFIXED_POINT Alpha + ); + +gceSTATUS +gco3D_SetTetxureColorF( + IN gco3D Engine, + IN gctINT Stage, + IN gctFLOAT Red, + IN gctFLOAT Green, + IN gctFLOAT Blue, + IN gctFLOAT Alpha + ); + +/* Configure color texture function. */ +gceSTATUS +gco3D_SetColorTextureFunction( + IN gco3D Engine, + IN gctINT Stage, + IN gceTEXTURE_FUNCTION Function, + IN gceTEXTURE_SOURCE Source0, + IN gceTEXTURE_CHANNEL Channel0, + IN gceTEXTURE_SOURCE Source1, + IN gceTEXTURE_CHANNEL Channel1, + IN gceTEXTURE_SOURCE Source2, + IN gceTEXTURE_CHANNEL Channel2, + IN gctINT Scale + ); + +/* Configure alpha texture function. */ +gceSTATUS +gco3D_SetAlphaTextureFunction( + IN gco3D Engine, + IN gctINT Stage, + IN gceTEXTURE_FUNCTION Function, + IN gceTEXTURE_SOURCE Source0, + IN gceTEXTURE_CHANNEL Channel0, + IN gceTEXTURE_SOURCE Source1, + IN gceTEXTURE_CHANNEL Channel1, + IN gceTEXTURE_SOURCE Source2, + IN gceTEXTURE_CHANNEL Channel2, + IN gctINT Scale + ); + +/******************************************************************************\ +******************************* gcoTEXTURE Object ******************************* +\******************************************************************************/ + +/* Cube faces. */ +typedef enum _gceTEXTURE_FACE +{ + gcvFACE_NONE, + gcvFACE_POSITIVE_X, + gcvFACE_NEGATIVE_X, + gcvFACE_POSITIVE_Y, + gcvFACE_NEGATIVE_Y, + gcvFACE_POSITIVE_Z, + gcvFACE_NEGATIVE_Z, +} +gceTEXTURE_FACE; + +typedef struct _gcsTEXTURE +{ + /* Addressing modes. */ + gceTEXTURE_ADDRESSING s; + gceTEXTURE_ADDRESSING t; + gceTEXTURE_ADDRESSING r; + + gceTEXTURE_SWIZZLE swizzle[gcvTEXTURE_COMPONENT_NUM]; + + /* Border color. */ + gctUINT8 border[gcvTEXTURE_COMPONENT_NUM]; + + /* Filters. */ + gceTEXTURE_FILTER minFilter; + gceTEXTURE_FILTER magFilter; + gceTEXTURE_FILTER mipFilter; + gctUINT anisoFilter; + + /* Level of detail. */ + gctFLOAT lodBias; + gctFLOAT lodMin; + gctFLOAT lodMax; + + /* base/max level */ + gctINT32 baseLevel; + gctINT32 maxLevel; + + /* depth texture comparison */ + gceTEXTURE_COMPARE_MODE compareMode; + gceCOMPARE compareFunc; + +} +gcsTEXTURE, * gcsTEXTURE_PTR; + +/* Construct a new gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_Construct( + IN gcoHAL Hal, + OUT gcoTEXTURE * Texture + ); + +/* Construct a new gcoTEXTURE object with type information. */ +gceSTATUS +gcoTEXTURE_ConstructEx( + IN gcoHAL Hal, + IN gceTEXTURE_TYPE Type, + OUT gcoTEXTURE * Texture + ); + + +/* Construct a new sized gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_ConstructSized( + IN gcoHAL Hal, + IN gceSURF_FORMAT Format, + IN gctUINT Width, + IN gctUINT Height, + IN gctUINT Depth, + IN gctUINT Faces, + IN gctUINT MipMapCount, + IN gcePOOL Pool, + OUT gcoTEXTURE * Texture + ); + +/* Destroy an gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_Destroy( + IN gcoTEXTURE Texture + ); + +/* Upload data to an gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_Upload( + IN gcoTEXTURE Texture, + IN gctINT MipMap, + IN gceTEXTURE_FACE Face, + IN gctSIZE_T Width, + IN gctSIZE_T Height, + IN gctUINT Slice, + IN gctCONST_POINTER Memory, + IN gctSIZE_T Stride, + IN gceSURF_FORMAT Format, + IN gceSURF_COLOR_SPACE SrcColorSpace + ); + +/* Upload data to an gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_UploadSub( + IN gcoTEXTURE Texture, + IN gctINT MipMap, + IN gceTEXTURE_FACE Face, + IN gctSIZE_T X, + IN gctSIZE_T Y, + IN gctSIZE_T Width, + IN gctSIZE_T Height, + IN gctUINT Slice, + IN gctCONST_POINTER Memory, + IN gctSIZE_T Stride, + IN gceSURF_FORMAT Format, + IN gceSURF_COLOR_SPACE SrcColorSpace, + IN gctUINT32 PhysicalAddress + ); + + +/* Upload YUV data to an gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_UploadYUV( + IN gcoTEXTURE Texture, + IN gceTEXTURE_FACE Face, + IN gctUINT Width, + IN gctUINT Height, + IN gctUINT Slice, + IN gctPOINTER Memory[3], + IN gctINT Stride[3], + IN gceSURF_FORMAT Format + ); + +/* Upload compressed data to an gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_UploadCompressed( + IN gcoTEXTURE Texture, + IN gctINT MipMap, + IN gceTEXTURE_FACE Face, + IN gctSIZE_T Width, + IN gctSIZE_T Height, + IN gctUINT Slice, + IN gctCONST_POINTER Memory, + IN gctSIZE_T Bytes + ); + +/* Upload compressed sub data to an gcoTEXTURE object. */ +gceSTATUS +gcoTEXTURE_UploadCompressedSub( + IN gcoTEXTURE Texture, + IN gctINT MipMap, + IN gceTEXTURE_FACE Face, + IN gctSIZE_T XOffset, + IN gctSIZE_T YOffset, + IN gctSIZE_T Width, + IN gctSIZE_T Height, + IN gctUINT Slice, + IN gctCONST_POINTER Memory, + IN gctSIZE_T Size + ); + +/* Get gcoSURF object for a mipmap level. */ +gceSTATUS +gcoTEXTURE_GetMipMap( + IN gcoTEXTURE Texture, + IN gctUINT MipMap, + OUT gcoSURF * Surface + ); + +/* Get gcoSURF object for a mipmap level and face offset. */ +gceSTATUS +gcoTEXTURE_GetMipMapFace( + IN gcoTEXTURE Texture, + IN gctUINT MipMap, + IN gceTEXTURE_FACE Face, + OUT gcoSURF * Surface, + OUT gctSIZE_T_PTR Offset + ); + +gceSTATUS +gcoTEXTURE_GetMipMapSlice( + IN gcoTEXTURE Texture, + IN gctUINT MipMap, + IN gctUINT Slice, + OUT gcoSURF * Surface, + OUT gctSIZE_T_PTR Offset + ); + +gceSTATUS +gcoTEXTURE_AddMipMap( + IN gcoTEXTURE Texture, + IN gctINT Level, + IN gctINT InternalFormat, + IN gceSURF_FORMAT Format, + IN gctSIZE_T Width, + IN gctSIZE_T Height, + IN gctSIZE_T Depth, + IN gctUINT Faces, + IN gcePOOL Pool, + OUT gcoSURF * Surface + ); + +gceSTATUS +gcoTEXTURE_AddMipMapWithFlag( + IN gcoTEXTURE Texture, + IN gctINT Level, + IN gctINT InternalFormat, + IN gceSURF_FORMAT Format, + IN gctSIZE_T Width, + IN gctSIZE_T Height, + IN gctSIZE_T Depth, + IN gctUINT Faces, + IN gcePOOL Pool, + IN gctBOOL Protected, + OUT gcoSURF * Surface + ); + +gceSTATUS +gcoTEXTURE_AddMipMapFromClient( + IN gcoTEXTURE Texture, + IN gctINT Level, + IN gcoSURF Surface + ); + +gceSTATUS +gcoTEXTURE_AddMipMapFromSurface( + IN gcoTEXTURE Texture, + IN gctINT Level, + IN gcoSURF Surface + ); + +gceSTATUS +gcoTEXTURE_SetEndianHint( + IN gcoTEXTURE Texture, + IN gceENDIAN_HINT EndianHint + ); + +gceSTATUS +gcoTEXTURE_Disable( + IN gcoHAL Hal, + IN gctINT Sampler + ); + +gceSTATUS +gcoTEXTURE_Flush( + IN gcoTEXTURE Texture + ); + +gceSTATUS +gcoTEXTURE_FlushVS( + IN gcoTEXTURE Texture + ); + +gceSTATUS +gcoTEXTURE_QueryCaps( + IN gcoHAL Hal, + OUT gctUINT * MaxWidth, + OUT gctUINT * MaxHeight, + OUT gctUINT * MaxDepth, + OUT gctBOOL * Cubic, + OUT gctBOOL * NonPowerOfTwo, + OUT gctUINT * VertexSamplers, + OUT gctUINT * PixelSamplers + ); + +gceSTATUS +gcoTEXTURE_GetClosestFormat( + IN gcoHAL Hal, + IN gceSURF_FORMAT InFormat, + OUT gceSURF_FORMAT* OutFormat + ); + +gceSTATUS +gcoTEXTURE_GetClosestFormatEx( + IN gcoHAL Hal, + IN gceSURF_FORMAT InFormat, + IN gceTEXTURE_TYPE TextureType, + OUT gceSURF_FORMAT* OutFormat + ); + +gceSTATUS +gcoTEXTURE_GetFormatInfo( + IN gcoTEXTURE Texture, + IN gctINT preferLevel, + OUT gcsSURF_FORMAT_INFO_PTR * TxFormatInfo + ); + +gceSTATUS +gcoTEXTURE_GetTextureFormatName( + IN gcsSURF_FORMAT_INFO_PTR TxFormatInfo, + OUT gctCONST_STRING * TxName + ); + +gceSTATUS +gcoTEXTURE_RenderIntoMipMap( + IN gcoTEXTURE Texture, + IN gctINT Level + ); + +gceSTATUS +gcoTEXTURE_RenderIntoMipMap2( + IN gcoTEXTURE Texture, + IN gctINT Level, + IN gctBOOL Sync + ); + +gceSTATUS +gcoTEXTURE_IsRenderable( + IN gcoTEXTURE Texture, + IN gctUINT Level + ); + +gceSTATUS +gcoTEXTURE_IsComplete( + IN gcoTEXTURE Texture, + IN gcsTEXTURE_PTR Info, + IN gctINT BaseLevel, + IN gctINT MaxLevel + ); + +gceSTATUS +gcoTEXTURE_BindTexture( + IN gcoTEXTURE Texture, + IN gctINT Target, + IN gctINT Sampler, + IN gcsTEXTURE_PTR Info + ); + +gceSTATUS +gcoTEXTURE_BindTextureEx( + IN gcoTEXTURE Texture, + IN gctINT Target, + IN gctINT Sampler, + IN gcsTEXTURE_PTR Info, + IN gctINT textureLayer + ); + +gceSTATUS +gcoTEXTURE_InitParams( + IN gcoHAL Hal, + IN gcsTEXTURE_PTR TexParams + ); + +gceSTATUS +gcoTEXTURE_SetDepthTextureFlag( + IN gcoTEXTURE Texture, + IN gctBOOL unsized + ); + + +/******************************************************************************\ +******************************* gcoSTREAM Object ****************************** +\******************************************************************************/ + +typedef enum _gceVERTEX_FORMAT +{ + gcvVERTEX_BYTE, + gcvVERTEX_UNSIGNED_BYTE, + gcvVERTEX_SHORT, + gcvVERTEX_UNSIGNED_SHORT, + gcvVERTEX_INT, + gcvVERTEX_UNSIGNED_INT, + gcvVERTEX_FIXED, + gcvVERTEX_HALF, + gcvVERTEX_FLOAT, + gcvVERTEX_UNSIGNED_INT_10_10_10_2, + gcvVERTEX_INT_10_10_10_2, + gcvVERTEX_UNSIGNED_INT_2_10_10_10_REV, + gcvVERTEX_INT_2_10_10_10_REV, + /* integer format */ + gcvVERTEX_INT8, + gcvVERTEX_INT16, + gcvVERTEX_INT32, +} +gceVERTEX_FORMAT; + +/* What the SW converting scheme to create temp attrib */ +typedef enum _gceATTRIB_SCHEME +{ + gcvATTRIB_SCHEME_KEEP = 0, + gcvATTRIB_SCHEME_2_10_10_10_REV_TO_FLOAT, + gcvATTRIB_SCHEME_BYTE_TO_INT, + gcvATTRIB_SCHEME_SHORT_TO_INT, + gcvATTRIB_SCHEME_UBYTE_TO_UINT, + gcvATTRIB_SCHEME_USHORT_TO_UINT, +} gceATTRIB_SCHEME; + +gceSTATUS +gcoSTREAM_Construct( + IN gcoHAL Hal, + OUT gcoSTREAM * Stream + ); + +gceSTATUS +gcoSTREAM_Destroy( + IN gcoSTREAM Stream + ); + +gceSTATUS +gcoSTREAM_Upload( + IN gcoSTREAM Stream, + IN gctCONST_POINTER Buffer, + IN gctSIZE_T Offset, + IN gctSIZE_T Bytes, + IN gctBOOL Dynamic + ); + +gceSTATUS +gcoSTREAM_SetStride( + IN gcoSTREAM Stream, + IN gctUINT32 Stride + ); + +gceSTATUS +gcoSTREAM_Size( + IN gcoSTREAM Stream, + OUT gctSIZE_T *Size + ); + +gceSTATUS +gcoSTREAM_Node( + IN gcoSTREAM Stream, + OUT gcsSURF_NODE_PTR * Node + ); + +gceSTATUS +gcoSTREAM_Lock( + IN gcoSTREAM Stream, + OUT gctPOINTER * Logical, + OUT gctUINT32 * Physical + ); + +gceSTATUS +gcoSTREAM_Unlock( + IN gcoSTREAM Stream + ); + +gceSTATUS +gcoSTREAM_Reserve( + IN gcoSTREAM Stream, + IN gctSIZE_T Bytes + ); + +gceSTATUS +gcoSTREAM_Flush( + IN gcoSTREAM Stream + ); + +/* Dynamic buffer API. */ +gceSTATUS +gcoSTREAM_SetDynamic( + IN gcoSTREAM Stream, + IN gctSIZE_T Bytes, + IN gctUINT Buffers + ); + +typedef struct _gcsSTREAM_INFO +{ + gctUINT index; + gceVERTEX_FORMAT format; + gctBOOL normalized; + gctUINT components; + gctSIZE_T size; + gctCONST_POINTER data; + gctUINT stride; +} +gcsSTREAM_INFO, * gcsSTREAM_INFO_PTR; + +gceSTATUS +gcoSTREAM_UploadDynamic( + IN gcoSTREAM Stream, + IN gctUINT VertexCount, + IN gctUINT InfoCount, + IN gcsSTREAM_INFO_PTR Info, + IN gcoVERTEX Vertex + ); + +gceSTATUS +gcoSTREAM_CPUCacheOperation( + IN gcoSTREAM Stream, + IN gceCACHEOPERATION Operation + ); + +gceSTATUS +gcoSTREAM_CPUCacheOperation_Range( + IN gcoSTREAM Stream, + IN gctSIZE_T Offset, + IN gctSIZE_T Length, + IN gceCACHEOPERATION Operation + ); + +/******************************************************************************\ +******************************** gcoVERTEX Object ****************************** +\******************************************************************************/ + +typedef struct _gcsVERTEX_ATTRIBUTES +{ + gceVERTEX_FORMAT format; + gctBOOL normalized; + gctUINT32 components; + gctSIZE_T size; + gctUINT32 stream; + gctUINT32 offset; + gctUINT32 stride; +} +gcsVERTEX_ATTRIBUTES; + +gceSTATUS +gcoVERTEX_Construct( + IN gcoHAL Hal, + OUT gcoVERTEX * Vertex + ); + +gceSTATUS +gcoVERTEX_Destroy( + IN gcoVERTEX Vertex + ); + +gceSTATUS +gcoVERTEX_Reset( + IN gcoVERTEX Vertex + ); + +gceSTATUS +gcoVERTEX_EnableAttribute( + IN gcoVERTEX Vertex, + IN gctUINT32 Index, + IN gceVERTEX_FORMAT Format, + IN gctBOOL Normalized, + IN gctUINT32 Components, + IN gcoSTREAM Stream, + IN gctUINT32 Offset, + IN gctUINT32 Stride + ); + +gceSTATUS +gcoVERTEX_DisableAttribute( + IN gcoVERTEX Vertex, + IN gctUINT32 Index + ); + +gceSTATUS +gcoVERTEX_Bind( + IN gcoVERTEX Vertex + ); + +/******************************************************************************* +***** gcoVERTEXARRAY Object ***************************************************/ + +typedef struct _gcsATTRIBUTE +{ + /* Enabled. */ + gctBOOL enable; + + /* Number of components. */ + gctINT size; + + /* Attribute format. */ + gceVERTEX_FORMAT format; + + /* Flag whether the attribute is normalized or not. */ + gctBOOL normalized; + + /* Stride of the component. */ + gctSIZE_T stride; + + /* Divisor of the attribute */ + gctUINT divisor; + + /* Pointer to the attribute data. */ + gctCONST_POINTER pointer; + + /* Stream object owning the attribute data. */ + gcoBUFOBJ stream; + + /* Generic values for attribute. */ + gctFLOAT genericValue[4]; + + /* Generic size for attribute. */ + gctINT genericSize; + + /* Vertex shader linkage. */ + gctUINT linkage; + +#if gcdUSE_WCLIP_PATCH + /* Does it hold positions? */ + gctBOOL isPosition; +#endif + + /* Index to vertex array */ + gctINT arrayIdx; + + gceATTRIB_SCHEME convertScheme; + + /* Pointer to the temporary buffer to be freed */ + gcoBUFOBJ tempStream; + + /* Pointer to the temporary memory to be freed */ + gctCONST_POINTER tempMemory; +} +gcsATTRIBUTE, +* gcsATTRIBUTE_PTR; + + +typedef struct _gcsVERTEXARRAY +{ + /* Enabled. */ + gctBOOL enable; + + /* Number of components. */ + gctINT size; + + /* Attribute format. */ + gceVERTEX_FORMAT format; + + /* Flag whether the attribute is normalized or not. */ + gctBOOL normalized; + + /* Stride of the component. */ + gctUINT stride; + + /* Divisor of the attribute */ + gctUINT divisor; + + /* Pointer to the attribute data. */ + gctCONST_POINTER pointer; + + /* Stream object owning the attribute data. */ + gcoSTREAM stream; + + /* Generic values for attribute. */ + gctFLOAT genericValue[4]; + + /* Generic size for attribute. */ + gctINT genericSize; + + /* Vertex shader linkage. */ + gctUINT linkage; + + gctBOOL isPosition; +} +gcsVERTEXARRAY, +* gcsVERTEXARRAY_PTR; + +gceSTATUS +gcoVERTEXARRAY_Construct( + IN gcoHAL Hal, + OUT gcoVERTEXARRAY * Vertex + ); + +gceSTATUS +gcoVERTEXARRAY_Destroy( + IN gcoVERTEXARRAY Vertex + ); + +gceSTATUS +gcoVERTEXARRAY_Bind_Ex( + IN gcoVERTEXARRAY Vertex, + IN gctUINT32 EnableBits, + IN gcsVERTEXARRAY_PTR VertexArray, + IN gctUINT First, + IN gctSIZE_T Count, + IN gctBOOL DrawArraysInstanced, + IN gctSIZE_T InstanceCount, + IN gceINDEX_TYPE IndexType, + IN gcoINDEX IndexObject, + IN gctPOINTER IndexMemory, + IN OUT gcePRIMITIVE * PrimitiveType, +#if gcdUSE_WCLIP_PATCH + IN OUT gctUINT * PrimitiveCount, + IN OUT gctFLOAT * wLimitRms, + IN OUT gctBOOL * wLimitDirty +#else + IN OUT gctUINT * PrimitiveCount +#endif + ); + +gceSTATUS +gcoVERTEXARRAY_Bind_Ex2( + IN gcoVERTEXARRAY Vertex, + IN gctUINT32 EnableBits, + IN gcsATTRIBUTE_PTR VertexArray, + IN gctSIZE_T First, + IN gctSIZE_T Count, + IN gctBOOL DrawArraysInstanced, + IN gctSIZE_T InstanceCount, + IN gceINDEX_TYPE IndexType, + IN gcoBUFOBJ IndexObject, + IN gctPOINTER IndexMemory, + IN OUT gcePRIMITIVE * PrimitiveType, +#if gcdUSE_WCLIP_PATCH + IN OUT gctSIZE_T * PrimitiveCount, + IN OUT gctFLOAT * wLimitRms, + IN OUT gctBOOL * wLimitDirty, +#else + IN OUT gctUINT * PrimitiveCount, +#endif + IN gctINT VertexInstanceIdLinkage + ); + +gceSTATUS +gcoVERTEXARRAY_Bind( + IN gcoVERTEXARRAY Vertex, + IN gctUINT32 EnableBits, + IN gcsVERTEXARRAY_PTR VertexArray, + IN gctUINT First, + IN gctSIZE_T Count, + IN gceINDEX_TYPE IndexType, + IN gcoINDEX IndexObject, + IN gctPOINTER IndexMemory, + IN OUT gcePRIMITIVE * PrimitiveType, +#if gcdUSE_WCLIP_PATCH + IN OUT gctUINT * PrimitiveCount, + IN OUT gctFLOAT * wLimitRms, + IN OUT gctBOOL * wLimitDirty +#else + IN OUT gctUINT * PrimitiveCount +#endif + ); + +/******************************************************************************* +***** Composition *************************************************************/ + +typedef enum _gceCOMPOSITION +{ + gcvCOMPOSE_CLEAR = 1, + gcvCOMPOSE_BLUR, + gcvCOMPOSE_DIM, + gcvCOMPOSE_LAYER +} +gceCOMPOSITION; + +typedef struct _gcsCOMPOSITION * gcsCOMPOSITION_PTR; +typedef struct _gcsCOMPOSITION +{ + /* Structure size. */ + gctUINT structSize; + + /* Composition operation. */ + gceCOMPOSITION operation; + + /* Layer to be composed. */ + gcoSURF layer; + + /* Source and target coordinates. */ + gcsRECT srcRect; + gcsRECT trgRect; + + /* Target rectangle */ + gcsPOINT v0; + gcsPOINT v1; + gcsPOINT v2; + + /* Blending parameters. */ + gctBOOL enableBlending; + gctBOOL premultiplied; + gctUINT8 alphaValue; + + /* Clear color. */ + gctFLOAT r; + gctFLOAT g; + gctFLOAT b; + gctFLOAT a; +} +gcsCOMPOSITION; + +gceSTATUS +gco3D_ProbeComposition( + IN gcoHARDWARE Hardware, + IN gctBOOL ResetIfEmpty + ); + +gceSTATUS +gco3D_CompositionBegin( + IN gcoHARDWARE Hardware + ); + +gceSTATUS +gco3D_ComposeLayer( + IN gcoHARDWARE Hardware, + IN gcsCOMPOSITION_PTR Layer + ); + +gceSTATUS +gco3D_CompositionSignals( + IN gcoHARDWARE Hardware, + IN gctHANDLE Process, + IN gctSIGNAL Signal1, + IN gctSIGNAL Signal2 + ); + +gceSTATUS +gco3D_CompositionEnd( + IN gcoHARDWARE Hardware, + IN gcoSURF Target, + IN gctBOOL Synchronous + ); + +/* Frame Database */ +gceSTATUS +gcoHAL_AddFrameDB( + void + ); + +gceSTATUS +gcoHAL_DumpFrameDB( + gctCONST_STRING Filename OPTIONAL + ); + +/****************************************************************************** +**********************gcoBUFOBJ object***************************************** +*******************************************************************************/ +typedef enum _gceBUFOBJ_TYPE +{ + gcvBUFOBJ_TYPE_ARRAY_BUFFER = 1, + gcvBUFOBJ_TYPE_ELEMENT_ARRAY_BUFFER = 2, + gcvBUFOBJ_TYPE_GENERIC_BUFFER = 100 + +} gceBUFOBJ_TYPE; + +typedef enum _gceBUFOBJ_USAGE +{ + gcvBUFOBJ_USAGE_STREAM_DRAW = 1, + gcvBUFOBJ_USAGE_STREAM_READ, + gcvBUFOBJ_USAGE_STREAM_COPY, + gcvBUFOBJ_USAGE_STATIC_DRAW, + gcvBUFOBJ_USAGE_STATIC_READ, + gcvBUFOBJ_USAGE_STATIC_COPY, + gcvBUFOBJ_USAGE_DYNAMIC_DRAW, + gcvBUFOBJ_USAGE_DYNAMIC_READ, + gcvBUFOBJ_USAGE_DYNAMIC_COPY, + +} gceBUFOBJ_USAGE; + +/* Construct a new gcoBUFOBJ object. */ +gceSTATUS +gcoBUFOBJ_Construct( + IN gcoHAL Hal, + IN gceBUFOBJ_TYPE Type, + OUT gcoBUFOBJ * BufObj + ); + +/* Destroy a gcoBUFOBJ object. */ +gceSTATUS +gcoBUFOBJ_Destroy( + IN gcoBUFOBJ BufObj + ); + +/* Lock pbo in memory. */ +gceSTATUS +gcoBUFOBJ_Lock( + IN gcoBUFOBJ BufObj, + OUT gctUINT32 * Address, + OUT gctPOINTER * Memory + ); + +/* Lock pbo in memory. */ +gceSTATUS +gcoBUFOBJ_FastLock( + IN gcoBUFOBJ BufObj, + OUT gctUINT32 * Address, + OUT gctPOINTER * Memory + ); + +/* Unlock pbo that was previously locked with gcoBUFOBJ_Lock. */ +gceSTATUS +gcoBUFOBJ_Unlock( + IN gcoBUFOBJ BufObj + ); + +/* Free existing pbo buffer. */ +gceSTATUS +gcoBUFOBJ_Free( + IN gcoBUFOBJ BufObj + ); + +/* Upload data into an pbo buffer. */ +gceSTATUS +gcoBUFOBJ_Upload( + IN gcoBUFOBJ BufObj, + IN gctCONST_POINTER Buffer, + IN gctSIZE_T Offset, + IN gctSIZE_T Bytes, + IN gceBUFOBJ_USAGE Usage + ); + +/* Bind an index object to the hardware. */ +gceSTATUS +gcoBUFOBJ_IndexBind ( + IN gcoBUFOBJ Index, + IN gceINDEX_TYPE Type, + IN gctUINT32 Offset, + IN gctSIZE_T Count + ); + +/* Find min and max index for the index buffer */ +gceSTATUS +gcoBUFOBJ_IndexGetRange( + IN gcoBUFOBJ Index, + IN gceINDEX_TYPE Type, + IN gctUINT32 Offset, + IN gctUINT32 Count, + OUT gctUINT32 * MinimumIndex, + OUT gctUINT32 * MaximumIndex + ); + +/* Sets a buffer object as dirty */ +gceSTATUS +gcoBUFOBJ_SetDirty( + IN gcoBUFOBJ BufObj + ); + +/* Creates a new buffer if needed */ +gceSTATUS +gcoBUFOBJ_AlignIndexBufferWhenNeeded( + IN gcoBUFOBJ BufObj, + IN gctSIZE_T Offset, + OUT gcoBUFOBJ * AlignedBufObj + ); + +/* Cache operations on whole range */ +gceSTATUS +gcoBUFOBJ_CPUCacheOperation( + IN gcoBUFOBJ BufObj, + IN gceCACHEOPERATION Operation + ); + +/* Cache operations on a specified range */ +gceSTATUS +gcoBUFOBJ_CPUCacheOperation_Range( + IN gcoBUFOBJ BufObj, + IN gctSIZE_T Offset, + IN gctSIZE_T Length, + IN gceCACHEOPERATION Operation + ); + +/* Return size of the bufobj */ +gceSTATUS +gcoBUFOBJ_GetSize( + IN gcoBUFOBJ BufObj, + OUT gctSIZE_T_PTR Size + ); + +/* Return memory node of the bufobj */ +gceSTATUS +gcoBUFOBJ_GetNode( + IN gcoBUFOBJ BufObj, + OUT gcsSURF_NODE_PTR * Node + ); + +/* Handle GPU cache operations */ +gceSTATUS +gcoBUFOBJ_GPUCacheOperation( + gcoBUFOBJ BufObj + ); + +/* Dump buffer. */ +void +gcoBUFOBJ_Dump( + IN gcoBUFOBJ BufObj + ); + +#ifdef __cplusplus +} +#endif + +#endif /* gcdENABLE_3D */ +#endif /* __gc_hal_engine_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_engine_vg.h b/drivers/gpu/galcore/inc/gc_hal_engine_vg.h new file mode 100644 index 00000000000000..7e98d054c34199 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_engine_vg.h @@ -0,0 +1,1215 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_engine_vg_h_ +#define __gc_hal_engine_vg_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "gc_hal_types.h" + +/******************************************************************************\ +******************************** VG Enumerations ******************************* +\******************************************************************************/ + +/** +** @ingroup gcoVG +** +** @brief Tiling mode for painting and imagig. +** +** This enumeration defines the tiling modes supported by the HAL. This is +** in fact a one-to-one mapping of the OpenVG 1.1 tile modes. +*/ +typedef enum _gceTILE_MODE +{ + gcvTILE_FILL, + gcvTILE_PAD, + gcvTILE_REPEAT, + gcvTILE_REFLECT +} +gceTILE_MODE; + +/******************************************************************************/ +/** @ingroup gcoVG +** +** @brief The different paint modes. +** +** This enumeration lists the available paint modes. +*/ +typedef enum _gcePAINT_TYPE +{ + /** Solid color. */ + gcvPAINT_MODE_SOLID, + + /** Linear gradient. */ + gcvPAINT_MODE_LINEAR, + + /** Radial gradient. */ + gcvPAINT_MODE_RADIAL, + + /** Pattern. */ + gcvPAINT_MODE_PATTERN, + + /** Mode count. */ + gcvPAINT_MODE_COUNT +} +gcePAINT_TYPE; + +/** +** @ingroup gcoVG +** +** @brief Types of path data supported by HAL. +** +** This enumeration defines the types of path data supported by the HAL. +** This is in fact a one-to-one mapping of the OpenVG 1.1 path types. +*/ +typedef enum _gcePATHTYPE +{ + gcePATHTYPE_UNKNOWN = -1, + gcePATHTYPE_INT8, + gcePATHTYPE_INT16, + gcePATHTYPE_INT32, + gcePATHTYPE_FLOAT +} +gcePATHTYPE; + +/** +** @ingroup gcoVG +** +** @brief Supported path segment commands. +** +** This enumeration defines the path segment commands supported by the HAL. +*/ +typedef enum _gceVGCMD +{ + gcvVGCMD_END, /* 0: GCCMD_TS_OPCODE_END */ + gcvVGCMD_CLOSE, /* 1: GCCMD_TS_OPCODE_CLOSE */ + gcvVGCMD_MOVE, /* 2: GCCMD_TS_OPCODE_MOVE */ + gcvVGCMD_MOVE_REL, /* 3: GCCMD_TS_OPCODE_MOVE_REL */ + gcvVGCMD_LINE, /* 4: GCCMD_TS_OPCODE_LINE */ + gcvVGCMD_LINE_REL, /* 5: GCCMD_TS_OPCODE_LINE_REL */ + gcvVGCMD_QUAD, /* 6: GCCMD_TS_OPCODE_QUADRATIC */ + gcvVGCMD_QUAD_REL, /* 7: GCCMD_TS_OPCODE_QUADRATIC_REL */ + gcvVGCMD_CUBIC, /* 8: GCCMD_TS_OPCODE_CUBIC */ + gcvVGCMD_CUBIC_REL, /* 9: GCCMD_TS_OPCODE_CUBIC_REL */ + gcvVGCMD_BREAK, /* 10: GCCMD_TS_OPCODE_BREAK */ + gcvVGCMD_HLINE, /* 11: ******* R E S E R V E D *******/ + gcvVGCMD_HLINE_REL, /* 12: ******* R E S E R V E D *******/ + gcvVGCMD_VLINE, /* 13: ******* R E S E R V E D *******/ + gcvVGCMD_VLINE_REL, /* 14: ******* R E S E R V E D *******/ + gcvVGCMD_SQUAD, /* 15: ******* R E S E R V E D *******/ + gcvVGCMD_SQUAD_REL, /* 16: ******* R E S E R V E D *******/ + gcvVGCMD_SCUBIC, /* 17: ******* R E S E R V E D *******/ + gcvVGCMD_SCUBIC_REL, /* 18: ******* R E S E R V E D *******/ + gcvVGCMD_SCCWARC, /* 19: ******* R E S E R V E D *******/ + gcvVGCMD_SCCWARC_REL, /* 20: ******* R E S E R V E D *******/ + gcvVGCMD_SCWARC, /* 21: ******* R E S E R V E D *******/ + gcvVGCMD_SCWARC_REL, /* 22: ******* R E S E R V E D *******/ + gcvVGCMD_LCCWARC, /* 23: ******* R E S E R V E D *******/ + gcvVGCMD_LCCWARC_REL, /* 24: ******* R E S E R V E D *******/ + gcvVGCMD_LCWARC, /* 25: ******* R E S E R V E D *******/ + gcvVGCMD_LCWARC_REL, /* 26: ******* R E S E R V E D *******/ + + /* The width of the command recognized by the hardware on bits. */ + gcvVGCMD_WIDTH = 5, + + /* Hardware command mask. */ + gcvVGCMD_MASK = (1 << gcvVGCMD_WIDTH) - 1, + + /* Command modifiers. */ + gcvVGCMD_H_MOD = 1 << gcvVGCMD_WIDTH, /* = 32 */ + gcvVGCMD_V_MOD = 2 << gcvVGCMD_WIDTH, /* = 64 */ + gcvVGCMD_S_MOD = 3 << gcvVGCMD_WIDTH, /* = 96 */ + gcvVGCMD_ARC_MOD = 4 << gcvVGCMD_WIDTH, /* = 128 */ + + /* Emulated LINE commands. */ + gcvVGCMD_HLINE_EMUL = gcvVGCMD_H_MOD | gcvVGCMD_LINE, /* = 36 */ + gcvVGCMD_HLINE_EMUL_REL = gcvVGCMD_H_MOD | gcvVGCMD_LINE_REL, /* = 37 */ + gcvVGCMD_VLINE_EMUL = gcvVGCMD_V_MOD | gcvVGCMD_LINE, /* = 68 */ + gcvVGCMD_VLINE_EMUL_REL = gcvVGCMD_V_MOD | gcvVGCMD_LINE_REL, /* = 69 */ + + /* Emulated SMOOTH commands. */ + gcvVGCMD_SQUAD_EMUL = gcvVGCMD_S_MOD | gcvVGCMD_QUAD, /* = 102 */ + gcvVGCMD_SQUAD_EMUL_REL = gcvVGCMD_S_MOD | gcvVGCMD_QUAD_REL, /* = 103 */ + gcvVGCMD_SCUBIC_EMUL = gcvVGCMD_S_MOD | gcvVGCMD_CUBIC, /* = 104 */ + gcvVGCMD_SCUBIC_EMUL_REL = gcvVGCMD_S_MOD | gcvVGCMD_CUBIC_REL, /* = 105 */ + + /* Emulation ARC commands. */ + gcvVGCMD_ARC_LINE = gcvVGCMD_ARC_MOD | gcvVGCMD_LINE, /* = 132 */ + gcvVGCMD_ARC_LINE_REL = gcvVGCMD_ARC_MOD | gcvVGCMD_LINE_REL, /* = 133 */ + gcvVGCMD_ARC_QUAD = gcvVGCMD_ARC_MOD | gcvVGCMD_QUAD, /* = 134 */ + gcvVGCMD_ARC_QUAD_REL = gcvVGCMD_ARC_MOD | gcvVGCMD_QUAD_REL /* = 135 */ +} +gceVGCMD; +typedef enum _gceVGCMD * gceVGCMD_PTR; + +/** +** @ingroup gcoVG +** +** @brief Blending modes supported by the HAL. +** +** This enumeration defines the blending modes supported by the HAL. This is +** in fact a one-to-one mapping of the OpenVG 1.1 blending modes. +*/ +typedef enum _gceVG_BLEND +{ + gcvVG_BLEND_SRC, + gcvVG_BLEND_SRC_OVER, + gcvVG_BLEND_DST_OVER, + gcvVG_BLEND_SRC_IN, + gcvVG_BLEND_DST_IN, + gcvVG_BLEND_MULTIPLY, + gcvVG_BLEND_SCREEN, + gcvVG_BLEND_DARKEN, + gcvVG_BLEND_LIGHTEN, + gcvVG_BLEND_ADDITIVE, + gcvVG_BLEND_SUBTRACT, + gcvVG_BLEND_FILTER +} +gceVG_BLEND; + +/** +** @ingroup gcoVG +** +** @brief Image modes supported by the HAL. +** +** This enumeration defines the image modes supported by the HAL. This is +** in fact a one-to-one mapping of the OpenVG 1.1 image modes with the addition +** of NO IMAGE. +*/ +typedef enum _gceVG_IMAGE +{ + gcvVG_IMAGE_NONE, + gcvVG_IMAGE_NORMAL, + gcvVG_IMAGE_MULTIPLY, + gcvVG_IMAGE_STENCIL, + gcvVG_IMAGE_FILTER +} +gceVG_IMAGE; + +/** +** @ingroup gcoVG +** +** @brief Filter mode patterns and imaging. +** +** This enumeration defines the filter modes supported by the HAL. +*/ +typedef enum _gceIMAGE_FILTER +{ + gcvFILTER_POINT, + gcvFILTER_LINEAR, + gcvFILTER_BI_LINEAR +} +gceIMAGE_FILTER; + +/** +** @ingroup gcoVG +** +** @brief Primitive modes supported by the HAL. +** +** This enumeration defines the primitive modes supported by the HAL. +*/ +typedef enum _gceVG_PRIMITIVE +{ + gcvVG_SCANLINE, + gcvVG_RECTANGLE, + gcvVG_TESSELLATED, + gcvVG_TESSELLATED_TILED +} +gceVG_PRIMITIVE; + +/** +** @ingroup gcoVG +** +** @brief Rendering quality modes supported by the HAL. +** +** This enumeration defines the rendering quality modes supported by the HAL. +*/ +typedef enum _gceRENDER_QUALITY +{ + gcvVG_NONANTIALIASED, + gcvVG_2X2_MSAA, + gcvVG_2X4_MSAA, + gcvVG_4X4_MSAA +} +gceRENDER_QUALITY; + +/** +** @ingroup gcoVG +** +** @brief Fill rules supported by the HAL. +** +** This enumeration defines the fill rules supported by the HAL. +*/ +typedef enum _gceFILL_RULE +{ + gcvVG_EVEN_ODD, + gcvVG_NON_ZERO +} +gceFILL_RULE; + +/** +** @ingroup gcoVG +** +** @brief Cap styles supported by the HAL. +** +** This enumeration defines the cap styles supported by the HAL. +*/ +typedef enum _gceCAP_STYLE +{ + gcvCAP_BUTT, + gcvCAP_ROUND, + gcvCAP_SQUARE +} +gceCAP_STYLE; + +/** +** @ingroup gcoVG +** +** @brief Join styles supported by the HAL. +** +** This enumeration defines the join styles supported by the HAL. +*/ +typedef enum _gceJOIN_STYLE +{ + gcvJOIN_MITER, + gcvJOIN_ROUND, + gcvJOIN_BEVEL +} +gceJOIN_STYLE; + +/** +** @ingroup gcoVG +** +** @brief Channel mask values. +** +** This enumeration defines the values for channel mask used in image +** filtering. +*/ + +/* Base values for channel mask definitions. */ +#define gcvCHANNEL_X (0) +#define gcvCHANNEL_R (1 << 0) +#define gcvCHANNEL_G (1 << 1) +#define gcvCHANNEL_B (1 << 2) +#define gcvCHANNEL_A (1 << 3) + +typedef enum _gceCHANNEL +{ + gcvCHANNEL_XXXX = (gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_X), + gcvCHANNEL_XXXA = (gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_A), + gcvCHANNEL_XXBX = (gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_B | gcvCHANNEL_X), + gcvCHANNEL_XXBA = (gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_B | gcvCHANNEL_A), + + gcvCHANNEL_XGXX = (gcvCHANNEL_X | gcvCHANNEL_G | gcvCHANNEL_X | gcvCHANNEL_X), + gcvCHANNEL_XGXA = (gcvCHANNEL_X | gcvCHANNEL_G | gcvCHANNEL_X | gcvCHANNEL_A), + gcvCHANNEL_XGBX = (gcvCHANNEL_X | gcvCHANNEL_G | gcvCHANNEL_B | gcvCHANNEL_X), + gcvCHANNEL_XGBA = (gcvCHANNEL_X | gcvCHANNEL_G | gcvCHANNEL_B | gcvCHANNEL_A), + + gcvCHANNEL_RXXX = (gcvCHANNEL_R | gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_X), + gcvCHANNEL_RXXA = (gcvCHANNEL_R | gcvCHANNEL_X | gcvCHANNEL_X | gcvCHANNEL_A), + gcvCHANNEL_RXBX = (gcvCHANNEL_R | gcvCHANNEL_X | gcvCHANNEL_B | gcvCHANNEL_X), + gcvCHANNEL_RXBA = (gcvCHANNEL_R | gcvCHANNEL_X | gcvCHANNEL_B | gcvCHANNEL_A), + + gcvCHANNEL_RGXX = (gcvCHANNEL_R | gcvCHANNEL_G | gcvCHANNEL_X | gcvCHANNEL_X), + gcvCHANNEL_RGXA = (gcvCHANNEL_R | gcvCHANNEL_G | gcvCHANNEL_X | gcvCHANNEL_A), + gcvCHANNEL_RGBX = (gcvCHANNEL_R | gcvCHANNEL_G | gcvCHANNEL_B | gcvCHANNEL_X), + gcvCHANNEL_RGBA = (gcvCHANNEL_R | gcvCHANNEL_G | gcvCHANNEL_B | gcvCHANNEL_A), +} +gceCHANNEL; + +/******************************************************************************\ +******************************** VG Structures ******************************* +\******************************************************************************/ + +/** +** @ingroup gcoVG +** +** @brief Definition of the color ramp used by the gradient paints. +** +** The gcsCOLOR_RAMP structure defines the layout of one single color inside +** a color ramp which is used by gradient paints. +*/ +typedef struct _gcsCOLOR_RAMP +{ + /** Value for the color stop. */ + gctFLOAT stop; + + /** Red color channel value for the color stop. */ + gctFLOAT red; + + /** Green color channel value for the color stop. */ + gctFLOAT green; + + /** Blue color channel value for the color stop. */ + gctFLOAT blue; + + /** Alpha color channel value for the color stop. */ + gctFLOAT alpha; +} +gcsCOLOR_RAMP, * gcsCOLOR_RAMP_PTR; + +/** +** @ingroup gcoVG +** +** @brief Definition of the color ramp used by the gradient paints in fixed form. +** +** The gcsCOLOR_RAMP structure defines the layout of one single color inside +** a color ramp which is used by gradient paints. +*/ +typedef struct _gcsFIXED_COLOR_RAMP +{ + /** Value for the color stop. */ + gctFIXED_POINT stop; + + /** Red color channel value for the color stop. */ + gctFIXED_POINT red; + + /** Green color channel value for the color stop. */ + gctFIXED_POINT green; + + /** Blue color channel value for the color stop. */ + gctFIXED_POINT blue; + + /** Alpha color channel value for the color stop. */ + gctFIXED_POINT alpha; +} +gcsFIXED_COLOR_RAMP, * gcsFIXED_COLOR_RAMP_PTR; + + +/** +** @ingroup gcoVG +** +** @brief Rectangle structure used by the gcoVG object. +** +** This structure defines the layout of a rectangle. Make sure width and +** height are larger than 0. +*/ +typedef struct _gcsVG_RECT * gcsVG_RECT_PTR; +typedef struct _gcsVG_RECT +{ + /** Left location of the rectangle. */ + gctINT x; + + /** Top location of the rectangle. */ + gctINT y; + + /** Width of the rectangle. */ + gctINT width; + + /** Height of the rectangle. */ + gctINT height; +} +gcsVG_RECT; + +/** +** @ingroup gcoVG +** +** @brief Path command buffer attribute structure. +** +** The gcsPATH_BUFFER_INFO structure contains the specifics about +** the layout of the path data command buffer. +*/ +typedef struct _gcsPATH_BUFFER_INFO * gcsPATH_BUFFER_INFO_PTR; +typedef struct _gcsPATH_BUFFER_INFO +{ + gctUINT reservedForHead; + gctUINT reservedForTail; +} +gcsPATH_BUFFER_INFO; + +/** +** @ingroup gcoVG +** +** @brief Definition of the path data container structure. +** +** The gcsPATH structure defines the layout of the path data container. +*/ +typedef struct _gcsPATH_DATA * gcsPATH_DATA_PTR; +typedef struct _gcsPATH_DATA +{ + /* Data container in command buffer format. */ + gcsCMDBUFFER data; + + /* Path data type. */ + gcePATHTYPE dataType; +} +gcsPATH_DATA; + + +/******************************************************************************\ +********************************* gcoHAL Object ******************************** +\******************************************************************************/ + +/* Query path data storage attributes. */ +gceSTATUS +gcoHAL_QueryPathStorage( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + OUT gcsPATH_BUFFER_INFO_PTR Information + ); + +/* Associate a completion signal with the command buffer. */ +gceSTATUS +gcoHAL_AssociateCompletion( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcsPATH_DATA_PTR PathData + ); + +/* Release the current command buffer completion signal. */ +gceSTATUS +gcoHAL_DeassociateCompletion( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcsPATH_DATA_PTR PathData + ); + +/* Verify whether the command buffer is still in use. */ +gceSTATUS +gcoHAL_CheckCompletion( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcsPATH_DATA_PTR PathData + ); + +/* Wait until the command buffer is no longer in use. */ +gceSTATUS +gcoHAL_WaitCompletion( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcsPATH_DATA_PTR PathData + ); + +/* Flush the pixel cache. */ +gceSTATUS +gcoHAL_Flush( + IN gcoHAL Hal +#if GC355_PROFILER + , + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth +#endif + ); + +/* Split a harwdare address into pool and offset. */ +gceSTATUS +gcoHAL_SplitAddress( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctUINT32 Address, + OUT gcePOOL * Pool, + OUT gctUINT32 * Offset + ); + +/* Combine pool and offset into a harwdare address. */ +gceSTATUS +gcoHAL_CombineAddress( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcePOOL Pool, + IN gctUINT32 Offset, + OUT gctUINT32 * Address + ); + +/* Schedule to free linear video memory allocated. */ +gceSTATUS +gcoHAL_ScheduleVideoMemory( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctUINT32 Node + ); + +/* Free linear video memory allocated with gcoHAL_AllocateLinearVideoMemory. */ +gceSTATUS +gcoHAL_FreeVideoMemory( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctUINT32 Node + ); + +/* Query command buffer attributes. */ +gceSTATUS +gcoHAL_QueryCommandBuffer( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + OUT gcsCOMMAND_BUFFER_INFO_PTR Information + ); +/* Allocate and lock linear video memory. */ +gceSTATUS +gcoHAL_AllocateLinearVideoMemory( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctUINT Size, + IN gctUINT Alignment, + IN gcePOOL Pool, + OUT gctUINT32 * Node, + OUT gctUINT32 * Address, + OUT gctPOINTER * Memory + ); + +/* Align the specified size accordingly to the hardware requirements. */ +gceSTATUS +gcoHAL_GetAlignedSurfaceSize( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceSURF_TYPE Type, + IN OUT gctUINT32_PTR Width, + IN OUT gctUINT32_PTR Height + ); + +gceSTATUS +gcoHAL_ReserveTask( + IN gcoHAL Hal, +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceBLOCK Block, + IN gctUINT TaskCount, + IN gctUINT32 Bytes, + OUT gctPOINTER * Memory + ); +/******************************************************************************\ +********************************** gcoVG Object ******************************** +\******************************************************************************/ + +/** @defgroup gcoVG gcoVG +** +** The gcoVG object abstracts the VG hardware pipe. +*/ +#if GC355_PROFILER +void +gcoVG_ProfilerEnableDisable( + IN gcoVG Vg, + IN gctUINT enableGetAPITimes, + IN gctFILE apiTimeFile + ); + +void +gcoVG_ProfilerTreeDepth( + IN gcoVG Vg, + IN gctUINT TreeDepth + ); + +void +gcoVG_ProfilerSetStates( + IN gcoVG Vg, + IN gctUINT treeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth + ); +#endif + +gctBOOL +gcoVG_IsMaskSupported( +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceSURF_FORMAT Format + ); + +gctBOOL +gcoVG_IsTargetSupported( +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceSURF_FORMAT Format + ); + +gctBOOL +gcoVG_IsImageSupported( +#if GC355_PROFILER + IN gcoVG Vg, + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceSURF_FORMAT Format + ); + +gctUINT8 gcoVG_PackColorComponent( +#if GC355_PROFILER + gcoVG Vg, + gctUINT TreeDepth, + gctUINT saveLayerTreeDepth, + gctUINT varTreeDepth, +#endif + gctFLOAT Value + ); + +gceSTATUS +gcoVG_Construct( + IN gcoHAL Hal, + OUT gcoVG * Vg + ); + +gceSTATUS +gcoVG_Destroy( + IN gcoVG Vg +#if GC355_PROFILER + , + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth +#endif + ); + +gceSTATUS +gcoVG_SetTarget( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Target + ); + +gceSTATUS +gcoVG_UnsetTarget( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Surface + ); + +gceSTATUS +gcoVG_SetUserToSurface( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT UserToSurface[9] + ); + +gceSTATUS +gcoVG_SetSurfaceToImage( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT SurfaceToImage[9] + ); + +gceSTATUS +gcoVG_EnableMask( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctBOOL Enable + ); + +gceSTATUS +gcoVG_SetMask( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Mask + ); + +gceSTATUS +gcoVG_UnsetMask( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Surface + ); + +gceSTATUS +gcoVG_FlushMask( + IN gcoVG Vg +#if GC355_PROFILER + , + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth +#endif + ); + +gceSTATUS +gcoVG_EnableScissor( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctBOOL Enable + ); + +gceSTATUS +gcoVG_SetScissor( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctSIZE_T RectangleCount, + IN gcsVG_RECT_PTR Rectangles + ); + +gceSTATUS +gcoVG_EnableColorTransform( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctBOOL Enable + ); + +gceSTATUS +gcoVG_SetColorTransform( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT ColorTransform[8] + ); + +gceSTATUS +gcoVG_SetTileFillColor( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT Red, + IN gctFLOAT Green, + IN gctFLOAT Blue, + IN gctFLOAT Alpha + ); + +gceSTATUS +gcoVG_SetSolidPaint( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctUINT8 Red, + IN gctUINT8 Green, + IN gctUINT8 Blue, + IN gctUINT8 Alpha + ); + +gceSTATUS +gcoVG_SetLinearPaint( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT Constant, + IN gctFLOAT StepX, + IN gctFLOAT StepY + ); + +gceSTATUS +gcoVG_SetRadialPaint( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT LinConstant, + IN gctFLOAT LinStepX, + IN gctFLOAT LinStepY, + IN gctFLOAT RadConstant, + IN gctFLOAT RadStepX, + IN gctFLOAT RadStepY, + IN gctFLOAT RadStepXX, + IN gctFLOAT RadStepYY, + IN gctFLOAT RadStepXY + ); + +gceSTATUS +gcoVG_SetPatternPaint( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctFLOAT UConstant, + IN gctFLOAT UStepX, + IN gctFLOAT UStepY, + IN gctFLOAT VConstant, + IN gctFLOAT VStepX, + IN gctFLOAT VStepY, + IN gctBOOL Linear + ); + +gceSTATUS +gcoVG_SetColorRamp( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF ColorRamp, + IN gceTILE_MODE ColorRampSpreadMode + ); + +gceSTATUS +gcoVG_SetPattern( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctINT32 width, + IN gctINT32 height, + IN gcoSURF Pattern, + IN gceTILE_MODE TileMode, + IN gceIMAGE_FILTER Filter + ); + +gceSTATUS +gcoVG_SetImageMode( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceVG_IMAGE Mode + ); + +gceSTATUS +gcoVG_SetBlendMode( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceVG_BLEND Mode + ); + +gceSTATUS +gcoVG_SetRenderingQuality( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceRENDER_QUALITY Quality + ); + +gceSTATUS +gcoVG_SetFillRule( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gceFILL_RULE FillRule + ); + +gceSTATUS +gcoVG_FinalizePath( + IN gcoVG Vg, + IN gcsPATH_DATA_PTR PathData + ); + +gceSTATUS +gcoVG_Clear( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctINT X, + IN gctINT Y, + IN gctINT Width, + IN gctINT Height + ); + +gceSTATUS +gcoVG_DrawPath( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcsPATH_DATA_PTR PathData, + IN gctFLOAT Scale, + IN gctFLOAT Bias, +#if gcdMOVG + IN gctUINT32 Width, + IN gctUINT32 Height, + IN gctFLOAT *Bounds, +#endif + IN gctBOOL SoftwareTesselation + ); + +gceSTATUS +gcoVG_DrawImage( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Source, + IN gcsPOINT_PTR SourceOrigin, + IN gcsPOINT_PTR TargetOrigin, + IN gcsSIZE_PTR SourceSize, + IN gctINT SourceX, + IN gctINT SourceY, + IN gctINT TargetX, + IN gctINT TargetY, + IN gctINT Width, + IN gctINT Height, + IN gctBOOL Mask, + IN gctBOOL isDrawImage + ); + +gceSTATUS +gcoVG_TesselateImage( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Image, + IN gcsVG_RECT_PTR Rectangle, + IN gceIMAGE_FILTER Filter, + IN gctBOOL Mask, +#if gcdMOVG + IN gctBOOL SoftwareTesselation, + IN gceVG_BLEND BlendMode +#else + IN gctBOOL SoftwareTesselation +#endif + ); + +gceSTATUS +gcoVG_Blit( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Source, + IN gcoSURF Target, + IN gcsVG_RECT_PTR SrcRect, + IN gcsVG_RECT_PTR TrgRect, + IN gceIMAGE_FILTER Filter, + IN gceVG_BLEND Mode + ); + +gceSTATUS +gcoVG_ColorMatrix( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Source, + IN gcoSURF Target, + IN const gctFLOAT * Matrix, + IN gceCHANNEL ColorChannels, + IN gctBOOL FilterLinear, + IN gctBOOL FilterPremultiplied, + IN gcsPOINT_PTR SourceOrigin, + IN gcsPOINT_PTR TargetOrigin, + IN gctINT Width, + IN gctINT Height + ); + +gceSTATUS +gcoVG_SeparableConvolve( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Source, + IN gcoSURF Target, + IN gctINT KernelWidth, + IN gctINT KernelHeight, + IN gctINT ShiftX, + IN gctINT ShiftY, + IN const gctINT16 * KernelX, + IN const gctINT16 * KernelY, + IN gctFLOAT Scale, + IN gctFLOAT Bias, + IN gceTILE_MODE TilingMode, + IN gctFLOAT_PTR FillColor, + IN gceCHANNEL ColorChannels, + IN gctBOOL FilterLinear, + IN gctBOOL FilterPremultiplied, + IN gcsPOINT_PTR SourceOrigin, + IN gcsPOINT_PTR TargetOrigin, + IN gcsSIZE_PTR SourceSize, + IN gctINT Width, + IN gctINT Height + ); + +gceSTATUS +gcoVG_GaussianBlur( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gcoSURF Source, + IN gcoSURF Target, + IN gctFLOAT StdDeviationX, + IN gctFLOAT StdDeviationY, + IN gceTILE_MODE TilingMode, + IN gctFLOAT_PTR FillColor, + IN gceCHANNEL ColorChannels, + IN gctBOOL FilterLinear, + IN gctBOOL FilterPremultiplied, + IN gcsPOINT_PTR SourceOrigin, + IN gcsPOINT_PTR TargetOrigin, + IN gcsSIZE_PTR SourceSize, + IN gctINT Width, + IN gctINT Height + ); + +gceSTATUS +gcoVG_EnableDither( + IN gcoVG Vg, +#if GC355_PROFILER + IN gctUINT TreeDepth, + IN gctUINT saveLayerTreeDepth, + IN gctUINT varTreeDepth, +#endif + IN gctBOOL Enable + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_vg_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_enum.h b/drivers/gpu/galcore/inc/gc_hal_enum.h new file mode 100644 index 00000000000000..d20f00774b9871 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_enum.h @@ -0,0 +1,1608 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_enum_h_ +#define __gc_hal_enum_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Chip models. */ +typedef enum _gceCHIPMODEL +{ + gcv200 = 0x0200, + gcv300 = 0x0300, + gcv320 = 0x0320, + gcv328 = 0x0328, + gcv350 = 0x0350, + gcv355 = 0x0355, + gcv400 = 0x0400, + gcv410 = 0x0410, + gcv420 = 0x0420, + gcv428 = 0x0428, + gcv450 = 0x0450, + gcv500 = 0x0500, + gcv520 = 0x0520, + gcv530 = 0x0530, + gcv600 = 0x0600, + gcv700 = 0x0700, + gcv800 = 0x0800, + gcv860 = 0x0860, + gcv880 = 0x0880, + gcv1000 = 0x1000, + gcv1500 = 0x1500, + gcv2000 = 0x2000, + gcv2100 = 0x2100, + gcv2200 = 0x2200, + gcv2500 = 0x2500, + gcv3000 = 0x3000, + gcv4000 = 0x4000, + gcv5000 = 0x5000, + gcv5200 = 0x5200, + gcv6400 = 0x6400, +} +gceCHIPMODEL; + +/* Chip features. */ +typedef enum _gceFEATURE +{ + gcvFEATURE_PIPE_2D = 0, + gcvFEATURE_PIPE_3D, + gcvFEATURE_PIPE_VG, + gcvFEATURE_DC, + gcvFEATURE_HIGH_DYNAMIC_RANGE, + gcvFEATURE_MODULE_CG, + gcvFEATURE_MIN_AREA, + gcvFEATURE_BUFFER_INTERLEAVING, + gcvFEATURE_BYTE_WRITE_2D, + gcvFEATURE_ENDIANNESS_CONFIG, + gcvFEATURE_DUAL_RETURN_BUS, + gcvFEATURE_DEBUG_MODE, + gcvFEATURE_YUY2_RENDER_TARGET, + gcvFEATURE_FRAGMENT_PROCESSOR, + gcvFEATURE_2DPE20, + gcvFEATURE_FAST_CLEAR, + gcvFEATURE_YUV420_TILER, + gcvFEATURE_YUY2_AVERAGING, + gcvFEATURE_FLIP_Y, + gcvFEATURE_EARLY_Z, + gcvFEATURE_COMPRESSION, + gcvFEATURE_MSAA, + gcvFEATURE_SPECIAL_ANTI_ALIASING, + gcvFEATURE_SPECIAL_MSAA_LOD, + gcvFEATURE_422_TEXTURE_COMPRESSION, + gcvFEATURE_DXT_TEXTURE_COMPRESSION, + gcvFEATURE_ETC1_TEXTURE_COMPRESSION, + gcvFEATURE_CORRECT_TEXTURE_CONVERTER, + gcvFEATURE_TEXTURE_8K, + gcvFEATURE_SCALER, + gcvFEATURE_YUV420_SCALER, + gcvFEATURE_SHADER_HAS_W, + gcvFEATURE_SHADER_HAS_SIGN, + gcvFEATURE_SHADER_HAS_FLOOR, + gcvFEATURE_SHADER_HAS_CEIL, + gcvFEATURE_SHADER_HAS_SQRT, + gcvFEATURE_SHADER_HAS_TRIG, + gcvFEATURE_VAA, + gcvFEATURE_HZ, + gcvFEATURE_CORRECT_STENCIL, + gcvFEATURE_VG20, + gcvFEATURE_VG_FILTER, + gcvFEATURE_VG21, + gcvFEATURE_VG_DOUBLE_BUFFER, + gcvFEATURE_MC20, + gcvFEATURE_SUPER_TILED, + gcvFEATURE_FAST_CLEAR_FLUSH, + gcvFEATURE_2D_FILTERBLIT_PLUS_ALPHABLEND, + gcvFEATURE_2D_DITHER, + gcvFEATURE_2D_A8_TARGET, + gcvFEATURE_2D_A8_NO_ALPHA, + gcvFEATURE_2D_FILTERBLIT_FULLROTATION, + gcvFEATURE_2D_BITBLIT_FULLROTATION, + gcvFEATURE_WIDE_LINE, + gcvFEATURE_FC_FLUSH_STALL, + gcvFEATURE_FULL_DIRECTFB, + gcvFEATURE_HALF_FLOAT_PIPE, + gcvFEATURE_LINE_LOOP, + gcvFEATURE_2D_YUV_BLIT, + gcvFEATURE_2D_TILING, + gcvFEATURE_NON_POWER_OF_TWO, + gcvFEATURE_3D_TEXTURE, + gcvFEATURE_TEXTURE_ARRAY, + gcvFEATURE_TILE_FILLER, + gcvFEATURE_LOGIC_OP, + gcvFEATURE_COMPOSITION, + gcvFEATURE_MIXED_STREAMS, + gcvFEATURE_2D_MULTI_SOURCE_BLT, + gcvFEATURE_END_EVENT, + gcvFEATURE_VERTEX_10_10_10_2, + gcvFEATURE_TEXTURE_10_10_10_2, + gcvFEATURE_TEXTURE_ANISOTROPIC_FILTERING, + gcvFEATURE_TEXTURE_FLOAT_HALF_FLOAT, + gcvFEATURE_2D_ROTATION_STALL_FIX, + gcvFEATURE_2D_MULTI_SOURCE_BLT_EX, + gcvFEATURE_BUG_FIXES10, + gcvFEATURE_2D_MINOR_TILING, + /* Supertiled compressed textures are supported. */ + gcvFEATURE_TEX_COMPRRESSION_SUPERTILED, + gcvFEATURE_FAST_MSAA, + gcvFEATURE_BUG_FIXED_INDEXED_TRIANGLE_STRIP, + gcvFEATURE_TEXTURE_TILE_STATUS_READ, + gcvFEATURE_DEPTH_BIAS_FIX, + gcvFEATURE_RECT_PRIMITIVE, + gcvFEATURE_BUG_FIXES11, + gcvFEATURE_SUPERTILED_TEXTURE, + gcvFEATURE_2D_NO_COLORBRUSH_INDEX8, + gcvFEATURE_RS_YUV_TARGET, + gcvFEATURE_2D_FC_SOURCE, + gcvFEATURE_2D_CC_NOAA_SOURCE, + gcvFEATURE_PE_DITHER_FIX, + gcvFEATURE_2D_YUV_SEPARATE_STRIDE, + gcvFEATURE_FRUSTUM_CLIP_FIX, + gcvFEATURE_TEXTURE_SWIZZLE, + gcvFEATURE_PRIMITIVE_RESTART, + gcvFEATURE_TEXTURE_LINEAR, + gcvFEATURE_TEXTURE_YUV_ASSEMBLER, + gcvFEATURE_LINEAR_RENDER_TARGET, + gcvFEATURE_SHADER_HAS_ATOMIC, + gcvFEATURE_SHADER_HAS_INSTRUCTION_CACHE, + gcvFEATURE_SHADER_ENHANCEMENTS2, + gcvFEATURE_BUG_FIXES7, + gcvFEATURE_SHADER_HAS_RTNE, + gcvFEATURE_SHADER_HAS_EXTRA_INSTRUCTIONS2, + gcvFEATURE_SHADER_ENHANCEMENTS3, + gcvFEATURE_DYNAMIC_FREQUENCY_SCALING, + gcvFEATURE_SINGLE_BUFFER, + gcvFEATURE_OCCLUSION_QUERY, + gcvFEATURE_2D_GAMMA, + gcvFEATURE_2D_COLOR_SPACE_CONVERSION, + gcvFEATURE_2D_SUPER_TILE_VERSION, + gcvFEATURE_HALTI0, + gcvFEATURE_HALTI1, + gcvFEATURE_HALTI2, + gcvFEATURE_2D_MIRROR_EXTENSION, + gcvFEATURE_TEXTURE_ASTC, + gcvFEATURE_2D_SUPER_TILE_V1, + gcvFEATURE_2D_SUPER_TILE_V2, + gcvFEATURE_2D_SUPER_TILE_V3, + gcvFEATURE_2D_MULTI_SOURCE_BLT_EX2, + gcvFEATURE_NEW_RA, + gcvFEATURE_BUG_FIXED_IMPLICIT_PRIMITIVE_RESTART, + gcvFEATURE_PE_MULTI_RT_BLEND_ENABLE_CONTROL, + gcvFEATURE_SMALL_MSAA, /* An upgraded version of Fast MSAA */ + gcvFEATURE_VERTEX_INST_ID_AS_ATTRIBUTE, + gcvFEATURE_DUAL_16, + gcvFEATURE_BRANCH_ON_IMMEDIATE_REG, + gcvFEATURE_2D_COMPRESSION, + gcvFEATURE_TPC_COMPRESSION, + gcvFEATURE_2D_OPF_YUV_OUTPUT, + gcvFEATURE_2D_FILTERBLIT_A8_ALPHA, + gcvFEATURE_2D_MULTI_SRC_BLT_TO_UNIFIED_DST_RECT, + gcvFEATURE_V2_COMPRESSION_Z16_FIX, + + gcvFEATURE_VERTEX_INST_ID_AS_INTEGER, + gcvFEATURE_2D_YUV_MODE, + gcvFEATURE_ACE, + gcvFEATURE_COLOR_COMPRESSION, + + gcvFEATURE_32BPP_COMPONENT_TEXTURE_CHANNEL_SWIZZLE, + gcvFEATURE_64BPP_HW_CLEAR_SUPPORT, + gcvFEATURE_TX_LERP_PRECISION_FIX, + gcvFEATURE_COMPRESSION_V2, + gcvFEATURE_MMU, + gcvFEATURE_COMPRESSION_V3, + gcvFEATURE_TX_DECOMPRESSOR, + gcvFEATURE_MRT_TILE_STATUS_BUFFER, + gcvFEATURE_COMPRESSION_V1, + gcvFEATURE_V1_COMPRESSION_Z16_DECOMPRESS_FIX, + gcvFEATURE_RTT, + gcvFEATURE_GENERICS, + gcvFEATURE_2D_ONE_PASS_FILTER, + gcvFEATURE_2D_ONE_PASS_FILTER_TAP, + gcvFEATURE_2D_POST_FLIP, + gcvFEATURE_2D_PIXEL_ALIGNMENT, + gcvFEATURE_CORRECT_AUTO_DISABLE_COUNT, + gcvFEATURE_CORRECT_AUTO_DISABLE_COUNT_WIDTH, + + gcvFEATURE_HALTI3, + gcvFEATURE_EEZ, + gcvFEATURE_INTEGER_PIPE_FIX, + gcvFEATURE_PSOUTPUT_MAPPING, + gcvFEATURE_8K_RT_FIX, + gcvFEATURE_TX_TILE_STATUS_MAPPING, + gcvFEATURE_SRGB_RT_SUPPORT, + gcvFEATURE_UNIFORM_APERTURE, + gcvFEATURE_TEXTURE_16K, + gcvFEATURE_PA_FARZCLIPPING_FIX, + gcvFEATURE_PE_DITHER_COLORMASK_FIX, + gcvFEATURE_ZSCALE_FIX, + + gcvFEATURE_MULTI_PIXELPIPES, + gcvFEATURE_PIPE_CL, + + gcvFEATURE_BUG_FIXES18, + + gcvFEATURE_UNIFIED_SAMPLERS, + gcvFEATURE_CL_PS_WALKER, + gcvFEATURE_NEW_HZ, + + gcvFEATURE_TX_FRAC_PRECISION_6BIT, + gcvFEATURE_SH_INSTRUCTION_PREFETCH, + gcvFEATURE_PROBE, + + gcvFEATURE_BUG_FIXES8, + gcvFEATURE_2D_ALL_QUAD, + + gcvFEATURE_SINGLE_PIPE_HALTI1, + + gcvFEATURE_BLOCK_SIZE_16x16, + + gcvFEATURE_NO_USER_CSC, + gcvFEATURE_ANDROID_ONLY, + gcvFEATURE_HAS_PRODUCTID, + + gcvFEATURE_V2_MSAA_COMP_FIX, + + gcvFEATURE_S8_ONLY_RENDERING, + + gcvFEATURE_SEPARATE_SRC_DST, + + gcvFEATURE_FE_START_VERTEX_SUPPORT, + gcvFEATURE_RS_DEPTHSTENCIL_NATIVE_SUPPORT, + + /* Insert features above this comment only. */ + gcvFEATURE_COUNT /* Not a feature. */ +} +gceFEATURE; + +/* Chip SWWA. */ +typedef enum _gceSWWA +{ + gcvSWWA_601 = 0, + gcvSWWA_706, + gcvSWWA_1163, + gcvSWWA_1165, + /* Insert SWWA above this comment only. */ + gcvSWWA_COUNT /* Not a SWWA. */ +} +gceSWWA; + + +/* Option Set*/ +typedef enum _gceOPITON +{ + /* HW setting we take PREFER */ + gcvOPTION_PREFER_MULTIPIPE_RS = 0, + gcvOPTION_PREFER_ZCONVERT_BYPASS =1, + + + gcvOPTION_HW_NULL = 50, + gcvOPTION_PRINT_OPTION = 51, + + gcvOPTION_FBO_PREFER_MEM = 80, + + /* Insert option above this comment only */ + gcvOPTION_COUNT /* Not a OPTION*/ +} +gceOPTION; + +typedef enum _gceFRAMEINFO +{ + gcvFRAMEINFO_FRAME_NUM = 0, + gcvFRAMEINFO_DRAW_NUM = 1, + gcvFRAMEINFO_DRAW_DUAL16_NUM = 2, + gcvFRAMEINFO_DRAW_FL32_NUM = 3, + + + gcvFRAMEINFO_COUNT, +} +gceFRAMEINFO; + +typedef enum _gceFRAMEINFO_OP +{ + gcvFRAMEINFO_OP_INC = 0, + gcvFRAMEINFO_OP_DEC = 1, + gcvFRAMEINFO_OP_ZERO = 2, + gcvFRAMEINFO_OP_GET = 3, + + + gcvFRAMEINFO_OP_COUNT, +} +gceFRAMEINFO_OP; + + +/* Chip Power Status. */ +typedef enum _gceCHIPPOWERSTATE +{ + gcvPOWER_ON = 0, + gcvPOWER_OFF, + gcvPOWER_IDLE, + gcvPOWER_SUSPEND, + gcvPOWER_SUSPEND_ATPOWERON, + gcvPOWER_OFF_ATPOWERON, + gcvPOWER_IDLE_BROADCAST, + gcvPOWER_SUSPEND_BROADCAST, + gcvPOWER_OFF_BROADCAST, + gcvPOWER_OFF_RECOVERY, + gcvPOWER_OFF_TIMEOUT, + gcvPOWER_ON_AUTO +} +gceCHIPPOWERSTATE; + +/* CPU cache operations */ +typedef enum _gceCACHEOPERATION +{ + gcvCACHE_CLEAN = 0x01, + gcvCACHE_INVALIDATE = 0x02, + gcvCACHE_FLUSH = gcvCACHE_CLEAN | gcvCACHE_INVALIDATE, + gcvCACHE_MEMORY_BARRIER = 0x04 +} +gceCACHEOPERATION; + +/* Surface types. */ +typedef enum _gceSURF_TYPE +{ + gcvSURF_TYPE_UNKNOWN = 0, + gcvSURF_INDEX, + gcvSURF_VERTEX, + gcvSURF_TEXTURE, + gcvSURF_RENDER_TARGET, + gcvSURF_DEPTH, + gcvSURF_BITMAP, + gcvSURF_TILE_STATUS, + gcvSURF_IMAGE, + gcvSURF_MASK, + gcvSURF_SCISSOR, + gcvSURF_HIERARCHICAL_DEPTH, + gcvSURF_NUM_TYPES, /* Make sure this is the last one! */ + + /* Combinations. */ + gcvSURF_NO_TILE_STATUS = 0x100, + gcvSURF_NO_VIDMEM = 0x200, /* Used to allocate surfaces with no underlying vidmem node. + In Android, vidmem node is allocated by another process. */ + gcvSURF_CACHEABLE = 0x400, /* Used to allocate a cacheable surface */ + + gcvSURF_FLIP = 0x800, /* The Resolve Target the will been flip resolve from RT */ + + gcvSURF_TILE_STATUS_DIRTY = 0x1000, /* Init tile status to all dirty */ + + gcvSURF_LINEAR = 0x2000, + + gcvSURF_CREATE_AS_TEXTURE = 0x4000, /* create it as a texture */ + + gcvSURF_PROTECTED_CONTENT = 0x8000, /* create it as content protected */ + + /* Create it as no compression, valid on when it has tile status. */ + gcvSURF_NO_COMPRESSION = 0x40000, + + gcvSURF_CONTIGUOUS = 0x20000, /*create it as contiguous */ + + gcvSURF_TEXTURE_LINEAR = gcvSURF_TEXTURE + | gcvSURF_LINEAR, + + gcvSURF_RENDER_TARGET_LINEAR = gcvSURF_RENDER_TARGET + | gcvSURF_LINEAR, + + gcvSURF_RENDER_TARGET_NO_TILE_STATUS = gcvSURF_RENDER_TARGET + | gcvSURF_NO_TILE_STATUS, + + gcvSURF_RENDER_TARGET_TS_DIRTY = gcvSURF_RENDER_TARGET + | gcvSURF_TILE_STATUS_DIRTY, + + gcvSURF_DEPTH_NO_TILE_STATUS = gcvSURF_DEPTH + | gcvSURF_NO_TILE_STATUS, + + gcvSURF_DEPTH_TS_DIRTY = gcvSURF_DEPTH + | gcvSURF_TILE_STATUS_DIRTY, + + /* Supported surface types with no vidmem node. */ + gcvSURF_BITMAP_NO_VIDMEM = gcvSURF_BITMAP + | gcvSURF_NO_VIDMEM, + + gcvSURF_TEXTURE_NO_VIDMEM = gcvSURF_TEXTURE + | gcvSURF_NO_VIDMEM, + + /* Cacheable surface types with no vidmem node. */ + gcvSURF_CACHEABLE_BITMAP_NO_VIDMEM = gcvSURF_BITMAP_NO_VIDMEM + | gcvSURF_CACHEABLE, + + gcvSURF_CACHEABLE_BITMAP = gcvSURF_BITMAP + | gcvSURF_CACHEABLE, + + gcvSURF_FLIP_BITMAP = gcvSURF_BITMAP + | gcvSURF_FLIP, +} +gceSURF_TYPE; + +typedef enum _gceSURF_USAGE +{ + gcvSURF_USAGE_UNKNOWN, + gcvSURF_USAGE_RESOLVE_AFTER_CPU, + gcvSURF_USAGE_RESOLVE_AFTER_3D +} +gceSURF_USAGE; + +typedef enum _gceSURF_COLOR_SPACE +{ + gcvSURF_COLOR_SPACE_UNKNOWN, + gcvSURF_COLOR_SPACE_LINEAR, + gcvSURF_COLOR_SPACE_NONLINEAR, +} +gceSURF_COLOR_SPACE; + +typedef enum _gceSURF_COLOR_TYPE +{ + gcvSURF_COLOR_UNKNOWN = 0, + gcvSURF_COLOR_LINEAR = 0x01, + gcvSURF_COLOR_ALPHA_PRE = 0x02, +} +gceSURF_COLOR_TYPE; + +/* Rotation. */ +typedef enum _gceSURF_ROTATION +{ + gcvSURF_0_DEGREE = 0, + gcvSURF_90_DEGREE, + gcvSURF_180_DEGREE, + gcvSURF_270_DEGREE, + gcvSURF_FLIP_X, + gcvSURF_FLIP_Y, + + gcvSURF_POST_FLIP_X = 0x40000000, + gcvSURF_POST_FLIP_Y = 0x80000000, +} +gceSURF_ROTATION; + +/* Surface flag */ +typedef enum _gceSURF_FLAG +{ + /* None flag */ + gcvSURF_FLAG_NONE = 0x0, + /* content is preserved after swap */ + gcvSURF_FLAG_CONTENT_PRESERVED = 0x1, + /* content is updated after swap*/ + gcvSURF_FLAG_CONTENT_UPDATED = 0x2, + /* content is y inverted */ + gcvSURF_FLAG_CONTENT_YINVERTED = 0x4, + /* content is protected */ + gcvSURF_FLAG_CONTENT_PROTECTED = 0x8, + /* surface is contiguous. */ + gcvSURF_FLAG_CONTIGUOUS = (1 << 4), +} +gceSURF_FLAG; + +typedef enum _gceMIPMAP_IMAGE_FORMAT +{ + gcvUNKNOWN_MIPMAP_IMAGE_FORMAT = -2 +} +gceMIPMAP_IMAGE_FORMAT; + +/* Surface formats. */ +typedef enum _gceSURF_FORMAT +{ + /* Unknown format. */ + gcvSURF_UNKNOWN = 0, + + /* Palettized formats. */ + gcvSURF_INDEX1 = 100, + gcvSURF_INDEX4, + gcvSURF_INDEX8, + + /* RGB formats. */ + gcvSURF_A2R2G2B2 = 200, + gcvSURF_R3G3B2, + gcvSURF_A8R3G3B2, + gcvSURF_X4R4G4B4, + gcvSURF_A4R4G4B4, + gcvSURF_R4G4B4A4, + gcvSURF_X1R5G5B5, + gcvSURF_A1R5G5B5, + gcvSURF_R5G5B5A1, + gcvSURF_R5G6B5, + gcvSURF_R8G8B8, + gcvSURF_X8R8G8B8, + gcvSURF_A8R8G8B8, + gcvSURF_R8G8B8A8, + gcvSURF_G8R8G8B8, + gcvSURF_R8G8B8G8, + gcvSURF_X2R10G10B10, + gcvSURF_A2R10G10B10, + gcvSURF_X12R12G12B12, + gcvSURF_A12R12G12B12, + gcvSURF_X16R16G16B16, + gcvSURF_A16R16G16B16, + gcvSURF_A32R32G32B32, + gcvSURF_R8G8B8X8, + gcvSURF_R5G5B5X1, + gcvSURF_R4G4B4X4, + gcvSURF_X16R16G16B16_2_A8R8G8B8, + gcvSURF_A16R16G16B16_2_A8R8G8B8, + gcvSURF_A32R32G32B32_2_G32R32F, + gcvSURF_A32R32G32B32_4_A8R8G8B8, + + /* BGR formats. */ + gcvSURF_A4B4G4R4 = 300, + gcvSURF_A1B5G5R5, + gcvSURF_B5G6R5, + gcvSURF_B8G8R8, + gcvSURF_B16G16R16, + gcvSURF_X8B8G8R8, + gcvSURF_A8B8G8R8, + gcvSURF_A2B10G10R10, + gcvSURF_X16B16G16R16, + gcvSURF_A16B16G16R16, + gcvSURF_B32G32R32, + gcvSURF_X32B32G32R32, + gcvSURF_A32B32G32R32, + gcvSURF_B4G4R4A4, + gcvSURF_B5G5R5A1, + gcvSURF_B8G8R8X8, + gcvSURF_B8G8R8A8, + gcvSURF_X4B4G4R4, + gcvSURF_X1B5G5R5, + gcvSURF_B4G4R4X4, + gcvSURF_B5G5R5X1, + gcvSURF_X2B10G10R10, + gcvSURF_B8G8R8_SNORM, + gcvSURF_X8B8G8R8_SNORM, + gcvSURF_A8B8G8R8_SNORM, + gcvSURF_A8B12G12R12_2_A8R8G8B8, + + /* Compressed formats. */ + gcvSURF_DXT1 = 400, + gcvSURF_DXT2, + gcvSURF_DXT3, + gcvSURF_DXT4, + gcvSURF_DXT5, + gcvSURF_CXV8U8, + gcvSURF_ETC1, + gcvSURF_R11_EAC, + gcvSURF_SIGNED_R11_EAC, + gcvSURF_RG11_EAC, + gcvSURF_SIGNED_RG11_EAC, + gcvSURF_RGB8_ETC2, + gcvSURF_SRGB8_ETC2, + gcvSURF_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, + gcvSURF_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, + gcvSURF_RGBA8_ETC2_EAC, + gcvSURF_SRGB8_ALPHA8_ETC2_EAC, + + /* YUV formats. */ + gcvSURF_YUY2 = 500, + gcvSURF_UYVY, + gcvSURF_YV12, + gcvSURF_I420, + gcvSURF_NV12, + gcvSURF_NV21, + gcvSURF_NV16, + gcvSURF_NV61, + gcvSURF_YVYU, + gcvSURF_VYUY, + + /* Depth formats. */ + gcvSURF_D16 = 600, + gcvSURF_D24S8, + gcvSURF_D32, + gcvSURF_D24X8, + gcvSURF_D32F, + gcvSURF_S8D32F, + gcvSURF_S8D32F_1_G32R32F, + gcvSURF_S8D32F_2_A8R8G8B8, + gcvSURF_D24S8_1_A8R8G8B8, + gcvSURF_S8, + + /* Alpha formats. */ + gcvSURF_A4 = 700, + gcvSURF_A8, + gcvSURF_A12, + gcvSURF_A16, + gcvSURF_A32, + gcvSURF_A1, + + /* Luminance formats. */ + gcvSURF_L4 = 800, + gcvSURF_L8, + gcvSURF_L12, + gcvSURF_L16, + gcvSURF_L32, + gcvSURF_L1, + + /* Alpha/Luminance formats. */ + gcvSURF_A4L4 = 900, + gcvSURF_A2L6, + gcvSURF_A8L8, + gcvSURF_A4L12, + gcvSURF_A12L12, + gcvSURF_A16L16, + + /* Bump formats. */ + gcvSURF_L6V5U5 = 1000, + gcvSURF_V8U8, + gcvSURF_X8L8V8U8, + gcvSURF_Q8W8V8U8, + gcvSURF_A2W10V10U10, + gcvSURF_V16U16, + gcvSURF_Q16W16V16U16, + + /* R/RG/RA formats. */ + gcvSURF_R8 = 1100, + gcvSURF_X8R8, + gcvSURF_G8R8, + gcvSURF_X8G8R8, + gcvSURF_A8R8, + gcvSURF_R16, + gcvSURF_X16R16, + gcvSURF_G16R16, + gcvSURF_X16G16R16, + gcvSURF_A16R16, + gcvSURF_R32, + gcvSURF_X32R32, + gcvSURF_G32R32, + gcvSURF_X32G32R32, + gcvSURF_A32R32, + gcvSURF_RG16, + gcvSURF_R8_SNORM, + gcvSURF_G8R8_SNORM, + + gcvSURF_R8_1_X8R8G8B8, + gcvSURF_G8R8_1_X8R8G8B8, + + /* Floating point formats. */ + gcvSURF_R16F = 1200, + gcvSURF_X16R16F, + gcvSURF_G16R16F, + gcvSURF_X16G16R16F, + gcvSURF_B16G16R16F, + gcvSURF_X16B16G16R16F, + gcvSURF_A16B16G16R16F, + gcvSURF_R32F, + gcvSURF_X32R32F, + gcvSURF_G32R32F, + gcvSURF_X32G32R32F, + gcvSURF_B32G32R32F, + gcvSURF_X32B32G32R32F, + gcvSURF_A32B32G32R32F, + gcvSURF_A16F, + gcvSURF_L16F, + gcvSURF_A16L16F, + gcvSURF_A16R16F, + gcvSURF_A32F, + gcvSURF_L32F, + gcvSURF_A32L32F, + gcvSURF_A32R32F, + gcvSURF_E5B9G9R9, + gcvSURF_B10G11R11F, + + gcvSURF_X16B16G16R16F_2_A8R8G8B8, + gcvSURF_A16B16G16R16F_2_A8R8G8B8, + gcvSURF_G32R32F_2_A8R8G8B8, + gcvSURF_X32B32G32R32F_2_G32R32F, + gcvSURF_A32B32G32R32F_2_G32R32F, + gcvSURF_X32B32G32R32F_4_A8R8G8B8, + gcvSURF_A32B32G32R32F_4_A8R8G8B8, + + gcvSURF_R16F_1_A4R4G4B4, + gcvSURF_G16R16F_1_A8R8G8B8, + gcvSURF_B16G16R16F_2_A8R8G8B8, + + gcvSURF_R32F_1_A8R8G8B8, + gcvSURF_B32G32R32F_3_A8R8G8B8, + + gcvSURF_B10G11R11F_1_A8R8G8B8, + + + /* sRGB format. */ + gcvSURF_SBGR8 = 1400, + gcvSURF_A8_SBGR8, + gcvSURF_X8_SBGR8, + + /* Integer formats. */ + gcvSURF_R8I = 1500, + gcvSURF_R8UI, + gcvSURF_R16I, + gcvSURF_R16UI, + gcvSURF_R32I, + gcvSURF_R32UI, + gcvSURF_X8R8I, + gcvSURF_G8R8I, + gcvSURF_X8R8UI, + gcvSURF_G8R8UI, + gcvSURF_X16R16I, + gcvSURF_G16R16I, + gcvSURF_X16R16UI, + gcvSURF_G16R16UI, + gcvSURF_X32R32I, + gcvSURF_G32R32I, + gcvSURF_X32R32UI, + gcvSURF_G32R32UI, + gcvSURF_X8G8R8I, + gcvSURF_B8G8R8I, + gcvSURF_X8G8R8UI, + gcvSURF_B8G8R8UI, + gcvSURF_X16G16R16I, + gcvSURF_B16G16R16I, + gcvSURF_X16G16R16UI, + gcvSURF_B16G16R16UI, + gcvSURF_X32G32R32I, + gcvSURF_B32G32R32I, + gcvSURF_X32G32R32UI, + gcvSURF_B32G32R32UI, + gcvSURF_X8B8G8R8I, + gcvSURF_A8B8G8R8I, + gcvSURF_X8B8G8R8UI, + gcvSURF_A8B8G8R8UI, + gcvSURF_X16B16G16R16I, + gcvSURF_A16B16G16R16I, + gcvSURF_X16B16G16R16UI, + gcvSURF_A16B16G16R16UI, + gcvSURF_X32B32G32R32I, + gcvSURF_A32B32G32R32I, + gcvSURF_X32B32G32R32UI, + gcvSURF_A32B32G32R32UI, + gcvSURF_A2B10G10R10UI, + gcvSURF_G32R32I_2_A8R8G8B8, + gcvSURF_G32R32UI_2_A8R8G8B8, + gcvSURF_X16B16G16R16I_2_A8R8G8B8, + gcvSURF_A16B16G16R16I_2_A8R8G8B8, + gcvSURF_X16B16G16R16UI_2_A8R8G8B8, + gcvSURF_A16B16G16R16UI_2_A8R8G8B8, + gcvSURF_X32B32G32R32I_2_G32R32I, + gcvSURF_A32B32G32R32I_2_G32R32I, + gcvSURF_X32B32G32R32I_3_A8R8G8B8, + gcvSURF_A32B32G32R32I_4_A8R8G8B8, + gcvSURF_X32B32G32R32UI_2_G32R32UI, + gcvSURF_A32B32G32R32UI_2_G32R32UI, + gcvSURF_X32B32G32R32UI_3_A8R8G8B8, + gcvSURF_A32B32G32R32UI_4_A8R8G8B8, + gcvSURF_A2B10G10R10UI_1_A8R8G8B8, + gcvSURF_A8B8G8R8I_1_A8R8G8B8, + gcvSURF_A8B8G8R8UI_1_A8R8G8B8, + gcvSURF_R8I_1_A4R4G4B4, + gcvSURF_R8UI_1_A4R4G4B4, + gcvSURF_R16I_1_A4R4G4B4, + gcvSURF_R16UI_1_A4R4G4B4, + gcvSURF_R32I_1_A8R8G8B8, + gcvSURF_R32UI_1_A8R8G8B8, + gcvSURF_X8R8I_1_A4R4G4B4, + gcvSURF_X8R8UI_1_A4R4G4B4, + gcvSURF_G8R8I_1_A4R4G4B4, + gcvSURF_G8R8UI_1_A4R4G4B4, + gcvSURF_X16R16I_1_A4R4G4B4, + gcvSURF_X16R16UI_1_A4R4G4B4, + gcvSURF_G16R16I_1_A8R8G8B8, + gcvSURF_G16R16UI_1_A8R8G8B8, + gcvSURF_X32R32I_1_A8R8G8B8, + gcvSURF_X32R32UI_1_A8R8G8B8, + gcvSURF_X8G8R8I_1_A4R4G4B4, + gcvSURF_X8G8R8UI_1_A4R4G4B4, + gcvSURF_B8G8R8I_1_A8R8G8B8, + gcvSURF_B8G8R8UI_1_A8R8G8B8, + gcvSURF_B16G16R16I_2_A8R8G8B8, + gcvSURF_B16G16R16UI_2_A8R8G8B8, + gcvSURF_B32G32R32I_3_A8R8G8B8, + gcvSURF_B32G32R32UI_3_A8R8G8B8, + + /* ASTC formats. */ + gcvSURF_ASTC4x4 = 1600, + gcvSURF_ASTC5x4, + gcvSURF_ASTC5x5, + gcvSURF_ASTC6x5, + gcvSURF_ASTC6x6, + gcvSURF_ASTC8x5, + gcvSURF_ASTC8x6, + gcvSURF_ASTC8x8, + gcvSURF_ASTC10x5, + gcvSURF_ASTC10x6, + gcvSURF_ASTC10x8, + gcvSURF_ASTC10x10, + gcvSURF_ASTC12x10, + gcvSURF_ASTC12x12, + gcvSURF_ASTC4x4_SRGB, + gcvSURF_ASTC5x4_SRGB, + gcvSURF_ASTC5x5_SRGB, + gcvSURF_ASTC6x5_SRGB, + gcvSURF_ASTC6x6_SRGB, + gcvSURF_ASTC8x5_SRGB, + gcvSURF_ASTC8x6_SRGB, + gcvSURF_ASTC8x8_SRGB, + gcvSURF_ASTC10x5_SRGB, + gcvSURF_ASTC10x6_SRGB, + gcvSURF_ASTC10x8_SRGB, + gcvSURF_ASTC10x10_SRGB, + gcvSURF_ASTC12x10_SRGB, + gcvSURF_ASTC12x12_SRGB, + + gcvSURF_FORMAT_COUNT +} +gceSURF_FORMAT; + +/* Format modifiers. */ +typedef enum _gceSURF_FORMAT_MODE +{ + gcvSURF_FORMAT_OCL = 0x80000000 +} +gceSURF_FORMAT_MODE; + +/* Pixel swizzle modes. */ +typedef enum _gceSURF_SWIZZLE +{ + gcvSURF_NOSWIZZLE = 0, + gcvSURF_ARGB, + gcvSURF_ABGR, + gcvSURF_RGBA, + gcvSURF_BGRA +} +gceSURF_SWIZZLE; + +/* Transparency modes. */ +typedef enum _gceSURF_TRANSPARENCY +{ + /* Valid only for PE 1.0 */ + gcvSURF_OPAQUE = 0, + gcvSURF_SOURCE_MATCH, + gcvSURF_SOURCE_MASK, + gcvSURF_PATTERN_MASK, +} +gceSURF_TRANSPARENCY; + +/* Surface Alignment. */ +typedef enum _gceSURF_ALIGNMENT +{ + gcvSURF_FOUR = 0, + gcvSURF_SIXTEEN, + gcvSURF_SUPER_TILED, + gcvSURF_SPLIT_TILED, + gcvSURF_SPLIT_SUPER_TILED +} +gceSURF_ALIGNMENT; + +/* Surface Addressing. */ +typedef enum _gceSURF_ADDRESSING +{ + gcvSURF_NO_STRIDE_TILED = 0, + gcvSURF_NO_STRIDE_LINEAR, + gcvSURF_STRIDE_TILED, + gcvSURF_STRIDE_LINEAR +} +gceSURF_ADDRESSING; + +/* Transparency modes. */ +typedef enum _gce2D_TRANSPARENCY +{ + /* Valid only for PE 2.0 */ + gcv2D_OPAQUE = 0, + gcv2D_KEYED, + gcv2D_MASKED +} +gce2D_TRANSPARENCY; + +/* Mono packing modes. */ +typedef enum _gceSURF_MONOPACK +{ + gcvSURF_PACKED8 = 0, + gcvSURF_PACKED16, + gcvSURF_PACKED32, + gcvSURF_UNPACKED, +} +gceSURF_MONOPACK; + +/* Blending modes. */ +typedef enum _gceSURF_BLEND_MODE +{ + /* Porter-Duff blending modes. */ + /* Fsrc Fdst */ + gcvBLEND_CLEAR = 0, /* 0 0 */ + gcvBLEND_SRC, /* 1 0 */ + gcvBLEND_DST, /* 0 1 */ + gcvBLEND_SRC_OVER_DST, /* 1 1 - Asrc */ + gcvBLEND_DST_OVER_SRC, /* 1 - Adst 1 */ + gcvBLEND_SRC_IN_DST, /* Adst 0 */ + gcvBLEND_DST_IN_SRC, /* 0 Asrc */ + gcvBLEND_SRC_OUT_DST, /* 1 - Adst 0 */ + gcvBLEND_DST_OUT_SRC, /* 0 1 - Asrc */ + gcvBLEND_SRC_ATOP_DST, /* Adst 1 - Asrc */ + gcvBLEND_DST_ATOP_SRC, /* 1 - Adst Asrc */ + gcvBLEND_SRC_XOR_DST, /* 1 - Adst 1 - Asrc */ + + /* Special blending modes. */ + gcvBLEND_SET, /* DST = 1 */ + gcvBLEND_SUB /* DST = DST * (1 - SRC) */ +} +gceSURF_BLEND_MODE; + +/* Per-pixel alpha modes. */ +typedef enum _gceSURF_PIXEL_ALPHA_MODE +{ + gcvSURF_PIXEL_ALPHA_STRAIGHT = 0, + gcvSURF_PIXEL_ALPHA_INVERSED +} +gceSURF_PIXEL_ALPHA_MODE; + +/* Global alpha modes. */ +typedef enum _gceSURF_GLOBAL_ALPHA_MODE +{ + gcvSURF_GLOBAL_ALPHA_OFF = 0, + gcvSURF_GLOBAL_ALPHA_ON, + gcvSURF_GLOBAL_ALPHA_SCALE +} +gceSURF_GLOBAL_ALPHA_MODE; + +/* Color component modes for alpha blending. */ +typedef enum _gceSURF_PIXEL_COLOR_MODE +{ + gcvSURF_COLOR_STRAIGHT = 0, + gcvSURF_COLOR_MULTIPLY +} +gceSURF_PIXEL_COLOR_MODE; + +/* Color component modes for alpha blending. */ +typedef enum _gce2D_PIXEL_COLOR_MULTIPLY_MODE +{ + gcv2D_COLOR_MULTIPLY_DISABLE = 0, + gcv2D_COLOR_MULTIPLY_ENABLE +} +gce2D_PIXEL_COLOR_MULTIPLY_MODE; + +/* Color component modes for alpha blending. */ +typedef enum _gce2D_GLOBAL_COLOR_MULTIPLY_MODE +{ + gcv2D_GLOBAL_COLOR_MULTIPLY_DISABLE = 0, + gcv2D_GLOBAL_COLOR_MULTIPLY_ALPHA, + gcv2D_GLOBAL_COLOR_MULTIPLY_COLOR +} +gce2D_GLOBAL_COLOR_MULTIPLY_MODE; + +/* Alpha blending factor modes. */ +typedef enum _gceSURF_BLEND_FACTOR_MODE +{ + gcvSURF_BLEND_ZERO = 0, + gcvSURF_BLEND_ONE, + gcvSURF_BLEND_STRAIGHT, + gcvSURF_BLEND_INVERSED, + gcvSURF_BLEND_COLOR, + gcvSURF_BLEND_COLOR_INVERSED, + gcvSURF_BLEND_SRC_ALPHA_SATURATED, + gcvSURF_BLEND_STRAIGHT_NO_CROSS, + gcvSURF_BLEND_INVERSED_NO_CROSS, + gcvSURF_BLEND_COLOR_NO_CROSS, + gcvSURF_BLEND_COLOR_INVERSED_NO_CROSS, + gcvSURF_BLEND_SRC_ALPHA_SATURATED_CROSS +} +gceSURF_BLEND_FACTOR_MODE; + +/* Alpha blending porter duff rules. */ +typedef enum _gce2D_PORTER_DUFF_RULE +{ + gcvPD_CLEAR = 0, + gcvPD_SRC, + gcvPD_SRC_OVER, + gcvPD_DST_OVER, + gcvPD_SRC_IN, + gcvPD_DST_IN, + gcvPD_SRC_OUT, + gcvPD_DST_OUT, + gcvPD_SRC_ATOP, + gcvPD_DST_ATOP, + gcvPD_ADD, + gcvPD_XOR, + gcvPD_DST +} +gce2D_PORTER_DUFF_RULE; + +/* Alpha blending factor modes. */ +typedef enum _gce2D_YUV_COLOR_MODE +{ + gcv2D_YUV_601= 0, + gcv2D_YUV_709, + gcv2D_YUV_USER_DEFINED, + gcv2D_YUV_USER_DEFINED_CLAMP, + + /* Default setting is for src. gcv2D_YUV_DST + can be ORed to set dst. + */ + gcv2D_YUV_DST = 0x80000000, +} +gce2D_YUV_COLOR_MODE; + +typedef enum _gce2D_COMMAND +{ + gcv2D_CLEAR = 0, + gcv2D_LINE, + gcv2D_BLT, + gcv2D_STRETCH, + gcv2D_HOR_FILTER, + gcv2D_VER_FILTER, + gcv2D_MULTI_SOURCE_BLT, + gcv2D_FILTER_BLT, +} +gce2D_COMMAND; + +typedef enum _gce2D_TILE_STATUS_CONFIG +{ + gcv2D_TSC_DISABLE = 0, + gcv2D_TSC_ENABLE = 0x00000001, + gcv2D_TSC_COMPRESSED = 0x00000002, + gcv2D_TSC_DOWN_SAMPLER = 0x00000004, + gcv2D_TSC_2D_COMPRESSED = 0x00000008, + gcv2D_TSC_TPC_COMPRESSED = 0x00000010, +} +gce2D_TILE_STATUS_CONFIG; + +typedef enum _gce2D_QUERY +{ + gcv2D_QUERY_RGB_ADDRESS_MIN_ALIGN = 0, + gcv2D_QUERY_RGB_STRIDE_MIN_ALIGN, + gcv2D_QUERY_YUV_ADDRESS_MIN_ALIGN, + gcv2D_QUERY_YUV_STRIDE_MIN_ALIGN, +} +gce2D_QUERY; + +typedef enum _gce2D_SUPER_TILE_VERSION +{ + gcv2D_SUPER_TILE_VERSION_V1 = 1, + gcv2D_SUPER_TILE_VERSION_V2 = 2, + gcv2D_SUPER_TILE_VERSION_V3 = 3, +} +gce2D_SUPER_TILE_VERSION; + +typedef enum _gce2D_STATE +{ + gcv2D_STATE_SPECIAL_FILTER_MIRROR_MODE = 1, + gcv2D_STATE_SUPER_TILE_VERSION, + gcv2D_STATE_EN_GAMMA, + gcv2D_STATE_DE_GAMMA, + gcv2D_STATE_MULTI_SRC_BLIT_UNIFIED_DST_RECT, + gcv2D_STATE_PROFILE_ENABLE, + gcv2D_STATE_XRGB_ENABLE, + + gcv2D_STATE_ARRAY_EN_GAMMA = 0x10001, + gcv2D_STATE_ARRAY_DE_GAMMA, + gcv2D_STATE_ARRAY_CSC_YUV_TO_RGB, + gcv2D_STATE_ARRAY_CSC_RGB_TO_YUV, +} +gce2D_STATE; + +typedef enum _gce2D_STATE_PROFILE +{ + gcv2D_STATE_PROFILE_NONE = 0x0, + gcv2D_STATE_PROFILE_COMMAND = 0x1, + gcv2D_STATE_PROFILE_SURFACE = 0x2, + gcv2D_STATE_PROFILE_ALL = 0xFFFF, +} +gce2D_STATE_PROFILE; + +/* Texture object types */ +typedef enum _gceTEXTURE_TYPE +{ + gcvTEXTURE_UNKNOWN = 0, + gcvTEXTURE_1D, + gcvTEXTURE_2D, + gcvTEXTURE_3D, + gcvTEXTURE_CUBEMAP, + gcvTEXTURE_1D_ARRAY, + gcvTEXTURE_2D_ARRAY, + gcvTEXTURE_EXTERNAL +} +gceTEXTURE_TYPE; + +#if gcdENABLE_3D +/* Texture functions. */ +typedef enum _gceTEXTURE_FUNCTION +{ + gcvTEXTURE_DUMMY = 0, + gcvTEXTURE_REPLACE = 0, + gcvTEXTURE_MODULATE, + gcvTEXTURE_ADD, + gcvTEXTURE_ADD_SIGNED, + gcvTEXTURE_INTERPOLATE, + gcvTEXTURE_SUBTRACT, + gcvTEXTURE_DOT3 +} +gceTEXTURE_FUNCTION; + +/* Texture sources. */ +typedef enum _gceTEXTURE_SOURCE +{ + gcvCOLOR_FROM_TEXTURE = 0, + gcvCOLOR_FROM_CONSTANT_COLOR, + gcvCOLOR_FROM_PRIMARY_COLOR, + gcvCOLOR_FROM_PREVIOUS_COLOR +} +gceTEXTURE_SOURCE; + +/* Texture source channels. */ +typedef enum _gceTEXTURE_CHANNEL +{ + gcvFROM_COLOR = 0, + gcvFROM_ONE_MINUS_COLOR, + gcvFROM_ALPHA, + gcvFROM_ONE_MINUS_ALPHA +} +gceTEXTURE_CHANNEL; +#endif /* gcdENABLE_3D */ + +/* Filter types. */ +typedef enum _gceFILTER_TYPE +{ + gcvFILTER_SYNC = 0, + gcvFILTER_BLUR, + gcvFILTER_USER +} +gceFILTER_TYPE; + +/* Filter pass types. */ +typedef enum _gceFILTER_PASS_TYPE +{ + gcvFILTER_HOR_PASS = 0, + gcvFILTER_VER_PASS +} +gceFILTER_PASS_TYPE; + +/* Endian hints. */ +typedef enum _gceENDIAN_HINT +{ + gcvENDIAN_NO_SWAP = 0, + gcvENDIAN_SWAP_WORD, + gcvENDIAN_SWAP_DWORD +} +gceENDIAN_HINT; + +/* Tiling modes. */ +typedef enum _gceTILING +{ + gcvINVALIDTILED = 0x0, /* Invalid tiling */ + /* Tiling basic modes enum'ed in power of 2. */ + gcvLINEAR = 0x1, /* No tiling. */ + gcvTILED = 0x2, /* 4x4 tiling. */ + gcvSUPERTILED = 0x4, /* 64x64 tiling. */ + gcvMINORTILED = 0x8, /* 2x2 tiling. */ + + /* Tiling special layouts. */ + gcvTILING_SPLIT_BUFFER = 0x100, + + /* Tiling combination layouts. */ + gcvMULTI_TILED = gcvTILED + | gcvTILING_SPLIT_BUFFER, + + gcvMULTI_SUPERTILED = gcvSUPERTILED + | gcvTILING_SPLIT_BUFFER, +} +gceTILING; + +/* 2D pattern type. */ +typedef enum _gce2D_PATTERN +{ + gcv2D_PATTERN_SOLID = 0, + gcv2D_PATTERN_MONO, + gcv2D_PATTERN_COLOR, + gcv2D_PATTERN_INVALID +} +gce2D_PATTERN; + +/* 2D source type. */ +typedef enum _gce2D_SOURCE +{ + gcv2D_SOURCE_MASKED = 0, + gcv2D_SOURCE_MONO, + gcv2D_SOURCE_COLOR, + gcv2D_SOURCE_INVALID +} +gce2D_SOURCE; + +/* Pipes. */ +typedef enum _gcePIPE_SELECT +{ + gcvPIPE_INVALID = ~0, + gcvPIPE_3D = 0, + gcvPIPE_2D +} +gcePIPE_SELECT; + +/* Hardware type. */ +typedef enum _gceHARDWARE_TYPE +{ + gcvHARDWARE_INVALID = 0x00, + gcvHARDWARE_3D = 0x01, + gcvHARDWARE_2D = 0x02, + gcvHARDWARE_VG = 0x04, +#if gcdMULTI_GPU_AFFINITY + gcvHARDWARE_OCL = 0x05, +#endif + gcvHARDWARE_3D2D = gcvHARDWARE_3D | gcvHARDWARE_2D +} +gceHARDWARE_TYPE; + +#define gcdCHIP_COUNT 3 + +typedef enum _gceMMU_MODE +{ + gcvMMU_MODE_1K, + gcvMMU_MODE_4K, +} gceMMU_MODE; + +/* User signal command codes. */ +typedef enum _gceUSER_SIGNAL_COMMAND_CODES +{ + gcvUSER_SIGNAL_CREATE, + gcvUSER_SIGNAL_DESTROY, + gcvUSER_SIGNAL_SIGNAL, + gcvUSER_SIGNAL_WAIT, + gcvUSER_SIGNAL_MAP, + gcvUSER_SIGNAL_UNMAP, +} +gceUSER_SIGNAL_COMMAND_CODES; + +/* Sync point command codes. */ +typedef enum _gceSYNC_POINT_COMMAND_CODES +{ + gcvSYNC_POINT_CREATE, + gcvSYNC_POINT_DESTROY, + gcvSYNC_POINT_SIGNAL, +} +gceSYNC_POINT_COMMAND_CODES; + +/* Shared buffer command codes. */ +typedef enum _gceSHBUF_COMMAND_CODES +{ + gcvSHBUF_CREATE, + gcvSHBUF_DESTROY, + gcvSHBUF_MAP, + gcvSHBUF_WRITE, + gcvSHBUF_READ, +} +gceSHBUF_COMMAND_CODES; + +/* Event locations. */ +typedef enum _gceKERNEL_WHERE +{ + gcvKERNEL_COMMAND, + gcvKERNEL_VERTEX, + gcvKERNEL_TRIANGLE, + gcvKERNEL_TEXTURE, + gcvKERNEL_PIXEL, +} +gceKERNEL_WHERE; + +#if gcdENABLE_VG +/* Hardware blocks. */ +typedef enum _gceBLOCK +{ + gcvBLOCK_COMMAND, + gcvBLOCK_TESSELLATOR, + gcvBLOCK_TESSELLATOR2, + gcvBLOCK_TESSELLATOR3, + gcvBLOCK_RASTER, + gcvBLOCK_VG, + gcvBLOCK_VG2, + gcvBLOCK_VG3, + gcvBLOCK_PIXEL, + + /* Number of defined blocks. */ + gcvBLOCK_COUNT +} +gceBLOCK; +#endif + +/* gcdDUMP message type. */ +typedef enum _gceDEBUG_MESSAGE_TYPE +{ + gcvMESSAGE_TEXT, + gcvMESSAGE_DUMP +} +gceDEBUG_MESSAGE_TYPE; + +/* Shading format. */ +typedef enum _gceSHADING +{ + gcvSHADING_SMOOTH, + gcvSHADING_FLAT_D3D, + gcvSHADING_FLAT_OPENGL, +} +gceSHADING; + +/* Culling modes. */ +typedef enum _gceCULL +{ + gcvCULL_NONE, + gcvCULL_CCW, + gcvCULL_CW, +} +gceCULL; + +/* Fill modes. */ +typedef enum _gceFILL +{ + gcvFILL_POINT, + gcvFILL_WIRE_FRAME, + gcvFILL_SOLID, +} +gceFILL; + +/* Compare modes. */ +typedef enum _gceCOMPARE +{ + gcvCOMPARE_INVALID = 0, + gcvCOMPARE_NEVER, + gcvCOMPARE_NOT_EQUAL, + gcvCOMPARE_LESS, + gcvCOMPARE_LESS_OR_EQUAL, + gcvCOMPARE_EQUAL, + gcvCOMPARE_GREATER, + gcvCOMPARE_GREATER_OR_EQUAL, + gcvCOMPARE_ALWAYS, +} +gceCOMPARE; + +/* Stencil modes. */ +typedef enum _gceSTENCIL_MODE +{ + gcvSTENCIL_NONE, + gcvSTENCIL_SINGLE_SIDED, + gcvSTENCIL_DOUBLE_SIDED, +} +gceSTENCIL_MODE; + +/* Stencil operations. */ +typedef enum _gceSTENCIL_OPERATION +{ + gcvSTENCIL_KEEP, + gcvSTENCIL_REPLACE, + gcvSTENCIL_ZERO, + gcvSTENCIL_INVERT, + gcvSTENCIL_INCREMENT, + gcvSTENCIL_DECREMENT, + gcvSTENCIL_INCREMENT_SATURATE, + gcvSTENCIL_DECREMENT_SATURATE, + gcvSTENCIL_OPERATION_INVALID = -1 +} +gceSTENCIL_OPERATION; + +/* Stencil selection. */ +typedef enum _gceSTENCIL_WHERE +{ + gcvSTENCIL_FRONT, + gcvSTENCIL_BACK, +} +gceSTENCIL_WHERE; + +/* Texture addressing selection. */ +typedef enum _gceTEXTURE_WHICH +{ + gcvTEXTURE_S, + gcvTEXTURE_T, + gcvTEXTURE_R, +} +gceTEXTURE_WHICH; + +/* Texture addressing modes. */ +typedef enum _gceTEXTURE_ADDRESSING +{ + gcvTEXTURE_INVALID = 0, + gcvTEXTURE_CLAMP, + gcvTEXTURE_WRAP, + gcvTEXTURE_MIRROR, + gcvTEXTURE_BORDER, + gcvTEXTURE_MIRROR_ONCE, +} +gceTEXTURE_ADDRESSING; + +/* Texture filters. */ +typedef enum _gceTEXTURE_FILTER +{ + gcvTEXTURE_NONE, + gcvTEXTURE_POINT, + gcvTEXTURE_LINEAR, + gcvTEXTURE_ANISOTROPIC, +} +gceTEXTURE_FILTER; + +typedef enum _gceTEXTURE_COMPONENT +{ + gcvTEXTURE_COMPONENT_R, + gcvTEXTURE_COMPONENT_G, + gcvTEXTURE_COMPONENT_B, + gcvTEXTURE_COMPONENT_A, + + gcvTEXTURE_COMPONENT_NUM, +} gceTEXTURE_COMPONENT; + +/* Texture swizzle modes. */ +typedef enum _gceTEXTURE_SWIZZLE +{ + gcvTEXTURE_SWIZZLE_R = 0, + gcvTEXTURE_SWIZZLE_G, + gcvTEXTURE_SWIZZLE_B, + gcvTEXTURE_SWIZZLE_A, + gcvTEXTURE_SWIZZLE_0, + gcvTEXTURE_SWIZZLE_1, + + gcvTEXTURE_SWIZZLE_INVALID, +} gceTEXTURE_SWIZZLE; + +typedef enum _gceTEXTURE_COMPARE_MODE +{ + gcvTEXTURE_COMPARE_MODE_INVALID = 0, + gcvTEXTURE_COMPARE_MODE_NONE, + gcvTEXTURE_COMPARE_MODE_REF, +} gceTEXTURE_COMPARE_MODE; + +/* Pixel output swizzle modes. */ +typedef enum _gcePIXEL_SWIZZLE +{ + gcvPIXEL_SWIZZLE_R = gcvTEXTURE_SWIZZLE_R, + gcvPIXEL_SWIZZLE_G = gcvTEXTURE_SWIZZLE_G, + gcvPIXEL_SWIZZLE_B = gcvTEXTURE_SWIZZLE_B, + gcvPIXEL_SWIZZLE_A = gcvTEXTURE_SWIZZLE_A, + + gcvPIXEL_SWIZZLE_INVALID, +} gcePIXEL_SWIZZLE; + +/* Primitive types. */ +typedef enum _gcePRIMITIVE +{ + gcvPRIMITIVE_POINT_LIST, + gcvPRIMITIVE_LINE_LIST, + gcvPRIMITIVE_LINE_STRIP, + gcvPRIMITIVE_LINE_LOOP, + gcvPRIMITIVE_TRIANGLE_LIST, + gcvPRIMITIVE_TRIANGLE_STRIP, + gcvPRIMITIVE_TRIANGLE_FAN, + gcvPRIMITIVE_RECTANGLE, +} +gcePRIMITIVE; + +/* Index types. */ +typedef enum _gceINDEX_TYPE +{ + gcvINDEX_8, + gcvINDEX_16, + gcvINDEX_32, +} +gceINDEX_TYPE; + +/* Multi GPU rendering modes. */ +typedef enum _gceMULTI_GPU_RENDERING_MODE +{ + gcvMULTI_GPU_RENDERING_MODE_OFF, + gcvMULTI_GPU_RENDERING_MODE_SPLIT_WIDTH, + gcvMULTI_GPU_RENDERING_MODE_SPLIT_HEIGHT, + gcvMULTI_GPU_RENDERING_MODE_INTERLEAVED_64x64, + gcvMULTI_GPU_RENDERING_MODE_INTERLEAVED_128x64, + gcvMULTI_GPU_RENDERING_MODE_INTERLEAVED_128x128 +} +gceMULTI_GPU_RENDERING_MODE; + +typedef enum _gceCORE_3D_MASK +{ + gcvCORE_3D_0_MASK = (1 << 0), + gcvCORE_3D_1_MASK = (1 << 1), + + gcvCORE_3D_ALL_MASK = (0xFFFF) +} +gceCORE_3D_MASK; + +typedef enum _gceCORE_3D_ID +{ + gcvCORE_3D_0_ID = 0, + gcvCORE_3D_1_ID = 1, + + gcvCORE_3D_ID_INVALID = ~0UL +} +gceCORE_3D_ID; + +typedef enum _gceMULTI_GPU_MODE +{ + gcvMULTI_GPU_MODE_COMBINED = 0, + gcvMULTI_GPU_MODE_INDEPENDENT = 1 +} +gceMULTI_GPU_MODE; + +typedef enum _gceMACHINECODE +{ + gcvMACHINECODE_ANTUTU0 = 0x0, + + gcvMACHINECODE_GLB27_RELEASE_0, + + gcvMACHINECODE_GLB25_RELEASE_0, + gcvMACHINECODE_GLB25_RELEASE_1, + gcvMACHINECODE_GLB25_RELEASE_2, + + /* keep it as the last enum */ + gcvMACHINECODE_COUNT +} +gceMACHINECODE; + +typedef enum _gceUNIFORMCVT +{ + gcvUNIFORMCVT_NONE = 0, + gcvUNIFORMCVT_TO_BOOL, + gcvUNIFORMCVT_TO_FLOAT, +} gceUNIFORMCVT; + +typedef enum _gceHAL_ARG_VERSION +{ + gcvHAL_ARG_VERSION_V1 = 0x0, +} +gceHAL_ARG_VERSION; + + +/* +* Bit of a requirment is 1 means requirement is a must, 0 means requirement can +* be ignored. +*/ +#define gcvALLOC_FLAG_CONTIGUOUS_BIT 0 +#define gcvALLOC_FLAG_CACHEABLE_BIT 1 +#define gcvALLOC_FLAG_SECURITY_BIT 2 +#define gcvALLOC_FLAG_NON_CONTIGUOUS_BIT 3 +#define gcvALLOC_FLAG_MEMLIMIT_BIT 4 + +/* No special needs. */ +#define gcvALLOC_FLAG_NONE (0) +/* Physical contiguous. */ +#define gcvALLOC_FLAG_CONTIGUOUS (1 << gcvALLOC_FLAG_CONTIGUOUS_BIT) +/* Can be remapped as cacheable. */ +#define gcvALLOC_FLAG_CACHEABLE (1 << gcvALLOC_FLAG_CACHEABLE_BIT) +/* Secure buffer. */ +#define gcvALLOC_FLAG_SECURITY (1 << gcvALLOC_FLAG_SECURITY_BIT) +/* Physical non contiguous. */ +#define gcvALLOC_FLAG_NON_CONTIGUOUS (1 << gcvALLOC_FLAG_NON_CONTIGUOUS_BIT) +#define gcvALLOC_FLAG_MEMLIMIT (1 << gcvALLOC_FLAG_MEMLIMIT_BIT) + +/* GL_VIV internal usage */ +#ifndef GL_MAP_BUFFER_OBJ_VIV +#define GL_MAP_BUFFER_OBJ_VIV 0x10000 +#endif + +/* Command buffer usage. */ +#define gcvCOMMAND_2D (1 << 0) +#define gcvCOMMAND_3D (1 << 1) + +/******************************************************************************\ +****************************** Object Declarations ***************************** +\******************************************************************************/ + +typedef struct _gckCONTEXT * gckCONTEXT; +typedef struct _gcoCMDBUF * gcoCMDBUF; + +typedef struct _gcsSTATE_DELTA * gcsSTATE_DELTA_PTR; +typedef struct _gcsQUEUE * gcsQUEUE_PTR; +typedef struct _gcoQUEUE * gcoQUEUE; +typedef struct _gcsHAL_INTERFACE * gcsHAL_INTERFACE_PTR; +typedef struct _gcs2D_PROFILE * gcs2D_PROFILE_PTR; + +#if gcdENABLE_VG +typedef struct _gcoVGHARDWARE * gcoVGHARDWARE; +typedef struct _gcoVGBUFFER * gcoVGBUFFER; +typedef struct _gckVGHARDWARE * gckVGHARDWARE; +typedef struct _gcsVGCONTEXT * gcsVGCONTEXT_PTR; +typedef struct _gcsVGCONTEXT_MAP * gcsVGCONTEXT_MAP_PTR; +typedef struct _gcsVGCMDQUEUE * gcsVGCMDQUEUE_PTR; +typedef struct _gcsTASK_MASTER_TABLE * gcsTASK_MASTER_TABLE_PTR; +typedef struct _gckVGKERNEL * gckVGKERNEL; +typedef void * gctTHREAD; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_enum_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h b/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h new file mode 100644 index 00000000000000..84283421f1dd70 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h @@ -0,0 +1,225 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_kernel_buffer_h_ +#define __gc_hal_kernel_buffer_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +************************ Command Buffer and Event Objects ********************** +\******************************************************************************/ + +/* The number of context buffers per user. */ +#define gcdCONTEXT_BUFFER_COUNT 2 + +/* State delta record. */ +typedef struct _gcsSTATE_DELTA_RECORD * gcsSTATE_DELTA_RECORD_PTR; +typedef struct _gcsSTATE_DELTA_RECORD +{ + /* State address. */ + gctUINT address; + + /* State mask. */ + gctUINT32 mask; + + /* State data. */ + gctUINT32 data; +} +gcsSTATE_DELTA_RECORD; + +/* State delta. */ +typedef struct _gcsSTATE_DELTA +{ + /* For debugging: the number of delta in the order of creation. */ +#if gcmIS_DEBUG(gcdDEBUG_CODE) + gctUINT num; +#endif + + /* Main state delta ID. Every time state delta structure gets reinitialized, + main ID is incremented. If main state ID overflows, all map entry IDs get + reinitialized to make sure there is no potential erroneous match after + the overflow.*/ + gctUINT id; + + /* The number of contexts pending modification by the delta. */ + gctINT refCount; + + /* Vertex element count for the delta buffer. */ + gctUINT elementCount; + + /* Number of states currently stored in the record array. */ + gctUINT recordCount; + + /* Record array; holds all modified states in gcsSTATE_DELTA_RECORD. */ + gctUINT64 recordArray; + + /* Map entry ID is used for map entry validation. If map entry ID does not + match the main state delta ID, the entry and the corresponding state are + considered not in use. */ + gctUINT64 mapEntryID; + gctUINT mapEntryIDSize; + + /* If the map entry ID matches the main state delta ID, index points to + the state record in the record array. */ + gctUINT64 mapEntryIndex; + + /* Previous and next state deltas in gcsSTATE_DELTA. */ + gctUINT64 prev; + gctUINT64 next; +} +gcsSTATE_DELTA; + +/* Command buffer patch record. */ +struct _gcsPATCH +{ + /* Pointer within the buffer. */ + gctUINT32_PTR pointer; + + /* 32-bit data to write at the specified offset. */ + gctUINT32 data; +}; + +/* List of patches for the command buffer. */ +struct _gcsPATCH_LIST +{ + /* Array of patch records. */ + struct _gcsPATCH patch[1024]; + + /* Number of patches in the array. */ + gctUINT count; + + /* Next item in the list. */ + struct _gcsPATCH_LIST *next; +}; + +/* Command buffer object. */ +struct _gcoCMDBUF +{ + /* The object. */ + gcsOBJECT object; + + /* Commit count. */ + gctUINT count; + + /* Command buffer entry and exit pipes. */ + gcePIPE_SELECT entryPipe; + gcePIPE_SELECT exitPipe; + + /* Feature usage flags. */ + gctBOOL using2D; + gctBOOL using3D; + gctBOOL usingFilterBlit; + gctBOOL usingPalette; + + /* Physical address of command buffer. Just a name. */ + gctUINT32 physical; + + /* Logical address of command buffer. */ + gctUINT64 logical; + + /* Number of bytes in command buffer. */ + gctUINT32 bytes; + + /* Start offset into the command buffer. */ + gctUINT32 startOffset; + + /* Current offset into the command buffer. */ + gctUINT32 offset; + + /* Number of free bytes in command buffer. */ + gctUINT32 free; + + /* Location of the last reserved area. */ + gctUINT64 lastReserve; + gctUINT32 lastOffset; + +#if gcdSECURE_USER + /* Hint array for the current command buffer. */ + gctUINT hintArraySize; + gctUINT64 hintArray; + gctUINT64 hintArrayTail; +#endif + +#if gcmIS_DEBUG(gcdDEBUG_CODE) + /* Last load state command location and hardware address. */ + gctUINT64 lastLoadStatePtr; + gctUINT32 lastLoadStateAddress; + gctUINT32 lastLoadStateCount; +#endif + + /* Completion signal. */ + gctSIGNAL signal; + + /* List of patches. */ + struct _gcsPATCH_LIST *patchHead; + struct _gcsPATCH_LIST *patchTail; + + /* Link to the siblings. */ + gcoCMDBUF prev; + gcoCMDBUF next; +}; + +typedef struct _gcsQUEUE +{ + /* Pointer to next gcsQUEUE structure in gcsQUEUE. */ + gctUINT64 next; + + /* Event information. */ + gcsHAL_INTERFACE iface; +} +gcsQUEUE; + +/* Event queue. */ +struct _gcoQUEUE +{ + /* The object. */ + gcsOBJECT object; + + /* Pointer to current event queue. */ + gcsQUEUE_PTR head; + gcsQUEUE_PTR tail; + + /* chunks of the records. */ + gctPOINTER chunks; + + /* List of free records. */ + gcsQUEUE_PTR freeList; + + #define gcdIN_QUEUE_RECORD_LIMIT 16 + /* Number of records currently in queue */ + gctUINT32 recordCount; +}; + +struct _gcsTEMPCMDBUF +{ + gctUINT32 currentByteSize; + gctPOINTER buffer; + gctBOOL inUse; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_kernel_buffer_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_mem.h b/drivers/gpu/galcore/inc/gc_hal_mem.h new file mode 100644 index 00000000000000..df35019bce2a5d --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_mem.h @@ -0,0 +1,530 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +/* +** Include file for the local memory management. +*/ + +#ifndef __gc_hal_mem_h_ +#define __gc_hal_mem_h_ +#if (gcdENABLE_3D || gcdENABLE_VG) + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* +** Usage: + + The macros to declare MemPool type and functions are + gcmMEM_DeclareFSMemPool (Type, TypeName, Prefix) + gcmMEM_DeclareVSMemPool (Type, TypeName, Prefix) + gcmMEM_DeclareAFSMemPool(Type, TypeName, Prefix) + + The data structures for MemPool are + typedef struct _gcsMEM_FS_MEM_POOL * gcsMEM_FS_MEM_POOL; + typedef struct _gcsMEM_VS_MEM_POOL * gcsMEM_VS_MEM_POOL; + typedef struct _gcsMEM_AFS_MEM_POOL * gcsMEM_AFS_MEM_POOL; + + The MemPool constructor and destructor functions are + gcfMEM_InitFSMemPool(gcsMEM_FS_MEM_POOL *, gcoOS, gctUINT, gctUINT); + gcfMEM_FreeFSMemPool(gcsMEM_FS_MEM_POOL *); + gcfMEM_InitVSMemPool(gcsMEM_VS_MEM_POOL *, gcoOS, gctUINT, gctBOOL); + gcfMEM_FreeVSMemPool(gcsMEM_VS_MEM_POOL *); + gcfMEM_InitAFSMemPool(gcsMEM_AFS_MEM_POOL *, gcoOS, gctUINT); + gcfMEM_FreeAFSMemPool(gcsMEM_AFS_MEM_POOL *); + + FS: for Fixed-Size data structures + VS: for Variable-size data structures + AFS: for Array of Fixed-Size data structures + + + // Example 1: For a fixed-size data structure, struct gcsNode. + // It is used locally in a file, so the functions are static without prefix. + // At top level, declear allocate and free functions. + // The first argument is the data type. + // The second armument is the short name used in the fuctions. + gcmMEM_DeclareFSMemPool(struct gcsNode, Node, ); + + // The previous macro creates two inline functions, + // _AllocateNode and _FreeNode. + + // In function or struct + gcsMEM_FS_MEM_POOL nodeMemPool; + + // In function, + struct gcsNode * node; + gceSTATUS status; + + // Before using the memory pool, initialize it. + // The second argument is the gcoOS object. + // The third argument is the number of data structures to allocate for each chunk. + status = gcfMEM_InitFSMemPool(&nodeMemPool, os, 100, sizeof(struct gcsNode)); + ... + + // Allocate a node. + status = _AllocateNode(nodeMemPool, &node); + ... + // Free a node. + _FreeNode(nodeMemPool, node); + + // After using the memory pool, free it. + gcfMEM_FreeFSMemPool(&nodeMemPool); + + + // Example 2: For array of fixed-size data structures, struct gcsNode. + // It is used in several files, so the functions are extern with prefix. + // At top level, declear allocate and free functions. + // The first argument is the data type, and the second one is the short name + // used in the fuctions. + gcmMEM_DeclareAFSMemPool(struct gcsNode, NodeArray, gcfOpt); + + // The previous macro creates two inline functions, + // gcfOpt_AllocateNodeArray and gcfOpt_FreeNodeArray. + + // In function or struct + gcsMEM_AFS_MEM_POOL nodeArrayMemPool; + + // In function, + struct gcsNode * nodeArray; + gceSTATUS status; + + // Before using the array memory pool, initialize it. + // The second argument is the gcoOS object, the third is the number of data + // structures to allocate for each chunk. + status = gcfMEM_InitAFSMemPool(&nodeArrayMemPool, os, sizeof(struct gcsNode)); + ... + + // Allocate a node array of size 100. + status = gcfOpt_AllocateNodeArray(nodeArrayMemPool, &nodeArray, 100); + ... + // Free a node array. + gcfOpt_FreeNodeArray(&nodeArrayMemPool, nodeArray); + + // After using the array memory pool, free it. + gcfMEM_FreeAFSMemPool(&nodeArrayMemPool); + +*******************************************************************************/ + +/******************************************************************************* +** To switch back to use gcoOS_Allocate and gcoOS_Free, add +** #define USE_LOCAL_MEMORY_POOL 0 +** before including this file. +*******************************************************************************/ +#ifndef USE_LOCAL_MEMORY_POOL +/* + USE_LOCAL_MEMORY_POOL + + This define enables the local memory management to improve performance. +*/ +#define USE_LOCAL_MEMORY_POOL 1 +#endif + +/******************************************************************************* +** Memory Pool Data Structures +*******************************************************************************/ +#if USE_LOCAL_MEMORY_POOL + typedef struct _gcsMEM_FS_MEM_POOL * gcsMEM_FS_MEM_POOL; + typedef struct _gcsMEM_VS_MEM_POOL * gcsMEM_VS_MEM_POOL; + typedef struct _gcsMEM_AFS_MEM_POOL * gcsMEM_AFS_MEM_POOL; +#else + typedef gcoOS gcsMEM_FS_MEM_POOL; + typedef gcoOS gcsMEM_VS_MEM_POOL; + typedef gcoOS gcsMEM_AFS_MEM_POOL; +#endif + +/******************************************************************************* +** Memory Pool Macros +*******************************************************************************/ +#if USE_LOCAL_MEMORY_POOL +#define gcmMEM_DeclareFSMemPool(Type, TypeName, Prefix) \ +gceSTATUS \ +Prefix##_Allocate##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type ** Pointer \ + ) \ +{ \ + return(gcfMEM_FSMemPoolGetANode(MemPool, (gctPOINTER *) Pointer)); \ +} \ + \ +gceSTATUS \ +Prefix##_CAllocate##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type ** Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + gcmERR_RETURN(gcfMEM_FSMemPoolGetANode(MemPool, (gctPOINTER *) Pointer)); \ + gcoOS_ZeroMemory(*(gctPOINTER *) Pointer, gcmSIZEOF(Type)); \ + gcmFOOTER(); \ + return gcvSTATUS_OK; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type * Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + status = gcfMEM_FSMemPoolFreeANode(MemPool, (gctPOINTER) Pointer); \ + gcmFOOTER(); \ + return status; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName##List( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type * FirstPointer, \ + Type * LastPointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x FirstPointer=0x%x LastPointer=0x%x", MemPool, FirstPointer, LastPointer); \ + status = gcfMEM_FSMemPoolFreeAList(MemPool, (gctPOINTER) FirstPointer, (gctPOINTER) LastPointer); \ + gcmFOOTER(); \ + return status; \ +} + +#define gcmMEM_DeclareVSMemPool(Type, TypeName, Prefix) \ +gceSTATUS \ +Prefix##_Allocate##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Size \ + ) \ +{ \ + gceSTATUS status;\ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Size=%u", MemPool, Pointer, Size); \ + status = gcfMEM_VSMemPoolGetANode(MemPool, Size, (gctPOINTER *) Pointer); \ + gcmFOOTER(); \ + return status; \ +} \ + \ +gceSTATUS \ + Prefix##_CAllocate##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Size \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Size=%u", MemPool, Pointer, Size); \ + gcmERR_RETURN(gcfMEM_VSMemPoolGetANode(MemPool, Size, (gctPOINTER *) Pointer)); \ + gcoOS_ZeroMemory(*(gctPOINTER *) Pointer, size); \ + gcmFOOTER(); \ + return gcvSTATUS_OK; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type * Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pinter); \ + status = gcfMEM_VSMemPoolFreeANode(MemPool, (gctPOINTER) Pointer); \ + gcmFOOTER(); \ + return status; \ +} + +#define gcmMEM_DeclareAFSMemPool(Type, TypeName, Prefix) \ +gceSTATUS \ +Prefix##_Allocate##TypeName( \ + gcsMEM_AFS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Count \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Count=%u", MemPool, Pointer, Count); \ + status = gcfMEM_AFSMemPoolGetANode(MemPool, Count, (gctPOINTER *) Pointer); \ + gcmFOOTER(); \ + return status; \ +} \ + \ +gceSTATUS \ +Prefix##_CAllocate##TypeName( \ + gcsMEM_AFS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Count \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Count=%u", MemPool, Pointer, Count); \ + gcmERR_RETURN(gcfMEM_AFSMemPoolGetANode(MemPool, Count, (gctPOINTER *) Pointer)); \ + gcoOS_ZeroMemory(*(gctPOINTER *) Pointer, Count * gcmSIZEOF(Type)); \ + gcmFOOTER(); \ + return gcvSTATUS_OK; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName( \ + gcsMEM_AFS_MEM_POOL MemPool, \ + Type * Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + status = gcfMEM_AFSMemPoolFreeANode(MemPool, (gctPOINTER) Pointer); \ + gcmFOOTER(); \ + return status; \ +} + +#else + +#define gcmMEM_DeclareFSMemPool(Type, TypeName, Prefix) \ +gceSTATUS \ +Prefix##_Allocate##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type ** Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + status = gcoOS_Allocate(MemPool, \ + gcmSIZEOF(Type), \ + (gctPOINTER *) Pointer); \ + gcmFOOTER(); \ + return status; \ +} \ + \ +gceSTATUS \ +Prefix##_CAllocate##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type ** Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + gcmERR_RETURN(gcoOS_Allocate(MemPool, \ + gcmSIZEOF(Type), \ + (gctPOINTER *) Pointer)); \ + gcoOS_ZeroMemory(*(gctPOINTER *) Pointer, gcmSIZEOF(Type)); \ + gcmFOOTER(); \ + return gcvSTATUS_OK; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName( \ + gcsMEM_FS_MEM_POOL MemPool, \ + Type * Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + status = gcmOS_SAFE_FREE(MemPool, Pointer); \ + gcmFOOTER(); \ + return status; \ +} + +#define gcmMEM_DeclareVSMemPool(Type, TypeName, Prefix) \ +gceSTATUS \ +Prefix##_Allocate##TypeName( \ + gcsMEM_VS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Size \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Size=%u", MemPool, Pointer, Size); \ + status = gcoOS_Allocate(MemPool, \ + Size, \ + (gctPOINTER *) Pointer); \ + gcmFOOTER(); \ + return status; \ +} \ + \ +gceSTATUS \ +Prefix##_CAllocate##TypeName( \ + gcsMEM_VS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Size \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Size=%u", MemPool, Pointer, Size); \ + gcmERR_RETURN(gcoOS_Allocate(MemPool, \ + Size, \ + (gctPOINTER *) Pointer)); \ + gcoOS_ZeroMemory(*(gctPOINTER *) Pointer, Size); \ + gcmFOOTER(); \ + return gcvSTATUS_OK; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName( \ + gcsMEM_VS_MEM_POOL MemPool, \ + Type * Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + status = gcmOS_SAFE_FREE(MemPool, Pointer); \ + gcmFOOTER(); \ + return status; \ +} + +#define gcmMEM_DeclareAFSMemPool(Type, TypeName, Prefix) \ +gceSTATUS \ +Prefix##_Allocate##TypeName( \ + gcsMEM_AFS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Count \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Count=%u", MemPool, Pointer, Count); \ + status = gcoOS_Allocate(MemPool, \ + Count * gcmSIZEOF(Type), \ + (gctPOINTER *) Pointer); \ + gcmFOOTER(); \ + return status; \ +} \ + \ +gceSTATUS \ +Prefix##_CAllocate##TypeName( \ + gcsMEM_AFS_MEM_POOL MemPool, \ + Type ** Pointer, \ + gctUINT Count \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x Count=%u", MemPool, Pointer, Count); \ + gcmERR_RETURN(gcoOS_Allocate(MemPool, \ + Count * gcmSIZEOF(Type), \ + (gctPOINTER *) Pointer)); \ + gcoOS_ZeroMemory(*(gctPOINTER *) Pointer, Count * gcmSIZEOF(Type)); \ + gcmFOOTER(); \ + return gcvSTATUS_OK; \ +} \ + \ +gceSTATUS \ +Prefix##_Free##TypeName( \ + gcsMEM_AFS_MEM_POOL MemPool, \ + Type * Pointer \ + ) \ +{ \ + gceSTATUS status; \ + gcmHEADER_ARG("MemPool=0x%x Pointer=0x%x", MemPool, Pointer); \ + status = gcmOS_SAFE_FREE(MemPool, Pointer); \ + gcmFOOTER(); \ + return status; \ +} +#endif + +/******************************************************************************* +** Memory Pool Data Functions +*******************************************************************************/ +gceSTATUS +gcfMEM_InitFSMemPool( + IN gcsMEM_FS_MEM_POOL * MemPool, + IN gcoOS OS, + IN gctUINT NodeCount, + IN gctUINT NodeSize + ); + +gceSTATUS +gcfMEM_FreeFSMemPool( + IN gcsMEM_FS_MEM_POOL * MemPool + ); + +gceSTATUS +gcfMEM_FSMemPoolGetANode( + IN gcsMEM_FS_MEM_POOL MemPool, + OUT gctPOINTER * Node + ); + +gceSTATUS +gcfMEM_FSMemPoolFreeANode( + IN gcsMEM_FS_MEM_POOL MemPool, + IN gctPOINTER Node + ); + +gceSTATUS +gcfMEM_FSMemPoolFreeAList( + IN gcsMEM_FS_MEM_POOL MemPool, + IN gctPOINTER FirstNode, + IN gctPOINTER LastNode + ); + +gceSTATUS +gcfMEM_InitVSMemPool( + IN gcsMEM_VS_MEM_POOL * MemPool, + IN gcoOS OS, + IN gctUINT BlockSize, + IN gctBOOL RecycleFreeNode + ); + +gceSTATUS +gcfMEM_FreeVSMemPool( + IN gcsMEM_VS_MEM_POOL * MemPool + ); + +gceSTATUS +gcfMEM_VSMemPoolGetANode( + IN gcsMEM_VS_MEM_POOL MemPool, + IN gctUINT Size, + IN gctUINT Alignment, + OUT gctPOINTER * Node + ); + +gceSTATUS +gcfMEM_VSMemPoolFreeANode( + IN gcsMEM_VS_MEM_POOL MemPool, + IN gctPOINTER Node + ); + +gceSTATUS +gcfMEM_InitAFSMemPool( + IN gcsMEM_AFS_MEM_POOL *MemPool, + IN gcoOS OS, + IN gctUINT NodeCount, + IN gctUINT NodeSize + ); + +gceSTATUS +gcfMEM_FreeAFSMemPool( + IN gcsMEM_AFS_MEM_POOL *MemPool + ); + +gceSTATUS +gcfMEM_AFSMemPoolGetANode( + IN gcsMEM_AFS_MEM_POOL MemPool, + IN gctUINT Count, + OUT gctPOINTER * Node + ); + +gceSTATUS +gcfMEM_AFSMemPoolFreeANode( + IN gcsMEM_AFS_MEM_POOL MemPool, + IN gctPOINTER Node + ); + +#ifdef __cplusplus +} +#endif + +#endif /* (gcdENABLE_3D || gcdENABLE_VG) */ +#endif /* __gc_hal_mem_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h new file mode 100644 index 00000000000000..b93015d32f923e --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -0,0 +1,1271 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +#ifndef __gc_hal_options_h_ +#define __gc_hal_options_h_ + +/* + gcdSECURITY + +*/ +#ifndef gcdSECURITY +# define gcdSECURITY 0 +#endif + +/* + gcdPRINT_VERSION + + Print HAL version. +*/ +#ifndef gcdPRINT_VERSION +# define gcdPRINT_VERSION 0 +#endif + +/* + USE_NEW_LINUX_SIGNAL + + This define enables the Linux kernel signaling between kernel and user. +*/ +#ifndef USE_NEW_LINUX_SIGNAL +# define USE_NEW_LINUX_SIGNAL 0 +#endif + +/* + VIVANTE_PROFILER + + This define enables the profiler. +*/ +#ifndef VIVANTE_PROFILER +# define VIVANTE_PROFILER 1 +#endif + +/* + VIVANTE_PROFILER_CONTEXT + + This define enables the profiler according each context. +*/ +#ifndef VIVANTE_PROFILER_CONTEXT +# define VIVANTE_PROFILER_CONTEXT 1 +#endif + +#ifndef VIVANTE_PROFILER_PERDRAW +# define VIVANTE_PROFILER_PERDRAW 0 +#endif + +#ifndef VIVANTE_PROFILER_NEW +# define VIVANTE_PROFILER_NEW 0 +#endif + +#ifndef VIVANTE_PROFILER_PM +# define VIVANTE_PROFILER_PM 1 +#endif +/* + gcdUSE_VG + + Enable VG HAL layer (only for GC350). +*/ +#ifndef gcdUSE_VG +# define gcdUSE_VG 0 +#endif + +/* + USE_SW_FB + + Set to 1 if the frame buffer memory cannot be accessed by the GPU. +*/ +#ifndef USE_SW_FB +# define USE_SW_FB 0 +#endif + +/* + PROFILE_HAL_COUNTERS + + This define enables HAL counter profiling support. HW and SHADER + counter profiling depends on this. +*/ +#ifndef PROFILE_HAL_COUNTERS +# define PROFILE_HAL_COUNTERS 1 +#endif + +/* + PROFILE_HW_COUNTERS + + This define enables HW counter profiling support. +*/ +#ifndef PROFILE_HW_COUNTERS +# define PROFILE_HW_COUNTERS 1 +#endif + +/* + PROFILE_SHADER_COUNTERS + + This define enables SHADER counter profiling support. +*/ +#ifndef PROFILE_SHADER_COUNTERS +# define PROFILE_SHADER_COUNTERS 1 +#endif + +/* + COMMAND_PROCESSOR_VERSION + + The version of the command buffer and task manager. +*/ +#define COMMAND_PROCESSOR_VERSION 1 + +/* + gcdDUMP_KEY + + Set this to a string that appears in 'cat /proc//cmdline'. E.g. 'camera'. + HAL will create dumps for the processes matching this key. +*/ +#ifndef gcdDUMP_KEY +# define gcdDUMP_KEY "process" +#endif + +/* + gcdDUMP_PATH + + The dump file location. Some processes cannot write to the sdcard. + Try apps' data dir, e.g. /data/data/com.android.launcher +*/ +#ifndef gcdDUMP_PATH +#if defined(ANDROID) +# define gcdDUMP_PATH "/mnt/sdcard/" +#else +# define gcdDUMP_PATH "./" +#endif +#endif + +/* + gcdDUMP + + When set to 1, a dump of all states and memory uploads, as well as other + hardware related execution will be printed to the debug console. This + data can be used for playing back applications. +*/ +#ifndef gcdDUMP +# define gcdDUMP 0 +#endif + +/* + gcdDUMP_API + + When set to 1, a high level dump of the EGL and GL/VG APs's are + captured. +*/ +#ifndef gcdDUMP_API +# define gcdDUMP_API 0 +#endif + + + +/* + gcdDEBUG_OPTION + When set to 1, the debug options are enabled. We must set other MACRO to enable + sub case. +*/ +#ifndef gcdDEBUG_OPTION +# define gcdDEBUG_OPTION 0 + +#if gcdDEBUG_OPTION +/* + gcdDEBUG_OPTION_KEY + The process name of debug application. +*/ +#ifndef gcdDEBUG_OPTION_KEY +# define gcdDEBUG_OPTION_KEY "process" +# endif +/* + gcdDEBUG_OPTION_NO_GL_DRAWS + When set to 1, all glDrawArrays and glDrawElements will be skip. +*/ +#ifndef gcdDEBUG_OPTION_NO_GL_DRAWS +# define gcdDEBUG_OPTION_NO_GL_DRAWS 0 +# endif +/* + gcdDEBUG_OPTION_NO_DRAW_PRIMITIVES + When set to 1, all DrawPrimitives will be skip. +*/ +#ifndef gcdDEBUG_OPTION_NO_DRAW_PRIMITIVES +# define gcdDEBUG_OPTION_NO_DRAW_PRIMITIVES 0 +# endif +/* + gcdDEBUG_OPTION_SKIP_SWAP + When set to 1, just one out of gcdDEBUG_OPTION_SKIP_FRAMES(such as 1/10) eglSwapBuffers will be resolve, + others skip. +*/ +#ifndef gcdDEBUG_OPTION_SKIP_SWAP +# define gcdDEBUG_OPTION_SKIP_SWAP 0 +# define gcdDEBUG_OPTION_SKIP_FRAMES 10 +# endif +/* + gcdDEBUG_OPTION_FORCE_16BIT_RENDER_TARGET + When set to 1, the format of render target will force to RGB565. +*/ +#ifndef gcdDEBUG_OPTION_FORCE_16BIT_RENDER_TARGET +# define gcdDEBUG_OPTION_FORCE_16BIT_RENDER_TARGET 0 +# endif +/* + gcdDEBUG_OPTION_NONE_TEXTURE + When set to 1, the type of texture will be set to AQ_TEXTURE_SAMPLE_MODE_TYPE_NONE. +*/ +#ifndef gcdDEBUG_OPTION_NONE_TEXTURE +# define gcdDEBUG_OPTION_NONE_TEXTURE 0 +# endif +/* + gcdDEBUG_OPTION_NONE_DEPTH + When set to 1, the depth format of surface will be set to gcvSURF_UNKNOWN. +*/ +#ifndef gcdDEBUG_OPTION_NONE_DEPTH +# define gcdDEBUG_OPTION_NONE_DEPTH 0 +# endif + +# endif +#endif + +/* + gcdDUMP_SWAP_PER_DRAW + + When set to 1, dump swap command for every single draw to make simulation comparison happy. + Only valid for ES3 driver for now. +*/ +#ifndef gcdDUMP_SWAP_PER_DRAW +# define gcdDUMP_SWAP_PER_DRAW 0 +#endif + +/* + gcdDUMP_FRAMERATE + When set to a value other than zero, averaqe frame rate will be dumped. + The value set is the starting frame that the average will be calculated. + This is needed because sometimes first few frames are too slow to be included + in the average. Frame count starts from 1. +*/ +#ifndef gcdDUMP_FRAMERATE +# define gcdDUMP_FRAMERATE 0 +#endif + +/* + gcdENABLE_FSCALE_VAL_ADJUST + When non-zero, FSCALE_VAL when gcvPOWER_ON can be adjusted externally. + */ +#ifndef gcdENABLE_FSCALE_VAL_ADJUST +# define gcdENABLE_FSCALE_VAL_ADJUST 1 +#endif + +/* + gcdDUMP_IN_KERNEL + + When set to 1, all dumps will happen in the kernel. This is handy if + you want the kernel to dump its command buffers as well and the data + needs to be in sync. +*/ +#ifndef gcdDUMP_IN_KERNEL +# define gcdDUMP_IN_KERNEL 0 +#endif + +/* + gcdDUMP_COMMAND + + When set to non-zero, the command queue will dump all incoming command + and context buffers as well as all other modifications to the command + queue. +*/ +#ifndef gcdDUMP_COMMAND +# define gcdDUMP_COMMAND 0 +#endif + +/* + gcdDUMP_2D + + When set to non-zero, it will dump the 2D command and surface. +*/ +#ifndef gcdDUMP_2D +# define gcdDUMP_2D 0 +#endif + +/* + gcdDUMP_FRAME_TGA + + When set to a value other than 0, a dump of the frame specified by the value, + will be done into frame.tga. Frame count starts from 1. + */ +#ifndef gcdDUMP_FRAME_TGA +# define gcdDUMP_FRAME_TGA 0 +#endif +/* + gcdNULL_DRIVER + + Set to 1 for infinite speed hardware. + Set to 2 for bypassing the HAL. + Set to 3 for bypassing the drivers. +*/ +#ifndef gcdNULL_DRIVER +# define gcdNULL_DRIVER 0 +#endif + +/* + gcdENABLE_TIMEOUT_DETECTION + + Enable timeout detection. +*/ +#ifndef gcdENABLE_TIMEOUT_DETECTION +# define gcdENABLE_TIMEOUT_DETECTION 0 +#endif + +/* + gcdCMD_BUFFER_SIZE + + Number of bytes in a command buffer. +*/ +#ifndef gcdCMD_BUFFER_SIZE +# define gcdCMD_BUFFER_SIZE (128 << 10) +#endif + +/* + gcdCMD_BUFFERS + + Number of command buffers to use per client. +*/ +#ifndef gcdCMD_BUFFERS +# define gcdCMD_BUFFERS 2 +#endif + +/* + gcdMAX_CMD_BUFFERS + + Maximum number of command buffers to use per client. +*/ +#ifndef gcdMAX_CMD_BUFFERS +# define gcdMAX_CMD_BUFFERS 8 +#endif + +/* + gcdCOMMAND_QUEUES + + Number of command queues in the kernel. +*/ +#ifndef gcdCOMMAND_QUEUES +# define gcdCOMMAND_QUEUES 2 +#endif + +/* + gcdPOWER_CONTROL_DELAY + + The delay in milliseconds required to wait until the GPU has woke up + from a suspend or power-down state. This is system dependent because + the bus clock also needs to stabalize. +*/ +#ifndef gcdPOWER_CONTROL_DELAY +# define gcdPOWER_CONTROL_DELAY 0 +#endif + +/* + gcdMIRROR_PAGETABLE + + Enable it when GPUs with old MMU and new MMU exist at same SoC. It makes + each GPU use same virtual address to access same physical memory. +*/ +#ifndef gcdMIRROR_PAGETABLE +# define gcdMIRROR_PAGETABLE 0 +#endif + +/* + gcdMMU_SIZE + + Size of the MMU page table in bytes. Each 4 bytes can hold 4kB worth of + virtual data. +*/ +#ifndef gcdMMU_SIZE +#if gcdMIRROR_PAGETABLE +# define gcdMMU_SIZE 0x200000 +#else +# define gcdMMU_SIZE (2048 << 10) +#endif +#endif + +/* + gcdSECURE_USER + + Use logical addresses instead of physical addresses in user land. In + this case a hint table is created for both command buffers and context + buffers, and that hint table will be used to patch up those buffers in + the kernel when they are ready to submit. +*/ +#ifndef gcdSECURE_USER +# define gcdSECURE_USER 0 +#endif + +/* + gcdSECURE_CACHE_SLOTS + + Number of slots in the logical to DMA address cache table. Each time a + logical address needs to be translated into a DMA address for the GPU, + this cache will be walked. The replacement scheme is LRU. +*/ +#ifndef gcdSECURE_CACHE_SLOTS +# define gcdSECURE_CACHE_SLOTS 1024 +#endif + +/* + gcdSECURE_CACHE_METHOD + + Replacement scheme used for Secure Cache. The following options are + available: + + gcdSECURE_CACHE_LRU + A standard LRU cache. + + gcdSECURE_CACHE_LINEAR + A linear walker with the idea that an application will always + render the scene in a similar way, so the next entry in the + cache should be a hit most of the time. + + gcdSECURE_CACHE_HASH + A 256-entry hash table. + + gcdSECURE_CACHE_TABLE + A simple cache but with potential of a lot of cache replacement. +*/ +#ifndef gcdSECURE_CACHE_METHOD +# define gcdSECURE_CACHE_METHOD gcdSECURE_CACHE_HASH +#endif + +/* + gcdREGISTER_ACCESS_FROM_USER + + Set to 1 to allow IOCTL calls to get through from user land. This + should only be in debug or development drops. +*/ +#ifndef gcdREGISTER_ACCESS_FROM_USER +# define gcdREGISTER_ACCESS_FROM_USER 1 +#endif + +/* + gcdHEAP_SIZE + + Set the allocation size for the internal heaps. Each time a heap is + full, a new heap will be allocated with this minmimum amount of bytes. + The bigger this size, the fewer heaps there are to allocate, the better + the performance. However, heaps won't be freed until they are + completely free, so there might be some more memory waste if the size is + too big. +*/ +#ifndef gcdHEAP_SIZE +# define gcdHEAP_SIZE (64 << 10) +#endif + +/* + gcdPOWER_SUSPEND_WHEN_IDLE + + Set to 1 to make GPU enter gcvPOWER_SUSPEND when idle detected, + otherwise GPU will enter gcvPOWER_IDLE. +*/ +#ifndef gcdPOWER_SUSPEND_WHEN_IDLE +# define gcdPOWER_SUSPEND_WHEN_IDLE 1 +#endif + +#ifndef gcdFPGA_BUILD +# define gcdFPGA_BUILD 0 +#endif + +/* + gcdGPU_TIMEOUT + + This define specified the number of milliseconds the system will wait + before it broadcasts the GPU is stuck. In other words, it will define + the timeout of any operation that needs to wait for the GPU. + + If the value is 0, no timeout will be checked for. +*/ +#ifndef gcdGPU_TIMEOUT +#if gcdFPGA_BUILD +# define gcdGPU_TIMEOUT 0 +# define gcdGPU_2D_TIMEOUT 0 +# else +# define gcdGPU_TIMEOUT 20000 +# define gcdGPU_2D_TIMEOUT 4000 +# endif +#endif + +/* + gcdGPU_ADVANCETIMER + + it is advance timer. +*/ +#ifndef gcdGPU_ADVANCETIMER +# define gcdGPU_ADVANCETIMER 250 +#endif + +/* + gcdSTATIC_LINK + + This define disalbes static linking; +*/ +#ifndef gcdSTATIC_LINK +# define gcdSTATIC_LINK 0 +#endif + +/* + gcdUSE_NEW_HEAP + + Setting this define to 1 enables new heap. +*/ +#ifndef gcdUSE_NEW_HEAP +# define gcdUSE_NEW_HEAP 0 +#endif + +/* + gcdCMD_NO_2D_CONTEXT + + This define enables no-context 2D command buffer. +*/ +#ifndef gcdCMD_NO_2D_CONTEXT +# define gcdCMD_NO_2D_CONTEXT 1 +#endif + +/* + gcdENABLE_BUFFER_ALIGNMENT + + When enabled, video memory is allocated with atleast 16KB aligment + between multiple sub-buffers. +*/ +#ifndef gcdENABLE_BUFFER_ALIGNMENT +# define gcdENABLE_BUFFER_ALIGNMENT 1 +#endif + +/* + gcdENABLE_BANK_ALIGNMENT + + When enabled, video memory is allocated bank aligned. The vendor can modify + _GetSurfaceBankAlignment() and _GetBankOffsetBytes() to define how + different types of allocations are bank and channel aligned. + When disabled (default), no bank alignment is done. +*/ +#ifndef gcdENABLE_BANK_ALIGNMENT +# define gcdENABLE_BANK_ALIGNMENT 0 +#endif + +/* + gcdBANK_BIT_START + + Specifies the start bit of the bank (inclusive). +*/ +#ifndef gcdBANK_BIT_START +# define gcdBANK_BIT_START 12 +#endif + +/* + gcdBANK_BIT_END + + Specifies the end bit of the bank (inclusive). +*/ +#ifndef gcdBANK_BIT_END +# define gcdBANK_BIT_END 14 +#endif + +/* + gcdBANK_CHANNEL_BIT + + When set, video memory when allocated bank aligned is allocated such that + render and depth buffer addresses alternate on the channel bit specified. + This option has an effect only when gcdENABLE_BANK_ALIGNMENT is enabled. + When disabled (default), no alteration is done. +*/ +#ifndef gcdBANK_CHANNEL_BIT +# define gcdBANK_CHANNEL_BIT 7 +#endif + +/* + gcdDYNAMIC_SPEED + + When non-zero, it informs the kernel driver to use the speed throttling + broadcasting functions to inform the system the GPU should be spet up or + slowed down. It will send a broadcast for slowdown each "interval" + specified by this define in milliseconds + (gckOS_BroadcastCalibrateSpeed). +*/ +#ifndef gcdDYNAMIC_SPEED +# define gcdDYNAMIC_SPEED 2000 +#endif + +/* + gcdDYNAMIC_EVENT_THRESHOLD + + When non-zero, it specifies the maximum number of available events at + which the kernel driver will issue a broadcast to speed up the GPU + (gckOS_BroadcastHurry). +*/ +#ifndef gcdDYNAMIC_EVENT_THRESHOLD +# define gcdDYNAMIC_EVENT_THRESHOLD 5 +#endif + +/* + gcdENABLE_PROFILING + + Enable profiling macros. +*/ +#ifndef gcdENABLE_PROFILING +# define gcdENABLE_PROFILING 0 +#endif + +/* + gcdENABLE_128B_MERGE + + Enable 128B merge for the BUS control. +*/ +#ifndef gcdENABLE_128B_MERGE +# define gcdENABLE_128B_MERGE 0 +#endif + +/* + gcdFRAME_DB + + When non-zero, it specified the number of frames inside the frame + database. The frame DB will collect per-frame timestamps and hardware + counters. +*/ +#ifndef gcdFRAME_DB +# define gcdFRAME_DB 0 +# define gcdFRAME_DB_RESET 0 +# define gcdFRAME_DB_NAME "/var/log/frameDB.log" +#endif + +/* + gcdDISABLE_CORES_2D3D + disable the 2D3D cores for 2D openVG +*/ +#ifndef gcdDISABLE_CORES_2D3D +# define gcdDISABLE_CORES_2D3D 0 +#endif + +/* + gcdPAGED_MEMORY_CACHEABLE + + When non-zero, paged memory will be cacheable. + + Normally, driver will detemines whether a video memory + is cacheable or not. When cacheable is not neccessary, + it will be writecombine. + + This option is only for those SOC which can't enable + writecombine without enabling cacheable. +*/ +#ifndef gcdPAGED_MEMORY_CACHEABLE +# define gcdPAGED_MEMORY_CACHEABLE 0 +#endif + +/* + gcdNONPAGED_MEMORY_CACHEABLE + + When non-zero, non paged memory will be cacheable. +*/ +#ifndef gcdNONPAGED_MEMORY_CACHEABLE +# define gcdNONPAGED_MEMORY_CACHEABLE 0 +#endif + +/* + gcdNONPAGED_MEMORY_BUFFERABLE + + When non-zero, non paged memory will be bufferable. + gcdNONPAGED_MEMORY_BUFFERABLE and gcdNONPAGED_MEMORY_CACHEABLE + can't be set 1 at same time +*/ +#ifndef gcdNONPAGED_MEMORY_BUFFERABLE +# define gcdNONPAGED_MEMORY_BUFFERABLE 1 +#endif + +/* + gcdENABLE_INFINITE_SPEED_HW + enable the Infinte HW , this is for 2D openVG +*/ +#ifndef gcdENABLE_INFINITE_SPEED_HW +# define gcdENABLE_INFINITE_SPEED_HW 0 +#endif + +/* + gcdMULTI_GPU + + Enable/disable multi-GPU support. + 0 : Disable multi-GPU support + 1 : Enable one of the 3D cores + [2..X] : Number of 3D GPU Cores +*/ +#ifndef gcdMULTI_GPU +# define gcdMULTI_GPU 0 +#endif + +/* + gcdMULTI_GPU_AFFINITY + + Enable/disable the binding of a context to one GPU +*/ +#ifndef gcdMULTI_GPU_AFFINITY +# define gcdMULTI_GPU_AFFINITY 0 +#endif + +/* + gcdPOWEROFF_TIMEOUT + + When non-zero, GPU will power off automatically from + idle state, and gcdPOWEROFF_TIMEOUT is also the default + timeout in milliseconds. + */ +#ifndef gcdPOWEROFF_TIMEOUT +# define gcdPOWEROFF_TIMEOUT 300 +#endif + +/* + QNX_SINGLE_THREADED_DEBUGGING +*/ +#ifndef QNX_SINGLE_THREADED_DEBUGGING +# define QNX_SINGLE_THREADED_DEBUGGING 0 +#endif + +/* + gcdRENDER_THREADS + + Number of render threads. Make it zero, and there will be no render + threads. +*/ +#ifndef gcdRENDER_THREADS +# define gcdRENDER_THREADS 0 +#endif + +/* + gcdSMP + + This define enables SMP support. + + Currently, it only works on Linux/Android, + Kbuild will config it according to whether + CONFIG_SMP is set. + +*/ +#ifndef gcdSMP +#ifdef __APPLE__ +# define gcdSMP 1 +#else +# define gcdSMP 0 +#endif +#endif + +/* + gcdSHARED_RESOLVE_BUFFER_ENABLED + + Use shared resolve buffer for all app buffers. +*/ +#ifndef gcdSHARED_RESOLVE_BUFFER_ENABLED +# define gcdSHARED_RESOLVE_BUFFER_ENABLED 0 +#endif + +/* + gcdUSE_TRIANGLE_STRIP_PATCH + */ +#ifndef gcdUSE_TRIANGLE_STRIP_PATCH +# define gcdUSE_TRIANGLE_STRIP_PATCH 1 +#endif + +/* + gcdENABLE_OUTER_CACHE_PATCH + + Enable the outer cache patch. +*/ +#ifndef gcdENABLE_OUTER_CACHE_PATCH +# define gcdENABLE_OUTER_CACHE_PATCH 0 +#endif + +/* + gcdPROCESS_ADDRESS_SPACE + + When non-zero, every process which attaches to galcore has its own GPU + address space, size of which is gcdPROCESS_ADDRESS_SPACE_SIZE. +*/ +#ifndef gcdPROCESS_ADDRESS_SPACE +# define gcdPROCESS_ADDRESS_SPACE 0 +# define gcdPROCESS_ADDRESS_SPACE_SIZE 0x80000000 +#endif + +/* + gcdSHARED_PAGETABLE + + When non-zero, multiple GPUs in one chip with same MMU use + one shared pagetable. So that when accessing same surface, + they can use same GPU virtual address. +*/ +#ifndef gcdSHARED_PAGETABLE +# define gcdSHARED_PAGETABLE !gcdPROCESS_ADDRESS_SPACE +#endif + +#ifndef gcdUSE_PVR +# define gcdUSE_PVR 1 +#endif + +/* + gcdSMALL_BLOCK_SIZE + + When non-zero, a part of VIDMEM will be reserved for requests + whose requesting size is less than gcdSMALL_BLOCK_SIZE. + + For Linux, it's the size of a page. If this requeset fallbacks + to gcvPOOL_CONTIGUOUS or gcvPOOL_VIRTUAL, memory will be wasted + because they allocate a page at least. +*/ +#ifndef gcdSMALL_BLOCK_SIZE +# define gcdSMALL_BLOCK_SIZE 4096 +# define gcdRATIO_FOR_SMALL_MEMORY 32 +#endif + +/* + gcdCONTIGUOUS_SIZE_LIMIT + When non-zero, size of video node from gcvPOOL_CONTIGUOUS is + limited by gcdCONTIGUOUS_SIZE_LIMIT. +*/ +#ifndef gcdCONTIGUOUS_SIZE_LIMIT +# define gcdCONTIGUOUS_SIZE_LIMIT 0 +#endif + +/* + gcdLINK_QUEUE_SIZE + + When non-zero, driver maintains a queue to record information of + latest lined context buffer and command buffer. Data in this queue + is be used to debug. +*/ +#ifndef gcdLINK_QUEUE_SIZE +# define gcdLINK_QUEUE_SIZE 5 +#endif + +/* gcdALPHA_KILL_IN_SHADER + + Enable alpha kill inside the shader. This will be set automatically by the + HAL if certain states match a criteria. +*/ +#ifndef gcdALPHA_KILL_IN_SHADER +# define gcdALPHA_KILL_IN_SHADER 1 +#endif + + + +/* + gcdDVFS + + When non-zero, software will make use of dynamic voltage and + frequency feature. + */ +#ifndef gcdDVFS +# define gcdDVFS 0 +# define gcdDVFS_ANAYLSE_WINDOW 4 +# define gcdDVFS_POLLING_TIME (gcdDVFS_ANAYLSE_WINDOW * 4) +#endif + +#ifndef gcdSYNC +# define gcdSYNC 1 +#endif + +#ifndef gcdSHADER_SRC_BY_MACHINECODE +# define gcdSHADER_SRC_BY_MACHINECODE 1 +#endif + +#ifndef gcdGLB27_SHADER_REPLACE_OPTIMIZATION +# define gcdGLB27_SHADER_REPLACE_OPTIMIZATION 1 +#endif + +/* + gcdSTREAM_OUT_BUFFER + + Enable suppport for the secondary stream out buffer. +*/ +#ifndef gcdSTREAM_OUT_BUFFER +# define gcdSTREAM_OUT_BUFFER 0 +# define gcdSTREAM_OUT_NAIVE_SYNC 0 +#endif + +/* + gcdUSE_HARDWARE_CONFIGURATION_TABLES + + Enable the use of hardware configuration tables, + instead of query hardware and determine the features. +*/ +#ifndef gcdUSE_HARDWARE_CONFIGURATION_TABLES +# define gcdUSE_HARDWARE_CONFIGURATION_TABLES 0 +#endif + +/* + gcdSUPPORT_SWAP_RECTANGLE + + Support swap with a specific rectangle. + + Set the rectangle with eglSetSwapRectangleVIV api. + Android only. +*/ +#ifndef gcdSUPPORT_SWAP_RECTANGLE +# define gcdSUPPORT_SWAP_RECTANGLE 1 +#endif + +/* + gcdGPU_LINEAR_BUFFER_ENABLED + + Use linear buffer for GPU apps so HWC can do 2D composition. + Android only. +*/ +#ifndef gcdGPU_LINEAR_BUFFER_ENABLED +# define gcdGPU_LINEAR_BUFFER_ENABLED 1 +#endif + +/* + gcdENABLE_RENDER_INTO_WINDOW + + Enable Render-Into-Window (ie, No-Resolve) feature on android. + NOTE that even if enabled, it still depends on hardware feature and + android application behavior. When hardware feature or application + behavior can not support render into window mode, it will fail back + to normal mode. + When Render-Into-Window is finally used, window back buffer of android + applications will be allocated matching render target tiling format. + Otherwise buffer tiling is decided by the above option + 'gcdGPU_LINEAR_BUFFER_ENABLED'. + Android only for now. +*/ +#ifndef gcdENABLE_RENDER_INTO_WINDOW +# define gcdENABLE_RENDER_INTO_WINDOW 1 +#endif + +/* + gcdENABLE_RENDER_INTO_WINDOW_WITH_FC + + Enable Direct-rendering (ie, No-Resolve) with tile status. + This is expremental and in development stage. + This will dynamically check if color compression is available. +*/ +#ifndef gcdENABLE_RENDER_INTO_WINDOW_WITH_FC +# define gcdENABLE_RENDER_INTO_WINDOW_WITH_FC 1 +#endif + +/* + gcdENABLE_BLIT_BUFFER_PRESERVE + + Render-Into-Window (ie, No-Resolve) does not include preserved swap + behavior. This feature can enable buffer preserve in No-Resolve mode. + When enabled, previous buffer (may be part of ) will be resolve-blitted + to current buffer. +*/ +#ifndef gcdENABLE_BLIT_BUFFER_PRESERVE +# define gcdENABLE_BLIT_BUFFER_PRESERVE 1 +#endif + +/* + gcdANDROID_NATIVE_FENCE_SYNC + + Enable android native fence sync. It is introduced since jellybean-4.2. + Depends on linux kernel option: CONFIG_SYNC. + + 0: Disabled + 1: Build framework for native fence sync feature, and EGL extension + 2: Enable async swap buffers for client + * Native fence sync for client 'queueBuffer' in EGL, which is + 'acquireFenceFd' for layer in compositor side. + 3. Enable async hwcomposer composition. + * 'releaseFenceFd' for layer in compositor side, which is native + fence sync when client 'dequeueBuffer' + * Native fence sync for compositor 'queueBuffer' in EGL, which is + 'acquireFenceFd' for framebuffer target for DC + */ +#ifndef gcdANDROID_NATIVE_FENCE_SYNC +# define gcdANDROID_NATIVE_FENCE_SYNC 0 +#endif + +/* + gcdANDROID_IMPLICIT_NATIVE_BUFFER_SYNC + + Enable implicit android native buffer sync. + + For non-HW_RENDER buffer, CPU (or other hardware) and GPU can access + the buffer at the same time. This is to add implicit synchronization + between CPU (or the hardware) and GPU. + + Eventually, please do not use implicit native buffer sync, but use + "fence sync" or "android native fence sync" instead in libgui, which + can be enabled in frameworks/native/libs/gui/Android.mk. This kind + of synchronization should be done by app but not driver itself. + + Please disable this option when either "fence sync" or + "android native fence sync" is enabled. + */ +#ifndef gcdANDROID_IMPLICIT_NATIVE_BUFFER_SYNC +# define gcdANDROID_IMPLICIT_NATIVE_BUFFER_SYNC 1 +#endif + +/* + * Implicit native buffer sync is not needed when ANDROID_native_fence_sync + * is available. + */ +#if gcdANDROID_NATIVE_FENCE_SYNC +# undef gcdANDROID_IMPLICIT_NATIVE_BUFFER_SYNC +# define gcdANDROID_IMPLICIT_NATIVE_BUFFER_SYNC 0 +#endif + +/* + gcdANDROID_UNALIGNED_LINEAR_COMPOSITION_ADJUST + + Enable source surface address adjust when composition on android. + Android only. +*/ +#ifndef gcdANDROID_UNALIGNED_LINEAR_COMPOSITION_ADJUST +# define gcdANDROID_UNALIGNED_LINEAR_COMPOSITION_ADJUST 1 +#endif + +/* + gcdUSE_WCLIP_PATCH + + Enable wclipping patch. +*/ +#ifndef gcdUSE_WCLIP_PATCH +# define gcdUSE_WCLIP_PATCH 1 +#endif + +#ifndef gcdUSE_NPOT_PATCH +# define gcdUSE_NPOT_PATCH 1 +#endif + +/* + gcd3DBLIT + + TODO: Should be replaced by feature bit if available. +*/ +#ifndef gcd3DBLIT +# define gcd3DBLIT 0 +#endif + +/* + gcdINTERNAL_COMMENT + + Wrap internal comment, content wrapped by it and the macor itself + will be removed in release driver. +*/ +#ifndef gcdINTERNAL_COMMENT +# define gcdINTERNAL_COMMENT 1 +#endif + +/* + gcdRTT_DISABLE_FC + + Disable RTT FC support. For test only. +*/ +#ifndef gcdRTT_DISABLE_FC +# define gcdRTT_DISABLE_FC 0 +#endif + +/* + gcdFORCE_MIPMAP + + Force generate mipmap for texture. +*/ +#ifndef gcdFORCE_MIPMAP +# define gcdFORCE_MIPMAP 0 +#endif + +/* + gcdFORCE_BILINEAR + + Force bilinear for mipfilter. +*/ +#ifndef gcdFORCE_BILINEAR +# define gcdFORCE_BILINEAR 1 +#endif + +/* + gcdBINARY_TRACE + + When non-zero, binary trace will be generated. + + When gcdBINARY_TRACE_FILE_SIZE is non-zero, binary trace buffer will + be written to a file which size is limited to + gcdBINARY_TRACE_FILE_SIZE. +*/ +#ifndef gcdBINARY_TRACE +# define gcdBINARY_TRACE 0 +# define gcdBINARY_TRACE_FILE_SIZE 0 +#endif + +#ifndef gcdMOVG +# define gcdMOVG 0 +#if gcdMOVG +# define GC355_PROFILER 1 +# endif +# define gcdENABLE_TS_DOUBLE_BUFFER 1 +#else +#if gcdMOVG +# define GC355_PROFILER 1 +# define gcdENABLE_TS_DOUBLE_BUFFER 0 +#else +# define gcdENABLE_TS_DOUBLE_BUFFER 1 +#endif +#endif + +/* gcdINTERRUPT_STATISTIC + * + * Monitor the event send to GPU and interrupt issued by GPU. + */ + +#ifndef gcdINTERRUPT_STATISTIC +#if defined(LINUX) +# define gcdINTERRUPT_STATISTIC 1 +#else +# define gcdINTERRUPT_STATISTIC 0 +#endif +#endif + +/* + gcdYINVERTED_RENDERING + When it's not zero, we will rendering display buffer + with top-bottom direction. All other offscreen rendering + will be bottom-top, which follow OpenGL ES spec. +*/ +#ifndef gcdYINVERTED_RENDERING +# define gcdYINVERTED_RENDERING 1 +#endif + +#if gcdYINVERTED_RENDERING +/* disable unaligned linear composition adjust in Y-inverted rendering mode. */ +# undef gcdANDROID_UNALIGNED_LINEAR_COMPOSITION_ADJUST +# define gcdANDROID_UNALIGNED_LINEAR_COMPOSITION_ADJUST 0 +#endif + +/* + gcdFENCE_WAIT_LOOP_COUNT + Wait fence, loop count. +*/ +#ifndef gcdFENCE_WAIT_LOOP_COUNT +# define gcdFENCE_WAIT_LOOP_COUNT 100 +#endif + +/* + gcdHAL_3D_DRAWBLIT + When it's not zero, we will enable HAL 3D drawblit + to replace client 3dblit. +*/ +#ifndef gcdHAL_3D_DRAWBLIT +# define gcdHAL_3D_DRAWBLIT 1 +#endif + +/* + gcdPARTIAL_FAST_CLEAR + When it's not zero, partial fast clear is enabled. + Depends on gcdHAL_3D_DRAWBLIT, if gcdHAL_3D_DRAWBLIT is not enabled, + only available when scissor box is completely aligned. + Expremental, under test. +*/ +#ifndef gcdPARTIAL_FAST_CLEAR +# define gcdPARTIAL_FAST_CLEAR 1 +#endif + +/* + gcdREMOVE_SURF_ORIENTATION + When it's not zero, we will remove surface orientation function. + It wil become to a parameter of resolve function. +*/ +#ifndef gcdREMOVE_SURF_ORIENTATION +# define gcdREMOVE_SURF_ORIENTATION 0 +#endif + +/* + gcdPATTERN_FAST_PATH + For pattern match +*/ +#ifndef gcdPATTERN_FAST_PATH +# define gcdPATTERN_FAST_PATH 1 +#endif + +/* + gcdUSE_INPUT_DEVICE + disable input devices usage under fb mode to support fb+vdk multi-process +*/ +#ifndef gcdUSE_INPUT_DEVICE +# define gcdUSE_INPUT_DEVICE 1 +#endif + + +/* + gcdFRAMEINFO_STATISTIC + When enable, collect frame information. +*/ +#ifndef gcdFRAMEINFO_STATISTIC + +#if (defined(DBG) && DBG) || defined(DEBUG) || defined(_DEBUG) || gcdDUMP +# define gcdFRAMEINFO_STATISTIC 1 +#else +# define gcdFRAMEINFO_STATISTIC 0 +#endif + +#endif + +/* + gcdPACKED_OUTPUT_ADDRESS + When it's not zero, ps output is already packed after linked +*/ +#ifndef gcdPACKED_OUTPUT_ADDRESS +# define gcdPACKED_OUTPUT_ADDRESS 1 +#endif + +/* + gcdENABLE_THIRD_PARTY_OPERATION + Enable third party operation like tpc or not. +*/ +#ifndef gcdENABLE_THIRD_PARTY_OPERATION +# define gcdENABLE_THIRD_PARTY_OPERATION 1 +#endif + + +/* + Core configurations. By default enable all cores. +*/ +#ifndef gcdENABLE_3D +# define gcdENABLE_3D 1 +#endif + +#ifndef gcdENABLE_2D +# define gcdENABLE_2D 1 +#endif + +#ifndef gcdENABLE_VG +# define gcdENABLE_VG 0 +#endif + +#ifndef gcdGC355_MEM_PRINT +# define gcdGC355_MEM_PRINT 0 +#else +#if (!((gcdENABLE_3D == 0) && (gcdENABLE_2D == 0) && (gcdENABLE_VG == 1))) +# undef gcdGC355_MEM_PRINT +# define gcdGC355_MEM_PRINT 0 +# endif +#endif + +#ifndef gcdENABLE_UNIFIED_CONSTANT +# define gcdENABLE_UNIFIED_CONSTANT 1 +#endif + +/* + gcdRECORD_COMMAND +*/ +#ifndef gcdRECORD_COMMAND +# define gcdRECORD_COMMAND 0 +#endif + +#endif /* __gc_hal_options_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_profiler.h b/drivers/gpu/galcore/inc/gc_hal_profiler.h new file mode 100644 index 00000000000000..d2abf9a5c685c2 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_profiler.h @@ -0,0 +1,585 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_profiler_h_ +#define __gc_hal_profiler_h_ + +#if VIVANTE_PROFILER_NEW +#include "gc_hal_engine.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define GLVERTEX_OBJECT 10 +#define GLVERTEX_OBJECT_BYTES 11 + +#define GLINDEX_OBJECT 20 +#define GLINDEX_OBJECT_BYTES 21 + +#define GLTEXTURE_OBJECT 30 +#define GLTEXTURE_OBJECT_BYTES 31 + +#define GLBUFOBJ_OBJECT 40 +#define GLBUFOBJ_OBJECT_BYTES 41 + +#if VIVANTE_PROFILER +#define gcmPROFILE_GC(Enum, Value) gcoPROFILER_Count(gcvNULL, Enum, Value) +#else +#define gcmPROFILE_GC(Enum, Value) do { } while (gcvFALSE) +#endif + +#ifndef gcdNEW_PROFILER_FILE +#define gcdNEW_PROFILER_FILE 1 +#endif + +#define ES11_CALLS 151 +#define ES11_DRAWCALLS (ES11_CALLS + 1) +#define ES11_STATECHANGECALLS (ES11_DRAWCALLS + 1) +#define ES11_POINTCOUNT (ES11_STATECHANGECALLS + 1) +#define ES11_LINECOUNT (ES11_POINTCOUNT + 1) +#define ES11_TRIANGLECOUNT (ES11_LINECOUNT + 1) + +#define ES30_CALLS 159 +#define ES30_DRAWCALLS (ES30_CALLS + 1) +#define ES30_STATECHANGECALLS (ES30_DRAWCALLS + 1) +#define ES30_POINTCOUNT (ES30_STATECHANGECALLS + 1) +#define ES30_LINECOUNT (ES30_POINTCOUNT + 1) +#define ES30_TRIANGLECOUNT (ES30_LINECOUNT + 1) + +#define VG11_CALLS 88 +#define VG11_DRAWCALLS (VG11_CALLS + 1) +#define VG11_STATECHANGECALLS (VG11_DRAWCALLS + 1) +#define VG11_FILLCOUNT (VG11_STATECHANGECALLS + 1) +#define VG11_STROKECOUNT (VG11_FILLCOUNT + 1) +/* End of Driver API ID Definitions. */ + +/* HAL & MISC IDs. */ +#define HAL_VERTBUFNEWBYTEALLOC 1 +#define HAL_VERTBUFTOTALBYTEALLOC (HAL_VERTBUFNEWBYTEALLOC + 1) +#define HAL_VERTBUFNEWOBJALLOC (HAL_VERTBUFTOTALBYTEALLOC + 1) +#define HAL_VERTBUFTOTALOBJALLOC (HAL_VERTBUFNEWOBJALLOC + 1) +#define HAL_INDBUFNEWBYTEALLOC (HAL_VERTBUFTOTALOBJALLOC + 1) +#define HAL_INDBUFTOTALBYTEALLOC (HAL_INDBUFNEWBYTEALLOC + 1) +#define HAL_INDBUFNEWOBJALLOC (HAL_INDBUFTOTALBYTEALLOC + 1) +#define HAL_INDBUFTOTALOBJALLOC (HAL_INDBUFNEWOBJALLOC + 1) +#define HAL_TEXBUFNEWBYTEALLOC (HAL_INDBUFTOTALOBJALLOC + 1) +#define HAL_TEXBUFTOTALBYTEALLOC (HAL_TEXBUFNEWBYTEALLOC + 1) +#define HAL_TEXBUFNEWOBJALLOC (HAL_TEXBUFTOTALBYTEALLOC + 1) +#define HAL_TEXBUFTOTALOBJALLOC (HAL_TEXBUFNEWOBJALLOC + 1) + +#define GPU_CYCLES 1 +#define GPU_READ64BYTE (GPU_CYCLES + 1) +#define GPU_WRITE64BYTE (GPU_READ64BYTE + 1) +#define GPU_TOTALCYCLES (GPU_WRITE64BYTE + 1) +#define GPU_IDLECYCLES (GPU_TOTALCYCLES + 1) + +#define VS_INSTCOUNT 1 +#define VS_BRANCHINSTCOUNT (VS_INSTCOUNT + 1) +#define VS_TEXLDINSTCOUNT (VS_BRANCHINSTCOUNT + 1) +#define VS_RENDEREDVERTCOUNT (VS_TEXLDINSTCOUNT + 1) +#define VS_SOURCE (VS_RENDEREDVERTCOUNT + 1) + +#define PS_INSTCOUNT 1 +#define PS_BRANCHINSTCOUNT (PS_INSTCOUNT + 1) +#define PS_TEXLDINSTCOUNT (PS_BRANCHINSTCOUNT + 1) +#define PS_RENDEREDPIXCOUNT (PS_TEXLDINSTCOUNT + 1) +#define PS_SOURCE (PS_RENDEREDPIXCOUNT + 1) + +#define PA_INVERTCOUNT 1 +#define PA_INPRIMCOUNT (PA_INVERTCOUNT + 1) +#define PA_OUTPRIMCOUNT (PA_INPRIMCOUNT + 1) +#define PA_DEPTHCLIPCOUNT (PA_OUTPRIMCOUNT + 1) +#define PA_TRIVIALREJCOUNT (PA_DEPTHCLIPCOUNT + 1) +#define PA_CULLCOUNT (PA_TRIVIALREJCOUNT + 1) + +#define SE_TRIANGLECOUNT 1 +#define SE_LINECOUNT (SE_TRIANGLECOUNT + 1) + +#define RA_VALIDPIXCOUNT 1 +#define RA_TOTALQUADCOUNT (RA_VALIDPIXCOUNT + 1) +#define RA_VALIDQUADCOUNTEZ (RA_TOTALQUADCOUNT + 1) +#define RA_TOTALPRIMCOUNT (RA_VALIDQUADCOUNTEZ + 1) +#define RA_PIPECACHEMISSCOUNT (RA_TOTALPRIMCOUNT + 1) +#define RA_PREFCACHEMISSCOUNT (RA_PIPECACHEMISSCOUNT + 1) +#define RA_EEZCULLCOUNT (RA_PREFCACHEMISSCOUNT + 1) + +#define TX_TOTBILINEARREQ 1 +#define TX_TOTTRILINEARREQ (TX_TOTBILINEARREQ + 1) +#define TX_TOTDISCARDTEXREQ (TX_TOTTRILINEARREQ + 1) +#define TX_TOTTEXREQ (TX_TOTDISCARDTEXREQ + 1) +#define TX_MEMREADCOUNT (TX_TOTTEXREQ + 1) +#define TX_MEMREADIN8BCOUNT (TX_MEMREADCOUNT + 1) +#define TX_CACHEMISSCOUNT (TX_MEMREADIN8BCOUNT + 1) +#define TX_CACHEHITTEXELCOUNT (TX_CACHEMISSCOUNT + 1) +#define TX_CACHEMISSTEXELCOUNT (TX_CACHEHITTEXELCOUNT + 1) + +#define PE_KILLEDBYCOLOR 1 +#define PE_KILLEDBYDEPTH (PE_KILLEDBYCOLOR + 1) +#define PE_DRAWNBYCOLOR (PE_KILLEDBYDEPTH + 1) +#define PE_DRAWNBYDEPTH (PE_DRAWNBYCOLOR + 1) + +#define MC_READREQ8BPIPE 1 +#define MC_READREQ8BIP (MC_READREQ8BPIPE + 1) +#define MC_WRITEREQ8BPIPE (MC_READREQ8BIP + 1) + +#define AXI_READREQSTALLED 1 +#define AXI_WRITEREQSTALLED (AXI_READREQSTALLED + 1) +#define AXI_WRITEDATASTALLED (AXI_WRITEREQSTALLED + 1) + +#define PVS_INSTRCOUNT 1 +#define PVS_ALUINSTRCOUNT (PVS_INSTRCOUNT + 1) +#define PVS_TEXINSTRCOUNT (PVS_ALUINSTRCOUNT + 1) +#define PVS_ATTRIBCOUNT (PVS_TEXINSTRCOUNT + 1) +#define PVS_UNIFORMCOUNT (PVS_ATTRIBCOUNT + 1) +#define PVS_FUNCTIONCOUNT (PVS_UNIFORMCOUNT + 1) +#define PVS_SOURCE (PVS_FUNCTIONCOUNT + 1) + +#define PPS_INSTRCOUNT 1 +#define PPS_ALUINSTRCOUNT (PPS_INSTRCOUNT + 1) +#define PPS_TEXINSTRCOUNT (PPS_ALUINSTRCOUNT + 1) +#define PPS_ATTRIBCOUNT (PPS_TEXINSTRCOUNT + 1) +#define PPS_UNIFORMCOUNT (PPS_ATTRIBCOUNT + 1) +#define PPS_FUNCTIONCOUNT (PPS_UNIFORMCOUNT + 1) +#define PPS_SOURCE (PPS_FUNCTIONCOUNT + 1) +/* End of MISC Counter IDs. */ + +#ifdef gcdNEW_PROFILER_FILE + +/* Category Constants. */ +#define VPHEADER 0x010000 +#define VPG_INFO 0x020000 +#define VPG_TIME 0x030000 +#define VPG_MEM 0x040000 +#define VPG_ES11 0x050000 +#define VPG_ES30 0x060000 +#define VPG_VG11 0x070000 +#define VPG_HAL 0x080000 +#define VPG_HW 0x090000 +#define VPG_GPU 0x0a0000 +#define VPG_VS 0x0b0000 +#define VPG_PS 0x0c0000 +#define VPG_PA 0x0d0000 +#define VPG_SETUP 0x0e0000 +#define VPG_RA 0x0f0000 +#define VPG_TX 0x100000 +#define VPG_PE 0x110000 +#define VPG_MC 0x120000 +#define VPG_AXI 0x130000 +#define VPG_PROG 0x140000 +#define VPG_PVS 0x150000 +#define VPG_PPS 0x160000 +#define VPG_ES11_TIME 0x170000 +#define VPG_ES30_TIME 0x180000 +#define VPG_FRAME 0x190000 +#define VPG_ES11_DRAW 0x200000 +#define VPG_ES30_DRAW 0x210000 +#define VPG_VG11_TIME 0x220000 +#define VPG_END 0xff0000 + +/* Info. */ +#define VPC_INFOCOMPANY (VPG_INFO + 1) +#define VPC_INFOVERSION (VPC_INFOCOMPANY + 1) +#define VPC_INFORENDERER (VPC_INFOVERSION + 1) +#define VPC_INFOREVISION (VPC_INFORENDERER + 1) +#define VPC_INFODRIVER (VPC_INFOREVISION + 1) +#define VPC_INFODRIVERMODE (VPC_INFODRIVER + 1) +#define VPC_INFOSCREENSIZE (VPC_INFODRIVERMODE + 1) + +/* Counter Constants. */ +#define VPC_ELAPSETIME (VPG_TIME + 1) +#define VPC_CPUTIME (VPC_ELAPSETIME + 1) + +#define VPC_MEMMAXRES (VPG_MEM + 1) +#define VPC_MEMSHARED (VPC_MEMMAXRES + 1) +#define VPC_MEMUNSHAREDDATA (VPC_MEMSHARED + 1) +#define VPC_MEMUNSHAREDSTACK (VPC_MEMUNSHAREDDATA + 1) + +/* OpenGL ES11 Statics Counter IDs. */ +#define VPC_ES11CALLS (VPG_ES11 + ES11_CALLS) +#define VPC_ES11DRAWCALLS (VPG_ES11 + ES11_DRAWCALLS) +#define VPC_ES11STATECHANGECALLS (VPG_ES11 + ES11_STATECHANGECALLS) +#define VPC_ES11POINTCOUNT (VPG_ES11 + ES11_POINTCOUNT) +#define VPC_ES11LINECOUNT (VPG_ES11 + ES11_LINECOUNT) +#define VPC_ES11TRIANGLECOUNT (VPG_ES11 + ES11_TRIANGLECOUNT) + +/* OpenGL ES30 Statistics Counter IDs. */ +#define VPC_ES30CALLS (VPG_ES30 + ES30_CALLS) +#define VPC_ES30DRAWCALLS (VPG_ES30 + ES30_DRAWCALLS) +#define VPC_ES30STATECHANGECALLS (VPG_ES30 + ES30_STATECHANGECALLS) +#define VPC_ES30POINTCOUNT (VPG_ES30 + ES30_POINTCOUNT) +#define VPC_ES30LINECOUNT (VPG_ES30 + ES30_LINECOUNT) +#define VPC_ES30TRIANGLECOUNT (VPG_ES30 + ES30_TRIANGLECOUNT) + +/* OpenVG Statistics Counter IDs. */ +#define VPC_VG11CALLS (VPG_VG11 + VG11_CALLS) +#define VPC_VG11DRAWCALLS (VPG_VG11 + VG11_DRAWCALLS) +#define VPC_VG11STATECHANGECALLS (VPG_VG11 + VG11_STATECHANGECALLS) +#define VPC_VG11FILLCOUNT (VPG_VG11 + VG11_FILLCOUNT) +#define VPC_VG11STROKECOUNT (VPG_VG11 + VG11_STROKECOUNT) + +/* HAL Counters. */ +#define VPC_HALVERTBUFNEWBYTEALLOC (VPG_HAL + HAL_VERTBUFNEWBYTEALLOC) +#define VPC_HALVERTBUFTOTALBYTEALLOC (VPG_HAL + HAL_VERTBUFTOTALBYTEALLOC) +#define VPC_HALVERTBUFNEWOBJALLOC (VPG_HAL + HAL_VERTBUFNEWOBJALLOC) +#define VPC_HALVERTBUFTOTALOBJALLOC (VPG_HAL + HAL_VERTBUFTOTALOBJALLOC) +#define VPC_HALINDBUFNEWBYTEALLOC (VPG_HAL + HAL_INDBUFNEWBYTEALLOC) +#define VPC_HALINDBUFTOTALBYTEALLOC (VPG_HAL + HAL_INDBUFTOTALBYTEALLOC) +#define VPC_HALINDBUFNEWOBJALLOC (VPG_HAL + HAL_INDBUFNEWOBJALLOC) +#define VPC_HALINDBUFTOTALOBJALLOC (VPG_HAL + HAL_INDBUFTOTALOBJALLOC) +#define VPC_HALTEXBUFNEWBYTEALLOC (VPG_HAL + HAL_TEXBUFNEWBYTEALLOC) +#define VPC_HALTEXBUFTOTALBYTEALLOC (VPG_HAL + HAL_TEXBUFTOTALBYTEALLOC) +#define VPC_HALTEXBUFNEWOBJALLOC (VPG_HAL + HAL_TEXBUFNEWOBJALLOC) +#define VPC_HALTEXBUFTOTALOBJALLOC (VPG_HAL + HAL_TEXBUFTOTALOBJALLOC) + +/* HW: GPU Counters. */ +#define VPC_GPUCYCLES (VPG_GPU + GPU_CYCLES) +#define VPC_GPUREAD64BYTE (VPG_GPU + GPU_READ64BYTE) +#define VPC_GPUWRITE64BYTE (VPG_GPU + GPU_WRITE64BYTE) +#define VPC_GPUTOTALCYCLES (VPG_GPU + GPU_TOTALCYCLES) +#define VPC_GPUIDLECYCLES (VPG_GPU + GPU_IDLECYCLES) + +/* HW: Shader Counters. */ +#define VPC_VSINSTCOUNT (VPG_VS + VS_INSTCOUNT) +#define VPC_VSBRANCHINSTCOUNT (VPG_VS + VS_BRANCHINSTCOUNT) +#define VPC_VSTEXLDINSTCOUNT (VPG_VS + VS_TEXLDINSTCOUNT) +#define VPC_VSRENDEREDVERTCOUNT (VPG_VS + VS_RENDEREDVERTCOUNT) +/* HW: PS Count. */ +#define VPC_PSINSTCOUNT (VPG_PS + PS_INSTCOUNT) +#define VPC_PSBRANCHINSTCOUNT (VPG_PS + PS_BRANCHINSTCOUNT) +#define VPC_PSTEXLDINSTCOUNT (VPG_PS + PS_TEXLDINSTCOUNT) +#define VPC_PSRENDEREDPIXCOUNT (VPG_PS + PS_RENDEREDPIXCOUNT) + + +/* HW: PA Counters. */ +#define VPC_PAINVERTCOUNT (VPG_PA + PA_INVERTCOUNT) +#define VPC_PAINPRIMCOUNT (VPG_PA + PA_INPRIMCOUNT) +#define VPC_PAOUTPRIMCOUNT (VPG_PA + PA_OUTPRIMCOUNT) +#define VPC_PADEPTHCLIPCOUNT (VPG_PA + PA_DEPTHCLIPCOUNT) +#define VPC_PATRIVIALREJCOUNT (VPG_PA + PA_TRIVIALREJCOUNT) +#define VPC_PACULLCOUNT (VPG_PA + PA_CULLCOUNT) + +/* HW: Setup Counters. */ +#define VPC_SETRIANGLECOUNT (VPG_SETUP + SE_TRIANGLECOUNT) +#define VPC_SELINECOUNT (VPG_SETUP + SE_LINECOUNT) + +/* HW: RA Counters. */ +#define VPC_RAVALIDPIXCOUNT (VPG_RA + RA_VALIDPIXCOUNT) +#define VPC_RATOTALQUADCOUNT (VPG_RA + RA_TOTALQUADCOUNT) +#define VPC_RAVALIDQUADCOUNTEZ (VPG_RA + RA_VALIDQUADCOUNTEZ) +#define VPC_RATOTALPRIMCOUNT (VPG_RA + RA_TOTALPRIMCOUNT) +#define VPC_RAPIPECACHEMISSCOUNT (VPG_RA + RA_PIPECACHEMISSCOUNT) +#define VPC_RAPREFCACHEMISSCOUNT (VPG_RA + RA_PREFCACHEMISSCOUNT) +#define VPC_RAEEZCULLCOUNT (VPG_RA + RA_EEZCULLCOUNT) + +/* HW: TEX Counters. */ +#define VPC_TXTOTBILINEARREQ (VPG_TX + TX_TOTBILINEARREQ) +#define VPC_TXTOTTRILINEARREQ (VPG_TX + TX_TOTTRILINEARREQ) +#define VPC_TXTOTDISCARDTEXREQ (VPG_TX + TX_TOTDISCARDTEXREQ) +#define VPC_TXTOTTEXREQ (VPG_TX + TX_TOTTEXREQ) +#define VPC_TXMEMREADCOUNT (VPG_TX + TX_MEMREADCOUNT) +#define VPC_TXMEMREADIN8BCOUNT (VPG_TX + TX_MEMREADIN8BCOUNT) +#define VPC_TXCACHEMISSCOUNT (VPG_TX + TX_CACHEMISSCOUNT) +#define VPC_TXCACHEHITTEXELCOUNT (VPG_TX + TX_CACHEHITTEXELCOUNT) +#define VPC_TXCACHEMISSTEXELCOUNT (VPG_TX + TX_CACHEMISSTEXELCOUNT) + +/* HW: PE Counters. */ +#define VPC_PEKILLEDBYCOLOR (VPG_PE + PE_KILLEDBYCOLOR) +#define VPC_PEKILLEDBYDEPTH (VPG_PE + PE_KILLEDBYDEPTH) +#define VPC_PEDRAWNBYCOLOR (VPG_PE + PE_DRAWNBYCOLOR) +#define VPC_PEDRAWNBYDEPTH (VPG_PE + PE_DRAWNBYDEPTH) + +/* HW: MC Counters. */ +#define VPC_MCREADREQ8BPIPE (VPG_MC + MC_READREQ8BPIPE) +#define VPC_MCREADREQ8BIP (VPG_MC + MC_READREQ8BIP) +#define VPC_MCWRITEREQ8BPIPE (VPG_MC + MC_WRITEREQ8BPIPE) + +/* HW: AXI Counters. */ +#define VPC_AXIREADREQSTALLED (VPG_AXI + AXI_READREQSTALLED) +#define VPC_AXIWRITEREQSTALLED (VPG_AXI + AXI_WRITEREQSTALLED) +#define VPC_AXIWRITEDATASTALLED (VPG_AXI + AXI_WRITEDATASTALLED) + +/* PROGRAM: Shader program counters. */ +#define VPC_PVSINSTRCOUNT (VPG_PVS + PVS_INSTRCOUNT) +#define VPC_PVSALUINSTRCOUNT (VPG_PVS + PVS_ALUINSTRCOUNT) +#define VPC_PVSTEXINSTRCOUNT (VPG_PVS + PVS_TEXINSTRCOUNT) +#define VPC_PVSATTRIBCOUNT (VPG_PVS + PVS_ATTRIBCOUNT) +#define VPC_PVSUNIFORMCOUNT (VPG_PVS + PVS_UNIFORMCOUNT) +#define VPC_PVSFUNCTIONCOUNT (VPG_PVS + PVS_FUNCTIONCOUNT) +#define VPC_PVSSOURCE (VPG_PVS + PVS_SOURCE) + +#define VPC_PPSINSTRCOUNT (VPG_PPS + PPS_INSTRCOUNT) +#define VPC_PPSALUINSTRCOUNT (VPG_PPS + PPS_ALUINSTRCOUNT) +#define VPC_PPSTEXINSTRCOUNT (VPG_PPS + PPS_TEXINSTRCOUNT) +#define VPC_PPSATTRIBCOUNT (VPG_PPS + PPS_ATTRIBCOUNT) +#define VPC_PPSUNIFORMCOUNT (VPG_PPS + PPS_UNIFORMCOUNT) +#define VPC_PPSFUNCTIONCOUNT (VPG_PPS + PPS_FUNCTIONCOUNT) +#define VPC_PPSSOURCE (VPG_PPS + PPS_SOURCE) + +#define VPC_PROGRAMHANDLE (VPG_PROG + 1) + +#define VPC_ES30_DRAW_NO (VPG_ES30_DRAW + 1) +#define VPC_ES11_DRAW_NO (VPG_ES11_DRAW + 1) +#endif + + +/* HW profile information. */ +typedef struct _gcsPROFILER_COUNTERS +{ + /* HW static counters. */ + gctUINT32 gpuClock; + gctUINT32 axiClock; + gctUINT32 shaderClock; + + /* HW vairable counters. */ + gctUINT32 gpuClockStart; + gctUINT32 gpuClockEnd; + + /* HW vairable counters. */ + gctUINT32 gpuCyclesCounter; + gctUINT32 gpuTotalCyclesCounter; + gctUINT32 gpuIdleCyclesCounter; + gctUINT32 gpuTotalRead64BytesPerFrame; + gctUINT32 gpuTotalWrite64BytesPerFrame; + + /* PE */ + gctUINT32 pe_pixel_count_killed_by_color_pipe; + gctUINT32 pe_pixel_count_killed_by_depth_pipe; + gctUINT32 pe_pixel_count_drawn_by_color_pipe; + gctUINT32 pe_pixel_count_drawn_by_depth_pipe; + + /* SH */ + gctUINT32 ps_inst_counter; + gctUINT32 rendered_pixel_counter; + gctUINT32 vs_inst_counter; + gctUINT32 rendered_vertice_counter; + gctUINT32 vtx_branch_inst_counter; + gctUINT32 vtx_texld_inst_counter; + gctUINT32 pxl_branch_inst_counter; + gctUINT32 pxl_texld_inst_counter; + + /* PA */ + gctUINT32 pa_input_vtx_counter; + gctUINT32 pa_input_prim_counter; + gctUINT32 pa_output_prim_counter; + gctUINT32 pa_depth_clipped_counter; + gctUINT32 pa_trivial_rejected_counter; + gctUINT32 pa_culled_counter; + + /* SE */ + gctUINT32 se_culled_triangle_count; + gctUINT32 se_culled_lines_count; + + /* RA */ + gctUINT32 ra_valid_pixel_count; + gctUINT32 ra_total_quad_count; + gctUINT32 ra_valid_quad_count_after_early_z; + gctUINT32 ra_total_primitive_count; + gctUINT32 ra_pipe_cache_miss_counter; + gctUINT32 ra_prefetch_cache_miss_counter; + gctUINT32 ra_eez_culled_counter; + + /* TX */ + gctUINT32 tx_total_bilinear_requests; + gctUINT32 tx_total_trilinear_requests; + gctUINT32 tx_total_discarded_texture_requests; + gctUINT32 tx_total_texture_requests; + gctUINT32 tx_mem_read_count; + gctUINT32 tx_mem_read_in_8B_count; + gctUINT32 tx_cache_miss_count; + gctUINT32 tx_cache_hit_texel_count; + gctUINT32 tx_cache_miss_texel_count; + + /* MC */ + gctUINT32 mc_total_read_req_8B_from_pipeline; + gctUINT32 mc_total_read_req_8B_from_IP; + gctUINT32 mc_total_write_req_8B_from_pipeline; + + /* HI */ + gctUINT32 hi_axi_cycles_read_request_stalled; + gctUINT32 hi_axi_cycles_write_request_stalled; + gctUINT32 hi_axi_cycles_write_data_stalled; +} +gcsPROFILER_COUNTERS; + +#if VIVANTE_PROFILER_NEW +#define NumOfDrawBuf 64 +#endif + +/* HAL profile information. */ +typedef struct _gcsPROFILER +{ + gctUINT32 enable; + gctBOOL enableHal; + gctBOOL enableHW; + gctBOOL enableSH; + gctBOOL isSyncMode; + gctBOOL disableOutputCounter; + + gctBOOL useSocket; + gctINT sockFd; + + gctFILE file; + + /* Aggregate Information */ + + /* Clock Info */ + gctUINT64 frameStart; + gctUINT64 frameEnd; + + /* Current frame information */ + gctUINT32 frameNumber; + gctUINT64 frameStartTimeusec; + gctUINT64 frameEndTimeusec; + gctUINT64 frameStartCPUTimeusec; + gctUINT64 frameEndCPUTimeusec; + +#if PROFILE_HAL_COUNTERS + gctUINT32 vertexBufferTotalBytesAlloc; + gctUINT32 vertexBufferNewBytesAlloc; + int vertexBufferTotalObjectsAlloc; + int vertexBufferNewObjectsAlloc; + + gctUINT32 indexBufferTotalBytesAlloc; + gctUINT32 indexBufferNewBytesAlloc; + int indexBufferTotalObjectsAlloc; + int indexBufferNewObjectsAlloc; + + gctUINT32 textureBufferTotalBytesAlloc; + gctUINT32 textureBufferNewBytesAlloc; + int textureBufferTotalObjectsAlloc; + int textureBufferNewObjectsAlloc; + + gctUINT32 numCommits; + gctUINT32 drawPointCount; + gctUINT32 drawLineCount; + gctUINT32 drawTriangleCount; + gctUINT32 drawVertexCount; + gctUINT32 redundantStateChangeCalls; +#endif + + gctUINT32 prevVSInstCount; + gctUINT32 prevVSBranchInstCount; + gctUINT32 prevVSTexInstCount; + gctUINT32 prevVSVertexCount; + gctUINT32 prevPSInstCount; + gctUINT32 prevPSBranchInstCount; + gctUINT32 prevPSTexInstCount; + gctUINT32 prevPSPixelCount; + +#if VIVANTE_PROFILER_NEW + gcoBUFOBJ newCounterBuf[NumOfDrawBuf]; + gctUINT32 curBufId; +#endif + +} +gcsPROFILER; + +/* Memory profile information. */ +struct _gcsMemProfile +{ + /* Memory Usage */ + gctUINT32 videoMemUsed; + gctUINT32 systemMemUsed; + gctUINT32 commitBufferSize; + gctUINT32 contextBufferCopyBytes; +}; + +/* Shader profile information. */ +struct _gcsSHADER_PROFILER +{ + gctUINT32 shaderLength; + gctUINT32 shaderALUCycles; + gctUINT32 shaderTexLoadCycles; + gctUINT32 shaderTempRegCount; + gctUINT32 shaderSamplerRegCount; + gctUINT32 shaderInputRegCount; + gctUINT32 shaderOutputRegCount; +}; + +/* Initialize the gcsProfiler. */ +gceSTATUS +gcoPROFILER_Initialize( + IN gcoHAL Hal, + IN gctBOOL Enable + ); + +/* Destroy the gcProfiler. */ +gceSTATUS +gcoPROFILER_Destroy( + IN gcoHAL Hal + ); + +/* Write data to profiler. */ +gceSTATUS +gcoPROFILER_Write( + IN gcoHAL Hal, + IN gctSIZE_T ByteCount, + IN gctCONST_POINTER Data + ); + +/* Flush data out. */ +gceSTATUS +gcoPROFILER_Flush( + IN gcoHAL Hal + ); + +/* Call to signal end of frame. */ +gceSTATUS +gcoPROFILER_EndFrame( + IN gcoHAL Hal + ); + +/* Call to signal end of draw. */ +gceSTATUS +gcoPROFILER_EndDraw( + IN gcoHAL Hal, + IN gctBOOL FirstDraw + ); + +/* Increase profile counter Enum by Value. */ +gceSTATUS +gcoPROFILER_Count( + IN gcoHAL Hal, + IN gctUINT32 Enum, + IN gctINT Value + ); + +/* Profile input vertex shader. */ +gceSTATUS +gcoPROFILER_ShaderVS( + IN gcoHAL Hal, + IN gctPOINTER Vs + ); + +/* Profile input fragment shader. */ +gceSTATUS +gcoPROFILER_ShaderFS( + IN gcoHAL Hal, + IN gctPOINTER Fs + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_profiler_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_raster.h b/drivers/gpu/galcore/inc/gc_hal_raster.h new file mode 100644 index 00000000000000..035b76e0927922 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_raster.h @@ -0,0 +1,1038 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_raster_h_ +#define __gc_hal_raster_h_ + +#include "gc_hal_enum.h" +#include "gc_hal_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +****************************** Object Declarations ***************************** +\******************************************************************************/ + +typedef struct _gcoBRUSH * gcoBRUSH; +typedef struct _gcoBRUSH_CACHE * gcoBRUSH_CACHE; + +/******************************************************************************\ +******************************** gcoBRUSH Object ******************************* +\******************************************************************************/ + +/* Create a new solid color gcoBRUSH object. */ +gceSTATUS +gcoBRUSH_ConstructSingleColor( + IN gcoHAL Hal, + IN gctUINT32 ColorConvert, + IN gctUINT32 Color, + IN gctUINT64 Mask, + gcoBRUSH * Brush + ); + +/* Create a new monochrome gcoBRUSH object. */ +gceSTATUS +gcoBRUSH_ConstructMonochrome( + IN gcoHAL Hal, + IN gctUINT32 OriginX, + IN gctUINT32 OriginY, + IN gctUINT32 ColorConvert, + IN gctUINT32 FgColor, + IN gctUINT32 BgColor, + IN gctUINT64 Bits, + IN gctUINT64 Mask, + gcoBRUSH * Brush + ); + +/* Create a color gcoBRUSH object. */ +gceSTATUS +gcoBRUSH_ConstructColor( + IN gcoHAL Hal, + IN gctUINT32 OriginX, + IN gctUINT32 OriginY, + IN gctPOINTER Address, + IN gceSURF_FORMAT Format, + IN gctUINT64 Mask, + gcoBRUSH * Brush + ); + +/* Destroy an gcoBRUSH object. */ +gceSTATUS +gcoBRUSH_Destroy( + IN gcoBRUSH Brush + ); + +/******************************************************************************\ +******************************** gcoSURF Object ******************************* +\******************************************************************************/ + +/* Set cipping rectangle. */ +gceSTATUS +gcoSURF_SetClipping( + IN gcoSURF Surface + ); + +/* Clear one or more rectangular areas. */ +gceSTATUS +gcoSURF_Clear2D( + IN gcoSURF DestSurface, + IN gctUINT32 RectCount, + IN gcsRECT_PTR DestRect, + IN gctUINT32 LoColor, + IN gctUINT32 HiColor + ); + +/* Draw one or more Bresenham lines. */ +gceSTATUS +gcoSURF_Line( + IN gcoSURF Surface, + IN gctUINT32 LineCount, + IN gcsRECT_PTR Position, + IN gcoBRUSH Brush, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop + ); + +/* Generic rectangular blit. */ +gceSTATUS +gcoSURF_Blit( + IN OPTIONAL gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gctUINT32 RectCount, + IN OPTIONAL gcsRECT_PTR SrcRect, + IN gcsRECT_PTR DestRect, + IN OPTIONAL gcoBRUSH Brush, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN OPTIONAL gceSURF_TRANSPARENCY Transparency, + IN OPTIONAL gctUINT32 TransparencyColor, + IN OPTIONAL gctPOINTER Mask, + IN OPTIONAL gceSURF_MONOPACK MaskPack + ); + +/* Monochrome blit. */ +gceSTATUS +gcoSURF_MonoBlit( + IN gcoSURF DestSurface, + IN gctPOINTER Source, + IN gceSURF_MONOPACK SourcePack, + IN gcsPOINT_PTR SourceSize, + IN gcsPOINT_PTR SourceOrigin, + IN gcsRECT_PTR DestRect, + IN OPTIONAL gcoBRUSH Brush, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gctBOOL ColorConvert, + IN gctUINT8 MonoTransparency, + IN gceSURF_TRANSPARENCY Transparency, + IN gctUINT32 FgColor, + IN gctUINT32 BgColor + ); + +/* Filter blit. */ +gceSTATUS +gcoSURF_FilterBlit( + IN gcoSURF SrcSurface, + IN gcoSURF DestSurface, + IN gcsRECT_PTR SrcRect, + IN gcsRECT_PTR DestRect, + IN gcsRECT_PTR DestSubRect + ); + +/* Enable alpha blending engine in the hardware and disengage the ROP engine. */ +gceSTATUS +gcoSURF_EnableAlphaBlend( + IN gcoSURF Surface, + IN gctUINT8 SrcGlobalAlphaValue, + IN gctUINT8 DstGlobalAlphaValue, + IN gceSURF_PIXEL_ALPHA_MODE SrcAlphaMode, + IN gceSURF_PIXEL_ALPHA_MODE DstAlphaMode, + IN gceSURF_GLOBAL_ALPHA_MODE SrcGlobalAlphaMode, + IN gceSURF_GLOBAL_ALPHA_MODE DstGlobalAlphaMode, + IN gceSURF_BLEND_FACTOR_MODE SrcFactorMode, + IN gceSURF_BLEND_FACTOR_MODE DstFactorMode, + IN gceSURF_PIXEL_COLOR_MODE SrcColorMode, + IN gceSURF_PIXEL_COLOR_MODE DstColorMode + ); + +/* Disable alpha blending engine in the hardware and engage the ROP engine. */ +gceSTATUS +gcoSURF_DisableAlphaBlend( + IN gcoSURF Surface + ); + +/* Copy a rectangular area with format conversion. */ +gceSTATUS +gcoSURF_CopyPixels( + IN gcoSURF Source, + IN gcoSURF Target, + IN gctINT SourceX, + IN gctINT SourceY, + IN gctINT TargetX, + IN gctINT TargetY, + IN gctINT Width, + IN gctINT Height + ); + +/* Read surface pixel. */ +gceSTATUS +gcoSURF_ReadPixel( + IN gcoSURF Surface, + IN gctPOINTER Memory, + IN gctINT X, + IN gctINT Y, + IN gceSURF_FORMAT Format, + OUT gctPOINTER PixelValue + ); + +/* Write surface pixel. */ +gceSTATUS +gcoSURF_WritePixel( + IN gcoSURF Surface, + IN gctPOINTER Memory, + IN gctINT X, + IN gctINT Y, + IN gceSURF_FORMAT Format, + IN gctPOINTER PixelValue + ); + +gceSTATUS +gcoSURF_SetDither( + IN gcoSURF Surface, + IN gctBOOL Dither + ); + +gceSTATUS +gcoSURF_Set2DSource( + gcoSURF Surface, + gceSURF_ROTATION Rotation + ); + +gceSTATUS +gcoSURF_Set2DTarget( + gcoSURF Surface, + gceSURF_ROTATION Rotation + ); + +/******************************************************************************\ +********************************** gco2D Object ********************************* +\******************************************************************************/ + +/* Construct a new gco2D object. */ +gceSTATUS +gco2D_Construct( + IN gcoHAL Hal, + OUT gco2D * Hardware + ); + +/* Destroy an gco2D object. */ +gceSTATUS +gco2D_Destroy( + IN gco2D Hardware + ); + +/* Sets the maximum number of brushes in the brush cache. */ +gceSTATUS +gco2D_SetBrushLimit( + IN gco2D Hardware, + IN gctUINT MaxCount + ); + +/* Flush the brush. */ +gceSTATUS +gco2D_FlushBrush( + IN gco2D Engine, + IN gcoBRUSH Brush, + IN gceSURF_FORMAT Format + ); + +/* Program the specified solid color brush. */ +gceSTATUS +gco2D_LoadSolidBrush( + IN gco2D Engine, + IN gceSURF_FORMAT Format, + IN gctUINT32 ColorConvert, + IN gctUINT32 Color, + IN gctUINT64 Mask + ); + +gceSTATUS +gco2D_LoadMonochromeBrush( + IN gco2D Engine, + IN gctUINT32 OriginX, + IN gctUINT32 OriginY, + IN gctUINT32 ColorConvert, + IN gctUINT32 FgColor, + IN gctUINT32 BgColor, + IN gctUINT64 Bits, + IN gctUINT64 Mask + ); + +gceSTATUS +gco2D_LoadColorBrush( + IN gco2D Engine, + IN gctUINT32 OriginX, + IN gctUINT32 OriginY, + IN gctUINT32 Address, + IN gceSURF_FORMAT Format, + IN gctUINT64 Mask + ); + +/* Configure monochrome source. */ +gceSTATUS +gco2D_SetMonochromeSource( + IN gco2D Engine, + IN gctBOOL ColorConvert, + IN gctUINT8 MonoTransparency, + IN gceSURF_MONOPACK DataPack, + IN gctBOOL CoordRelative, + IN gceSURF_TRANSPARENCY Transparency, + IN gctUINT32 FgColor, + IN gctUINT32 BgColor + ); + +/* Configure color source. */ +gceSTATUS +gco2D_SetColorSource( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_FORMAT Format, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctBOOL CoordRelative, + IN gceSURF_TRANSPARENCY Transparency, + IN gctUINT32 TransparencyColor + ); + +/* Configure color source extension for full rotation. */ +gceSTATUS +gco2D_SetColorSourceEx( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_FORMAT Format, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight, + IN gctBOOL CoordRelative, + IN gceSURF_TRANSPARENCY Transparency, + IN gctUINT32 TransparencyColor + ); + +/* Configure color source. */ +gceSTATUS +gco2D_SetColorSourceAdvanced( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_FORMAT Format, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight, + IN gctBOOL CoordRelative + ); + +gceSTATUS +gco2D_SetColorSourceN( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_FORMAT Format, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight, + IN gctUINT32 SurfaceNumber + ); + +/* Configure masked color source. */ +gceSTATUS +gco2D_SetMaskedSource( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_FORMAT Format, + IN gctBOOL CoordRelative, + IN gceSURF_MONOPACK MaskPack + ); + +/* Configure masked color source extension for full rotation. */ +gceSTATUS +gco2D_SetMaskedSourceEx( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_FORMAT Format, + IN gctBOOL CoordRelative, + IN gceSURF_MONOPACK MaskPack, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight + ); + +/* Setup the source rectangle. */ +gceSTATUS +gco2D_SetSource( + IN gco2D Engine, + IN gcsRECT_PTR SrcRect + ); + +/* Set clipping rectangle. */ +gceSTATUS +gco2D_SetClipping( + IN gco2D Engine, + IN gcsRECT_PTR Rect + ); + +/* Configure destination. */ +gceSTATUS +gco2D_SetTarget( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth + ); + +/* Configure destination extension for full rotation. */ +gceSTATUS +gco2D_SetTargetEx( + IN gco2D Engine, + IN gctUINT32 Address, + IN gctUINT32 Stride, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight + ); + +/* Calculate and program the stretch factors. */ +gceSTATUS +gco2D_CalcStretchFactor( + IN gco2D Engine, + IN gctINT32 SrcSize, + IN gctINT32 DestSize, + OUT gctUINT32_PTR Factor + ); + +gceSTATUS +gco2D_SetStretchFactors( + IN gco2D Engine, + IN gctUINT32 HorFactor, + IN gctUINT32 VerFactor + ); + +/* Calculate and program the stretch factors based on the rectangles. */ +gceSTATUS +gco2D_SetStretchRectFactors( + IN gco2D Engine, + IN gcsRECT_PTR SrcRect, + IN gcsRECT_PTR DestRect + ); + +/* Create a new solid color gcoBRUSH object. */ +gceSTATUS +gco2D_ConstructSingleColorBrush( + IN gco2D Engine, + IN gctUINT32 ColorConvert, + IN gctUINT32 Color, + IN gctUINT64 Mask, + gcoBRUSH * Brush + ); + +/* Create a new monochrome gcoBRUSH object. */ +gceSTATUS +gco2D_ConstructMonochromeBrush( + IN gco2D Engine, + IN gctUINT32 OriginX, + IN gctUINT32 OriginY, + IN gctUINT32 ColorConvert, + IN gctUINT32 FgColor, + IN gctUINT32 BgColor, + IN gctUINT64 Bits, + IN gctUINT64 Mask, + gcoBRUSH * Brush + ); + +/* Create a color gcoBRUSH object. */ +gceSTATUS +gco2D_ConstructColorBrush( + IN gco2D Engine, + IN gctUINT32 OriginX, + IN gctUINT32 OriginY, + IN gctPOINTER Address, + IN gceSURF_FORMAT Format, + IN gctUINT64 Mask, + gcoBRUSH * Brush + ); + +/* Clear one or more rectangular areas. */ +gceSTATUS +gco2D_Clear( + IN gco2D Engine, + IN gctUINT32 RectCount, + IN gcsRECT_PTR Rect, + IN gctUINT32 Color32, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +/* Draw one or more Bresenham lines. */ +gceSTATUS +gco2D_Line( + IN gco2D Engine, + IN gctUINT32 LineCount, + IN gcsRECT_PTR Position, + IN gcoBRUSH Brush, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +/* Draw one or more Bresenham lines based on the 32-bit color. */ +gceSTATUS +gco2D_ColorLine( + IN gco2D Engine, + IN gctUINT32 LineCount, + IN gcsRECT_PTR Position, + IN gctUINT32 Color32, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +/* Generic blit. */ +gceSTATUS +gco2D_Blit( + IN gco2D Engine, + IN gctUINT32 RectCount, + IN gcsRECT_PTR Rect, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +gceSTATUS +gco2D_Blend( + IN gco2D Engine, + IN gctUINT32 SrcCount, + IN gctUINT32 RectCount, + IN gcsRECT_PTR Rect, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +/* Batch blit. */ +gceSTATUS +gco2D_BatchBlit( + IN gco2D Engine, + IN gctUINT32 RectCount, + IN gcsRECT_PTR SrcRect, + IN gcsRECT_PTR DestRect, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +/* Stretch blit. */ +gceSTATUS +gco2D_StretchBlit( + IN gco2D Engine, + IN gctUINT32 RectCount, + IN gcsRECT_PTR Rect, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +/* Monochrome blit. */ +gceSTATUS +gco2D_MonoBlit( + IN gco2D Engine, + IN gctPOINTER StreamBits, + IN gcsPOINT_PTR StreamSize, + IN gcsRECT_PTR StreamRect, + IN gceSURF_MONOPACK SrcStreamPack, + IN gceSURF_MONOPACK DestStreamPack, + IN gcsRECT_PTR DestRect, + IN gctUINT32 FgRop, + IN gctUINT32 BgRop, + IN gceSURF_FORMAT DestFormat + ); + +gceSTATUS +gco2D_MonoBlitEx( + IN gco2D Engine, + IN gctPOINTER StreamBits, + IN gctINT32 StreamStride, + IN gctINT32 StreamWidth, + IN gctINT32 StreamHeight, + IN gctINT32 StreamX, + IN gctINT32 StreamY, + IN gctUINT32 FgColor, + IN gctUINT32 BgColor, + IN gcsRECT_PTR SrcRect, + IN gcsRECT_PTR DstRect, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop + ); + +/* Set kernel size. */ +gceSTATUS +gco2D_SetKernelSize( + IN gco2D Engine, + IN gctUINT8 HorKernelSize, + IN gctUINT8 VerKernelSize + ); + +/* Set filter type. */ +gceSTATUS +gco2D_SetFilterType( + IN gco2D Engine, + IN gceFILTER_TYPE FilterType + ); + +/* Set the filter kernel by user. */ +gceSTATUS +gco2D_SetUserFilterKernel( + IN gco2D Engine, + IN gceFILTER_PASS_TYPE PassType, + IN gctUINT16_PTR KernelArray + ); + +/* Select the pass(es) to be done for user defined filter. */ +gceSTATUS +gco2D_EnableUserFilterPasses( + IN gco2D Engine, + IN gctBOOL HorPass, + IN gctBOOL VerPass + ); + +/* Frees the temporary buffer allocated by filter blit operation. */ +gceSTATUS +gco2D_FreeFilterBuffer( + IN gco2D Engine + ); + +/* Filter blit. */ +gceSTATUS +gco2D_FilterBlit( + IN gco2D Engine, + IN gctUINT32 SrcAddress, + IN gctUINT SrcStride, + IN gctUINT32 SrcUAddress, + IN gctUINT SrcUStride, + IN gctUINT32 SrcVAddress, + IN gctUINT SrcVStride, + IN gceSURF_FORMAT SrcFormat, + IN gceSURF_ROTATION SrcRotation, + IN gctUINT32 SrcSurfaceWidth, + IN gcsRECT_PTR SrcRect, + IN gctUINT32 DestAddress, + IN gctUINT DestStride, + IN gceSURF_FORMAT DestFormat, + IN gceSURF_ROTATION DestRotation, + IN gctUINT32 DestSurfaceWidth, + IN gcsRECT_PTR DestRect, + IN gcsRECT_PTR DestSubRect + ); + +/* Filter blit extension for full rotation. */ +gceSTATUS +gco2D_FilterBlitEx( + IN gco2D Engine, + IN gctUINT32 SrcAddress, + IN gctUINT SrcStride, + IN gctUINT32 SrcUAddress, + IN gctUINT SrcUStride, + IN gctUINT32 SrcVAddress, + IN gctUINT SrcVStride, + IN gceSURF_FORMAT SrcFormat, + IN gceSURF_ROTATION SrcRotation, + IN gctUINT32 SrcSurfaceWidth, + IN gctUINT32 SrcSurfaceHeight, + IN gcsRECT_PTR SrcRect, + IN gctUINT32 DestAddress, + IN gctUINT DestStride, + IN gceSURF_FORMAT DestFormat, + IN gceSURF_ROTATION DestRotation, + IN gctUINT32 DestSurfaceWidth, + IN gctUINT32 DestSurfaceHeight, + IN gcsRECT_PTR DestRect, + IN gcsRECT_PTR DestSubRect + ); + +gceSTATUS +gco2D_FilterBlitEx2( + IN gco2D Engine, + IN gctUINT32_PTR SrcAddresses, + IN gctUINT32 SrcAddressNum, + IN gctUINT32_PTR SrcStrides, + IN gctUINT32 SrcStrideNum, + IN gceTILING SrcTiling, + IN gceSURF_FORMAT SrcFormat, + IN gceSURF_ROTATION SrcRotation, + IN gctUINT32 SrcSurfaceWidth, + IN gctUINT32 SrcSurfaceHeight, + IN gcsRECT_PTR SrcRect, + IN gctUINT32_PTR DestAddresses, + IN gctUINT32 DestAddressNum, + IN gctUINT32_PTR DestStrides, + IN gctUINT32 DestStrideNum, + IN gceTILING DestTiling, + IN gceSURF_FORMAT DestFormat, + IN gceSURF_ROTATION DestRotation, + IN gctUINT32 DestSurfaceWidth, + IN gctUINT32 DestSurfaceHeight, + IN gcsRECT_PTR DestRect, + IN gcsRECT_PTR DestSubRect + ); + +/* Enable alpha blending engine in the hardware and disengage the ROP engine. */ +gceSTATUS +gco2D_EnableAlphaBlend( + IN gco2D Engine, + IN gctUINT8 SrcGlobalAlphaValue, + IN gctUINT8 DstGlobalAlphaValue, + IN gceSURF_PIXEL_ALPHA_MODE SrcAlphaMode, + IN gceSURF_PIXEL_ALPHA_MODE DstAlphaMode, + IN gceSURF_GLOBAL_ALPHA_MODE SrcGlobalAlphaMode, + IN gceSURF_GLOBAL_ALPHA_MODE DstGlobalAlphaMode, + IN gceSURF_BLEND_FACTOR_MODE SrcFactorMode, + IN gceSURF_BLEND_FACTOR_MODE DstFactorMode, + IN gceSURF_PIXEL_COLOR_MODE SrcColorMode, + IN gceSURF_PIXEL_COLOR_MODE DstColorMode + ); + +/* Enable alpha blending engine in the hardware. */ +gceSTATUS +gco2D_EnableAlphaBlendAdvanced( + IN gco2D Engine, + IN gceSURF_PIXEL_ALPHA_MODE SrcAlphaMode, + IN gceSURF_PIXEL_ALPHA_MODE DstAlphaMode, + IN gceSURF_GLOBAL_ALPHA_MODE SrcGlobalAlphaMode, + IN gceSURF_GLOBAL_ALPHA_MODE DstGlobalAlphaMode, + IN gceSURF_BLEND_FACTOR_MODE SrcFactorMode, + IN gceSURF_BLEND_FACTOR_MODE DstFactorMode + ); + +/* Enable alpha blending engine with Porter Duff rule. */ +gceSTATUS +gco2D_SetPorterDuffBlending( + IN gco2D Engine, + IN gce2D_PORTER_DUFF_RULE Rule + ); + +/* Disable alpha blending engine in the hardware and engage the ROP engine. */ +gceSTATUS +gco2D_DisableAlphaBlend( + IN gco2D Engine + ); + +/* Retrieve the maximum number of 32-bit data chunks for a single DE command. */ +gctUINT32 +gco2D_GetMaximumDataCount( + void + ); + +/* Retrieve the maximum number of rectangles, that can be passed in a single DE command. */ +gctUINT32 +gco2D_GetMaximumRectCount( + void + ); + +/* Returns the pixel alignment of the surface. */ +gceSTATUS +gco2D_GetPixelAlignment( + gceSURF_FORMAT Format, + gcsPOINT_PTR Alignment + ); + +/* Retrieve monochrome stream pack size. */ +gceSTATUS +gco2D_GetPackSize( + IN gceSURF_MONOPACK StreamPack, + OUT gctUINT32 * PackWidth, + OUT gctUINT32 * PackHeight + ); + +/* Flush the 2D pipeline. */ +gceSTATUS +gco2D_Flush( + IN gco2D Engine + ); + +/* Load 256-entry color table for INDEX8 source surfaces. */ +gceSTATUS +gco2D_LoadPalette( + IN gco2D Engine, + IN gctUINT FirstIndex, + IN gctUINT IndexCount, + IN gctPOINTER ColorTable, + IN gctBOOL ColorConvert + ); + +/* Enable/disable 2D BitBlt mirrorring. */ +gceSTATUS +gco2D_SetBitBlitMirror( + IN gco2D Engine, + IN gctBOOL HorizontalMirror, + IN gctBOOL VerticalMirror + ); + +/* + * Set the transparency for source, destination and pattern. + * It also enable or disable the DFB color key mode. + */ +gceSTATUS +gco2D_SetTransparencyAdvancedEx( + IN gco2D Engine, + IN gce2D_TRANSPARENCY SrcTransparency, + IN gce2D_TRANSPARENCY DstTransparency, + IN gce2D_TRANSPARENCY PatTransparency, + IN gctBOOL EnableDFBColorKeyMode + ); + +/* Set the transparency for source, destination and pattern. */ +gceSTATUS +gco2D_SetTransparencyAdvanced( + IN gco2D Engine, + IN gce2D_TRANSPARENCY SrcTransparency, + IN gce2D_TRANSPARENCY DstTransparency, + IN gce2D_TRANSPARENCY PatTransparency + ); + +/* Set the source color key. */ +gceSTATUS +gco2D_SetSourceColorKeyAdvanced( + IN gco2D Engine, + IN gctUINT32 ColorKey + ); + +/* Set the source color key range. */ +gceSTATUS +gco2D_SetSourceColorKeyRangeAdvanced( + IN gco2D Engine, + IN gctUINT32 ColorKeyLow, + IN gctUINT32 ColorKeyHigh + ); + +/* Set the target color key. */ +gceSTATUS +gco2D_SetTargetColorKeyAdvanced( + IN gco2D Engine, + IN gctUINT32 ColorKey + ); + +/* Set the target color key range. */ +gceSTATUS +gco2D_SetTargetColorKeyRangeAdvanced( + IN gco2D Engine, + IN gctUINT32 ColorKeyLow, + IN gctUINT32 ColorKeyHigh + ); + +/* Set the YUV color space mode. */ +gceSTATUS +gco2D_SetYUVColorMode( + IN gco2D Engine, + IN gce2D_YUV_COLOR_MODE Mode + ); + +/* Setup the source global color value in ARGB8 format. */ +gceSTATUS gco2D_SetSourceGlobalColorAdvanced( + IN gco2D Engine, + IN gctUINT32 Color32 + ); + +/* Setup the target global color value in ARGB8 format. */ +gceSTATUS gco2D_SetTargetGlobalColorAdvanced( + IN gco2D Engine, + IN gctUINT32 Color32 + ); + +/* Setup the source and target pixel multiply modes. */ +gceSTATUS +gco2D_SetPixelMultiplyModeAdvanced( + IN gco2D Engine, + IN gce2D_PIXEL_COLOR_MULTIPLY_MODE SrcPremultiplySrcAlpha, + IN gce2D_PIXEL_COLOR_MULTIPLY_MODE DstPremultiplyDstAlpha, + IN gce2D_GLOBAL_COLOR_MULTIPLY_MODE SrcPremultiplyGlobalMode, + IN gce2D_PIXEL_COLOR_MULTIPLY_MODE DstDemultiplyDstAlpha + ); + +/* Set the GPU clock cycles after which the idle engine will keep auto-flushing. */ +gceSTATUS +gco2D_SetAutoFlushCycles( + IN gco2D Engine, + IN gctUINT32 Cycles + ); + +#if VIVANTE_PROFILER +/* Read the profile registers available in the 2D engine and sets them in the profile. + The function will also reset the pixelsRendered counter every time. +*/ +gceSTATUS +gco2D_ProfileEngine( + IN gco2D Engine, + OPTIONAL gcs2D_PROFILE_PTR Profile + ); +#endif + +/* Enable or disable 2D dithering. */ +gceSTATUS +gco2D_EnableDither( + IN gco2D Engine, + IN gctBOOL Enable + ); + +gceSTATUS +gco2D_SetGenericSource( + IN gco2D Engine, + IN gctUINT32_PTR Addresses, + IN gctUINT32 AddressNum, + IN gctUINT32_PTR Strides, + IN gctUINT32 StrideNum, + IN gceTILING Tiling, + IN gceSURF_FORMAT Format, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight +); + +gceSTATUS +gco2D_SetGenericTarget( + IN gco2D Engine, + IN gctUINT32_PTR Addresses, + IN gctUINT32 AddressNum, + IN gctUINT32_PTR Strides, + IN gctUINT32 StrideNum, + IN gceTILING Tiling, + IN gceSURF_FORMAT Format, + IN gceSURF_ROTATION Rotation, + IN gctUINT32 SurfaceWidth, + IN gctUINT32 SurfaceHeight +); + +gceSTATUS +gco2D_SetCurrentSourceIndex( + IN gco2D Engine, + IN gctUINT32 SrcIndex + ); + +gceSTATUS +gco2D_MultiSourceBlit( + IN gco2D Engine, + IN gctUINT32 SourceMask, + IN gcsRECT_PTR DestRect, + IN gctUINT32 RectCount + ); + +gceSTATUS +gco2D_SetROP( + IN gco2D Engine, + IN gctUINT8 FgRop, + IN gctUINT8 BgRop + ); + +gceSTATUS +gco2D_SetGdiStretchMode( + IN gco2D Engine, + IN gctBOOL Enable + ); + +gceSTATUS +gco2D_SetSourceTileStatus( + IN gco2D Engine, + IN gce2D_TILE_STATUS_CONFIG TSControl, + IN gceSURF_FORMAT CompressedFormat, + IN gctUINT32 ClearValue, + IN gctUINT32 GpuAddress + ); + +gceSTATUS +gco2D_SetTargetTileStatus( + IN gco2D Engine, + IN gce2D_TILE_STATUS_CONFIG TileStatusConfig, + IN gceSURF_FORMAT CompressedFormat, + IN gctUINT32 ClearValue, + IN gctUINT32 GpuAddress + ); + +gceSTATUS +gco2D_QueryU32( + IN gco2D Engine, + IN gce2D_QUERY Item, + OUT gctUINT32_PTR Value + ); + +gceSTATUS +gco2D_SetStateU32( + IN gco2D Engine, + IN gce2D_STATE State, + IN gctUINT32 Value + ); + +gceSTATUS +gco2D_SetStateArrayI32( + IN gco2D Engine, + IN gce2D_STATE State, + IN gctINT32_PTR Array, + IN gctINT32 ArraySize + ); + +gceSTATUS +gco2D_SetStateArrayU32( + IN gco2D Engine, + IN gce2D_STATE State, + IN gctUINT32_PTR Array, + IN gctINT32 ArraySize + ); + +gceSTATUS +gco2D_SetTargetRect( + IN gco2D Engine, + IN gcsRECT_PTR Rect + ); + +gceSTATUS +gco2D_Set2DEngine( + IN gco2D Engine + ); + +gceSTATUS +gco2D_UnSet2DEngine( + IN gco2D Engine + ); + +gceSTATUS +gco2D_Get2DEngine( + OUT gco2D * Engine + ); + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_raster_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_rename.h b/drivers/gpu/galcore/inc/gc_hal_rename.h new file mode 100644 index 00000000000000..745a1044c0d2d5 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_rename.h @@ -0,0 +1,243 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_rename_h_ +#define __gc_hal_rename_h_ + + +#if defined(_HAL2D_APPENDIX) + +#define _HAL2D_RENAME_2(api, appendix) api ## appendix +#define _HAL2D_RENAME_1(api, appendix) _HAL2D_RENAME_2(api, appendix) +#define gcmHAL2D(api) _HAL2D_RENAME_1(api, _HAL2D_APPENDIX) + + +#define gckOS_Construct gcmHAL2D(gckOS_Construct) +#define gckOS_Destroy gcmHAL2D(gckOS_Destroy) +#define gckOS_QueryVideoMemory gcmHAL2D(gckOS_QueryVideoMemory) +#define gckOS_Allocate gcmHAL2D(gckOS_Allocate) +#define gckOS_Free gcmHAL2D(gckOS_Free) +#define gckOS_AllocateMemory gcmHAL2D(gckOS_AllocateMemory) +#define gckOS_FreeMemory gcmHAL2D(gckOS_FreeMemory) +#define gckOS_AllocatePagedMemory gcmHAL2D(gckOS_AllocatePagedMemory) +#define gckOS_AllocatePagedMemoryEx gcmHAL2D(gckOS_AllocatePagedMemoryEx) +#define gckOS_LockPages gcmHAL2D(gckOS_LockPages) +#define gckOS_MapPages gcmHAL2D(gckOS_MapPages) +#define gckOS_UnlockPages gcmHAL2D(gckOS_UnlockPages) +#define gckOS_FreePagedMemory gcmHAL2D(gckOS_FreePagedMemory) +#define gckOS_AllocateNonPagedMemory gcmHAL2D(gckOS_AllocateNonPagedMemory) +#define gckOS_FreeNonPagedMemory gcmHAL2D(gckOS_FreeNonPagedMemory) +#define gckOS_AllocateContiguous gcmHAL2D(gckOS_AllocateContiguous) +#define gckOS_FreeContiguous gcmHAL2D(gckOS_FreeContiguous) +#define gckOS_GetPageSize gcmHAL2D(gckOS_GetPageSize) +#define gckOS_GetPhysicalAddress gcmHAL2D(gckOS_GetPhysicalAddress) +#define gckOS_UserLogicalToPhysical gcmHAL2D(gckOS_UserLogicalToPhysical) +#define gckOS_GetPhysicalAddressProcess gcmHAL2D(gckOS_GetPhysicalAddressProcess) +#define gckOS_MapPhysical gcmHAL2D(gckOS_MapPhysical) +#define gckOS_UnmapPhysical gcmHAL2D(gckOS_UnmapPhysical) +#define gckOS_ReadRegister gcmHAL2D(gckOS_ReadRegister) +#define gckOS_WriteRegister gcmHAL2D(gckOS_WriteRegister) +#define gckOS_WriteMemory gcmHAL2D(gckOS_WriteMemory) +#define gckOS_MapMemory gcmHAL2D(gckOS_MapMemory) +#define gckOS_UnmapMemory gcmHAL2D(gckOS_UnmapMemory) +#define gckOS_UnmapMemoryEx gcmHAL2D(gckOS_UnmapMemoryEx) +#define gckOS_CreateMutex gcmHAL2D(gckOS_CreateMutex) +#define gckOS_DeleteMutex gcmHAL2D(gckOS_DeleteMutex) +#define gckOS_AcquireMutex gcmHAL2D(gckOS_AcquireMutex) +#define gckOS_ReleaseMutex gcmHAL2D(gckOS_ReleaseMutex) +#define gckOS_AtomicExchange gcmHAL2D(gckOS_AtomicExchange) +#define gckOS_AtomicExchangePtr gcmHAL2D(gckOS_AtomicExchangePtr) +#define gckOS_AtomConstruct gcmHAL2D(gckOS_AtomConstruct) +#define gckOS_AtomDestroy gcmHAL2D(gckOS_AtomDestroy) +#define gckOS_AtomGet gcmHAL2D(gckOS_AtomGet) +#define gckOS_AtomIncrement gcmHAL2D(gckOS_AtomIncrement) +#define gckOS_AtomDecrement gcmHAL2D(gckOS_AtomDecrement) +#define gckOS_Delay gcmHAL2D(gckOS_Delay) +#define gckOS_GetTime gcmHAL2D(gckOS_GetTime) +#define gckOS_MemoryBarrier gcmHAL2D(gckOS_MemoryBarrier) +#define gckOS_MapUserPointer gcmHAL2D(gckOS_MapUserPointer) +#define gckOS_UnmapUserPointer gcmHAL2D(gckOS_UnmapUserPointer) +#define gckOS_QueryNeedCopy gcmHAL2D(gckOS_QueryNeedCopy) +#define gckOS_CopyFromUserData gcmHAL2D(gckOS_CopyFromUserData) +#define gckOS_CopyToUserData gcmHAL2D(gckOS_CopyToUserData) +#define gckOS_SuspendInterrupt gcmHAL2D(gckOS_SuspendInterrupt) +#define gckOS_ResumeInterrupt gcmHAL2D(gckOS_ResumeInterrupt) +#define gckOS_GetBaseAddress gcmHAL2D(gckOS_GetBaseAddress) +#define gckOS_MemCopy gcmHAL2D(gckOS_MemCopy) +#define gckOS_ZeroMemory gcmHAL2D(gckOS_ZeroMemory) +#define gckOS_DeviceControl gcmHAL2D(gckOS_DeviceControl) +#define gckOS_GetProcessID gcmHAL2D(gckOS_GetProcessID) +#define gckOS_GetThreadID gcmHAL2D(gckOS_GetThreadID) +#define gckOS_CreateSignal gcmHAL2D(gckOS_CreateSignal) +#define gckOS_DestroySignal gcmHAL2D(gckOS_DestroySignal) +#define gckOS_Signal gcmHAL2D(gckOS_Signal) +#define gckOS_WaitSignal gcmHAL2D(gckOS_WaitSignal) +#define gckOS_MapSignal gcmHAL2D(gckOS_MapSignal) +#define gckOS_MapUserMemory gcmHAL2D(gckOS_MapUserMemory) +#define gckOS_UnmapUserMemory gcmHAL2D(gckOS_UnmapUserMemory) +#define gckOS_CreateUserSignal gcmHAL2D(gckOS_CreateUserSignal) +#define gckOS_DestroyUserSignal gcmHAL2D(gckOS_DestroyUserSignal) +#define gckOS_WaitUserSignal gcmHAL2D(gckOS_WaitUserSignal) +#define gckOS_SignalUserSignal gcmHAL2D(gckOS_SignalUserSignal) +#define gckOS_UserSignal gcmHAL2D(gckOS_UserSignal) +#define gckOS_UserSignal gcmHAL2D(gckOS_UserSignal) +#define gckOS_CacheClean gcmHAL2D(gckOS_CacheClean) +#define gckOS_CacheFlush gcmHAL2D(gckOS_CacheFlush) +#define gckOS_SetDebugLevel gcmHAL2D(gckOS_SetDebugLevel) +#define gckOS_SetDebugZone gcmHAL2D(gckOS_SetDebugZone) +#define gckOS_SetDebugLevelZone gcmHAL2D(gckOS_SetDebugLevelZone) +#define gckOS_SetDebugZones gcmHAL2D(gckOS_SetDebugZones) +#define gckOS_SetDebugFile gcmHAL2D(gckOS_SetDebugFile) +#define gckOS_Broadcast gcmHAL2D(gckOS_Broadcast) +#define gckOS_SetGPUPower gcmHAL2D(gckOS_SetGPUPower) +#define gckOS_CreateSemaphore gcmHAL2D(gckOS_CreateSemaphore) +#define gckOS_DestroySemaphore gcmHAL2D(gckOS_DestroySemaphore) +#define gckOS_AcquireSemaphore gcmHAL2D(gckOS_AcquireSemaphore) +#define gckOS_ReleaseSemaphore gcmHAL2D(gckOS_ReleaseSemaphore) +#define gckHEAP_Construct gcmHAL2D(gckHEAP_Construct) +#define gckHEAP_Destroy gcmHAL2D(gckHEAP_Destroy) +#define gckHEAP_Allocate gcmHAL2D(gckHEAP_Allocate) +#define gckHEAP_Free gcmHAL2D(gckHEAP_Free) +#define gckHEAP_ProfileStart gcmHAL2D(gckHEAP_ProfileStart) +#define gckHEAP_ProfileEnd gcmHAL2D(gckHEAP_ProfileEnd) +#define gckHEAP_Test gcmHAL2D(gckHEAP_Test) +#define gckVIDMEM_Construct gcmHAL2D(gckVIDMEM_Construct) +#define gckVIDMEM_Destroy gcmHAL2D(gckVIDMEM_Destroy) +#define gckVIDMEM_Allocate gcmHAL2D(gckVIDMEM_Allocate) +#define gckVIDMEM_AllocateLinear gcmHAL2D(gckVIDMEM_AllocateLinear) +#define gckVIDMEM_Free gcmHAL2D(gckVIDMEM_Free) +#define gckVIDMEM_Lock gcmHAL2D(gckVIDMEM_Lock) +#define gckVIDMEM_Unlock gcmHAL2D(gckVIDMEM_Unlock) +#define gckVIDMEM_ConstructVirtual gcmHAL2D(gckVIDMEM_ConstructVirtual) +#define gckVIDMEM_DestroyVirtual gcmHAL2D(gckVIDMEM_DestroyVirtual) +#define gckKERNEL_Construct gcmHAL2D(gckKERNEL_Construct) +#define gckKERNEL_Destroy gcmHAL2D(gckKERNEL_Destroy) +#define gckKERNEL_Dispatch gcmHAL2D(gckKERNEL_Dispatch) +#define gckKERNEL_QueryVideoMemory gcmHAL2D(gckKERNEL_QueryVideoMemory) +#define gckKERNEL_GetVideoMemoryPool gcmHAL2D(gckKERNEL_GetVideoMemoryPool) +#define gckKERNEL_MapVideoMemory gcmHAL2D(gckKERNEL_MapVideoMemory) +#define gckKERNEL_UnmapVideoMemory gcmHAL2D(gckKERNEL_UnmapVideoMemory) +#define gckKERNEL_MapMemory gcmHAL2D(gckKERNEL_MapMemory) +#define gckKERNEL_UnmapMemory gcmHAL2D(gckKERNEL_UnmapMemory) +#define gckKERNEL_Notify gcmHAL2D(gckKERNEL_Notify) +#define gckKERNEL_QuerySettings gcmHAL2D(gckKERNEL_QuerySettings) +#define gckKERNEL_Recovery gcmHAL2D(gckKERNEL_Recovery) +#define gckKERNEL_OpenUserData gcmHAL2D(gckKERNEL_OpenUserData) +#define gckKERNEL_CloseUserData gcmHAL2D(gckKERNEL_CloseUserData) +#define gckHARDWARE_Construct gcmHAL2D(gckHARDWARE_Construct) +#define gckHARDWARE_Destroy gcmHAL2D(gckHARDWARE_Destroy) +#define gckHARDWARE_QuerySystemMemory gcmHAL2D(gckHARDWARE_QuerySystemMemory) +#define gckHARDWARE_BuildVirtualAddress gcmHAL2D(gckHARDWARE_BuildVirtualAddress) +#define gckHARDWARE_QueryCommandBuffer gcmHAL2D(gckHARDWARE_QueryCommandBuffer) +#define gckHARDWARE_WaitLink gcmHAL2D(gckHARDWARE_WaitLink) +#define gckHARDWARE_Execute gcmHAL2D(gckHARDWARE_Execute) +#define gckHARDWARE_End gcmHAL2D(gckHARDWARE_End) +#define gckHARDWARE_Nop gcmHAL2D(gckHARDWARE_Nop) +#define gckHARDWARE_PipeSelect gcmHAL2D(gckHARDWARE_PipeSelect) +#define gckHARDWARE_Link gcmHAL2D(gckHARDWARE_Link) +#define gckHARDWARE_Event gcmHAL2D(gckHARDWARE_Event) +#define gckHARDWARE_QueryMemory gcmHAL2D(gckHARDWARE_QueryMemory) +#define gckHARDWARE_QueryChipIdentity gcmHAL2D(gckHARDWARE_QueryChipIdentity) +#define gckHARDWARE_QueryChipSpecs gcmHAL2D(gckHARDWARE_QueryChipSpecs) +#define gckHARDWARE_QueryShaderCaps gcmHAL2D(gckHARDWARE_QueryShaderCaps) +#define gckHARDWARE_ConvertFormat gcmHAL2D(gckHARDWARE_ConvertFormat) +#define gckHARDWARE_SplitMemory gcmHAL2D(gckHARDWARE_SplitMemory) +#define gckHARDWARE_AlignToTile gcmHAL2D(gckHARDWARE_AlignToTile) +#define gckHARDWARE_UpdateQueueTail gcmHAL2D(gckHARDWARE_UpdateQueueTail) +#define gckHARDWARE_ConvertLogical gcmHAL2D(gckHARDWARE_ConvertLogical) +#define gckHARDWARE_Interrupt gcmHAL2D(gckHARDWARE_Interrupt) +#define gckHARDWARE_SetMMU gcmHAL2D(gckHARDWARE_SetMMU) +#define gckHARDWARE_FlushMMU gcmHAL2D(gckHARDWARE_FlushMMU) +#define gckHARDWARE_GetIdle gcmHAL2D(gckHARDWARE_GetIdle) +#define gckHARDWARE_Flush gcmHAL2D(gckHARDWARE_Flush) +#define gckHARDWARE_SetFastClear gcmHAL2D(gckHARDWARE_SetFastClear) +#define gckHARDWARE_ReadInterrupt gcmHAL2D(gckHARDWARE_ReadInterrupt) +#define gckHARDWARE_SetPowerManagementState gcmHAL2D(gckHARDWARE_SetPowerManagementState) +#define gckHARDWARE_QueryPowerManagementState gcmHAL2D(gckHARDWARE_QueryPowerManagementState) +#define gckHARDWARE_ProfileEngine2D gcmHAL2D(gckHARDWARE_ProfileEngine2D) +#define gckHARDWARE_InitializeHardware gcmHAL2D(gckHARDWARE_InitializeHardware) +#define gckHARDWARE_Reset gcmHAL2D(gckHARDWARE_Reset) +#define gckINTERRUPT_Construct gcmHAL2D(gckINTERRUPT_Construct) +#define gckINTERRUPT_Destroy gcmHAL2D(gckINTERRUPT_Destroy) +#define gckINTERRUPT_SetHandler gcmHAL2D(gckINTERRUPT_SetHandler) +#define gckINTERRUPT_Notify gcmHAL2D(gckINTERRUPT_Notify) +#define gckEVENT_Construct gcmHAL2D(gckEVENT_Construct) +#define gckEVENT_Destroy gcmHAL2D(gckEVENT_Destroy) +#define gckEVENT_AddList gcmHAL2D(gckEVENT_AddList) +#define gckEVENT_FreeNonPagedMemory gcmHAL2D(gckEVENT_FreeNonPagedMemory) +#define gckEVENT_FreeContiguousMemory gcmHAL2D(gckEVENT_FreeContiguousMemory) +#define gckEVENT_FreeVideoMemory gcmHAL2D(gckEVENT_FreeVideoMemory) +#define gckEVENT_Signal gcmHAL2D(gckEVENT_Signal) +#define gckEVENT_Unlock gcmHAL2D(gckEVENT_Unlock) +#define gckEVENT_Submit gcmHAL2D(gckEVENT_Submit) +#define gckEVENT_Commit gcmHAL2D(gckEVENT_Commit) +#define gckEVENT_Notify gcmHAL2D(gckEVENT_Notify) +#define gckEVENT_Interrupt gcmHAL2D(gckEVENT_Interrupt) +#define gckCOMMAND_Construct gcmHAL2D(gckCOMMAND_Construct) +#define gckCOMMAND_Destroy gcmHAL2D(gckCOMMAND_Destroy) +#define gckCOMMAND_EnterCommit gcmHAL2D(gckCOMMAND_EnterCommit) +#define gckCOMMAND_ExitCommit gcmHAL2D(gckCOMMAND_ExitCommit) +#define gckCOMMAND_Start gcmHAL2D(gckCOMMAND_Start) +#define gckCOMMAND_Stop gcmHAL2D(gckCOMMAND_Stop) +#define gckCOMMAND_Commit gcmHAL2D(gckCOMMAND_Commit) +#define gckCOMMAND_Reserve gcmHAL2D(gckCOMMAND_Reserve) +#define gckCOMMAND_Execute gcmHAL2D(gckCOMMAND_Execute) +#define gckCOMMAND_Stall gcmHAL2D(gckCOMMAND_Stall) +#define gckCOMMAND_Attach gcmHAL2D(gckCOMMAND_Attach) +#define gckCOMMAND_Detach gcmHAL2D(gckCOMMAND_Detach) +#define gckMMU_Construct gcmHAL2D(gckMMU_Construct) +#define gckMMU_Destroy gcmHAL2D(gckMMU_Destroy) +#define gckMMU_AllocatePages gcmHAL2D(gckMMU_AllocatePages) +#define gckMMU_FreePages gcmHAL2D(gckMMU_FreePages) +#define gckMMU_Test gcmHAL2D(gckMMU_Test) +#define gckHARDWARE_QueryProfileRegisters gcmHAL2D(gckHARDWARE_QueryProfileRegisters) + + +#define FindMdlMap gcmHAL2D(FindMdlMap) +#define OnProcessExit gcmHAL2D(OnProcessExit) + +#define gckGALDEVICE_Destroy gcmHAL2D(gckGALDEVICE_Destroy) +#define gckOS_Print gcmHAL2D(gckOS_Print) +#define gckGALDEVICE_FreeMemory gcmHAL2D(gckGALDEVICE_FreeMemory) +#define gckGALDEVICE_AllocateMemory gcmHAL2D(gckGALDEVICE_AllocateMemory) +#define gckOS_DebugBreak gcmHAL2D(gckOS_DebugBreak) +#define gckGALDEVICE_Release_ISR gcmHAL2D(gckGALDEVICE_Release_ISR) +#define gckOS_Verify gcmHAL2D(gckOS_Verify) +#define gckCOMMAND_Release gcmHAL2D(gckCOMMAND_Release) +#define gckGALDEVICE_Stop gcmHAL2D(gckGALDEVICE_Stop) +#define gckGALDEVICE_Construct gcmHAL2D(gckGALDEVICE_Construct) +#define gckOS_DebugFatal gcmHAL2D(gckOS_DebugFatal) +#define gckOS_DebugTrace gcmHAL2D(gckOS_DebugTrace) +#define gckHARDWARE_GetBaseAddress gcmHAL2D(gckHARDWARE_GetBaseAddress) +#define gckGALDEVICE_Setup_ISR gcmHAL2D(gckGALDEVICE_Setup_ISR) +#define gckKERNEL_AttachProcess gcmHAL2D(gckKERNEL_AttachProcess) +#define gckKERNEL_AttachProcessEx gcmHAL2D(gckKERNEL_AttachProcessEx) +#define gckGALDEVICE_Start_Thread gcmHAL2D(gckGALDEVICE_Start_Thread) +#define gckHARDWARE_QueryIdle gcmHAL2D(gckHARDWARE_QueryIdle) +#define gckGALDEVICE_Start gcmHAL2D(gckGALDEVICE_Start) +#define gckOS_GetKernelLogical gcmHAL2D(gckOS_GetKernelLogical) +#define gckOS_DebugTraceZone gcmHAL2D(gckOS_DebugTraceZone) +#define gckGALDEVICE_Stop_Thread gcmHAL2D(gckGALDEVICE_Stop_Thread) +#define gckHARDWARE_NeedBaseAddress gcmHAL2D(gckHARDWARE_NeedBaseAddress) + +#endif + +#endif /* __gc_hal_rename_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_security_interface.h b/drivers/gpu/galcore/inc/gc_hal_security_interface.h new file mode 100644 index 00000000000000..cb86d767c60ae0 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_security_interface.h @@ -0,0 +1,137 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef _GC_HAL_SECURITY_INTERFACE_H_ +#define _GC_HAL_SECURITY_INTERFACE_H_ +/*! + @brief Command codes between kernel module and TrustZone + @discussion + Critical services must be done in TrustZone to avoid sensitive content leak. Most of kernel module is kept in non-Secure os to minimize + code in TrustZone. + */ +typedef enum kernel_packet_command { + KERNEL_START_COMMAND, + KERNEL_SUBMIT, + KERNEL_MAP_MEMORY, /* */ + KERNEL_UNMAP_MEMORY, + KERNEL_ALLOCATE_SECRUE_MEMORY, /*! Security memory management. */ + KERNEL_FREE_SECURE_MEMORY, + KERNEL_EXECUTE, /* Execute a command buffer. */ +} kernel_packet_command_t; + +/*! + @brief gckCOMMAND Object requests TrustZone to start FE. + @discussion + DMA enabled register can only be written in TrustZone to avoid GPU from jumping to a hacked code. + Kernel module need use these command to ask TrustZone start command parser. + */ +struct kernel_start_command { + kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ + gctUINT8 gpu; /*! Which GPU. */ +}; + +/*! + @brief gckCOMMAND Object requests TrustZone to submit command buffer. + @discussion + Code in trustzone will check content of command buffer after copying command buffer to TrustZone. + */ +struct kernel_submit { + kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ + gctUINT8 gpu; /*! Which GPU. */ + gctUINT8 kernel_command; /*! Whether it is a kernel command. */ + gctUINT32 command_buffer_handle; /*! Handle to command buffer. */ + gctUINT32 offset; /* Offset in command buffer. */ + gctUINT32 * command_buffer; /*! Content of command buffer need to be submit. */ + gctUINT32 command_buffer_length; /*! Length of command buffer. */ +}; + + +/*! + @brief gckVIDMEM Object requests TrustZone to allocate security memory. + @discussion + Allocate a buffer from security GPU memory. + */ +struct kernel_allocate_security_memory { + kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ + gctUINT32 bytes; /*! Requested bytes. */ + gctUINT32 memory_handle; /*! Handle of allocated memory. */ +}; + +/*! + @brief gckVIDMEM Object requests TrustZone to allocate security memory. + @discussion + Free a video memory buffer from security GPU memory. + */ +struct kernel_free_security_memory { + kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ + gctUINT32 memory_handle; /*! Handle of allocated memory. */ +}; + +struct kernel_execute { + kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ + gctUINT8 gpu; /*! Which GPU. */ + gctUINT8 kernel_command; /*! Whether it is a kernel command. */ + gctUINT32 * command_buffer; /*! Content of command buffer need to be submit. */ + gctUINT32 command_buffer_length; /*! Length of command buffer. */ +}; + +typedef struct kernel_map_scatter_gather { + gctUINT32 bytes; + gctUINT32 physical; + struct kernel_map_scatter_gather *next; +} +kernel_map_scatter_gather_t; + +struct kernel_map_memory { + kernel_packet_command_t command; + kernel_map_scatter_gather_t *scatter; + gctUINT32 *physicals; + gctUINT32 pageCount; + gctUINT32 gpuAddress; +}; + +struct kernel_unmap_memory { + gctUINT32 gpuAddress; + gctUINT32 pageCount; +}; + +typedef struct _gcsTA_INTERFACE { + kernel_packet_command_t command; + union { + struct kernel_submit Submit; + struct kernel_start_command StartCommand; + struct kernel_allocate_security_memory AllocateSecurityMemory; + struct kernel_execute Execute; + struct kernel_map_memory MapMemory; + struct kernel_unmap_memory UnmapMemory; + } u; + gceSTATUS result; +} gcsTA_INTERFACE; + +enum { + gcvTA_COMMAND_INIT, + gcvTA_COMMAND_DISPATCH, + + gcvTA_CALLBACK_ALLOC_SECURE_MEM, + gcvTA_CALLBACK_FREE_SECURE_MEM, +}; + +#endif diff --git a/drivers/gpu/galcore/inc/gc_hal_statistics.h b/drivers/gpu/galcore/inc/gc_hal_statistics.h new file mode 100644 index 00000000000000..07b8e725f29aac --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_statistics.h @@ -0,0 +1,99 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_statistics_h_ +#define __gc_hal_statistics_h_ + + +#define VIV_STAT_ENABLE_STATISTICS 0 + +/* Toal number of frames for which the frame time is accounted. We have storage + to keep frame times for last this many frames. +*/ +#define VIV_STAT_FRAME_BUFFER_SIZE 30 + + +/* + Total number of frames sampled for a mode. This means + + # of frames for HZ Current : VIV_STAT_EARLY_Z_SAMPLE_FRAMES + # of frames for HZ Switched : VIV_STAT_EARLY_Z_SAMPLE_FRAMES + + + -------------------------------------------------------- + : (2 * VIV_STAT_EARLY_Z_SAMPLE_FRAMES) frames needed + + IMPORTANT: This total must be smaller than VIV_STAT_FRAME_BUFFER_SIZE +*/ +#define VIV_STAT_EARLY_Z_SAMPLE_FRAMES 7 +#define VIV_STAT_EARLY_Z_LATENCY_FRAMES 2 + +/* Multiplication factor for previous Hz off mode. Make it more than 1.0 to advertise HZ on.*/ +#define VIV_STAT_EARLY_Z_FACTOR (1.05f) + +/* Defines the statistical data keys monitored by the statistics module */ +typedef enum _gceSTATISTICS +{ + gcvFRAME_FPS = 1, +} +gceSTATISTICS; + +/* HAL statistics information. */ +typedef struct _gcsSTATISTICS_EARLYZ +{ + gctUINT switchBackCount; + gctUINT nextCheckPoint; + gctBOOL disabled; +} +gcsSTATISTICS_EARLYZ; + + +/* HAL statistics information. */ +typedef struct _gcsSTATISTICS +{ + gctUINT64 frameTime[VIV_STAT_FRAME_BUFFER_SIZE]; + gctUINT64 previousFrameTime; + gctUINT frame; + gcsSTATISTICS_EARLYZ earlyZ; +} +gcsSTATISTICS; + + +/* Add a frame based data into current statistics. */ +void +gcfSTATISTICS_AddData( + IN gceSTATISTICS Key, + IN gctUINT Value + ); + +/* Marks the frame end and triggers statistical calculations and decisions.*/ +void +gcfSTATISTICS_MarkFrameEnd ( + void + ); + +/* Sets whether the dynmaic HZ is disabled or not .*/ +void +gcfSTATISTICS_DisableDynamicEarlyZ ( + IN gctBOOL Disabled + ); + +#endif /*__gc_hal_statistics_h_ */ + diff --git a/drivers/gpu/galcore/inc/gc_hal_types.h b/drivers/gpu/galcore/inc/gc_hal_types.h new file mode 100644 index 00000000000000..d6fc3949945d0b --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_types.h @@ -0,0 +1,929 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_types_h_ +#define __gc_hal_types_h_ + +#include "gc_hal_version.h" +#include "gc_hal_options.h" + +#if !defined(VIV_KMD) +#if defined(__KERNEL__) +#include "linux/version.h" +#include "linux/types.h" +#elif defined(UNDER_CE) +#include +#elif defined(_MSC_VER) && (_MSC_VER <= 1500) +#include +#include "vadefs.h" +#elif defined(__QNXNTO__) +#define _QNX_SOURCE +#include +#include +#else +#include +#include +#include +#endif +#endif + +#ifdef _WIN32 +#pragma warning(disable:4127) /* Conditional expression is constant (do { } + ** while(0)). */ +#pragma warning(disable:4100) /* Unreferenced formal parameter. */ +#pragma warning(disable:4204) /* Non-constant aggregate initializer (C99). */ +#pragma warning(disable:4131) /* Uses old-style declarator (for Bison and + ** Flex generated files). */ +#pragma warning(disable:4206) /* Translation unit is empty. */ +#pragma warning(disable:4214) /* Nonstandard extension used : + ** bit field types other than int. */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************\ +** Platform macros. +*/ + +#if defined(__GNUC__) +# define gcdHAS_ELLIPSIS 1 /* GCC always has it. */ +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +# define gcdHAS_ELLIPSIS 1 /* C99 has it. */ +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) +# define gcdHAS_ELLIPSIS 1 /* MSVC 2007+ has it. */ +#elif defined(UNDER_CE) +#if UNDER_CE >= 600 +# define gcdHAS_ELLIPSIS 1 +# else +# define gcdHAS_ELLIPSIS 0 +# endif +#else +# error "gcdHAS_ELLIPSIS: Platform could not be determined" +#endif + +/******************************************************************************\ +************************************ Keyword *********************************** +\******************************************************************************/ +#if defined(ANDROID) && defined(__BIONIC_FORTIFY) +# define gcmINLINE __inline__ __attribute__ ((always_inline)) __attribute__ ((gnu_inline)) __attribute__ ((artificial)) +#elif ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__APPLE__)) +# define gcmINLINE inline /* C99 keyword. */ +#elif defined(__GNUC__) +# define gcmINLINE __inline__ /* GNU keyword. */ +#elif defined(_MSC_VER) || defined(UNDER_CE) +# define gcmINLINE __inline /* Internal keyword. */ +#else +# error "gcmINLINE: Platform could not be determined" +#endif + +/* Possible debug flags. */ +#define gcdDEBUG_NONE 0 +#define gcdDEBUG_ALL (1 << 0) +#define gcdDEBUG_FATAL (1 << 1) +#define gcdDEBUG_TRACE (1 << 2) +#define gcdDEBUG_BREAK (1 << 3) +#define gcdDEBUG_ASSERT (1 << 4) +#define gcdDEBUG_CODE (1 << 5) +#define gcdDEBUG_STACK (1 << 6) + +#define gcmIS_DEBUG(flag) ( gcdDEBUG & (flag | gcdDEBUG_ALL) ) + +#ifndef gcdDEBUG +#if (defined(DBG) && DBG) || defined(DEBUG) || defined(_DEBUG) +# define gcdDEBUG gcdDEBUG_ALL +# else +# define gcdDEBUG gcdDEBUG_NONE +# endif +#endif + +#ifdef _USRDLL +#ifdef _MSC_VER +#ifdef HAL_EXPORTS +# define HALAPI __declspec(dllexport) +# else +# define HALAPI __declspec(dllimport) +# endif +# define HALDECL __cdecl +# else +#ifdef HAL_EXPORTS +# define HALAPI +# else +# define HALAPI extern +# endif +# endif +#else +# define HALAPI +# define HALDECL +#endif + +/******************************************************************************\ +********************************** Common Types ******************************** +\******************************************************************************/ + +#define gcvFALSE 0 +#define gcvTRUE 1 + +#define gcvINFINITE ((gctUINT32) ~0U) + +#define gcvINVALID_HANDLE ((gctHANDLE) ~0U) + +typedef int gctBOOL; +typedef gctBOOL * gctBOOL_PTR; + +typedef int gctINT; +typedef signed char gctINT8; +typedef signed short gctINT16; +typedef signed int gctINT32; +typedef signed long long gctINT64; + +typedef gctINT * gctINT_PTR; +typedef gctINT8 * gctINT8_PTR; +typedef gctINT16 * gctINT16_PTR; +typedef gctINT32 * gctINT32_PTR; +typedef gctINT64 * gctINT64_PTR; + +typedef unsigned int gctUINT; +typedef unsigned char gctUINT8; +typedef unsigned short gctUINT16; +typedef unsigned int gctUINT32; +typedef unsigned long long gctUINT64; +typedef uintptr_t gctUINTPTR_T; + +typedef gctUINT * gctUINT_PTR; +typedef gctUINT8 * gctUINT8_PTR; +typedef gctUINT16 * gctUINT16_PTR; +typedef gctUINT32 * gctUINT32_PTR; +typedef gctUINT64 * gctUINT64_PTR; + +typedef size_t gctSIZE_T; +typedef gctSIZE_T * gctSIZE_T_PTR; +typedef gctUINT32 gctTRACE; + +#ifdef __cplusplus +# define gcvNULL 0 +#else +# define gcvNULL ((void *) 0) +#endif + +#define gcvMAXINT8 0x7f +#define gcvMININT8 0x80 +#define gcvMAXINT16 0x7fff +#define gcvMININT16 0x8000 +#define gcvMAXINT32 0x7fffffff +#define gcvMININT32 0x80000000 +#define gcvMAXINT64 0x7fffffffffffffff +#define gcvMININT64 0x8000000000000000 +#define gcvMAXUINT8 0xff +#define gcvMINUINT8 0x0 +#define gcvMAXUINT16 0xffff +#define gcvMINUINT16 0x8000 +#define gcvMAXUINT32 0xffffffff +#define gcvMINUINT32 0x80000000 +#define gcvMAXUINT64 0xffffffffffffffff +#define gcvMINUINT64 0x8000000000000000 +#define gcvMAXUINTPTR_T (~(gctUINTPTR_T)0) + +typedef float gctFLOAT; +typedef signed int gctFIXED_POINT; +typedef float * gctFLOAT_PTR; + +typedef void * gctPHYS_ADDR; +typedef void * gctHANDLE; +typedef void * gctFILE; +typedef void * gctSIGNAL; +typedef void * gctWINDOW; +typedef void * gctIMAGE; +typedef void * gctSYNC_POINT; +typedef void * gctSHBUF; + +typedef void * gctSEMAPHORE; + +typedef void * gctPOINTER; +typedef const void * gctCONST_POINTER; + +typedef char gctCHAR; +typedef char * gctSTRING; +typedef const char * gctCONST_STRING; + +typedef struct _gcsCOUNT_STRING +{ + gctSIZE_T Length; + gctCONST_STRING String; +} +gcsCOUNT_STRING; + +typedef union _gcuFLOAT_UINT32 +{ + gctFLOAT f; + gctUINT32 u; +} +gcuFLOAT_UINT32; + +/* Fixed point constants. */ +#define gcvZERO_X ((gctFIXED_POINT) 0x00000000) +#define gcvHALF_X ((gctFIXED_POINT) 0x00008000) +#define gcvONE_X ((gctFIXED_POINT) 0x00010000) +#define gcvNEGONE_X ((gctFIXED_POINT) 0xFFFF0000) +#define gcvTWO_X ((gctFIXED_POINT) 0x00020000) + + + +#define gcmFIXEDCLAMP_NEG1_TO_1(_x) \ + (((_x) < gcvNEGONE_X) \ + ? gcvNEGONE_X \ + : (((_x) > gcvONE_X) \ + ? gcvONE_X \ + : (_x))) + +#define gcmFLOATCLAMP_NEG1_TO_1(_f) \ + (((_f) < -1.0f) \ + ? -1.0f \ + : (((_f) > 1.0f) \ + ? 1.0f \ + : (_f))) + + +#define gcmFIXEDCLAMP_0_TO_1(_x) \ + (((_x) < 0) \ + ? 0 \ + : (((_x) > gcvONE_X) \ + ? gcvONE_X \ + : (_x))) + +#define gcmFLOATCLAMP_0_TO_1(_f) \ + (((_f) < 0.0f) \ + ? 0.0f \ + : (((_f) > 1.0f) \ + ? 1.0f \ + : (_f))) + + +/******************************************************************************\ +******************************* Multicast Values ******************************* +\******************************************************************************/ + +/* Value types. */ +typedef enum _gceVALUE_TYPE +{ + gcvVALUE_UINT = 0x0, + gcvVALUE_FIXED, + gcvVALUE_FLOAT, + gcvVALUE_INT, + + /* + ** The value need be unsigned denormalized. clamp (0.0-1.0) should be done first. + */ + gcvVALUE_FLAG_UNSIGNED_DENORM = 0x00010000, + + /* + ** The value need be signed denormalized. clamp (-1.0-1.0) should be done first. + */ + gcvVALUE_FLAG_SIGNED_DENORM = 0x00020000, + + /* + ** The value need to gammar + */ + gcvVALUE_FLAG_GAMMAR = 0x00040000, + + /* + ** The value need to convert from float to float16 + */ + gcvVALUE_FLAG_FLOAT_TO_FLOAT16 = 0x0080000, + + /* + ** Mask for flag field. + */ + gcvVALUE_FLAG_MASK = 0xFFFF0000, +} +gceVALUE_TYPE; + +/* Value unions. */ +typedef union _gcuVALUE +{ + gctUINT uintValue; + gctFIXED_POINT fixedValue; + gctFLOAT floatValue; + gctINT intValue; +} +gcuVALUE; + + + + +/* Stringizing macro. */ +#define gcmSTRING(Value) #Value + +/******************************************************************************\ +******************************* Fixed Point Math ******************************* +\******************************************************************************/ + +#define gcmXMultiply(x1, x2) gcoMATH_MultiplyFixed(x1, x2) +#define gcmXDivide(x1, x2) gcoMATH_DivideFixed(x1, x2) +#define gcmXMultiplyDivide(x1, x2, x3) gcoMATH_MultiplyDivideFixed(x1, x2, x3) + +/* 2D Engine profile. */ +typedef struct _gcs2D_PROFILE +{ + /* Cycle count. + 32bit counter incremented every 2D clock cycle. + Wraps back to 0 when the counter overflows. + */ + gctUINT32 cycleCount; + + /* Pixels rendered by the 2D engine. + Resets to 0 every time it is read. */ + gctUINT32 pixelsRendered; +} +gcs2D_PROFILE; + +/* Macro to combine four characters into a Charcater Code. */ +#define gcmCC(c1, c2, c3, c4) \ +( \ + (char) (c1) \ + | \ + ((char) (c2) << 8) \ + | \ + ((char) (c3) << 16) \ + | \ + ((char) (c4) << 24) \ +) + +#define gcmPRINTABLE(c) ((((c) >= ' ') && ((c) <= '}')) ? ((c) != '%' ? (c) : ' ') : ' ') + +#define gcmCC_PRINT(cc) \ + gcmPRINTABLE((char) ( (cc) & 0xFF)), \ + gcmPRINTABLE((char) (((cc) >> 8) & 0xFF)), \ + gcmPRINTABLE((char) (((cc) >> 16) & 0xFF)), \ + gcmPRINTABLE((char) (((cc) >> 24) & 0xFF)) + +/******************************************************************************\ +****************************** Function Parameters ***************************** +\******************************************************************************/ + +#define IN +#define OUT +#define INOUT +#define OPTIONAL + +/******************************************************************************\ +********************************* Status Codes ********************************* +\******************************************************************************/ + +typedef enum _gceSTATUS +{ + gcvSTATUS_OK = 0, + gcvSTATUS_FALSE = 0, + gcvSTATUS_TRUE = 1, + gcvSTATUS_NO_MORE_DATA = 2, + gcvSTATUS_CACHED = 3, + gcvSTATUS_MIPMAP_TOO_LARGE = 4, + gcvSTATUS_NAME_NOT_FOUND = 5, + gcvSTATUS_NOT_OUR_INTERRUPT = 6, + gcvSTATUS_MISMATCH = 7, + gcvSTATUS_MIPMAP_TOO_SMALL = 8, + gcvSTATUS_LARGER = 9, + gcvSTATUS_SMALLER = 10, + gcvSTATUS_CHIP_NOT_READY = 11, + gcvSTATUS_NEED_CONVERSION = 12, + gcvSTATUS_SKIP = 13, + gcvSTATUS_DATA_TOO_LARGE = 14, + gcvSTATUS_INVALID_CONFIG = 15, + gcvSTATUS_CHANGED = 16, + gcvSTATUS_NOT_SUPPORT_DITHER = 17, + gcvSTATUS_EXECUTED = 18, + gcvSTATUS_TERMINATE = 19, + + gcvSTATUS_INVALID_ARGUMENT = -1, + gcvSTATUS_INVALID_OBJECT = -2, + gcvSTATUS_OUT_OF_MEMORY = -3, + gcvSTATUS_MEMORY_LOCKED = -4, + gcvSTATUS_MEMORY_UNLOCKED = -5, + gcvSTATUS_HEAP_CORRUPTED = -6, + gcvSTATUS_GENERIC_IO = -7, + gcvSTATUS_INVALID_ADDRESS = -8, + gcvSTATUS_CONTEXT_LOSSED = -9, + gcvSTATUS_TOO_COMPLEX = -10, + gcvSTATUS_BUFFER_TOO_SMALL = -11, + gcvSTATUS_INTERFACE_ERROR = -12, + gcvSTATUS_NOT_SUPPORTED = -13, + gcvSTATUS_MORE_DATA = -14, + gcvSTATUS_TIMEOUT = -15, + gcvSTATUS_OUT_OF_RESOURCES = -16, + gcvSTATUS_INVALID_DATA = -17, + gcvSTATUS_INVALID_MIPMAP = -18, + gcvSTATUS_NOT_FOUND = -19, + gcvSTATUS_NOT_ALIGNED = -20, + gcvSTATUS_INVALID_REQUEST = -21, + gcvSTATUS_GPU_NOT_RESPONDING = -22, + gcvSTATUS_TIMER_OVERFLOW = -23, + gcvSTATUS_VERSION_MISMATCH = -24, + gcvSTATUS_LOCKED = -25, + gcvSTATUS_INTERRUPTED = -26, + gcvSTATUS_DEVICE = -27, + gcvSTATUS_NOT_MULTI_PIPE_ALIGNED = -28, + + /* Linker errors. */ + gcvSTATUS_GLOBAL_TYPE_MISMATCH = -1000, + gcvSTATUS_TOO_MANY_ATTRIBUTES = -1001, + gcvSTATUS_TOO_MANY_UNIFORMS = -1002, + gcvSTATUS_TOO_MANY_VARYINGS = -1003, + gcvSTATUS_UNDECLARED_VARYING = -1004, + gcvSTATUS_VARYING_TYPE_MISMATCH = -1005, + gcvSTATUS_MISSING_MAIN = -1006, + gcvSTATUS_NAME_MISMATCH = -1007, + gcvSTATUS_INVALID_INDEX = -1008, + gcvSTATUS_UNIFORM_MISMATCH = -1009, + gcvSTATUS_UNSAT_LIB_SYMBOL = -1010, + gcvSTATUS_TOO_MANY_SHADERS = -1011, + gcvSTATUS_LINK_INVALID_SHADERS = -1012, + gcvSTATUS_CS_NO_WORKGROUP_SIZE = -1013, + gcvSTATUS_LINK_LIB_ERROR = -1014, + gcvSTATUS_SHADER_VERSION_MISMATCH = -1015, + gcvSTATUS_TOO_MANY_INSTRUCTION = -1016, + gcvSTATUS_SSBO_MISMATCH = -1017, + gcvSTATUS_TOO_MANY_OUTPUT = -1018, + gcvSTATUS_TOO_MANY_INPUT = -1019, + gcvSTATUS_NOT_SUPPORT_CL = -1020, + gcvSTATUS_NOT_SUPPORT_INTEGER = -1021, + gcvSTATUS_UNIFORM_TYPE_MISMATCH = -1022, + gcvSTATUS_TOO_MANY_SAMPLER = -1023, + + /* Compiler errors. */ + gcvSTATUS_COMPILER_FE_PREPROCESSOR_ERROR = -2000, + gcvSTATUS_COMPILER_FE_PARSER_ERROR = -2001, + + /* Recompilation Errors */ + gcvSTATUS_RECOMPILER_CONVERT_UNIMPLEMENTED = -3000, +} +gceSTATUS; + +/******************************************************************************\ +********************************* Status Macros ******************************** +\******************************************************************************/ + +#define gcmIS_ERROR(status) (status < 0) +#define gcmNO_ERROR(status) (status >= 0) +#define gcmIS_SUCCESS(status) (status == gcvSTATUS_OK) + +/******************************************************************************\ +********************************* Field Macros ********************************* +\******************************************************************************/ + +#define __gcmSTART(reg_field) \ + (0 ? reg_field) + +#define __gcmEND(reg_field) \ + (1 ? reg_field) + +#define __gcmGETSIZE(reg_field) \ + (__gcmEND(reg_field) - __gcmSTART(reg_field) + 1) + +#define __gcmALIGN(data, reg_field) \ + (((gctUINT32) (data)) << __gcmSTART(reg_field)) + +#define __gcmMASK(reg_field) \ + ((gctUINT32) ((__gcmGETSIZE(reg_field) == 32) \ + ? ~0 \ + : (~(~0 << __gcmGETSIZE(reg_field))))) + +/******************************************************************************* +** +** gcmFIELDMASK +** +** Get aligned field mask. +** +** ARGUMENTS: +** +** reg Name of register. +** field Name of field within register. +*/ +#define gcmFIELDMASK(reg, field) \ +( \ + __gcmALIGN(__gcmMASK(reg##_##field), reg##_##field) \ +) + +/******************************************************************************* +** +** gcmGETFIELD +** +** Extract the value of a field from specified data. +** +** ARGUMENTS: +** +** data Data value. +** reg Name of register. +** field Name of field within register. +*/ +#define gcmGETFIELD(data, reg, field) \ +( \ + ((((gctUINT32) (data)) >> __gcmSTART(reg##_##field)) \ + & __gcmMASK(reg##_##field)) \ +) + +/******************************************************************************* +** +** gcmSETFIELD +** +** Set the value of a field within specified data. +** +** ARGUMENTS: +** +** data Data value. +** reg Name of register. +** field Name of field within register. +** value Value for field. +*/ +#define gcmSETFIELD(data, reg, field, value) \ +( \ + (((gctUINT32) (data)) \ + & ~__gcmALIGN(__gcmMASK(reg##_##field), reg##_##field)) \ + | __gcmALIGN((gctUINT32) (value) \ + & __gcmMASK(reg##_##field), reg##_##field) \ +) + +/******************************************************************************* +** +** gcmSETFIELDVALUE +** +** Set the value of a field within specified data with a +** predefined value. +** +** ARGUMENTS: +** +** data Data value. +** reg Name of register. +** field Name of field within register. +** value Name of the value within the field. +*/ +#define gcmSETFIELDVALUE(data, reg, field, value) \ +( \ + (((gctUINT32) (data)) \ + & ~__gcmALIGN(__gcmMASK(reg##_##field), reg##_##field)) \ + | __gcmALIGN(reg##_##field##_##value \ + & __gcmMASK(reg##_##field), reg##_##field) \ +) + +/******************************************************************************* +** +** gcmGETMASKEDFIELDMASK +** +** Determine field mask of a masked field. +** +** ARGUMENTS: +** +** reg Name of register. +** field Name of field within register. +*/ +#define gcmGETMASKEDFIELDMASK(reg, field) \ +( \ + gcmSETFIELD(0, reg, field, ~0) | \ + gcmSETFIELD(0, reg, MASK_ ## field, ~0) \ +) + +/******************************************************************************* +** +** gcmSETMASKEDFIELD +** +** Set the value of a masked field with specified data. +** +** ARGUMENTS: +** +** reg Name of register. +** field Name of field within register. +** value Value for field. +*/ +#define gcmSETMASKEDFIELD(reg, field, value) \ +( \ + gcmSETFIELD (~0, reg, field, value) & \ + gcmSETFIELDVALUE(~0, reg, MASK_ ## field, ENABLED) \ +) + +/******************************************************************************* +** +** gcmSETMASKEDFIELDVALUE +** +** Set the value of a masked field with specified data. +** +** ARGUMENTS: +** +** reg Name of register. +** field Name of field within register. +** value Value for field. +*/ +#define gcmSETMASKEDFIELDVALUE(reg, field, value) \ +( \ + gcmSETFIELDVALUE(~0, reg, field, value) & \ + gcmSETFIELDVALUE(~0, reg, MASK_ ## field, ENABLED) \ +) + +/******************************************************************************* +** +** gcmVERIFYFIELDVALUE +** +** Verify if the value of a field within specified data equals a +** predefined value. +** +** ARGUMENTS: +** +** data Data value. +** reg Name of register. +** field Name of field within register. +** value Name of the value within the field. +*/ +#define gcmVERIFYFIELDVALUE(data, reg, field, value) \ +( \ + (((gctUINT32) (data)) >> __gcmSTART(reg##_##field) & \ + __gcmMASK(reg##_##field)) \ + == \ + (reg##_##field##_##value & __gcmMASK(reg##_##field)) \ +) + +/******************************************************************************* +** Bit field macros. +*/ + +#define __gcmSTARTBIT(Field) \ + ( 1 ? Field ) + +#define __gcmBITSIZE(Field) \ + ( 0 ? Field ) + +#define __gcmBITMASK(Field) \ +( \ + (1 << __gcmBITSIZE(Field)) - 1 \ +) + +#define gcmGETBITS(Value, Type, Field) \ +( \ + ( ((Type) (Value)) >> __gcmSTARTBIT(Field) ) \ + & \ + __gcmBITMASK(Field) \ +) + +#define gcmSETBITS(Value, Type, Field, NewValue) \ +( \ + ( ((Type) (Value)) \ + & ~(__gcmBITMASK(Field) << __gcmSTARTBIT(Field)) \ + ) \ + | \ + ( ( ((Type) (NewValue)) \ + & __gcmBITMASK(Field) \ + ) << __gcmSTARTBIT(Field) \ + ) \ +) + +/******************************************************************************* +** +** gcmISINREGRANGE +** +** Verify whether the specified address is in the register range. +** +** ARGUMENTS: +** +** Address Address to be verified. +** Name Name of a register. +*/ + +#define gcmISINREGRANGE(Address, Name) \ +( \ + ((Address & (~0U << Name ## _LSB)) == (Name ## _Address >> 2)) \ +) + +/******************************************************************************\ +******************************** Ceiling Macro ******************************** +\******************************************************************************/ +#define gcmCEIL(x) ((x - (gctUINT32)x) == 0 ? (gctUINT32)x : (gctUINT32)x + 1) + +/******************************************************************************\ +******************************** Min/Max Macros ******************************** +\******************************************************************************/ + +#define gcmMIN(x, y) (((x) <= (y)) ? (x) : (y)) +#define gcmMAX(x, y) (((x) >= (y)) ? (x) : (y)) +#define gcmCLAMP(x, min, max) (((x) < (min)) ? (min) : \ + ((x) > (max)) ? (max) : (x)) +#define gcmABS(x) (((x) < 0) ? -(x) : (x)) +#define gcmNEG(x) (((x) < 0) ? (x) : -(x)) + +/******************************************************************************\ +******************************** Bit Macro ******************************** +\******************************************************************************/ +#define gcmBITSET(x, y) ((x) & (y)) +/******************************************************************************* +** +** gcmPTR2INT +** +** Convert a pointer to an integer value. +** +** ARGUMENTS: +** +** p Pointer value. +*/ +#define gcmPTR2INT(p) \ +( \ + (gctUINTPTR_T) (p) \ +) + +#define gcmPTR2INT32(p) \ +( \ + (gctUINT32)(gctUINTPTR_T) (p) \ +) + +/******************************************************************************* +** +** gcmINT2PTR +** +** Convert an integer value into a pointer. +** +** ARGUMENTS: +** +** v Integer value. +*/ + +#define gcmINT2PTR(i) \ +( \ + (gctPOINTER) (gctUINTPTR_T)(i) \ +) + +/******************************************************************************* +** +** gcmOFFSETOF +** +** Compute the byte offset of a field inside a structure. +** +** ARGUMENTS: +** +** s Structure name. +** field Field name. +*/ +#define gcmOFFSETOF(s, field) \ +( \ + gcmPTR2INT32(& (((struct s *) 0)->field)) \ +) + +/******************************************************************************* +** +** gcmSWAB32 +** +** Return a value with all bytes in the 32 bit argument swapped. +*/ +#define gcmSWAB32(x) ((gctUINT32)( \ + (((gctUINT32)(x) & (gctUINT32)0x000000FFUL) << 24) | \ + (((gctUINT32)(x) & (gctUINT32)0x0000FF00UL) << 8) | \ + (((gctUINT32)(x) & (gctUINT32)0x00FF0000UL) >> 8) | \ + (((gctUINT32)(x) & (gctUINT32)0xFF000000UL) >> 24))) + +/******************************************************************************* +***** Database ****************************************************************/ + +typedef struct _gcsDATABASE_COUNTERS +{ + /* Number of currently allocated bytes. */ + gctUINT64 bytes; + + /* Maximum number of bytes allocated (memory footprint). */ + gctUINT64 maxBytes; + + /* Total number of bytes allocated. */ + gctUINT64 totalBytes; +} +gcsDATABASE_COUNTERS; + +typedef struct _gcuDATABASE_INFO +{ + /* Counters. */ + gcsDATABASE_COUNTERS counters; + + /* Time value. */ + gctUINT64 time; +} +gcuDATABASE_INFO; + +/******************************************************************************* +***** Frame database **********************************************************/ + +/* gcsHAL_FRAME_INFO */ +typedef struct _gcsHAL_FRAME_INFO +{ + /* Current timer tick. */ + OUT gctUINT64 ticks; + + /* Bandwidth counters. */ + OUT gctUINT readBytes8[8]; + OUT gctUINT writeBytes8[8]; + + /* Counters. */ + OUT gctUINT cycles[8]; + OUT gctUINT idleCycles[8]; + OUT gctUINT mcCycles[8]; + OUT gctUINT readRequests[8]; + OUT gctUINT writeRequests[8]; + + /* 3D counters. */ + OUT gctUINT vertexCount; + OUT gctUINT primitiveCount; + OUT gctUINT rejectedPrimitives; + OUT gctUINT culledPrimitives; + OUT gctUINT clippedPrimitives; + OUT gctUINT outPrimitives; + OUT gctUINT inPrimitives; + OUT gctUINT culledQuadCount; + OUT gctUINT totalQuadCount; + OUT gctUINT quadCount; + OUT gctUINT totalPixelCount; + + /* PE counters. */ + OUT gctUINT colorKilled[8]; + OUT gctUINT colorDrawn[8]; + OUT gctUINT depthKilled[8]; + OUT gctUINT depthDrawn[8]; + + /* Shader counters. */ + OUT gctUINT shaderCycles; + OUT gctUINT vsInstructionCount; + OUT gctUINT vsTextureCount; + OUT gctUINT psInstructionCount; + OUT gctUINT psTextureCount; + + /* Texture counters. */ + OUT gctUINT bilinearRequests; + OUT gctUINT trilinearRequests; + OUT gctUINT txBytes8; + OUT gctUINT txHitCount; + OUT gctUINT txMissCount; +} +gcsHAL_FRAME_INFO; + +#if gcdLINK_QUEUE_SIZE +typedef struct _gckLINKDATA * gckLINKDATA; +struct _gckLINKDATA +{ + gctUINT32 start; + gctUINT32 end; + gctUINT32 pid; +}; + +typedef struct _gckLINKQUEUE * gckLINKQUEUE; +struct _gckLINKQUEUE +{ + struct _gckLINKDATA data[gcdLINK_QUEUE_SIZE]; + gctUINT32 rear; + gctUINT32 front; + gctUINT32 count; +}; +#endif + +#define gcdENTRY_QUEUE_SIZE 256 +typedef struct _gckENTRYDATA * gckENTRYDATA; +struct _gckENTRYDATA +{ + gctUINT32 physical; + gctUINT32 bytes; +}; + +typedef struct _gckENTRYQUEUE * gckENTRYQUEUE; +struct _gckENTRYQUEUE +{ + struct _gckENTRYDATA data[gcdENTRY_QUEUE_SIZE]; + gctUINT32 rear; + gctUINT32 front; + gctUINT32 count; +}; + +typedef enum _gceTRACEMODE +{ + gcvTRACEMODE_NONE = 0, + gcvTRACEMODE_FULL = 1, + gcvTRACEMODE_LOGGER = 2, + gcvTRACEMODE_PRE = 3, + gcvTRACEMODE_POST = 4, + gcvTRACEMODE_SYSTRACE = 5, + +} gceTRACEMODE; + + +#ifdef __cplusplus +} +#endif + +#endif /* __gc_hal_types_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_version.h b/drivers/gpu/galcore/inc/gc_hal_version.h new file mode 100644 index 00000000000000..4f95350184f78c --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_version.h @@ -0,0 +1,39 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_version_h_ +#define __gc_hal_version_h_ + +#define gcvVERSION_MAJOR 5 + +#define gcvVERSION_MINOR 0 + +#define gcvVERSION_PATCH 11 + +#define gcvVERSION_BUILD 25762 + +#define gcvVERSION_STRING "5.0.11.p4.25762" + +#define gcvVERSION_DATE __DATE__ + +#define gcvVERSION_TIME __TIME__ + +#endif /* __gc_hal_version_h_ */ diff --git a/drivers/gpu/galcore/inc/gc_hal_vg.h b/drivers/gpu/galcore/inc/gc_hal_vg.h new file mode 100644 index 00000000000000..09e8b9647423d7 --- /dev/null +++ b/drivers/gpu/galcore/inc/gc_hal_vg.h @@ -0,0 +1,896 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#ifndef __gc_hal_vg_h_ +#define __gc_hal_vg_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "gc_hal_rename.h" +#include "gc_hal_types.h" +#include "gc_hal_enum.h" +#include "gc_hal_base.h" + +#if gcdENABLE_VG + +/* Thread routine type. */ +#if defined(LINUX) + typedef gctINT gctTHREADFUNCRESULT; + typedef gctPOINTER gctTHREADFUNCPARAMETER; +# define gctTHREADFUNCTYPE +#elif defined(WIN32) + typedef gctUINT gctTHREADFUNCRESULT; + typedef gctPOINTER gctTHREADFUNCPARAMETER; +# define gctTHREADFUNCTYPE __stdcall +#elif defined(__QNXNTO__) + typedef void * gctTHREADFUNCRESULT; + typedef gctPOINTER gctTHREADFUNCPARAMETER; +# define gctTHREADFUNCTYPE +#endif + +typedef gctTHREADFUNCRESULT (gctTHREADFUNCTYPE * gctTHREADFUNC) ( + gctTHREADFUNCPARAMETER ThreadParameter + ); + + +#if defined(gcvDEBUG) +# undef gcvDEBUG +#endif + +#define gcdFORCE_DEBUG 0 +#define gcdFORCE_MESSAGES 0 + + +#if DBG || defined(DEBUG) || defined(_DEBUG) || gcdFORCE_DEBUG +# define gcvDEBUG 1 +#else +# define gcvDEBUG 0 +#endif + +#define _gcmERROR_RETURN(prefix, func) \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_ERROR, \ + #prefix "ERR_RETURN: status=%d(%s) @ %s(%d)", \ + status, gcoOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + return status; \ + } \ + do { } while (gcvFALSE) + +#define gcmERROR_RETURN(func) _gcmERROR_RETURN(gcm, func) + +#define gcmLOG_LOCATION() + +#define gcmkIS_ERROR(status) (status < 0) + +#define gcmALIGNDOWN(n, align) \ +( \ + (n) & ~((align) - 1) \ +) + +#define gcmIS_VALID_INDEX(Index, Array) \ + (((gctUINT) (Index)) < gcmCOUNTOF(Array)) + + +#define gcmIS_NAN(x) \ +( \ + ((* (gctUINT32_PTR) &(x)) & 0x7FFFFFFF) == 0x7FFFFFFF \ +) + +#define gcmLERP(v1, v2, w) \ + ((v1) * (w) + (v2) * (1.0f - (w))) + +#define gcmINTERSECT(Start1, Start2, Length) \ + (gcmABS((Start1) - (Start2)) < (Length)) + +/******************************************************************************* +** +** gcmERR_GOTO +** +** Prints a message and terminates the current loop on error. +** +** ASSUMPTIONS: +** +** 'status' variable of gceSTATUS type must be defined. +** +** ARGUMENTS: +** +** Function +** Function to evaluate. +*/ + +#define gcmERR_GOTO(Function) \ + status = Function; \ + if (gcmIS_ERROR(status)) \ + { \ + gcmTRACE( \ + gcvLEVEL_ERROR, \ + "gcmERR_GOTO: status=%d @ line=%d in function %s.\n", \ + status, __LINE__, __FUNCTION__ \ + ); \ + goto ErrorHandler; \ + } + +#if gcvDEBUG || gcdFORCE_MESSAGES +# define gcmVERIFY_BOOLEAN(Expression) \ + gcmASSERT( \ + ( (Expression) == gcvFALSE ) || \ + ( (Expression) == gcvTRUE ) \ + ) +#else +# define gcmVERIFY_BOOLEAN(Expression) +#endif + +/******************************************************************************* +** +** gcmVERIFYFIELDFIT +** +** Verify whether the value fits in the field. +** +** ARGUMENTS: +** +** data Data value. +** reg Name of register. +** field Name of field within register. +** value Value for field. +*/ +#define gcmVERIFYFIELDFIT(reg, field, value) \ + gcmASSERT( \ + (value) <= gcmFIELDMAX(reg, field) \ + ) +/******************************************************************************* +** +** gcmFIELDMAX +** +** Get field maximum value. +** +** ARGUMENTS: +** +** reg Name of register. +** field Name of field within register. +*/ +#define gcmFIELDMAX(reg, field) \ +( \ + (gctUINT32) \ + ( \ + (__gcmGETSIZE(reg##_##field) == 32) \ + ? ~0 \ + : (~(~0 << __gcmGETSIZE(reg##_##field))) \ + ) \ +) + + +/* ANSI C does not have the 'f' functions, define replacements here. */ +#define gcmSINF(x) ((gctFLOAT) sin(x)) +#define gcmCOSF(x) ((gctFLOAT) cos(x)) +#define gcmASINF(x) ((gctFLOAT) asin(x)) +#define gcmACOSF(x) ((gctFLOAT) acos(x)) +#define gcmSQRTF(x) ((gctFLOAT) sqrt(x)) +#define gcmFABSF(x) ((gctFLOAT) fabs(x)) +#define gcmFMODF(x, y) ((gctFLOAT) fmod((x), (y))) +#define gcmCEILF(x) ((gctFLOAT) ceil(x)) +#define gcmFLOORF(x) ((gctFLOAT) floor(x)) + + + +/* Fixed point constants. */ +#define gcvZERO_X ((gctFIXED_POINT) 0x00000000) +#define gcvHALF_X ((gctFIXED_POINT) 0x00008000) +#define gcvONE_X ((gctFIXED_POINT) 0x00010000) +#define gcvNEGONE_X ((gctFIXED_POINT) 0xFFFF0000) +#define gcvTWO_X ((gctFIXED_POINT) 0x00020000) + +/* Integer constants. */ +#define gcvMAX_POS_INT ((gctINT) 0x7FFFFFFF) +#define gcvMAX_NEG_INT ((gctINT) 0x80000000) + +/* Float constants. */ +#define gcvMAX_POS_FLOAT ((gctFLOAT) 3.4028235e+038) +#define gcvMAX_NEG_FLOAT ((gctFLOAT) -3.4028235e+038) + +/******************************************************************************\ +***************************** Miscellaneous Macro ****************************** +\******************************************************************************/ + +#define gcmKB2BYTES(Kilobyte) \ +( \ + (Kilobyte) << 10 \ +) + +#define gcmMB2BYTES(Megabyte) \ +( \ + (Megabyte) << 20 \ +) + +#define gcmMAT(Matrix, Row, Column) \ +( \ + (Matrix) [(Row) * 3 + (Column)] \ +) + +#define gcmMAKE2CHAR(Char1, Char2) \ +( \ + ((gctUINT16) (gctUINT8) (Char1) << 0) | \ + ((gctUINT16) (gctUINT8) (Char2) << 8) \ +) + +#define gcmMAKE4CHAR(Char1, Char2, Char3, Char4) \ +( \ + ((gctUINT32)(gctUINT8) (Char1) << 0) | \ + ((gctUINT32)(gctUINT8) (Char2) << 8) | \ + ((gctUINT32)(gctUINT8) (Char3) << 16) | \ + ((gctUINT32)(gctUINT8) (Char4) << 24) \ +) + +/* some platforms need to fix the physical address for HW to access*/ +#define gcmFIXADDRESS(address) \ +(\ + (address)\ +) + +#define gcmkFIXADDRESS(address) \ +(\ + (address)\ +) + +/******************************************************************************\ +****************************** Kernel Debug Macro ****************************** +\******************************************************************************/ + +/* Set signal to signaled state for specified process. */ +gceSTATUS +gckOS_SetSignal( + IN gckOS Os, + IN gctHANDLE Process, + IN gctSIGNAL Signal + ); + +/* Return the kernel logical pointer for the given physical one. */ +gceSTATUS +gckOS_GetKernelLogical( + IN gckOS Os, + IN gctUINT32 Address, + OUT gctPOINTER * KernelPointer + ); + +/* Return the kernel logical pointer for the given physical one. */ +gceSTATUS +gckOS_GetKernelLogicalEx( + IN gckOS Os, + IN gceCORE Core, + IN gctUINT32 Address, + OUT gctPOINTER * KernelPointer + ); + +/*----------------------------------------------------------------------------*/ +/*----------------------------- Semaphore Object -----------------------------*/ + +/* Increment the value of a semaphore. */ +gceSTATUS +gckOS_IncrementSemaphore( + IN gckOS Os, + IN gctSEMAPHORE Semaphore + ); + +/* Decrement the value of a semaphore (waiting might occur). */ +gceSTATUS +gckOS_DecrementSemaphore( + IN gckOS Os, + IN gctSEMAPHORE Semaphore + ); + + +/*----------------------------------------------------------------------------*/ +/*------------------------------- Thread Object ------------------------------*/ + +/* Start a thread. */ +gceSTATUS +gckOS_StartThread( + IN gckOS Os, + IN gctTHREADFUNC ThreadFunction, + IN gctPOINTER ThreadParameter, + OUT gctTHREAD * Thread + ); + +/* Stop a thread. */ +gceSTATUS +gckOS_StopThread( + IN gckOS Os, + IN gctTHREAD Thread + ); + +/* Verify whether the thread is still running. */ +gceSTATUS +gckOS_VerifyThread( + IN gckOS Os, + IN gctTHREAD Thread + ); + + +/* Construct a new gckVGKERNEL object. */ +gceSTATUS +gckVGKERNEL_Construct( + IN gckOS Os, + IN gctPOINTER Context, + IN gckKERNEL inKernel, + OUT gckVGKERNEL * Kernel + ); + +/* Destroy an gckVGKERNEL object. */ +gceSTATUS +gckVGKERNEL_Destroy( + IN gckVGKERNEL Kernel + ); + +/* Allocate linear video memory. */ +gceSTATUS +gckVGKERNEL_AllocateLinearMemory( + IN gckKERNEL Kernel, + IN OUT gcePOOL * Pool, + IN gctSIZE_T Bytes, + IN gctUINT32 Alignment, + IN gceSURF_TYPE Type, + OUT gcuVIDMEM_NODE_PTR * Node + ); + +/* Unmap memory. */ +gceSTATUS +gckKERNEL_UnmapMemory( + IN gckKERNEL Kernel, + IN gctPHYS_ADDR Physical, + IN gctSIZE_T Bytes, + IN gctPOINTER Logical + ); + +/* Dispatch a user-level command. */ +gceSTATUS +gckVGKERNEL_Dispatch( + IN gckKERNEL Kernel, + IN gctBOOL FromUser, + IN OUT struct _gcsHAL_INTERFACE * Interface + ); + +/* Query command buffer requirements. */ +gceSTATUS +gckKERNEL_QueryCommandBuffer( + IN gckKERNEL Kernel, + OUT gcsCOMMAND_BUFFER_INFO_PTR Information + ); + +/******************************************************************************\ +******************************* gckVGHARDWARE Object ****************************** +\******************************************************************************/ + +/* Construct a new gckVGHARDWARE object. */ +gceSTATUS +gckVGHARDWARE_Construct( + IN gckOS Os, + OUT gckVGHARDWARE * Hardware + ); + +/* Destroy an gckVGHARDWARE object. */ +gceSTATUS +gckVGHARDWARE_Destroy( + IN gckVGHARDWARE Hardware + ); + +/* Query system memory requirements. */ +gceSTATUS +gckVGHARDWARE_QuerySystemMemory( + IN gckVGHARDWARE Hardware, + OUT gctSIZE_T * SystemSize, + OUT gctUINT32 * SystemBaseAddress + ); + +/* Build virtual address. */ +gceSTATUS +gckVGHARDWARE_BuildVirtualAddress( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Index, + IN gctUINT32 Offset, + OUT gctUINT32 * Address + ); + +/* Kickstart the command processor. */ +gceSTATUS +gckVGHARDWARE_Execute( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Address, + IN gctUINT32 Count + ); + +/* Query the available memory. */ +gceSTATUS +gckVGHARDWARE_QueryMemory( + IN gckVGHARDWARE Hardware, + OUT gctSIZE_T * InternalSize, + OUT gctUINT32 * InternalBaseAddress, + OUT gctUINT32 * InternalAlignment, + OUT gctSIZE_T * ExternalSize, + OUT gctUINT32 * ExternalBaseAddress, + OUT gctUINT32 * ExternalAlignment, + OUT gctUINT32 * HorizontalTileSize, + OUT gctUINT32 * VerticalTileSize + ); + +/* Query the identity of the hardware. */ +gceSTATUS +gckVGHARDWARE_QueryChipIdentity( + IN gckVGHARDWARE Hardware, + OUT gceCHIPMODEL* ChipModel, + OUT gctUINT32* ChipRevision, + OUT gctUINT32* ChipFeatures, + OUT gctUINT32* ChipMinorFeatures, + OUT gctUINT32* ChipMinorFeatures1 + ); + +/* Convert an API format. */ +gceSTATUS +gckVGHARDWARE_ConvertFormat( + IN gckVGHARDWARE Hardware, + IN gceSURF_FORMAT Format, + OUT gctUINT32 * BitsPerPixel, + OUT gctUINT32 * BytesPerTile + ); + +/* Split a harwdare specific address into API stuff. */ +gceSTATUS +gckVGHARDWARE_SplitMemory( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Address, + OUT gcePOOL * Pool, + OUT gctUINT32 * Offset + ); + +/* Align size to tile boundary. */ +gceSTATUS +gckVGHARDWARE_AlignToTile( + IN gckVGHARDWARE Hardware, + IN gceSURF_TYPE Type, + IN OUT gctUINT32_PTR Width, + IN OUT gctUINT32_PTR Height + ); + +/* Convert logical address to hardware specific address. */ +gceSTATUS +gckVGHARDWARE_ConvertLogical( + IN gckVGHARDWARE Hardware, + IN gctPOINTER Logical, + IN gctBOOL InUserSpace, + OUT gctUINT32 * Address + ); + +/* Program MMU. */ +gceSTATUS +gckVGHARDWARE_SetMMU( + IN gckVGHARDWARE Hardware, + IN gctPOINTER Logical + ); + +/* Flush the MMU. */ +gceSTATUS +gckVGHARDWARE_FlushMMU( + IN gckVGHARDWARE Hardware + ); + +/* Get idle register. */ +gceSTATUS +gckVGHARDWARE_GetIdle( + IN gckVGHARDWARE Hardware, + OUT gctUINT32 * Data + ); + +/* Flush the caches. */ +gceSTATUS +gckVGHARDWARE_Flush( + IN gckVGHARDWARE Hardware, + IN gceKERNEL_FLUSH Flush, + IN gctPOINTER Logical, + IN OUT gctSIZE_T * Bytes + ); + +/* Enable/disable fast clear. */ +gceSTATUS +gckVGHARDWARE_SetFastClear( + IN gckVGHARDWARE Hardware, + IN gctINT Enable + ); + +gceSTATUS +gckVGHARDWARE_ReadInterrupt( + IN gckVGHARDWARE Hardware, + OUT gctUINT32_PTR IDs + ); + +/* Power management. */ +gceSTATUS +gckVGHARDWARE_SetPowerManagementState( + IN gckVGHARDWARE Hardware, + IN gceCHIPPOWERSTATE State + ); + +gceSTATUS +gckVGHARDWARE_QueryPowerManagementState( + IN gckVGHARDWARE Hardware, + OUT gceCHIPPOWERSTATE* State + ); + +gceSTATUS +gckVGHARDWARE_SetPowerManagement( + IN gckVGHARDWARE Hardware, + IN gctBOOL PowerManagement + ); + +gceSTATUS +gckVGHARDWARE_SetPowerOffTimeout( + IN gckVGHARDWARE Hardware, + IN gctUINT32 Timeout + ); + +gceSTATUS +gckVGHARDWARE_QueryPowerOffTimeout( + IN gckVGHARDWARE Hardware, + OUT gctUINT32* Timeout + ); + +gceSTATUS +gckVGHARDWARE_QueryIdle( + IN gckVGHARDWARE Hardware, + OUT gctBOOL_PTR IsIdle + ); +/******************************************************************************\ +*************************** Command Buffer Structures ************************** +\******************************************************************************/ + +/* Vacant command buffer marker. */ +#define gcvVACANT_BUFFER ((gcsCOMPLETION_SIGNAL_PTR) ((gctSIZE_T)1)) + +/* Command buffer header. */ +typedef struct _gcsCMDBUFFER * gcsCMDBUFFER_PTR; +typedef struct _gcsCMDBUFFER +{ + /* Pointer to the completion signal. */ + gcsCOMPLETION_SIGNAL_PTR completion; + + /* The user sets this to the node of the container buffer whitin which + this particular command buffer resides. The kernel sets this to the + node of the internally allocated buffer. */ + gcuVIDMEM_NODE_PTR node; + + /* Command buffer hardware address. */ + gctUINT32 address; + + /* The offset of the buffer from the beginning of the header. */ + gctUINT32 bufferOffset; + + /* Size of the area allocated for the data portion of this particular + command buffer (headers and tail reserves are excluded). */ + gctUINT32 size; + + /* Offset into the buffer [0..size]; reflects exactly how much data has + been put into the command buffer. */ + gctUINT offset; + + /* The number of command units in the buffer for the hardware to + execute. */ + gctUINT32 dataCount; + + /* MANAGED BY : user HAL (gcoBUFFER object). + USED BY : user HAL (gcoBUFFER object). + Points to the immediate next allocated command buffer. */ + gcsCMDBUFFER_PTR nextAllocated; + + /* MANAGED BY : user layers (HAL and drivers). + USED BY : kernel HAL (gcoBUFFER object). + Points to the next subbuffer if any. A family of subbuffers are chained + together and are meant to be executed inseparably as a unit. Meaning + that context switching cannot occur while a chain of subbuffers is being + executed. */ + gcsCMDBUFFER_PTR nextSubBuffer; +} +gcsCMDBUFFER; + +/* Command queue element. */ +typedef struct _gcsVGCMDQUEUE +{ + /* Pointer to the command buffer header. */ + gcsCMDBUFFER_PTR commandBuffer; + + /* Dynamic vs. static command buffer state. */ + gctBOOL dynamic; +} +gcsVGCMDQUEUE; + +/* Context map entry. */ +typedef struct _gcsVGCONTEXT_MAP +{ + /* State index. */ + gctUINT32 index; + + /* New state value. */ + gctUINT32 data; + + /* Points to the next entry in the mod list. */ + gcsVGCONTEXT_MAP_PTR next; +} +gcsVGCONTEXT_MAP; + +/* gcsVGCONTEXT structure that holds the current context. */ +typedef struct _gcsVGCONTEXT +{ + /* Context ID. */ + gctUINT64 id; + + /* State caching ebable flag. */ + gctBOOL stateCachingEnabled; + + /* Current pipe. */ + gctUINT32 currentPipe; + + /* State map/mod buffer. */ + gctUINT32 mapFirst; + gctUINT32 mapLast; + gcsVGCONTEXT_MAP_PTR mapContainer; + gcsVGCONTEXT_MAP_PTR mapPrev; + gcsVGCONTEXT_MAP_PTR mapCurr; + gcsVGCONTEXT_MAP_PTR firstPrevMap; + gcsVGCONTEXT_MAP_PTR firstCurrMap; + + /* Main context buffer. */ + gcsCMDBUFFER_PTR header; + gctUINT32_PTR buffer; + + /* Completion signal. */ + gctHANDLE process; + gctSIGNAL signal; + +#if defined(__QNXNTO__) + gctINT32 coid; + gctINT32 rcvid; +#endif +} +gcsVGCONTEXT; + +/* User space task header. */ +typedef struct _gcsTASK * gcsTASK_PTR; +typedef struct _gcsTASK +{ + /* Pointer to the next task for the same interrupt in user space. */ + gcsTASK_PTR next; + + /* Size of the task data that immediately follows the structure. */ + gctUINT size; + + /* Task data starts here. */ + /* ... */ +} +gcsTASK; + +/* User space task master table entry. */ +typedef struct _gcsTASK_MASTER_ENTRY * gcsTASK_MASTER_ENTRY_PTR; +typedef struct _gcsTASK_MASTER_ENTRY +{ + /* Pointers to the head and to the tail of the task chain. */ + gcsTASK_PTR head; + gcsTASK_PTR tail; +} +gcsTASK_MASTER_ENTRY; + +/* User space task master table entry. */ +typedef struct _gcsTASK_MASTER_TABLE +{ + /* Table with one entry per block. */ + gcsTASK_MASTER_ENTRY table[gcvBLOCK_COUNT]; + + /* The total number of tasks sckeduled. */ + gctUINT count; + + /* The total size of event data in bytes. */ + gctUINT size; + +#if defined(__QNXNTO__) + gctINT32 coid; + gctINT32 rcvid; +#endif +} +gcsTASK_MASTER_TABLE; + +/******************************************************************************\ +***************************** gckVGINTERRUPT Object ****************************** +\******************************************************************************/ + +typedef struct _gckVGINTERRUPT * gckVGINTERRUPT; + +typedef gceSTATUS (* gctINTERRUPT_HANDLER)( + IN gckVGKERNEL Kernel + ); + +gceSTATUS +gckVGINTERRUPT_Construct( + IN gckVGKERNEL Kernel, + OUT gckVGINTERRUPT * Interrupt + ); + +gceSTATUS +gckVGINTERRUPT_Destroy( + IN gckVGINTERRUPT Interrupt + ); + +gceSTATUS +gckVGINTERRUPT_Enable( + IN gckVGINTERRUPT Interrupt, + IN OUT gctINT32_PTR Id, + IN gctINTERRUPT_HANDLER Handler + ); + +gceSTATUS +gckVGINTERRUPT_Disable( + IN gckVGINTERRUPT Interrupt, + IN gctINT32 Id + ); + +#ifndef __QNXNTO__ + +gceSTATUS +gckVGINTERRUPT_Enque( + IN gckVGINTERRUPT Interrupt + ); + +#else + +gceSTATUS +gckVGINTERRUPT_Enque( + IN gckVGINTERRUPT Interrupt, + OUT gckOS *Os, + OUT gctSEMAPHORE *Semaphore + ); + +#endif + +gceSTATUS +gckVGINTERRUPT_DumpState( + IN gckVGINTERRUPT Interrupt + ); + + +/******************************************************************************\ +******************************* gckVGCOMMAND Object ******************************* +\******************************************************************************/ + +typedef struct _gckVGCOMMAND * gckVGCOMMAND; + +/* Construct a new gckVGCOMMAND object. */ +gceSTATUS +gckVGCOMMAND_Construct( + IN gckVGKERNEL Kernel, + IN gctUINT TaskGranularity, + IN gctUINT QueueSize, + OUT gckVGCOMMAND * Command + ); + +/* Destroy an gckVGCOMMAND object. */ +gceSTATUS +gckVGCOMMAND_Destroy( + IN gckVGCOMMAND Command + ); + +/* Query command buffer attributes. */ +gceSTATUS +gckVGCOMMAND_QueryCommandBuffer( + IN gckVGCOMMAND Command, + OUT gcsCOMMAND_BUFFER_INFO_PTR Information + ); + +/* Allocate a command queue. */ +gceSTATUS +gckVGCOMMAND_Allocate( + IN gckVGCOMMAND Command, + IN gctSIZE_T Size, + OUT gcsCMDBUFFER_PTR * CommandBuffer, + OUT gctPOINTER * Data + ); + +/* Release memory held by the command queue. */ +gceSTATUS +gckVGCOMMAND_Free( + IN gckVGCOMMAND Command, + IN gcsCMDBUFFER_PTR CommandBuffer + ); + +/* Schedule the command queue for execution. */ +gceSTATUS +gckVGCOMMAND_Execute( + IN gckVGCOMMAND Command, + IN gcsCMDBUFFER_PTR CommandBuffer + ); + +/* Commit a buffer to the command queue. */ +gceSTATUS +gckVGCOMMAND_Commit( + IN gckVGCOMMAND Command, + IN gcsVGCONTEXT_PTR Context, + IN gcsVGCMDQUEUE_PTR Queue, + IN gctUINT EntryCount, + IN gcsTASK_MASTER_TABLE_PTR TaskTable + ); + +/******************************************************************************\ +********************************* gckVGMMU Object ******************************** +\******************************************************************************/ + +typedef struct _gckVGMMU * gckVGMMU; + +/* Construct a new gckVGMMU object. */ +gceSTATUS +gckVGMMU_Construct( + IN gckVGKERNEL Kernel, + IN gctUINT32 MmuSize, + OUT gckVGMMU * Mmu + ); + +/* Destroy an gckVGMMU object. */ +gceSTATUS +gckVGMMU_Destroy( + IN gckVGMMU Mmu + ); + +/* Allocate pages inside the MMU. */ +gceSTATUS +gckVGMMU_AllocatePages( + IN gckVGMMU Mmu, + IN gctSIZE_T PageCount, + OUT gctPOINTER * PageTable, + OUT gctUINT32 * Address + ); + +/* Remove a page table from the MMU. */ +gceSTATUS +gckVGMMU_FreePages( + IN gckVGMMU Mmu, + IN gctPOINTER PageTable, + IN gctSIZE_T PageCount + ); + +/* Set the MMU page with info. */ +gceSTATUS +gckVGMMU_SetPage( + IN gckVGMMU Mmu, + IN gctUINT32 PageAddress, + IN gctUINT32 *PageEntry + ); + +/* Flush MMU */ +gceSTATUS +gckVGMMU_Flush( + IN gckVGMMU Mmu + ); + +#endif /* gcdENABLE_VG */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __gc_hal_h_ */ diff --git a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c new file mode 100644 index 00000000000000..06738c3aeb9718 --- /dev/null +++ b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c @@ -0,0 +1,683 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include "gc_hal_kernel_linux.h" +#include "gc_hal_kernel_platform.h" +#include "gc_hal_kernel_device.h" +#include "gc_hal_driver.h" +#include + +#if USE_PLATFORM_DRIVER +# include +#endif + +#include +#include +#include +#include + +#ifdef CONFIG_MXC_BUSFREQ +#include +#endif + + +#ifdef CONFIG_DEVICE_THERMAL +#include +#define REG_THERMAL_NOTIFIER(a) register_devfreq_cooling_notifier(a); +#define UNREG_THERMAL_NOTIFIER(a) unregister_devfreq_cooling_notifier(a); +#else +#define REG_THERMAL_NOTIFIER(a); +#define UNREG_THERMAL_NOTIFIER(a); +#endif + +#if gcdENABLE_FSCALE_VAL_ADJUST +static int initgpu3DMinClock = 1; +module_param(initgpu3DMinClock, int, 0644); +#endif + +struct platform_device *pdevice; + +#ifdef CONFIG_GPU_LOW_MEMORY_KILLER +# include +# include +# include +# include + +struct task_struct *lowmem_deathpending; + +static int +task_notify_func(struct notifier_block *self, unsigned long val, void *data); + +static struct notifier_block task_nb = { + .notifier_call = task_notify_func, +}; + +static int +task_notify_func(struct notifier_block *self, unsigned long val, void *data) +{ + struct task_struct *task = data; + + if (task == lowmem_deathpending) + lowmem_deathpending = NULL; + + return NOTIFY_OK; +} + +extern struct task_struct *lowmem_deathpending; +static unsigned long lowmem_deathpending_timeout; + +static int force_contiguous_lowmem_shrink(IN gckKERNEL Kernel) +{ + struct task_struct *p; + struct task_struct *selected = NULL; + int tasksize; + int ret = -1; + int min_adj = 0; + int selected_tasksize = 0; + int selected_oom_adj; + /* + * If we already have a death outstanding, then + * bail out right away; indicating to vmscan + * that we have nothing further to offer on + * this pass. + * + */ + if (lowmem_deathpending && + time_before_eq(jiffies, lowmem_deathpending_timeout)) + return 0; + selected_oom_adj = min_adj; + + rcu_read_lock(); + for_each_process(p) { + struct mm_struct *mm; + struct signal_struct *sig; + gcuDATABASE_INFO info; + int oom_adj; + + task_lock(p); + mm = p->mm; + sig = p->signal; + if (!mm || !sig) { + task_unlock(p); + continue; + } + oom_adj = sig->oom_score_adj; + if (oom_adj < min_adj) { + task_unlock(p); + continue; + } + + tasksize = 0; + task_unlock(p); + rcu_read_unlock(); + + if (gckKERNEL_QueryProcessDB(Kernel, p->pid, gcvFALSE, gcvDB_VIDEO_MEMORY, &info) == gcvSTATUS_OK){ + tasksize += info.counters.bytes / PAGE_SIZE; + } + if (gckKERNEL_QueryProcessDB(Kernel, p->pid, gcvFALSE, gcvDB_CONTIGUOUS, &info) == gcvSTATUS_OK){ + tasksize += info.counters.bytes / PAGE_SIZE; + } + + rcu_read_lock(); + + if (tasksize <= 0) + continue; + + gckOS_Print(" pid %d (%s), adj %d, size %d \n", p->pid, p->comm, oom_adj, tasksize); + + if (selected) { + if (oom_adj < selected_oom_adj) + continue; + if (oom_adj == selected_oom_adj && + tasksize <= selected_tasksize) + continue; + } + selected = p; + selected_tasksize = tasksize; + selected_oom_adj = oom_adj; + } + if (selected) { + gckOS_Print(" send sigkill to %d (%s), adj %d, size %d\n", + selected->pid, selected->comm, + selected_oom_adj, selected_tasksize); + lowmem_deathpending = selected; + lowmem_deathpending_timeout = jiffies + HZ; + force_sig(SIGKILL, selected); + ret = 0; + } + rcu_read_unlock(); + return ret; +} + + +gceSTATUS +_ShrinkMemory( + IN gckPLATFORM Platform + ) +{ + struct platform_device *pdev; + gckGALDEVICE galDevice; + gckKERNEL kernel; + + pdev = Platform->device; + + galDevice = platform_get_drvdata(pdev); + + kernel = galDevice->kernels[gcvCORE_MAJOR]; + + if (kernel != gcvNULL) + { + force_contiguous_lowmem_shrink(kernel); + } + else + { + gcmkPRINT("%s(%d) can't find kernel! ", __FUNCTION__, __LINE__); + } + + return gcvSTATUS_OK; +} +#endif + +#if gcdENABLE_FSCALE_VAL_ADJUST +#ifdef CONFIG_DEVICE_THERMAL +static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, + void *dummy) +{ + static gctUINT orgFscale, minFscale, maxFscale; + static gctBOOL bAlreadyTooHot = gcvFALSE; + gckHARDWARE hardware; + gckGALDEVICE galDevice; + + galDevice = platform_get_drvdata(pdevice); + if (!galDevice) + { + /* GPU is not ready, so it is meaningless to change GPU freq. */ + return NOTIFY_OK; + } + + if (!galDevice->kernels[gcvCORE_MAJOR]) + { + return NOTIFY_OK; + } + + hardware = galDevice->kernels[gcvCORE_MAJOR]->hardware; + + if (!hardware) + { + return NOTIFY_OK; + } + + if (event && !bAlreadyTooHot) { + gckHARDWARE_GetFscaleValue(hardware,&orgFscale,&minFscale, &maxFscale); + gckHARDWARE_SetFscaleValue(hardware, minFscale); + bAlreadyTooHot = gcvTRUE; + gckOS_Print("System is too hot. GPU3D will work at %d/64 clock.\n", minFscale); + } else if (!event && bAlreadyTooHot) { + gckHARDWARE_SetFscaleValue(hardware, orgFscale); + gckOS_Print("Hot alarm is canceled. GPU3D clock will return to %d/64\n", orgFscale); + bAlreadyTooHot = gcvFALSE; + } + return NOTIFY_OK; +} + +static struct notifier_block thermal_hot_pm_notifier = { + .notifier_call = thermal_hot_pm_notify, + }; +#endif + +static ssize_t show_gpu3DMinClock(struct device_driver *dev, char *buf) +{ + gctUINT currentf,minf,maxf; + gckGALDEVICE galDevice; + + galDevice = platform_get_drvdata(pdevice); + if(galDevice->kernels[gcvCORE_MAJOR]) + { + gckHARDWARE_GetFscaleValue(galDevice->kernels[gcvCORE_MAJOR]->hardware, + ¤tf, &minf, &maxf); + } + snprintf(buf, PAGE_SIZE, "%d\n", minf); + return strlen(buf); +} + +static ssize_t update_gpu3DMinClock(struct device_driver *dev, const char *buf, size_t count) +{ + + gctINT fields; + gctUINT MinFscaleValue; + gckGALDEVICE galDevice; + + galDevice = platform_get_drvdata(pdevice); + if(galDevice->kernels[gcvCORE_MAJOR]) + { + fields = sscanf(buf, "%d", &MinFscaleValue); + if (fields < 1) + return -EINVAL; + + gckHARDWARE_SetMinFscaleValue(galDevice->kernels[gcvCORE_MAJOR]->hardware,MinFscaleValue); + } + + return count; +} + +static DRIVER_ATTR(gpu3DMinClock, S_IRUGO | S_IWUSR, show_gpu3DMinClock, update_gpu3DMinClock); +#endif + + + + +static const struct of_device_id mxs_gpu_dt_ids[] = { + { .compatible = "fsl,imx6q-gpu", }, + { .compatible = "vivante,gc", }, + {/* sentinel */} +}; +MODULE_DEVICE_TABLE(of, mxs_gpu_dt_ids); + + +struct contiguous_mem_pool { + struct dma_attrs attrs; + dma_addr_t phys; + void *virt; + size_t size; +}; + +struct imx_priv { + /* Clock management.*/ + struct clk *clk_3d_core; + struct clk *clk_3d_shader; + struct clk *clk_3d_axi; + struct clk *clk_2d_core; + struct clk *clk_2d_axi; + struct clk *clk_vg_axi; + + /*Run time pm*/ + struct device *pmdev; + struct contiguous_mem_pool *pool; + struct reset_control *rstc[gcdMAX_GPU_COUNT]; +}; + +static struct imx_priv imxPriv; + +gceSTATUS +gckPLATFORM_AdjustParam( + IN gckPLATFORM Platform, + OUT gcsMODULE_PARAMETERS *Args + ) +{ + struct resource* res; + struct platform_device* pdev = Platform->device; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phys_baseaddr"); + if (res) + Args->baseAddress = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq_3d"); + if (res) + Args->irqLine = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iobase_3d"); + if (res) + { + Args->registerMemBase = res->start; + Args->registerMemSize = res->end - res->start + 1; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq_2d"); + if (res) + Args->irqLine2D = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iobase_2d"); + if (res) + { + Args->registerMemBase2D = res->start; + Args->registerMemSize2D = res->end - res->start + 1; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq_vg"); + if (res) + Args->irqLineVG = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iobase_vg"); + if (res) + { + Args->registerMemBaseVG = res->start; + Args->registerMemSizeVG = res->end - res->start + 1; + } + +#if gcdENABLE_FSCALE_VAL_ADJUST + Args->gpu3DMinClock = initgpu3DMinClock; +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +_AllocPriv( + IN gckPLATFORM Platform + ) +{ + Platform->priv = &imxPriv; + +#ifdef CONFIG_GPU_LOW_MEMORY_KILLER + task_free_register(&task_nb); +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +_FreePriv( + IN gckPLATFORM Platform + ) +{ +#ifdef CONFIG_GPU_LOW_MEMORY_KILLER + task_free_unregister(&task_nb); +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +_GetPower( + IN gckPLATFORM Platform + ) +{ + struct device* pdev = &Platform->device->dev; + struct imx_priv *priv = Platform->priv; + struct reset_control *rstc; + +#ifdef CONFIG_PM + /*Init runtime pm for gpu*/ + pm_runtime_use_autosuspend(pdev); + pm_runtime_set_autosuspend_delay(pdev, 200); + pm_runtime_enable(pdev); + priv->pmdev = pdev; +#endif + + + rstc = devm_reset_control_get(pdev, "gpu3d"); + priv->rstc[gcvCORE_MAJOR] = IS_ERR(rstc) ? NULL : rstc; + rstc = devm_reset_control_get(pdev, "gpu2d"); + priv->rstc[gcvCORE_2D] = IS_ERR(rstc) ? NULL : rstc; + rstc = devm_reset_control_get(pdev, "gpuvg"); + priv->rstc[gcvCORE_VG] = IS_ERR(rstc) ? NULL : rstc; + + /*Initialize the clock structure*/ + priv->clk_3d_core = clk_get(pdev, "gpu3d_clk"); + if (!IS_ERR(priv->clk_3d_core)) { + priv->clk_3d_axi = clk_get(pdev, "gpu3d_axi_clk"); + priv->clk_3d_shader = clk_get(pdev, "gpu3d_shader_clk"); + if (IS_ERR(priv->clk_3d_shader)) { + clk_put(priv->clk_3d_core); + priv->clk_3d_core = NULL; + priv->clk_3d_shader = NULL; + gckOS_Print("galcore: clk_get gpu3d_shader_clk failed, disable 3d!\n"); + } + } else { + priv->clk_3d_core = NULL; + gckOS_Print("galcore: clk_get gpu3d_clk failed, disable 3d!\n"); + } + + priv->clk_2d_core = clk_get(pdev, "gpu2d_clk"); + if (IS_ERR(priv->clk_2d_core)) { + priv->clk_2d_core = NULL; + gckOS_Print("galcore: clk_get 2d core clock failed, disable 2d/vg!\n"); + } else { + priv->clk_2d_axi = clk_get(pdev, "gpu2d_axi_clk"); + if (IS_ERR(priv->clk_2d_axi)) { + priv->clk_2d_axi = NULL; + gckOS_Print("galcore: clk_get 2d axi clock failed, disable 2d\n"); + } + + priv->clk_vg_axi = clk_get(pdev, "openvg_axi_clk"); + if (IS_ERR(priv->clk_vg_axi)) { + priv->clk_vg_axi = NULL; + gckOS_Print("galcore: clk_get vg clock failed, disable vg!\n"); + } + } + + +#if gcdENABLE_FSCALE_VAL_ADJUST + pdevice = Platform->device; + REG_THERMAL_NOTIFIER(&thermal_hot_pm_notifier); + { + int ret = 0; + ret = driver_create_file(pdevice->dev.driver, &driver_attr_gpu3DMinClock); + if(ret) + dev_err(&pdevice->dev, "create gpu3DMinClock attr failed (%d)\n", ret); + } +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +_PutPower( + IN gckPLATFORM Platform + ) +{ + struct imx_priv *priv = Platform->priv; + + /*Disable clock*/ + if (priv->clk_3d_axi) { + clk_put(priv->clk_3d_axi); + priv->clk_3d_axi = NULL; + } + if (priv->clk_3d_core) { + clk_put(priv->clk_3d_core); + priv->clk_3d_core = NULL; + } + if (priv->clk_3d_shader) { + clk_put(priv->clk_3d_shader); + priv->clk_3d_shader = NULL; + } + if (priv->clk_2d_core) { + clk_put(priv->clk_2d_core); + priv->clk_2d_core = NULL; + } + if (priv->clk_2d_axi) { + clk_put(priv->clk_2d_axi); + priv->clk_2d_axi = NULL; + } + if (priv->clk_vg_axi) { + clk_put(priv->clk_vg_axi); + priv->clk_vg_axi = NULL; + } + +#ifdef CONFIG_PM + if(priv->pmdev) + pm_runtime_disable(priv->pmdev); +#endif + +#if gcdENABLE_FSCALE_VAL_ADJUST + UNREG_THERMAL_NOTIFIER(&thermal_hot_pm_notifier); + + driver_remove_file(pdevice->dev.driver, &driver_attr_gpu3DMinClock); +#endif + + return gcvSTATUS_OK; +} + +gceSTATUS +_SetPower( + IN gckPLATFORM Platform, + IN gceCORE GPU, + IN gctBOOL Enable + ) +{ + struct imx_priv* priv = Platform->priv; + + if (Enable) + { +#ifdef CONFIG_PM + pm_runtime_get_sync(priv->pmdev); +#endif + } + else + { +#ifdef CONFIG_PM + pm_runtime_put_sync(priv->pmdev); +#endif + } + + return gcvSTATUS_OK; +} + +gceSTATUS +_SetClock( + IN gckPLATFORM Platform, + IN gceCORE GPU, + IN gctBOOL Enable + ) +{ + struct imx_priv* priv = Platform->priv; + struct clk *clk_3dcore = priv->clk_3d_core; + struct clk *clk_3dshader = priv->clk_3d_shader; + struct clk *clk_3d_axi = priv->clk_3d_axi; + struct clk *clk_2dcore = priv->clk_2d_core; + struct clk *clk_2d_axi = priv->clk_2d_axi; + struct clk *clk_vg_axi = priv->clk_vg_axi; + + + if (Enable) { + switch (GPU) { + case gcvCORE_MAJOR: + clk_prepare(clk_3dcore); + clk_enable(clk_3dcore); + clk_prepare(clk_3dshader); + clk_enable(clk_3dshader); + clk_prepare(clk_3d_axi); + clk_enable(clk_3d_axi); + break; + case gcvCORE_2D: + clk_prepare(clk_2dcore); + clk_enable(clk_2dcore); + clk_prepare(clk_2d_axi); + clk_enable(clk_2d_axi); + break; + case gcvCORE_VG: + clk_prepare(clk_2dcore); + clk_enable(clk_2dcore); + clk_prepare(clk_vg_axi); + clk_enable(clk_vg_axi); + break; + default: + break; + } + } else { + switch (GPU) { + case gcvCORE_MAJOR: + clk_disable(clk_3dshader); + clk_unprepare(clk_3dshader); + clk_disable(clk_3dcore); + clk_unprepare(clk_3dcore); + clk_disable(clk_3d_axi); + clk_unprepare(clk_3d_axi); + break; + case gcvCORE_2D: + clk_disable(clk_2dcore); + clk_unprepare(clk_2dcore); + clk_disable(clk_2d_axi); + clk_unprepare(clk_2d_axi); + break; + case gcvCORE_VG: + clk_disable(clk_2dcore); + clk_unprepare(clk_2dcore); + clk_disable(clk_vg_axi); + clk_unprepare(clk_vg_axi); + break; + default: + break; + } + } + + return gcvSTATUS_OK; +} + +#if 0 +#ifdef CONFIG_PM +static int gpu_runtime_suspend(struct device *dev) +{ +#ifdef CONFIG_MXC_BUSFREQ + release_bus_freq(BUS_FREQ_HIGH); +#endif + return 0; +} + +static int gpu_runtime_resume(struct device *dev) +{ +#ifdef CONFIG_MXC_BUSFREQ + request_bus_freq(BUS_FREQ_HIGH); +#endif + return 0; +} + +static struct dev_pm_ops gpu_pm_ops; +#endif +#endif + +gceSTATUS +_AdjustDriver( + IN gckPLATFORM Platform + ) +{ + struct platform_driver * driver = Platform->driver; + driver->driver.of_match_table = mxs_gpu_dt_ids; + + return gcvSTATUS_OK; +} + +gceSTATUS +_Reset( + IN gckPLATFORM Platform, + gceCORE GPU + ) +{ + struct imx_priv* priv = Platform->priv; + struct reset_control *rstc = priv->rstc[GPU]; + if (rstc) + reset_control_reset(rstc); + return gcvSTATUS_OK; +} + +gcsPLATFORM_OPERATIONS platformOperations = { + .adjustParam = gckPLATFORM_AdjustParam, + .allocPriv = _AllocPriv, + .freePriv = _FreePriv, + .getPower = _GetPower, + .putPower = _PutPower, + .setPower = _SetPower, + .setClock = _SetClock, + .adjustDriver = _AdjustDriver, + .reset = _Reset, +#ifdef CONFIG_GPU_LOW_MEMORY_KILLER + .shrinkMemory = _ShrinkMemory, +#endif +}; + +void +gckPLATFORM_QueryOperations( + IN gcsPLATFORM_OPERATIONS ** Operations + ) +{ + *Operations = &platformOperations; +} + diff --git a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.config b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.config new file mode 100644 index 00000000000000..6575148b596e19 --- /dev/null +++ b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.config @@ -0,0 +1,15 @@ +EXTRA_CFLAGS += -DgcdDEFAULT_CONTIGUOUS_SIZE=134217728 + +ifneq ($(CONFIG_ANDROID),) +# build for android +EXTRA_CFLAGS += -DgcdANDROID_NATIVE_FENCE_SYNC=3 + +ifeq ($(CONFIG_SYNC),) +$(warn CONFIG_SYNC is not set in kernel config) +$(warn Android native fence sync needs CONFIG_SYNC) +endif +endif + +EXTRA_CFLAGS += -DLINUX_CMA_FSL=1 +ALLOCATOR_ARRAY_H_LOCATION := $(OS_KERNEL_DIR)/allocator/freescale +CUSTOMER_ALLOCATOR_OBJS := $(ALLOCATOR_ARRAY_H_LOCATION)/gc_hal_kernel_allocator_cma.o diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1ac2ce6c5c6b24..6dfe3139c9be07 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -21,6 +21,8 @@ source "drivers/gpu/vga/Kconfig" source "drivers/gpu/host1x/Kconfig" +source "drivers/gpu/galcore/Kconfig" + source "drivers/gpu/drm/Kconfig" config VGASTATE From bcb215948854a32cb83e390ca364885e22a50972 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 27 Jun 2015 14:46:22 +0200 Subject: [PATCH 0405/1983] gpu: galcore: Remove unneeded code I probably should have broken this code into multiple removals but I am tired of this game. This is a final reference for testing the galcore driver, so I am only keeping parts needed to make this as straight forward as possible. The multi 3d core implementation in the v5 drivers is completely atrocious and breaks any semblance of api with other binaries so rip that out as well. --- .../gpu/galcore/arch/gc_hal_kernel_context.c | 5 - .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 311 +----- drivers/gpu/galcore/gc_hal_kernel.c | 213 ----- drivers/gpu/galcore/gc_hal_kernel.h | 32 - drivers/gpu/galcore/gc_hal_kernel_command.c | 171 ---- .../gpu/galcore/gc_hal_kernel_command_vg.c | 109 --- drivers/gpu/galcore/gc_hal_kernel_debug.c | 19 - drivers/gpu/galcore/gc_hal_kernel_device.c | 888 ++---------------- drivers/gpu/galcore/gc_hal_kernel_device.h | 27 - drivers/gpu/galcore/gc_hal_kernel_event.c | 519 +--------- .../gpu/galcore/gc_hal_kernel_interrupt_vg.c | 19 - drivers/gpu/galcore/gc_hal_kernel_linux.c | 6 - drivers/gpu/galcore/gc_hal_kernel_os.c | 140 +-- drivers/gpu/galcore/gc_hal_kernel_platform.h | 9 - drivers/gpu/galcore/gc_hal_kernel_probe.c | 74 +- .../gpu/galcore/gc_hal_kernel_video_memory.c | 131 +-- drivers/gpu/galcore/inc/gc_hal.h | 134 --- drivers/gpu/galcore/inc/gc_hal_base.h | 12 - drivers/gpu/galcore/inc/gc_hal_driver.h | 70 -- drivers/gpu/galcore/inc/gc_hal_driver_vg.h | 5 - drivers/gpu/galcore/inc/gc_hal_eglplatform.h | 27 +- .../gpu/galcore/inc/gc_hal_eglplatform_type.h | 6 - drivers/gpu/galcore/inc/gc_hal_enum.h | 3 - drivers/gpu/galcore/inc/gc_hal_options.h | 32 - drivers/gpu/galcore/inc/gc_hal_types.h | 23 +- drivers/gpu/galcore/inc/gc_hal_vg.h | 39 +- 26 files changed, 176 insertions(+), 2848 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c index 08644a7cf5e52e..0c5d4df85961ce 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c @@ -742,11 +742,6 @@ _InitializeContextBuffer( index += _State(Context, index, 0x00A8C >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); index += _State(Context, index, 0x00A88 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); -#if gcdMULTI_GPU - index += _State(Context, index, 0x03A00 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); - index += _State(Context, index, 0x03A04 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); - index += _State(Context, index, 0x03A08 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); -#endif /* Setup states. */ index += _State(Context, index, 0x00C00 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); index += _State(Context, index, 0x00C04 >> 2, 0x00000000, 1, gcvTRUE, gcvFALSE); diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 3a225a9a208db1..583e8449f89d22 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -106,9 +106,6 @@ _IdentifyHardware( gctUINT32 numConstants = 0; gctUINT32 bufferSize = 0; gctUINT32 varyingsCount = 0; -#if gcdMULTI_GPU - gctUINT32 gpuCoreCount = 0; -#endif gcmkHEADER_ARG("Os=0x%x", Os); @@ -372,9 +369,6 @@ _IdentifyHardware( &specs3)); varyingsCount = (((((gctUINT32) (specs3)) >> (0 ? 8:4)) & ((gctUINT32) ((((1 ? 8:4) - (0 ? 8:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 8:4) - (0 ? 8:4) + 1)))))) ); -#if gcdMULTI_GPU - gpuCoreCount = (((((gctUINT32) (specs3)) >> (0 ? 2:0)) & ((gctUINT32) ((((1 ? 2:0) - (0 ? 2:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:0) - (0 ? 2:0) + 1)))))) ); -#endif /* Read gcChipSpecs4 register. */ gcmkONERROR( @@ -579,14 +573,6 @@ _IdentifyHardware( } } -#if gcdMULTI_GPU -#if gcdMULTI_GPU > 1 - Identity->gpuCoreCount = gpuCoreCount + 1; -#else - Identity->gpuCoreCount = 1; -#endif -#endif - /* Success. */ gcmkFOOTER(); return gcvSTATUS_OK; @@ -940,9 +926,6 @@ gckHARDWARE_Construct( gckHARDWARE hardware = gcvNULL; gctUINT16 data = 0xff00; gctPOINTER pointer = gcvNULL; -#if gcdMULTI_GPU_AFFINITY - gctUINT32 control; -#endif gcmkHEADER_ARG("Os=0x%x", Os); @@ -990,11 +973,7 @@ gckHARDWARE_Construct( break; default: -#if gcdMULTI_GPU_AFFINITY - hardware->type = (Core == gcvCORE_MAJOR) ? gcvHARDWARE_3D : gcvHARDWARE_OCL; -#else hardware->type = gcvHARDWARE_3D; -#endif if(hardware->identity.chipModel == gcv880 && hardware->identity.chipRevision == 0x5107) { @@ -1027,25 +1006,6 @@ gckHARDWARE_Construct( "_ResetGPU failed: status=%d\n", status); } -#if gcdMULTI_GPU - gcmkONERROR(gckOS_WriteRegisterEx(Os, - Core, - 0x0055C, -#if gcdDISABLE_FE_L2 - 0x00FFFFFF)); -#else - 0x00FFFF05)); -#endif - -#elif gcdMULTI_GPU_AFFINITY - control = ((((gctUINT32) (0x00FF0A05)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) << (0 ? 27:27))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 27:27) - (0 ? 27:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 27:27) - (0 ? 27:27) + 1))))))) << (0 ? 27:27))); - - gcmkONERROR(gckOS_WriteRegisterEx(Os, - Core, - 0x0055C, - control)); -#endif - hardware->powerMutex = gcvNULL; hardware->mmuVersion @@ -1110,13 +1070,11 @@ gckHARDWARE_Construct( /* Disable profiler by default */ hardware->gpuProfiler = gcvFALSE; -#if defined(LINUX) || defined(__QNXNTO__) || defined(UNDERCE) if (hardware->mmuVersion) { hardware->endAfterFlushMmuCache = gcvTRUE; } else -#endif { hardware->endAfterFlushMmuCache = gcvFALSE; } @@ -1836,9 +1794,6 @@ gckHARDWARE_QueryChipIdentity( Identity->bufferSize = Hardware->identity.bufferSize; Identity->varyingsCount = Hardware->identity.varyingsCount; Identity->superTileMode = Hardware->identity.superTileMode; -#if gcdMULTI_GPU - Identity->gpuCoreCount = Hardware->identity.gpuCoreCount; -#endif Identity->chip2DControl = Hardware->identity.chip2DControl; Identity->productID = Hardware->identity.productID; @@ -2058,12 +2013,8 @@ gckHARDWARE_WaitLink( gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); gcmkVERIFY_ARGUMENT((Logical != gcvNULL) || (Bytes != gcvNULL)); -#if gcdMULTI_GPU && !gcdDISABLE_FE_L2 - bytes = gcmALIGN(Offset + 40, 8) - Offset; -#else /* Compute number of bytes required. */ bytes = gcmALIGN(Offset + 16, 8) - Offset; -#endif /* Cast the input pointer. */ logical = (gctUINT32_PTR) Logical; @@ -2087,45 +2038,6 @@ gckHARDWARE_WaitLink( = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x07 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (waitCount) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); -#if gcdMULTI_GPU && !gcdDISABLE_FE_L2 - logical[2] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x0D & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) - | gcvCORE_3D_0_MASK; - - logical[3] = 0; - - /* LoadState(AQFlush, 1), flush. */ - logical[4] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x01 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) - | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (0x0E03) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) - | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))) | (((gctUINT32) ((gctUINT32) (1) & ((gctUINT32) ((((1 ? 25:16) - (0 ? 25:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 25:16) - (0 ? 25:16) + 1))))))) << (0 ? 25:16))); - - logical[5] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); - - logical[6] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x0D & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) - | gcvCORE_3D_ALL_MASK; - - logical[7] = 0; - - /* Append LINK(2, address). */ - logical[8] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) - | ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))) | (((gctUINT32) ((gctUINT32) (bytes >> 3) & ((gctUINT32) ((((1 ? 15:0) - (0 ? 15:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 15:0) - (0 ? 15:0) + 1))))))) << (0 ? 15:0))); - - logical[9] = address; - - gcmkTRACE_ZONE( - gcvLEVEL_INFO, gcvZONE_HARDWARE, - "0x%08x: WAIT %u", address, waitCount - ); - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, - "0x%x: FLUSH 0x%x", address + 8, logical[3]); - - gcmkTRACE_ZONE( - gcvLEVEL_INFO, gcvZONE_HARDWARE, - "0x%08x: LINK 0x%08x, #%lu", - address + 16, address, bytes - ); -#else - /* Append LINK(2, address). */ logical[2] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) @@ -2143,7 +2055,6 @@ gckHARDWARE_WaitLink( "0x%08x: LINK 0x%08x, #%lu", address + 8, address, bytes ); -#endif if (WaitOffset != gcvNULL) { /* Return the offset pointer to WAIT command. */ @@ -2153,11 +2064,7 @@ gckHARDWARE_WaitLink( if (WaitSize != gcvNULL) { /* Return number of bytes used by the WAIT command. */ -#if gcdMULTI_GPU && !gcdDISABLE_FE_L2 - *WaitSize = 32; -#else *WaitSize = 8; -#endif } } @@ -2262,57 +2169,6 @@ gckHARDWARE_End( return status; } -#if gcdMULTI_GPU -gceSTATUS -gckHARDWARE_ChipEnable( - IN gckHARDWARE Hardware, - IN gctPOINTER Logical, - IN gceCORE_3D_MASK ChipEnable, - IN OUT gctSIZE_T * Bytes - ) -{ - gctUINT32_PTR logical = (gctUINT32_PTR) Logical; - gceSTATUS status; - - gcmkHEADER_ARG("Hardware=0x%x Logical=0x%x ChipEnable=0x%x *Bytes=%lu", - Hardware, Logical, ChipEnable, gcmOPT_VALUE(Bytes)); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Hardware, gcvOBJ_HARDWARE); - gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); - - if (Logical != gcvNULL) - { - if (*Bytes < 8) - { - /* Command queue too small. */ - gcmkONERROR(gcvSTATUS_BUFFER_TOO_SMALL); - } - - /* Append CHIPENABLE. */ - logical[0] = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x0D & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) - | ChipEnable; - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "0x%x: CHIPENABLE 0x%x", Logical, ChipEnable); - } - - if (Bytes != gcvNULL) - { - /* Return number of bytes required by the CHIPENABLE command. */ - *Bytes = 8; - } - - /* Success. */ - gcmkFOOTER_ARG("*Bytes=%lu", gcmOPT_VALUE(Bytes)); - return gcvSTATUS_OK; - -OnError: - /* Return the status. */ - gcmkFOOTER(); - return status; -} -#endif - /******************************************************************************* ** ** gckHARDWARE_Nop @@ -2440,10 +2296,6 @@ gckHARDWARE_Event( gcmkVERIFY_ARGUMENT((Logical == gcvNULL) || (Bytes != gcvNULL)); gcmkVERIFY_ARGUMENT(Event < 32); -#if gcdMULTI_GPU - if (FromWhere == gcvKERNEL_COMMAND) FromWhere = gcvKERNEL_PIXEL; -#endif - /* Determine the size of the command. */ size = (Hardware->extraEventStates && (FromWhere == gcvKERNEL_PIXEL)) @@ -2825,18 +2677,10 @@ gckHARDWARE_UpdateQueueTail( gckOS_MemoryBarrier(Hardware->os, Logical)); /* Notify gckKERNEL object of change. */ -#if gcdMULTI_GPU - gcmkONERROR( - gckKERNEL_Notify(Hardware->kernel, - 0, - gcvNOTIFY_COMMAND_QUEUE, - gcvFALSE)); -#else gcmkONERROR( gckKERNEL_Notify(Hardware->kernel, gcvNOTIFY_COMMAND_QUEUE, gcvFALSE)); -#endif if (status == gcvSTATUS_CHIP_NOT_READY) { @@ -2958,9 +2802,6 @@ gckHARDWARE_ConvertLogical( gceSTATUS gckHARDWARE_Interrupt( IN gckHARDWARE Hardware, -#if gcdMULTI_GPU - IN gctUINT CoreId, -#endif IN gctBOOL InterruptValid ) { @@ -2980,31 +2821,11 @@ gckHARDWARE_Interrupt( if (InterruptValid) { /* Read AQIntrAcknowledge register. */ -#if gcdMULTI_GPU - if (Hardware->core == gcvCORE_MAJOR) - { - gcmkONERROR( - gckOS_ReadRegisterByCoreId(Hardware->os, - Hardware->core, - CoreId, - 0x00010, - &data)); - } - else - { - gcmkONERROR( - gckOS_ReadRegisterEx(Hardware->os, - Hardware->core, - 0x00010, - &data)); - } -#else gcmkONERROR( gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00010, &data)); -#endif if (data == 0) { @@ -3020,9 +2841,6 @@ gckHARDWARE_Interrupt( /* Inform gckEVENT of the interrupt. */ status = gckEVENT_Interrupt(eventObj, -#if gcdMULTI_GPU - CoreId, -#endif data); } } @@ -3950,11 +3768,7 @@ gckHARDWARE_ConfigMMU( if (WaitLinkBytes != gcvNULL) { -#if gcdMULTI_GPU - *WaitLinkBytes = 40; -#else *WaitLinkBytes = 4 * 4; -#endif } gcmkFOOTER_NO(); @@ -4150,14 +3964,6 @@ gckHARDWARE_Flush( flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1))))))) << (0 ? 3:3))); } -#if gcdMULTI_GPU - /* Flush L2 cache. */ - if ((Flush & gcvFLUSH_L2) && (pipe == 0x0)) - { - flush |= ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))) | (((gctUINT32) (0x1 & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1))))))) << (0 ? 6:6))); - } -#endif - /* Determine reserve bytes. */ if (flush) { @@ -4934,11 +4740,7 @@ gckHARDWARE_SetPowerManagementState( commitEntered = gcvFALSE; /* Wait to finish all commands. */ -#if gcdMULTI_GPU - gcmkONERROR(gckCOMMAND_Stall(command, gcvTRUE, gcvCORE_3D_ALL_MASK)); -#else gcmkONERROR(gckCOMMAND_Stall(command, gcvTRUE)); -#endif } } @@ -5544,11 +5346,6 @@ gckHARDWARE_QueryIdle( gceSTATUS status; gctUINT32 idle, address; gctBOOL isIdle; -#if gcdMULTI_GPU > 1 - gctUINT32 idle3D1 = 0; - gctUINT32 address3D1; - gctBOOL isIdle3D1 = gcvFALSE; -#endif #if gcdINTERRUPT_STATISTIC gctINT32 pendingInterrupt; @@ -5564,9 +5361,6 @@ gckHARDWARE_QueryIdle( if (Hardware->chipPowerState != gcvPOWER_ON) { isIdle = gcvTRUE; -#if gcdMULTI_GPU > 1 - isIdle3D1 = gcvTRUE; -#endif } else @@ -5575,18 +5369,6 @@ gckHARDWARE_QueryIdle( gcmkONERROR( gckOS_ReadRegisterEx(Hardware->os, Hardware->core, 0x00004, &idle)); -#if gcdMULTI_GPU > 1 - if (Hardware->core == gcvCORE_MAJOR) - { - gcmkONERROR( - gckOS_ReadRegisterByCoreId(Hardware->os, - Hardware->core, - gcvCORE_3D_1_ID, - 0x00004, - &idle3D1)); - } -#endif - /* Pipe must be idle. */ if (((((((gctUINT32) (idle)) >> (0 ? 1:1)) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1)))))) ) != 1) || ((((((gctUINT32) (idle)) >> (0 ? 3:3)) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) ) != 1) @@ -5615,11 +5397,7 @@ gckHARDWARE_QueryIdle( /* Test if address is inside the last WAIT/LINK sequence. */ if ((address >= Hardware->lastWaitLink) -#if gcdMULTI_GPU - && (address <= Hardware->lastWaitLink + 40) -#else && (address <= Hardware->lastWaitLink + 16) -#endif ) { /* FE is in last WAIT/LINK and the pipe is idle. */ @@ -5632,50 +5410,6 @@ gckHARDWARE_QueryIdle( } #endif } - -#if gcdMULTI_GPU > 1 - if (Hardware->core == gcvCORE_MAJOR) - { - /* Pipe must be idle. */ - if (((((((gctUINT32) (idle3D1)) >> (0 ? 1:1)) & ((gctUINT32) ((((1 ? 1:1) - (0 ? 1:1) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 1:1) - (0 ? 1:1) + 1)))))) ) != 1) - || ((((((gctUINT32) (idle3D1)) >> (0 ? 3:3)) & ((gctUINT32) ((((1 ? 3:3) - (0 ? 3:3) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 3:3) - (0 ? 3:3) + 1)))))) ) != 1) - || ((((((gctUINT32) (idle3D1)) >> (0 ? 4:4)) & ((gctUINT32) ((((1 ? 4:4) - (0 ? 4:4) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 4:4) - (0 ? 4:4) + 1)))))) ) != 1) - || ((((((gctUINT32) (idle3D1)) >> (0 ? 5:5)) & ((gctUINT32) ((((1 ? 5:5) - (0 ? 5:5) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 5:5) - (0 ? 5:5) + 1)))))) ) != 1) - || ((((((gctUINT32) (idle3D1)) >> (0 ? 6:6)) & ((gctUINT32) ((((1 ? 6:6) - (0 ? 6:6) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 6:6) - (0 ? 6:6) + 1)))))) ) != 1) - || ((((((gctUINT32) (idle3D1)) >> (0 ? 7:7)) & ((gctUINT32) ((((1 ? 7:7) - (0 ? 7:7) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 7:7) - (0 ? 7:7) + 1)))))) ) != 1) - || ((((((gctUINT32) (idle3D1)) >> (0 ? 2:2)) & ((gctUINT32) ((((1 ? 2:2) - (0 ? 2:2) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 2:2) - (0 ? 2:2) + 1)))))) ) != 1) - ) - { - /* Something is busy. */ - isIdle3D1 = gcvFALSE; - } - - else - { - /* Read the current FE address. */ - gcmkONERROR(gckOS_ReadRegisterByCoreId(Hardware->os, - Hardware->core, - gcvCORE_3D_1_ID, - 0x00664, - &address3D1)); - - /* Test if address is inside the last WAIT/LINK sequence. */ - if ((address3D1 >= Hardware->lastWaitLink) - && (address3D1 <= Hardware->lastWaitLink + 40) - ) - { - /* FE is in last WAIT/LINK and the pipe is idle. */ - isIdle3D1 = gcvTRUE; - } - else - { - /* FE is not in WAIT/LINK yet. */ - isIdle3D1 = gcvFALSE; - } - } - } -#endif - } #if gcdINTERRUPT_STATISTIC @@ -5691,16 +5425,7 @@ gckHARDWARE_QueryIdle( } #endif -#if gcdMULTI_GPU > 1 - if (Hardware->core == gcvCORE_MAJOR) - { - *IsIdle = (isIdle & isIdle3D1); - } - else -#endif - { - *IsIdle = isIdle; - } + *IsIdle = isIdle; /* Success. */ gcmkFOOTER_NO(); @@ -6580,22 +6305,6 @@ _ResetGPU( continue; } -#if gcdMULTI_GPU > 1 - if (Core == gcvCORE_MAJOR) - { - /* Read idle register. */ - gcmkONERROR(gckOS_ReadRegisterByCoreId(Os, - Core, - gcvCORE_3D_1_ID, - 0x00004, - &idle)); - - if ((((((gctUINT32) (idle)) >> (0 ? 0:0)) & ((gctUINT32) ((((1 ? 0:0) - (0 ? 0:0) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 0:0) - (0 ? 0:0) + 1)))))) ) == 0) - { - continue; - } - } -#endif /* Read reset register. */ gcmkONERROR(gckOS_ReadRegisterEx(Os, Core, @@ -6609,24 +6318,6 @@ _ResetGPU( continue; } -#if gcdMULTI_GPU > 1 - if (Core == gcvCORE_MAJOR) - { - /* Read reset register. */ - gcmkONERROR(gckOS_ReadRegisterByCoreId(Os, - Core, - gcvCORE_3D_1_ID, - 0x00000, - &control)); - - if (((((((gctUINT32) (control)) >> (0 ? 16:16)) & ((gctUINT32) ((((1 ? 16:16) - (0 ? 16:16) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 16:16) - (0 ? 16:16) + 1)))))) ) == 0) - || ((((((gctUINT32) (control)) >> (0 ? 17:17)) & ((gctUINT32) ((((1 ? 17:17) - (0 ? 17:17) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 17:17) - (0 ? 17:17) + 1)))))) ) == 0) - ) - { - continue; - } - } -#endif /* GPU is idle. */ break; } diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c index f9d277ae76b783..a4801b490dab08 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.c +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -429,12 +429,7 @@ gckKERNEL_Construct( ; /* Initialize virtual command buffer. */ - /* TODO: Remove platform limitation after porting. */ -#if (defined(LINUX) || defined(__QNXNTO__)) kernel->virtualCommandBuffer = gcvTRUE; -#else - kernel->virtualCommandBuffer = gcvFALSE; -#endif #if gcdSECURITY kernel->virtualCommandBuffer = gcvFALSE; @@ -630,9 +625,6 @@ gckKERNEL_Destroy( /* Verify the arguments. */ gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); -#if QNX_SINGLE_THREADED_DEBUGGING - gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Kernel->debugMutex)); -#endif /* Destroy the database. */ if (Kernel->dbCreated) @@ -1143,9 +1135,7 @@ gckKERNEL_LockVideoMemory( gcuVIDMEM_NODE_PTR node = gcvNULL; gctBOOL locked = gcvFALSE; gctBOOL asynchronous = gcvFALSE; -#ifndef __QNXNTO__ gctPOINTER pointer = gcvNULL; -#endif gcmkHEADER_ARG("Kernel=0x%08X ProcessID=%d", Kernel, ProcessID); @@ -1173,21 +1163,6 @@ gckKERNEL_LockVideoMemory( if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) { /* Map video memory address into user space. */ -#ifdef __QNXNTO__ - if (node->VidMem.logical == gcvNULL) - { - gcmkONERROR( - gckKERNEL_MapVideoMemory(Kernel, - FromUser, - Interface->u.LockVideoMemory.address, - ProcessID, - node->VidMem.bytes, - &node->VidMem.logical)); - } - gcmkASSERT(node->VidMem.logical != gcvNULL); - - Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(node->VidMem.logical); -#else gcmkONERROR( gckKERNEL_MapVideoMemoryEx(Kernel, Core, @@ -1196,7 +1171,6 @@ gckKERNEL_LockVideoMemory( &pointer)); Interface->u.LockVideoMemory.memory = gcmPTR_TO_UINT64(pointer); -#endif } else { @@ -1531,9 +1505,6 @@ gckKERNEL_Dispatch( "Dispatching command %d (%s)", Interface->command, _DispatchText[Interface->command]); #endif -#if QNX_SINGLE_THREADED_DEBUGGING - gckOS_AcquireMutex(Kernel->os, Kernel->debugMutex, gcvINFINITE); -#endif /* Get the current process ID. */ gcmkONERROR(gckOS_GetProcessID(&processID)); @@ -1779,56 +1750,13 @@ gckKERNEL_Dispatch( case gcvHAL_EVENT_COMMIT: /* Commit an event queue. */ -#if gcdMULTI_GPU - if (Interface->u.Event.gpuMode == gcvMULTI_GPU_MODE_INDEPENDENT) - { - gcmkONERROR( - gckEVENT_Commit(Kernel->eventObj, - gcmUINT64_TO_PTR(Interface->u.Event.queue), - Interface->u.Event.chipEnable)); - } - else - { - gcmkONERROR( - gckEVENT_Commit(Kernel->eventObj, - gcmUINT64_TO_PTR(Interface->u.Event.queue), - gcvCORE_3D_ALL_MASK)); - } -#else gcmkONERROR( gckEVENT_Commit(Kernel->eventObj, gcmUINT64_TO_PTR(Interface->u.Event.queue))); -#endif break; case gcvHAL_COMMIT: /* Commit a command and context buffer. */ -#if gcdMULTI_GPU - if (Interface->u.Commit.gpuMode == gcvMULTI_GPU_MODE_INDEPENDENT) - { - gcmkONERROR( - gckCOMMAND_Commit(Kernel->command, - Interface->u.Commit.context ? - gcmNAME_TO_PTR(Interface->u.Commit.context) : gcvNULL, - gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffer), - gcmUINT64_TO_PTR(Interface->u.Commit.delta), - gcmUINT64_TO_PTR(Interface->u.Commit.queue), - processID, - Interface->u.Commit.chipEnable)); - } - else - { - gcmkONERROR( - gckCOMMAND_Commit(Kernel->command, - Interface->u.Commit.context ? - gcmNAME_TO_PTR(Interface->u.Commit.context) : gcvNULL, - gcmUINT64_TO_PTR(Interface->u.Commit.commandBuffer), - gcmUINT64_TO_PTR(Interface->u.Commit.delta), - gcmUINT64_TO_PTR(Interface->u.Commit.queue), - processID, - gcvCORE_3D_ALL_MASK)); - } -#else gcmkONERROR( gckCOMMAND_Commit(Kernel->command, Interface->u.Commit.context ? @@ -1837,17 +1765,12 @@ gckKERNEL_Dispatch( gcmUINT64_TO_PTR(Interface->u.Commit.delta), gcmUINT64_TO_PTR(Interface->u.Commit.queue), processID)); -#endif break; case gcvHAL_STALL: /* Stall the command queue. */ -#if gcdMULTI_GPU - gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE, gcvCORE_3D_ALL_MASK)); -#else gcmkONERROR(gckCOMMAND_Stall(Kernel->command, gcvFALSE)); -#endif break; case gcvHAL_MAP_USER_MEMORY: @@ -2038,106 +1961,6 @@ gckKERNEL_Dispatch( #endif break; -#if gcdMULTI_GPU - case gcvHAL_READ_REGISTER_EX: -#if gcdREGISTER_ACCESS_FROM_USER - { - gceCHIPPOWERSTATE power; - gctUINT32 coreId = 0; - gctUINT32 coreSelect = Interface->u.ReadRegisterDataEx.coreSelect; - - gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); - powerMutexAcquired = gcvTRUE; - gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, - &power)); - if (power == gcvPOWER_ON) - { - for (; coreSelect != 0; coreSelect >>= 1, coreId++) - { - if (coreSelect & 1UL) - { - /* Read a register. */ - gcmkONERROR( - gckOS_ReadRegisterByCoreId( - Kernel->os, - Kernel->core, - coreId, - Interface->u.ReadRegisterDataEx.address, - &Interface->u.ReadRegisterDataEx.data[coreId])); - } - } - } - else - { - for (coreId = 0; coreId < gcdMULTI_GPU; coreId++) - { - /* Chip is in power-state. */ - Interface->u.ReadRegisterDataEx.data[coreId] = 0; - } - status = gcvSTATUS_CHIP_NOT_READY; - } - gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); - powerMutexAcquired = gcvFALSE; - } -#else - gctUINT32 coreId; - - /* No access from user land to read registers. */ - for (coreId = 0; coreId < gcdMULTI_GPU; coreId++) - { - Interface->u.ReadRegisterDataEx.data[coreId] = 0; - } - - status = gcvSTATUS_NOT_SUPPORTED; -#endif - break; - - case gcvHAL_WRITE_REGISTER_EX: -#if gcdREGISTER_ACCESS_FROM_USER - { - gceCHIPPOWERSTATE power; - gctUINT32 coreId = 0; - gctUINT32 coreSelect = Interface->u.WriteRegisterDataEx.coreSelect; - - gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Kernel->hardware->powerMutex, gcvINFINITE)); - powerMutexAcquired = gcvTRUE; - gcmkONERROR(gckHARDWARE_QueryPowerManagementState(Kernel->hardware, - &power)); - if (power == gcvPOWER_ON) - { - for (; coreSelect != 0; coreSelect >>= 1, coreId++) - { - if (coreSelect & 1UL) - { - /* Write a register. */ - gcmkONERROR( - gckOS_WriteRegisterByCoreId( - Kernel->os, - Kernel->core, - coreId, - Interface->u.WriteRegisterDataEx.address, - Interface->u.WriteRegisterDataEx.data[coreId])); - } - } - } - else - { - /* Chip is in power-state. */ - for (coreId = 0; coreId < gcdMULTI_GPU; coreId++) - { - Interface->u.WriteRegisterDataEx.data[coreId] = 0; - } - status = gcvSTATUS_CHIP_NOT_READY; - } - gcmkONERROR(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); - powerMutexAcquired = gcvFALSE; - } -#else - status = gcvSTATUS_NOT_SUPPORTED; -#endif - break; -#endif - case gcvHAL_WRITE_REGISTER: #if gcdREGISTER_ACCESS_FROM_USER { @@ -2731,10 +2554,6 @@ gckKERNEL_Dispatch( /* Save status. */ Interface->status = status; -#if QNX_SINGLE_THREADED_DEBUGGING - gckOS_ReleaseMutex(Kernel->os, Kernel->debugMutex); -#endif - if (powerMutexAcquired == gcvTRUE) { gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Kernel->hardware->powerMutex)); @@ -2873,11 +2692,7 @@ gckKERNEL_AttachProcessEx( if (Kernel->vg == gcvNULL) #endif { -#if gcdMULTI_GPU - status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE, gcvCORE_3D_ALL_MASK); -#else status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE); -#endif if (status == gcvSTATUS_INTERRUPTED && Kernel->eventObj->submitTimer) { @@ -3518,38 +3333,10 @@ gckKERNEL_Recovery( /* Handle all outstanding events now. */ #if gcdSMP -#if gcdMULTI_GPU - if (Kernel->core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending3D[i], mask)); - } - } - else - { - gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, mask)); - } -#else gcmkONERROR(gckOS_AtomSet(Kernel->os, eventObj->pending, mask)); -#endif -#else -#if gcdMULTI_GPU - if (Kernel->core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - eventObj->pending3D[i] = mask; - } - } - else - { - eventObj->pending = mask; - } #else eventObj->pending = mask; #endif -#endif #if gcdINTERRUPT_STATISTIC while (count--) diff --git a/drivers/gpu/galcore/gc_hal_kernel.h b/drivers/gpu/galcore/gc_hal_kernel.h index 93764878a42d1b..613104e2c00954 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.h +++ b/drivers/gpu/galcore/gc_hal_kernel.h @@ -518,10 +518,6 @@ struct _gckKERNEL gctBOOL profileCleanRegister; #endif -#ifdef QNX_SINGLE_THREADED_DEBUGGING - gctPOINTER debugMutex; -#endif - /* Database management. */ gckDB db; gctBOOL dbCreated; @@ -704,11 +700,6 @@ typedef struct _gcsEVENT /* Process ID owning the event. */ gctUINT32 processID; -#ifdef __QNXNTO__ - /* Kernel. */ - gckKERNEL kernel; -#endif - gctBOOL fromKernel; } gcsEVENT; @@ -723,11 +714,6 @@ typedef struct _gcsEVENT_QUEUE /* Source of the event. */ gceKERNEL_WHERE source; -#if gcdMULTI_GPU - /* Which chip(s) of the event */ - gceCORE_3D_MASK chipEnable; -#endif - /* Pointer to head of event queue. */ gcsEVENT_PTR head; @@ -777,23 +763,10 @@ struct _gckEVENT /* Pending events. */ #if gcdSMP -#if gcdMULTI_GPU - gctPOINTER pending3D[gcdMULTI_GPU]; - gctPOINTER pending3DMask[gcdMULTI_GPU]; - gctPOINTER pendingMask; -#endif gctPOINTER pending; #else -#if gcdMULTI_GPU - volatile gctUINT pending3D[gcdMULTI_GPU]; - volatile gctUINT pending3DMask[gcdMULTI_GPU]; - volatile gctUINT pendingMask; -#endif volatile gctUINT pending; #endif -#if gcdMULTI_GPU - gctUINT32 busy; -#endif /* List of free event structures and its mutex. */ gcsEVENT_PTR freeEventList; @@ -878,11 +851,6 @@ typedef union _gcuVIDMEM_NODE gctSIZE_T bytes; gctUINT32 alignment; -#ifdef __QNXNTO__ - /* Client virtual address. */ - gctPOINTER logical; -#endif - /* Locked counter. */ gctINT32 locked; diff --git a/drivers/gpu/galcore/gc_hal_kernel_command.c b/drivers/gpu/galcore/gc_hal_kernel_command.c index 72aa966d6dad0f..3208979316ec97 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_command.c +++ b/drivers/gpu/galcore/gc_hal_kernel_command.c @@ -1171,18 +1171,6 @@ gckCOMMAND_Stop( ** ** Nothing. */ -#if gcdMULTI_GPU -gceSTATUS -gckCOMMAND_Commit( - IN gckCOMMAND Command, - IN gckCONTEXT Context, - IN gcoCMDBUF CommandBuffer, - IN gcsSTATE_DELTA_PTR StateDelta, - IN gcsQUEUE_PTR EventQueue, - IN gctUINT32 ProcessID, - IN gceCORE_3D_MASK ChipEnable - ) -#else gceSTATUS gckCOMMAND_Commit( IN gckCOMMAND Command, @@ -1192,7 +1180,6 @@ gckCOMMAND_Commit( IN gcsQUEUE_PTR EventQueue, IN gctUINT32 ProcessID ) -#endif { gceSTATUS status; gctBOOL commitEntered = gcvFALSE; @@ -1239,13 +1226,6 @@ gckCOMMAND_Commit( gctUINT32 waitOffset; gctUINT32 waitSize; -#ifdef __QNXNTO__ - gctPOINTER userCommandBufferLogical = gcvNULL; - gctBOOL userCommandBufferLogicalMapped = gcvFALSE; - gctPOINTER userCommandBufferLink = gcvNULL; - gctBOOL userCommandBufferLinkMapped = gcvFALSE; -#endif - #if gcdPROCESS_ADDRESS_SPACE gctSIZE_T mmuConfigureBytes; gctPOINTER mmuConfigureLogical = gcvNULL; @@ -1271,10 +1251,6 @@ gckCOMMAND_Commit( gctPOINTER pointer = gcvNULL; -#if gcdMULTI_GPU - gctSIZE_T chipEnableBytes; -#endif - gcmkHEADER_ARG( "Command=0x%x CommandBuffer=0x%x ProcessID=%d", Command, CommandBuffer, ProcessID @@ -1380,13 +1356,6 @@ gckCOMMAND_Commit( hardware, gcvNULL, 0, 0, &linkBytes )); -#if gcdMULTI_GPU - /* Query the size of chip enable command sequence. */ - gcmkONERROR(gckHARDWARE_ChipEnable( - hardware, gcvNULL, 0, &chipEnableBytes - )); -#endif - /* Compute the command buffer entry and the size. */ commandBufferLogical = (gctUINT8_PTR) gcmUINT64_TO_PTR(commandBufferObject->logical) @@ -1419,20 +1388,6 @@ gckCOMMAND_Commit( (gctUINT32_PTR)&commandBufferPhysical )); -#ifdef __QNXNTO__ - userCommandBufferLogical = (gctPOINTER) commandBufferLogical; - - gcmkONERROR(gckOS_MapUserPointer( - Command->os, - userCommandBufferLogical, - 0, - &pointer)); - - commandBufferLogical = pointer; - - userCommandBufferLogicalMapped = gcvTRUE; -#endif - commandBufferSize = commandBufferObject->offset + Command->reservedTail @@ -1446,43 +1401,6 @@ gckCOMMAND_Commit( /* Compute number of bytes left in current kernel command queue. */ bytes = Command->pageSize - offset; -#if gcdMULTI_GPU - if (Command->kernel->core == gcvCORE_MAJOR) - { - commandBufferSize += chipEnableBytes; - - gcmkONERROR(gckHARDWARE_ChipEnable( - hardware, - commandBufferLogical + pipeBytes, - ChipEnable, - &chipEnableBytes - )); - - gcmkONERROR(gckHARDWARE_ChipEnable( - hardware, - commandBufferLogical + commandBufferSize - linkBytes - chipEnableBytes, - gcvCORE_3D_ALL_MASK, - &chipEnableBytes - )); - } - else - { - commandBufferSize += nopBytes; - - gcmkONERROR(gckHARDWARE_Nop( - hardware, - commandBufferLogical + pipeBytes, - &nopBytes - )); - - gcmkONERROR(gckHARDWARE_Nop( - hardware, - commandBufferLogical + commandBufferSize - linkBytes - nopBytes, - &nopBytes - )); - } -#endif - /* Query the size of WAIT/LINK command sequence. */ gcmkONERROR(gckHARDWARE_WaitLink( hardware, @@ -2182,31 +2100,6 @@ gckCOMMAND_Commit( = (gctUINT8_PTR) gcmUINT64_TO_PTR(commandBufferObject->logical) + commandBufferObject->offset; -#ifdef __QNXNTO__ - userCommandBufferLink = (gctPOINTER) commandBufferLink; - - gcmkONERROR(gckOS_MapUserPointer( - Command->os, - userCommandBufferLink, - 0, - &pointer)); - - commandBufferLink = pointer; - - userCommandBufferLinkMapped = gcvTRUE; -#endif - -#if gcdMULTI_GPU - if (Command->kernel->core == gcvCORE_MAJOR) - { - commandBufferLink += chipEnableBytes; - } - else - { - commandBufferLink += nopBytes; - } -#endif - /* Generate a LINK from the end of the command buffer being scheduled back to the kernel command queue. */ #if !gcdSECURITY @@ -2219,16 +2112,6 @@ gckCOMMAND_Commit( )); #endif -#ifdef __QNXNTO__ - gcmkONERROR(gckOS_UnmapUserPointer( - Command->os, - userCommandBufferLink, - 0, - commandBufferLink)); - - userCommandBufferLinkMapped = gcvFALSE; -#endif - #if gcdNONPAGED_MEMORY_CACHEABLE /* Flush the command buffer cache. */ gcmkONERROR(gckOS_CacheClean( @@ -2354,11 +2237,7 @@ gckCOMMAND_Commit( #if VIVANTE_PROFILER_CONTEXT if(sequenceAcquired) { -#if gcdMULTI_GPU - gcmkONERROR(gckCOMMAND_Stall(Command, gcvTRUE, ChipEnable)); -#else gcmkONERROR(gckCOMMAND_Stall(Command, gcvTRUE)); -#endif if (Command->currContext) { gcmkONERROR(gckHARDWARE_UpdateContextProfile( @@ -2427,11 +2306,7 @@ gckCOMMAND_Commit( } /* Submit events. */ -#if gcdMULTI_GPU - status = gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE, ChipEnable); -#else status = gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE); -#endif if (status == gcvSTATUS_INTERRUPTED) { gcmkTRACE( @@ -2446,19 +2321,6 @@ gckCOMMAND_Commit( gcmkONERROR(status); } -#ifdef __QNXNTO__ - if (userCommandBufferLogicalMapped) - { - gcmkONERROR(gckOS_UnmapUserPointer( - Command->os, - userCommandBufferLogical, - 0, - commandBufferLogical)); - - userCommandBufferLogicalMapped = gcvFALSE; - } -#endif - /* Unmap the command buffer pointer. */ if (commandBufferMapped) { @@ -2508,26 +2370,6 @@ gckCOMMAND_Commit( } #endif -#ifdef __QNXNTO__ - if (userCommandBufferLinkMapped) - { - gcmkONERROR(gckOS_UnmapUserPointer( - Command->os, - userCommandBufferLink, - 0, - commandBufferLink)); - } - - if (userCommandBufferLogicalMapped) - { - gcmkVERIFY_OK(gckOS_UnmapUserPointer( - Command->os, - userCommandBufferLogical, - 0, - commandBufferLogical)); - } -#endif - /* Unmap the command buffer pointer. */ if (commandBufferMapped) { @@ -2835,20 +2677,11 @@ gckCOMMAND_Execute( ** ** Nothing. */ -#if gcdMULTI_GPU -gceSTATUS -gckCOMMAND_Stall( - IN gckCOMMAND Command, - IN gctBOOL FromPower, - IN gceCORE_3D_MASK ChipEnable - ) -#else gceSTATUS gckCOMMAND_Stall( IN gckCOMMAND Command, IN gctBOOL FromPower ) -#endif { #if gcdNULL_DRIVER /* Do nothing with infinite hardware. */ @@ -2885,11 +2718,7 @@ gckCOMMAND_Stall( gcmkONERROR(gckEVENT_Signal(eventObject, signal, gcvKERNEL_PIXEL)); /* Submit the event queue. */ -#if gcdMULTI_GPU - gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower, ChipEnable)); -#else gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower)); -#endif #if gcdDUMP_COMMAND gcmkPRINT("@[kernel.stall]"); diff --git a/drivers/gpu/galcore/gc_hal_kernel_command_vg.c b/drivers/gpu/galcore/gc_hal_kernel_command_vg.c index 0eafaf6c0e3881..6a6a659d7b2e7c 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_command_vg.c +++ b/drivers/gpu/galcore/gc_hal_kernel_command_vg.c @@ -727,11 +727,6 @@ _ScheduleTasks( gctINT32 interrupt; gctUINT8_PTR eventCommand; -#ifdef __QNXNTO__ - gcsTASK_PTR oldUserTask = gcvNULL; - gctPOINTER pointer; -#endif - /* Nothing to schedule? */ if (TaskTable->size == 0) { @@ -831,18 +826,6 @@ _ScheduleTasks( { gcsTASK_HEADER_PTR taskHeader; -#ifdef __QNXNTO__ - oldUserTask = userTask; - - gcmkERR_BREAK(gckOS_MapUserPointer( - Command->os, - oldUserTask, - 0, - &pointer)); - - userTask = pointer; -#endif - taskHeader = (gcsTASK_HEADER_PTR) (userTask + 1); gcmkVERIFY_OK(_RemoveRecordFromProcesDB(Command, taskHeader)); @@ -854,14 +837,6 @@ _ScheduleTasks( userTask->size ); -#ifdef __QNXNTO__ - if (taskHeader->id == gcvTASK_SIGNAL) - { - ((gcsTASK_SIGNAL_PTR)taskHeader)->coid = TaskTable->coid; - ((gcsTASK_SIGNAL_PTR)taskHeader)->rcvid = TaskTable->rcvid; - } -#endif - /* Copy the task data. */ gcmkVERIFY_OK(gckOS_MemCopy( kernelTask, taskHeader, userTask->size @@ -870,14 +845,6 @@ _ScheduleTasks( /* Advance to the next task. */ kernelTask += userTask->size; userTask = userTask->next; - -#ifdef __QNXNTO__ - gcmkERR_BREAK(gckOS_UnmapUserPointer( - Command->os, - oldUserTask, - 0, - pointer)); -#endif } while (userTask != gcvNULL); @@ -1546,15 +1513,9 @@ _TaskSignal( /* Map the signal into kernel space. */ -#ifdef __QNXNTO__ - gcmkERR_BREAK(gckOS_UserSignal( - Command->os, task->signal, task->rcvid, task->coid - )); -#else gcmkERR_BREAK(gckOS_UserSignal( Command->os, task->signal, task->process )); -#endif /* __QNXNTO__ */ /* Update the reference counter. */ TaskHeader->container->referenceCount -= 1; @@ -3430,14 +3391,6 @@ gckVGCOMMAND_Commit( gceSTATUS status, last; -#ifdef __QNXNTO__ - gcsVGCONTEXT_PTR userContext = gcvNULL; - gctBOOL userContextMapped = gcvFALSE; - gcsTASK_MASTER_TABLE_PTR userTaskTable = gcvNULL; - gctBOOL userTaskTableMapped = gcvFALSE; - gctPOINTER pointer = gcvNULL; -#endif - gcmkHEADER_ARG("Command=0x%x Context=0x%x Queue=0x%x EntryCount=0x%x TaskTable=0x%x", Command, Context, Queue, EntryCount, TaskTable); @@ -3463,38 +3416,6 @@ gckVGCOMMAND_Commit( gctBOOL previousExecuted; gctUINT controlIndex; -#ifdef __QNXNTO__ - /* Map the context into the kernel space. */ - userContext = Context; - - gcmkERR_BREAK(gckOS_MapUserPointer( - Command->os, - userContext, - gcmSIZEOF(*userContext), - &pointer)); - - Context = pointer; - - userContextMapped = gcvTRUE; - - /* Map the taskTable into the kernel space. */ - userTaskTable = TaskTable; - - gcmkERR_BREAK(gckOS_MapUserPointer( - Command->os, - userTaskTable, - gcmSIZEOF(*userTaskTable), - &pointer)); - - TaskTable = pointer; - - userTaskTableMapped = gcvTRUE; - - /* Update the signal info. */ - TaskTable->coid = Context->coid; - TaskTable->rcvid = Context->rcvid; -#endif - gcmkERR_BREAK(gckVGHARDWARE_SetPowerManagementState( Command->hardware, gcvPOWER_ON_AUTO )); @@ -3547,17 +3468,9 @@ gckVGCOMMAND_Commit( Queue += 1; /* Set the signal to avoid user waiting. */ -#ifdef __QNXNTO__ - gcmkERR_BREAK(gckOS_UserSignal( - Command->os, Context->signal, Context->rcvid, Context->coid - )); -#else gcmkERR_BREAK(gckOS_UserSignal( Command->os, Context->signal, Context->process )); - -#endif /* __QNXNTO__ */ - } else { @@ -3757,28 +3670,6 @@ gckVGCOMMAND_Commit( } while (gcvFALSE); -#ifdef __QNXNTO__ - if (userContextMapped) - { - /* Unmap the user context. */ - gcmkVERIFY_OK(gckOS_UnmapUserPointer( - Command->os, - userContext, - gcmSIZEOF(*userContext), - Context)); - } - - if (userTaskTableMapped) - { - /* Unmap the user taskTable. */ - gcmkVERIFY_OK(gckOS_UnmapUserPointer( - Command->os, - userTaskTable, - gcmSIZEOF(*userTaskTable), - TaskTable)); - } -#endif - gcmkFOOTER(); /* Return status. */ return status; diff --git a/drivers/gpu/galcore/gc_hal_kernel_debug.c b/drivers/gpu/galcore/gc_hal_kernel_debug.c index 97b8275e326f13..5eac72fdc5a71d 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_debug.c +++ b/drivers/gpu/galcore/gc_hal_kernel_debug.c @@ -1784,24 +1784,6 @@ _Print( ********************************* Debug Macros ********************************* \******************************************************************************/ -#ifdef __QNXNTO__ - -extern volatile unsigned g_nQnxInIsrs; - -#define gcmDEBUGPRINT(ArgumentSize, CopyMessage, Message) \ -{ \ - if (atomic_add_value(&g_nQnxInIsrs, 1) == 0) \ - { \ - gctARGUMENTS __arguments__; \ - gcmkARGUMENTS_START(__arguments__, Message); \ - _Print(ArgumentSize, CopyMessage, Message, &__arguments__); \ - gcmkARGUMENTS_END(__arguments__); \ - } \ - atomic_sub(&g_nQnxInIsrs, 1); \ -} - -#else - #define gcmDEBUGPRINT(ArgumentSize, CopyMessage, Message) \ { \ gctARGUMENTS __arguments__; \ @@ -1810,7 +1792,6 @@ extern volatile unsigned g_nQnxInIsrs; gcmkARGUMENTS_END(__arguments__); \ } -#endif /******************************************************************************\ ********************************** Debug Code ********************************** diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index e64db71e9dead8..ff7b28e0b81e61 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -487,229 +487,6 @@ _FreeMemory( /******************************************************************************\ ******************************* Interrupt Handler ****************************** \******************************************************************************/ -#if gcdMULTI_GPU -static irqreturn_t isrRoutine3D0(int irq, void *ctxt) -{ - gceSTATUS status; - gckGALDEVICE device; - - device = (gckGALDEVICE) ctxt; - - /* Call kernel interrupt notification. */ - status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], - gcvCORE_3D_0_ID, - gcvNOTIFY_INTERRUPT, - gcvTRUE); - - if (gcmIS_SUCCESS(status)) - { - /* Wake up the threadRoutine to process events. */ - device->dataReady3D[gcvCORE_3D_0_ID] = gcvTRUE; - wake_up_interruptible(&device->intrWaitQueue3D[gcvCORE_3D_0_ID]); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static int threadRoutine3D0(void *ctxt) -{ - gckGALDEVICE device = (gckGALDEVICE) ctxt; - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, - "Starting isr Thread with extension=%p", - device); - - for (;;) - { - /* Sleep until being awaken by the interrupt handler. */ - wait_event_interruptible(device->intrWaitQueue3D[gcvCORE_3D_0_ID], - device->dataReady3D[gcvCORE_3D_0_ID] == gcvTRUE); - device->dataReady3D[gcvCORE_3D_0_ID] = gcvFALSE; - - if (device->killThread == gcvTRUE) - { - /* The daemon exits. */ - while (!kthread_should_stop()) - { - gckOS_Delay(device->os, 1); - } - - return 0; - } - - gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], - gcvCORE_3D_0_ID, - gcvNOTIFY_INTERRUPT, - gcvFALSE); - } -} - -#if gcdMULTI_GPU > 1 -static irqreturn_t isrRoutine3D1(int irq, void *ctxt) -{ - gceSTATUS status; - gckGALDEVICE device; - - device = (gckGALDEVICE) ctxt; - - /* Call kernel interrupt notification. */ - status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], - gcvCORE_3D_1_ID, - gcvNOTIFY_INTERRUPT, - gcvTRUE); - - if (gcmIS_SUCCESS(status)) - { - /* Wake up the worker thread to process events. */ - device->dataReady3D[gcvCORE_3D_1_ID] = gcvTRUE; - wake_up_interruptible(&device->intrWaitQueue3D[gcvCORE_3D_1_ID]); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static int threadRoutine3D1(void *ctxt) -{ - gckGALDEVICE device = (gckGALDEVICE) ctxt; - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, - "Starting isr Thread with extension=%p", - device); - - for (;;) - { - /* Sleep until being awaken by the interrupt handler. */ - wait_event_interruptible(device->intrWaitQueue3D[gcvCORE_3D_1_ID], - device->dataReady3D[gcvCORE_3D_1_ID] == gcvTRUE); - device->dataReady3D[gcvCORE_3D_1_ID] = gcvFALSE; - - if (device->killThread == gcvTRUE) - { - /* The daemon exits. */ - while (!kthread_should_stop()) - { - gckOS_Delay(device->os, 1); - } - - return 0; - } - - gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], - gcvCORE_3D_1_ID, - gcvNOTIFY_INTERRUPT, - gcvFALSE); - } -} -#endif -#elif gcdMULTI_GPU_AFFINITY -static irqreturn_t isrRoutine3D0(int irq, void *ctxt) -{ - gceSTATUS status; - gckGALDEVICE device; - - device = (gckGALDEVICE) ctxt; - - /* Call kernel interrupt notification. */ - status = gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], gcvNOTIFY_INTERRUPT, gcvTRUE); - - if (gcmIS_SUCCESS(status)) - { - up(&device->semas[gcvCORE_MAJOR]); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static int threadRoutine3D0(void *ctxt) -{ - gckGALDEVICE device = (gckGALDEVICE) ctxt; - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, - "Starting isr Thread with extension=%p", - device); - - for (;;) - { - static int down; - - down = down_interruptible(&device->semas[gcvCORE_MAJOR]); - if (down); /*To make gcc 4.6 happye*/ - - if (device->killThread == gcvTRUE) - { - /* The daemon exits. */ - while (!kthread_should_stop()) - { - gckOS_Delay(device->os, 1); - } - - return 0; - } - - gckKERNEL_Notify(device->kernels[gcvCORE_MAJOR], - gcvNOTIFY_INTERRUPT, - gcvFALSE); - } -} - -static irqreturn_t isrRoutine3D1(int irq, void *ctxt) -{ - gceSTATUS status; - gckGALDEVICE device; - - device = (gckGALDEVICE) ctxt; - - /* Call kernel interrupt notification. */ - status = gckKERNEL_Notify(device->kernels[gcvCORE_OCL], gcvNOTIFY_INTERRUPT, gcvTRUE); - - if (gcmIS_SUCCESS(status)) - { - up(&device->semas[gcvCORE_OCL]); - - return IRQ_HANDLED; - } - - return IRQ_NONE; -} - -static int threadRoutine3D1(void *ctxt) -{ - gckGALDEVICE device = (gckGALDEVICE) ctxt; - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_DRIVER, - "Starting isr Thread with extension=%p", - device); - - for (;;) - { - static int down; - - down = down_interruptible(&device->semas[gcvCORE_OCL]); - if (down); /*To make gcc 4.6 happye*/ - - if (device->killThread == gcvTRUE) - { - /* The daemon exits. */ - while (!kthread_should_stop()) - { - gckOS_Delay(device->os, 1); - } - - return 0; - } - - gckKERNEL_Notify(device->kernels[gcvCORE_OCL], - gcvNOTIFY_INTERRUPT, - gcvFALSE); - } -} -#else static irqreturn_t isrRoutine(int irq, void *ctxt) { gceSTATUS status; @@ -761,7 +538,6 @@ static int threadRoutine(void *ctxt) gcvFALSE); } } -#endif static irqreturn_t isrRoutine2D(int irq, void *ctxt) { @@ -772,9 +548,6 @@ static irqreturn_t isrRoutine2D(int irq, void *ctxt) /* Call kernel interrupt notification. */ status = gckKERNEL_Notify(device->kernels[gcvCORE_2D], -#if gcdMULTI_GPU - 0, -#endif gcvNOTIFY_INTERRUPT, gcvTRUE); if (gcmIS_SUCCESS(status)) @@ -813,9 +586,6 @@ static int threadRoutine2D(void *ctxt) return 0; } gckKERNEL_Notify(device->kernels[gcvCORE_2D], -#if gcdMULTI_GPU - 0, -#endif gcvNOTIFY_INTERRUPT, gcvFALSE); } @@ -867,9 +637,6 @@ static int threadRoutineVG(void *ctxt) return 0; } gckKERNEL_Notify(device->kernels[gcvCORE_VG], -#if gcdMULTI_GPU - 0, -#endif gcvNOTIFY_INTERRUPT, gcvFALSE); } @@ -895,18 +662,9 @@ static int threadRoutineVG(void *ctxt) */ gceSTATUS gckGALDEVICE_Construct( -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - IN gctINT IrqLine3D0, - IN gctUINT32 RegisterMemBase3D0, - IN gctSIZE_T RegisterMemSize3D0, - IN gctINT IrqLine3D1, - IN gctUINT32 RegisterMemBase3D1, - IN gctSIZE_T RegisterMemSize3D1, -#else IN gctINT IrqLine, IN gctUINT32 RegisterMemBase, IN gctSIZE_T RegisterMemSize, -#endif IN gctINT IrqLine2D, IN gctUINT32 RegisterMemBase2D, IN gctSIZE_T RegisterMemSize2D, @@ -937,25 +695,10 @@ gckGALDEVICE_Construct( gckGALDEVICE device; gceSTATUS status; gctINT32 i; -#if gcdMULTI_GPU - gctINT32 j; -#endif gceHARDWARE_TYPE type; gckDB sharedDB = gcvNULL; gckKERNEL kernel = gcvNULL; -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - gcmkHEADER_ARG("IrqLine3D0=%d RegisterMemBase3D0=0x%08x RegisterMemSize3D0=%u " - "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u " - "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u " - "ContiguousBase=0x%08x ContiguousSize=%lu BankSize=%lu " - "FastClear=%d Compression=%d PhysBaseAddr=0x%x PhysSize=%d Signal=%d", - IrqLine3D0, RegisterMemBase3D0, RegisterMemSize3D0, - IrqLine2D, RegisterMemBase2D, RegisterMemSize2D, - IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG, - ContiguousBase, ContiguousSize, BankSize, FastClear, Compression, - PhysBaseAddr, PhysSize, Signal); -#else gcmkHEADER_ARG("IrqLine=%d RegisterMemBase=0x%08x RegisterMemSize=%u " "IrqLine2D=%d RegisterMemBase2D=0x%08x RegisterMemSize2D=%u " "IrqLineVG=%d RegisterMemBaseVG=0x%08x RegisterMemSizeVG=%u " @@ -966,7 +709,6 @@ gckGALDEVICE_Construct( IrqLineVG, RegisterMemBaseVG, RegisterMemSizeVG, ContiguousBase, ContiguousSize, BankSize, FastClear, Compression, PhysBaseAddr, PhysSize, Signal); -#endif #if gcdDISABLE_CORES_2D3D IrqLine = -1; @@ -1004,37 +746,11 @@ gckGALDEVICE_Construct( gckDEBUGFS_SetCurrentNode(device->dbgNode); } -#if gcdMULTI_GPU - if (IrqLine3D0 != -1) - { - device->requestedRegisterMemBase3D[gcvCORE_3D_0_ID] = RegisterMemBase3D0; - device->requestedRegisterMemSize3D[gcvCORE_3D_0_ID] = RegisterMemSize3D0; - } - - if (IrqLine3D1 != -1) - { - device->requestedRegisterMemBase3D[gcvCORE_3D_1_ID] = RegisterMemBase3D1; - device->requestedRegisterMemSize3D[gcvCORE_3D_1_ID] = RegisterMemSize3D1; - } -#elif gcdMULTI_GPU_AFFINITY - if (IrqLine3D0 != -1) - { - device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase3D0; - device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize3D0; - } - - if (IrqLine3D1 != -1) - { - device->requestedRegisterMemBases[gcvCORE_OCL] = RegisterMemBase3D1; - device->requestedRegisterMemSizes[gcvCORE_OCL] = RegisterMemSize3D1; - } -#else if (IrqLine != -1) { device->requestedRegisterMemBases[gcvCORE_MAJOR] = RegisterMemBase; device->requestedRegisterMemSizes[gcvCORE_MAJOR] = RegisterMemSize; } -#endif if (IrqLine2D != -1) { @@ -1053,256 +769,126 @@ gckGALDEVICE_Construct( for (i = 0; i < gcdMAX_GPU_COUNT; i++) { -#if gcdMULTI_GPU - if (i == gcvCORE_MAJOR) - { - for (j = 0; j < gcdMULTI_GPU; j++) - { - physical = device->requestedRegisterMemBase3D[j]; - - /* Set up register memory region. */ - if (physical != 0) - { - mem_region = request_mem_region(physical, - device->requestedRegisterMemSize3D[j], - "galcore register region"); - - if (mem_region == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Failed to claim %lu bytes @ 0x%08X\n", - __FUNCTION__, __LINE__, - physical, device->requestedRegisterMemSize3D[j] - ); - - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } - - device->registerBase3D[j] = (gctPOINTER) ioremap_nocache( - physical, device->requestedRegisterMemSize3D[j]); - - if (device->registerBase3D[j] == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Unable to map %ld bytes @ 0x%08X\n", - __FUNCTION__, __LINE__, - physical, device->requestedRegisterMemSize3D[j] - ); + physical = device->requestedRegisterMemBases[i]; - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } - - physical += device->requestedRegisterMemSize3D[j]; - } - else - { - device->registerBase3D[j] = gcvNULL; - } - } - } - else -#endif + /* Set up register memory region. */ + if (physical != 0) { - physical = device->requestedRegisterMemBases[i]; + mem_region = request_mem_region(physical, + device->requestedRegisterMemSizes[i], + "galcore register region"); - /* Set up register memory region. */ - if (physical != 0) + if (mem_region == gcvNULL) { - mem_region = request_mem_region(physical, - device->requestedRegisterMemSizes[i], - "galcore register region"); - - if (mem_region == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Failed to claim %lu bytes @ 0x%08X\n", - __FUNCTION__, __LINE__, - physical, device->requestedRegisterMemSizes[i] - ); - - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } - - device->registerBases[i] = (gctPOINTER) ioremap_nocache( - physical, device->requestedRegisterMemSizes[i]); - - if (device->registerBases[i] == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Unable to map %ld bytes @ 0x%08X\n", - __FUNCTION__, __LINE__, - physical, device->requestedRegisterMemSizes[i] - ); - - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to claim %lu bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + physical, device->requestedRegisterMemSizes[i] + ); - physical += device->requestedRegisterMemSizes[i]; + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); } - } - } - - /* Set the base address */ - device->baseAddress = device->physBase = PhysBaseAddr; - device->physSize = PhysSize; - device->mmu = Args->mmu; - - /* Construct the gckOS object. */ - gcmkONERROR(gckOS_Construct(device, &device->os)); - -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - if (IrqLine3D0 != -1) -#else - if (IrqLine != -1) -#endif - { - /* Construct the gckKERNEL object. */ - gcmkONERROR(gckKERNEL_Construct( - device->os, gcvCORE_MAJOR, device, - gcvNULL, &device->kernels[gcvCORE_MAJOR])); - - sharedDB = device->kernels[gcvCORE_MAJOR]->db; - - /* Initialize core mapping */ - for (i = 0; i < 8; i++) - { - device->coreMapping[i] = gcvCORE_MAJOR; - } - - /* Setup the ISR manager. */ - gcmkONERROR(gckHARDWARE_SetIsrManager( - device->kernels[gcvCORE_MAJOR]->hardware, - (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR, - (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR, - device - )); - - gcmkONERROR(gckHARDWARE_SetFastClear( - device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression - )); - - if(PowerManagement != -1) - { - gcmkONERROR(gckHARDWARE_SetPowerManagementLock( - device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE - )); - gcmkONERROR(gckHARDWARE_SetPowerManagement( - device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement - )); - gcmkONERROR(gckHARDWARE_SetPowerManagementLock( - device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE - )); - } - else - { - gcmkONERROR(gckHARDWARE_SetPowerManagementLock( - device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE - )); - gcmkONERROR(gckHARDWARE_SetPowerManagement( - device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE - )); - } -#if gcdENABLE_FSCALE_VAL_ADJUST - gcmkONERROR(gckHARDWARE_SetMinFscaleValue( - device->kernels[gcvCORE_MAJOR]->hardware, Args->gpu3DMinClock - )); -#endif + device->registerBases[i] = (gctPOINTER) ioremap_nocache( + physical, device->requestedRegisterMemSizes[i]); - gcmkONERROR(gckHARDWARE_SetGpuProfiler( - device->kernels[gcvCORE_MAJOR]->hardware, GpuProfiler - )); + if (device->registerBases[i] == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Unable to map %ld bytes @ 0x%08X\n", + __FUNCTION__, __LINE__, + physical, device->requestedRegisterMemSizes[i] + ); - gcmkVERIFY_OK(gckKERNEL_SetRecovery( - device->kernels[gcvCORE_MAJOR], Args->recovery, Args->stuckDump - )); + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } -#if COMMAND_PROCESSOR_VERSION == 1 - /* Start the command queue. */ - gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command)); -#endif - } - else - { - device->kernels[gcvCORE_MAJOR] = gcvNULL; + physical += device->requestedRegisterMemSizes[i]; + } } -#if gcdMULTI_GPU_AFFINITY - if (IrqLine3D1 != -1) + /* Set the base address */ + device->baseAddress = device->physBase = PhysBaseAddr; + device->physSize = PhysSize; + device->mmu = Args->mmu; + + /* Construct the gckOS object. */ + gcmkONERROR(gckOS_Construct(device, &device->os)); + + if (IrqLine != -1) { /* Construct the gckKERNEL object. */ gcmkONERROR(gckKERNEL_Construct( - device->os, gcvCORE_OCL, device, - gcvNULL, &device->kernels[gcvCORE_OCL])); + device->os, gcvCORE_MAJOR, device, + gcvNULL, &device->kernels[gcvCORE_MAJOR])); - if (sharedDB == gcvNULL) sharedDB = device->kernels[gcvCORE_OCL]->db; + sharedDB = device->kernels[gcvCORE_MAJOR]->db; /* Initialize core mapping */ - if (device->kernels[gcvCORE_MAJOR] == gcvNULL) - { - for (i = 0; i < 8; i++) - { - device->coreMapping[i] = gcvCORE_OCL; - } - } - else + for (i = 0; i < 8; i++) { - device->coreMapping[gcvHARDWARE_OCL] = gcvCORE_OCL; + device->coreMapping[i] = gcvCORE_MAJOR; } /* Setup the ISR manager. */ gcmkONERROR(gckHARDWARE_SetIsrManager( - device->kernels[gcvCORE_OCL]->hardware, + device->kernels[gcvCORE_MAJOR]->hardware, (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR, (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR, device )); gcmkONERROR(gckHARDWARE_SetFastClear( - device->kernels[gcvCORE_OCL]->hardware, FastClear, Compression + device->kernels[gcvCORE_MAJOR]->hardware, FastClear, Compression )); -#if gcdENABLE_FSCALE_VAL_ADJUST - gcmkONERROR(gckHARDWARE_SetMinFscaleValue( - device->kernels[gcvCORE_OCL]->hardware, Args->gpu3DMinClock - )); -#endif if(PowerManagement != -1) { gcmkONERROR(gckHARDWARE_SetPowerManagementLock( - device->kernels[gcvCORE_OCL]->hardware, gcvFALSE + device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE )); gcmkONERROR(gckHARDWARE_SetPowerManagement( - device->kernels[gcvCORE_OCL]->hardware, PowerManagement + device->kernels[gcvCORE_MAJOR]->hardware, PowerManagement )); gcmkONERROR(gckHARDWARE_SetPowerManagementLock( - device->kernels[gcvCORE_OCL]->hardware, gcvTRUE + device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE )); } else { gcmkONERROR(gckHARDWARE_SetPowerManagementLock( - device->kernels[gcvCORE_OCL]->hardware, gcvFALSE + device->kernels[gcvCORE_MAJOR]->hardware, gcvFALSE )); gcmkONERROR(gckHARDWARE_SetPowerManagement( - device->kernels[gcvCORE_OCL]->hardware, gcvTRUE + device->kernels[gcvCORE_MAJOR]->hardware, gcvTRUE )); } +#if gcdENABLE_FSCALE_VAL_ADJUST + gcmkONERROR(gckHARDWARE_SetMinFscaleValue( + device->kernels[gcvCORE_MAJOR]->hardware, Args->gpu3DMinClock + )); +#endif + + gcmkONERROR(gckHARDWARE_SetGpuProfiler( + device->kernels[gcvCORE_MAJOR]->hardware, GpuProfiler + )); + + gcmkVERIFY_OK(gckKERNEL_SetRecovery( + device->kernels[gcvCORE_MAJOR], Args->recovery, Args->stuckDump + )); + #if COMMAND_PROCESSOR_VERSION == 1 /* Start the command queue. */ - gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_OCL]->command)); + gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command)); #endif } else { - device->kernels[gcvCORE_OCL] = gcvNULL; + device->kernels[gcvCORE_MAJOR] = gcvNULL; } -#endif if (IrqLine2D != -1) { @@ -1328,11 +914,7 @@ gckGALDEVICE_Construct( } /* Initialize core mapping */ - if (device->kernels[gcvCORE_MAJOR] == gcvNULL -#if gcdMULTI_GPU_AFFINITY - && device->kernels[gcvCORE_OCL] == gcvNULL -#endif - ) + if (device->kernels[gcvCORE_MAJOR] == gcvNULL) { for (i = 0; i < 8; i++) { @@ -1403,9 +985,6 @@ gckGALDEVICE_Construct( /* Initialize core mapping */ if (device->kernels[gcvCORE_MAJOR] == gcvNULL && device->kernels[gcvCORE_2D] == gcvNULL -#if gcdMULTI_GPU_AFFINITY - && device->kernels[gcvCORE_OCL] == gcvNULL -#endif ) { for (i = 0; i < 8; i++) @@ -1442,36 +1021,14 @@ gckGALDEVICE_Construct( } /* Initialize the ISR. */ -#if gcdMULTI_GPU - device->irqLine3D[gcvCORE_3D_0_ID] = IrqLine3D0; -#if gcdMULTI_GPU > 1 - device->irqLine3D[gcvCORE_3D_1_ID] = IrqLine3D1; -#endif -#elif gcdMULTI_GPU_AFFINITY - device->irqLines[gcvCORE_MAJOR] = IrqLine3D0; - device->irqLines[gcvCORE_OCL] = IrqLine3D1; -#else device->irqLines[gcvCORE_MAJOR] = IrqLine; -#endif device->irqLines[gcvCORE_2D] = IrqLine2D; device->irqLines[gcvCORE_VG] = IrqLineVG; /* Initialize the kernel thread semaphores. */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { -#if gcdMULTI_GPU - if (i == gcvCORE_MAJOR) - { - for (j = 0; j < gcdMULTI_GPU; j++) - { - if (device->irqLine3D[j] != -1) init_waitqueue_head(&device->intrWaitQueue3D[j]); - } - } - else -#endif - { - if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0); - } + if (device->irqLines[i] != -1) sema_init(&device->semas[i], 0); } device->signal = Signal; @@ -1526,26 +1083,10 @@ gckGALDEVICE_Construct( /* Grab the first availiable kernel */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { -#if gcdMULTI_GPU - if (i == gcvCORE_MAJOR) - { - for (j = 0; j < gcdMULTI_GPU; j++) - { - if (device->irqLine3D[j] != -1) - { - kernel = device->kernels[i]; - break; - } - } - } - else -#endif + if (device->irqLines[i] != -1) { - if (device->irqLines[i] != -1) - { - kernel = device->kernels[i]; - break; - } + kernel = device->kernels[i]; + break; } } @@ -1754,9 +1295,6 @@ gckGALDEVICE_Destroy( gckGALDEVICE Device) { gctINT i; -#if gcdMULTI_GPU - gctINT j; -#endif gckKERNEL kernel = gcvNULL; gcmkHEADER_ARG("Device=0x%x", Device); @@ -1766,26 +1304,10 @@ gckGALDEVICE_Destroy( /* Grab the first availiable kernel */ for (i = 0; i < gcdMAX_GPU_COUNT; i++) { -#if gcdMULTI_GPU - if (i == gcvCORE_MAJOR) - { - for (j = 0; j < gcdMULTI_GPU; j++) - { - if (Device->irqLine3D[j] != -1) - { - kernel = Device->kernels[i]; - break; - } - } - } - else -#endif + if (Device->irqLines[i] != -1) { - if (Device->irqLines[i] != -1) - { - kernel = Device->kernels[i]; - break; - } + kernel = Device->kernels[i]; + break; } } @@ -1888,44 +1410,19 @@ gckGALDEVICE_Destroy( for (i = 0; i < gcdMAX_GPU_COUNT; i++) { -#if gcdMULTI_GPU - if (i == gcvCORE_MAJOR) + if (Device->registerBases[i] != gcvNULL) { - for (j = 0; j < gcdMULTI_GPU; j++) + /* Unmap register memory. */ + iounmap(Device->registerBases[i]); + if (Device->requestedRegisterMemBases[i] != 0) { - if (Device->registerBase3D[j] != gcvNULL) - { - /* Unmap register memory. */ - iounmap(Device->registerBase3D[j]); - if (Device->requestedRegisterMemBase3D[j] != 0) - { - release_mem_region(Device->requestedRegisterMemBase3D[j], - Device->requestedRegisterMemSize3D[j]); - } - - Device->registerBase3D[j] = gcvNULL; - Device->requestedRegisterMemBase3D[j] = 0; - Device->requestedRegisterMemSize3D[j] = 0; - } + release_mem_region(Device->requestedRegisterMemBases[i], + Device->requestedRegisterMemSizes[i]); } - } - else -#endif - { - if (Device->registerBases[i] != gcvNULL) - { - /* Unmap register memory. */ - iounmap(Device->registerBases[i]); - if (Device->requestedRegisterMemBases[i] != 0) - { - release_mem_region(Device->requestedRegisterMemBases[i], - Device->requestedRegisterMemSizes[i]); - } - Device->registerBases[i] = gcvNULL; - Device->requestedRegisterMemBases[i] = 0; - Device->requestedRegisterMemSizes[i] = 0; - } + Device->registerBases[i] = gcvNULL; + Device->requestedRegisterMemBases[i] = 0; + Device->requestedRegisterMemSizes[i] = 0; } } @@ -1986,89 +1483,6 @@ gckGALDEVICE_Setup_ISR( } /* Hook up the isr based on the irq line. */ -#if gcdMULTI_GPU - ret = request_irq( - Device->irqLine3D[gcvCORE_3D_0_ID], isrRoutine3D0, 0, - "galcore_3d_0", Device - ); - - if (ret != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not register irq line %d (error=%d)\n", - __FUNCTION__, __LINE__, - Device->irqLine3D[gcvCORE_3D_0_ID], ret - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - /* Mark ISR as initialized. */ - Device->isrInitialized3D[gcvCORE_3D_0_ID] = gcvTRUE; - -#if gcdMULTI_GPU > 1 - ret = request_irq( - Device->irqLine3D[gcvCORE_3D_1_ID], isrRoutine3D1, 0, - "galcore_3d_1", Device - ); - - if (ret != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not register irq line %d (error=%d)\n", - __FUNCTION__, __LINE__, - Device->irqLine3D[gcvCORE_3D_1_ID], ret - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - /* Mark ISR as initialized. */ - Device->isrInitialized3D[gcvCORE_3D_1_ID] = gcvTRUE; -#endif -#elif gcdMULTI_GPU_AFFINITY - ret = request_irq( - Device->irqLines[gcvCORE_MAJOR], isrRoutine3D0, 0, - "galcore_3d_0", Device - ); - - if (ret != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not register irq line %d (error=%d)\n", - __FUNCTION__, __LINE__, - Device->irqLines[gcvCORE_MAJOR], ret - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - /* Mark ISR as initialized. */ - Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE; - - ret = request_irq( - Device->irqLines[gcvCORE_OCL], isrRoutine3D1, 0, - "galcore_3d_1", Device - ); - - if (ret != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not register irq line %d (error=%d)\n", - __FUNCTION__, __LINE__, - Device->irqLines[gcvCORE_OCL], ret - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - /* Mark ISR as initialized. */ - Device->isrInitializeds[gcvCORE_OCL] = gcvTRUE; -#else ret = request_irq( Device->irqLines[gcvCORE_MAJOR], isrRoutine, 0, "galcore interrupt service", Device @@ -2088,7 +1502,6 @@ gckGALDEVICE_Setup_ISR( /* Mark ISR as initialized. */ Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE; -#endif gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -2218,29 +1631,12 @@ gckGALDEVICE_Release_ISR( gcmkVERIFY_ARGUMENT(Device != NULL); -#if gcdMULTI_GPU - /* release the irq */ - if (Device->isrInitialized3D[gcvCORE_3D_0_ID]) - { - free_irq(Device->irqLine3D[gcvCORE_3D_0_ID], Device); - Device->isrInitialized3D[gcvCORE_3D_0_ID] = gcvFALSE; - } -#if gcdMULTI_GPU > 1 - /* release the irq */ - if (Device->isrInitialized3D[gcvCORE_3D_1_ID]) - { - free_irq(Device->irqLine3D[gcvCORE_3D_1_ID], Device); - Device->isrInitialized3D[gcvCORE_3D_1_ID] = gcvFALSE; - } -#endif -#else /* release the irq */ if (Device->isrInitializeds[gcvCORE_MAJOR]) { free_irq(Device->irqLines[gcvCORE_MAJOR], Device); Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE; } -#endif gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -2321,86 +1717,6 @@ gckGALDEVICE_Start_Threads( gcmkVERIFY_ARGUMENT(Device != NULL); -#if gcdMULTI_GPU - if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) - { - /* Start the kernel thread. */ - task = kthread_run(threadRoutine3D0, Device, "galcore_3d_0"); - - if (IS_ERR(task)) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not start the kernel thread.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - Device->threadCtxt3D[gcvCORE_3D_0_ID] = task; - Device->threadInitialized3D[gcvCORE_3D_0_ID] = gcvTRUE; - -#if gcdMULTI_GPU > 1 - /* Start the kernel thread. */ - task = kthread_run(threadRoutine3D1, Device, "galcore_3d_1"); - - if (IS_ERR(task)) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not start the kernel thread.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - Device->threadCtxt3D[gcvCORE_3D_1_ID] = task; - Device->threadInitialized3D[gcvCORE_3D_1_ID] = gcvTRUE; -#endif - } -#elif gcdMULTI_GPU_AFFINITY - if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) - { - /* Start the kernel thread. */ - task = kthread_run(threadRoutine3D0, Device, "galcore_3d_0"); - - if (IS_ERR(task)) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not start the kernel thread.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - Device->threadCtxts[gcvCORE_MAJOR] = task; - Device->threadInitializeds[gcvCORE_MAJOR] = gcvTRUE; - } - - if (Device->kernels[gcvCORE_OCL] != gcvNULL) - { - /* Start the kernel thread. */ - task = kthread_run(threadRoutine3D1, Device, "galcore_3d_1"); - - if (IS_ERR(task)) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not start the kernel thread.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - Device->threadCtxts[gcvCORE_OCL] = task; - Device->threadInitializeds[gcvCORE_OCL] = gcvTRUE; - } -#else if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) { /* Start the kernel thread. */ @@ -2420,7 +1736,6 @@ gckGALDEVICE_Start_Threads( Device->threadCtxts[gcvCORE_MAJOR] = task; Device->threadInitializeds[gcvCORE_MAJOR] = gcvTRUE; } -#endif if (Device->kernels[gcvCORE_2D] != gcvNULL) { @@ -2504,9 +1819,6 @@ gckGALDEVICE_Stop_Threads( ) { gctINT i; -#if gcdMULTI_GPU - gctINT j; -#endif gcmkHEADER_ARG("Device=0x%x", Device); @@ -2514,37 +1826,15 @@ gckGALDEVICE_Stop_Threads( for (i = 0; i < gcdMAX_GPU_COUNT; i++) { -#if gcdMULTI_GPU - if (i == gcvCORE_MAJOR) - { - for (j = 0; j < gcdMULTI_GPU; j++) - { - /* Stop the kernel threads. */ - if (Device->threadInitialized3D[j]) - { - Device->killThread = gcvTRUE; - Device->dataReady3D[j] = gcvTRUE; - wake_up_interruptible(&Device->intrWaitQueue3D[j]); - - kthread_stop(Device->threadCtxt3D[j]); - Device->threadCtxt3D[j] = gcvNULL; - Device->threadInitialized3D[j] = gcvFALSE; - } - } - } - else -#endif + /* Stop the kernel threads. */ + if (Device->threadInitializeds[i]) { - /* Stop the kernel threads. */ - if (Device->threadInitializeds[i]) - { - Device->killThread = gcvTRUE; - up(&Device->semas[i]); + Device->killThread = gcvTRUE; + up(&Device->semas[i]); - kthread_stop(Device->threadCtxts[i]); - Device->threadCtxts[i] = gcvNULL; - Device->threadInitializeds[i] = gcvFALSE; - } + kthread_stop(Device->threadCtxts[i]); + Device->threadCtxts[i] = gcvNULL; + Device->threadInitializeds[i] = gcvFALSE; } } diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.h b/drivers/gpu/galcore/gc_hal_kernel_device.h index 08900158497704..a05aa928d8300d 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.h +++ b/drivers/gpu/galcore/gc_hal_kernel_device.h @@ -57,40 +57,22 @@ typedef struct _gckGALDEVICE gctBOOL contiguousRequested; gctSIZE_T systemMemorySize; gctUINT32 systemMemoryBaseAddress; -#if gcdMULTI_GPU - gctPOINTER registerBase3D[gcdMULTI_GPU]; - gctSIZE_T registerSize3D[gcdMULTI_GPU]; -#endif gctPOINTER registerBases[gcdMAX_GPU_COUNT]; gctSIZE_T registerSizes[gcdMAX_GPU_COUNT]; gctUINT32 baseAddress; gctUINT32 physBase; gctUINT32 physSize; gctBOOL mmu; -#if gcdMULTI_GPU - gctUINT32 requestedRegisterMemBase3D[gcdMULTI_GPU]; - gctSIZE_T requestedRegisterMemSize3D[gcdMULTI_GPU]; -#endif gctUINT32 requestedRegisterMemBases[gcdMAX_GPU_COUNT]; gctSIZE_T requestedRegisterMemSizes[gcdMAX_GPU_COUNT]; gctUINT32 requestedContiguousBase; gctSIZE_T requestedContiguousSize; /* IRQ management. */ -#if gcdMULTI_GPU - gctINT irqLine3D[gcdMULTI_GPU]; - gctBOOL isrInitialized3D[gcdMULTI_GPU]; - gctBOOL dataReady3D[gcdMULTI_GPU]; -#endif gctINT irqLines[gcdMAX_GPU_COUNT]; gctBOOL isrInitializeds[gcdMAX_GPU_COUNT]; /* Thread management. */ -#if gcdMULTI_GPU - struct task_struct *threadCtxt3D[gcdMULTI_GPU]; - wait_queue_head_t intrWaitQueue3D[gcdMULTI_GPU]; - gctBOOL threadInitialized3D[gcdMULTI_GPU]; -#endif struct task_struct *threadCtxts[gcdMAX_GPU_COUNT]; struct semaphore semas[gcdMAX_GPU_COUNT]; gctBOOL threadInitializeds[gcdMAX_GPU_COUNT]; @@ -175,18 +157,9 @@ gceSTATUS gckGALDEVICE_Stop( ); gceSTATUS gckGALDEVICE_Construct( -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - IN gctINT IrqLine3D0, - IN gctUINT32 RegisterMemBase3D0, - IN gctSIZE_T RegisterMemSize3D0, - IN gctINT IrqLine3D1, - IN gctUINT32 RegisterMemBase3D1, - IN gctSIZE_T RegisterMemSize3D1, -#else IN gctINT IrqLine, IN gctUINT32 RegisterMemBase, IN gctSIZE_T RegisterMemSize, -#endif IN gctINT IrqLine2D, IN gctUINT32 RegisterMemBase2D, IN gctSIZE_T RegisterMemSize2D, diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index f160ae9c7e729e..603fa653e8ab10 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -22,11 +22,6 @@ #include "gc_hal_kernel_precomp.h" #include "gc_hal_kernel_buffer.h" -#ifdef __QNXNTO__ -#include -#include "gc_hal_kernel_qnx.h" -#endif - #define _GC_OBJ_ZONE gcvZONE_EVENT #define gcdEVENT_ALLOCATION_COUNT (4096 / gcmSIZEOF(gcsHAL_INTERFACE)) @@ -434,11 +429,7 @@ _SubmitTimerFunction( ) { gckEVENT event = (gckEVENT)Data; -#if gcdMULTI_GPU - gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE, gcvCORE_3D_ALL_MASK)); -#else gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE)); -#endif } /******************************************************************************\ @@ -531,17 +522,6 @@ gckEVENT_Construct( #if gcdSMP gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending)); - -#if gcdMULTI_GPU - for (i = 0; i < gcdMULTI_GPU; i++) - { - gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending3D[i])); - gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pending3DMask[i])); - } - - gcmkONERROR(gckOS_AtomConstruct(os, &eventObj->pendingMask)); -#endif - #endif gcmkVERIFY_OK(gckOS_CreateTimer(os, @@ -598,21 +578,6 @@ gckEVENT_Construct( { gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending)); } - -#if gcdMULTI_GPU - for (i = 0; i < gcdMULTI_GPU; i++) - { - if (eventObj->pending3D[i] != gcvNULL) - { - gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending3D[i])); - } - - if (eventObj->pending3DMask[i] != gcvNULL) - { - gcmkVERIFY_OK(gckOS_AtomDestroy(os, eventObj->pending3DMask[i])); - } - } -#endif #endif #if gcdINTERRUPT_STATISTIC @@ -723,17 +688,6 @@ gckEVENT_Destroy( #if gcdSMP gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending)); - -#if gcdMULTI_GPU - { - gctINT i; - for (i = 0; i < gcdMULTI_GPU; i++) - { - gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending3D[i])); - gcmkVERIFY_OK(gckOS_AtomDestroy(Event->os, Event->pending3DMask[i])); - } - } -#endif #endif #if gcdINTERRUPT_STATISTIC @@ -776,16 +730,6 @@ gckEVENT_Destroy( */ #define gcdINVALID_EVENT_PTR ((gcsEVENT_PTR)gcvMAXUINTPTR_T) -#if gcdMULTI_GPU -gceSTATUS -gckEVENT_GetEvent( - IN gckEVENT Event, - IN gctBOOL Wait, - OUT gctUINT8 * EventID, - IN gceKERNEL_WHERE Source, - IN gceCORE_3D_MASK ChipEnable - ) -#else gceSTATUS gckEVENT_GetEvent( IN gckEVENT Event, @@ -793,15 +737,11 @@ gckEVENT_GetEvent( OUT gctUINT8 * EventID, IN gceKERNEL_WHERE Source ) -#endif { gctINT i, id; gceSTATUS status; gctBOOL acquired = gcvFALSE; gctINT32 free; -#if gcdMULTI_GPU - gctINT j; -#endif gcmkHEADER_ARG("Event=0x%x Source=%d", Event, Source); @@ -831,30 +771,6 @@ gckEVENT_GetEvent( Event->queues[id].stamp = ++(Event->stamp); Event->queues[id].source = Source; -#if gcdMULTI_GPU - Event->queues[id].chipEnable = ChipEnable; - - if (ChipEnable == gcvCORE_3D_ALL_MASK) - { - gckOS_AtomSetMask(Event->pendingMask, (1 << id)); - - for (j = 0; j < gcdMULTI_GPU; j++) - { - gckOS_AtomSetMask(Event->pending3DMask[j], (1 << id)); - } - } - else - { - for (j = 0; j < gcdMULTI_GPU; j++) - { - if (ChipEnable & (1 << j)) - { - gckOS_AtomSetMask(Event->pending3DMask[j], (1 << id)); - } - } - } -#endif - gcmkONERROR(gckOS_AtomDecrement(Event->os, Event->freeAtom, &free)); @@ -1110,10 +1026,6 @@ gckEVENT_AddList( } } -#ifdef __QNXNTO__ - record->kernel = Event->kernel; -#endif - /* Acquire the mutex. */ gcmkONERROR(gckOS_AcquireMutex(Event->os, Event->eventListMutex, gcvINFINITE)); acquired = gcvTRUE; @@ -1494,10 +1406,6 @@ gckEVENT_Signal( /* Mark the event as a signal. */ iface.command = gcvHAL_SIGNAL; iface.u.Signal.signal = gcmPTR_TO_UINT64(Signal); -#ifdef __QNXNTO__ - iface.u.Signal.coid = 0; - iface.u.Signal.rcvid = 0; -#endif iface.u.Signal.auxSignal = 0; iface.u.Signal.process = 0; @@ -1619,22 +1527,12 @@ gckEVENT_DestroyMmu( ** ** Nothing. */ -#if gcdMULTI_GPU -gceSTATUS -gckEVENT_Submit( - IN gckEVENT Event, - IN gctBOOL Wait, - IN gctBOOL FromPower, - IN gceCORE_3D_MASK ChipEnable - ) -#else gceSTATUS gckEVENT_Submit( IN gckEVENT Event, IN gctBOOL Wait, IN gctBOOL FromPower ) -#endif { gceSTATUS status; gctUINT8 id = 0xFF; @@ -1647,10 +1545,6 @@ gckEVENT_Submit( gctPOINTER buffer; #endif -#if gcdMULTI_GPU - gctSIZE_T chipEnableBytes; -#endif - #if gcdINTERRUPT_STATISTIC gctINT32 oldValue; #endif @@ -1695,11 +1589,7 @@ gckEVENT_Submit( queue = Event->queueHead; /* Allocate an event ID. */ -#if gcdMULTI_GPU - gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source, ChipEnable)); -#else gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source)); -#endif /* Copy event list to event ID queue. */ Event->queues[id].head = queue->head; @@ -1758,17 +1648,6 @@ gckEVENT_Submit( bytes += flushBytes; -#if gcdMULTI_GPU - gcmkONERROR(gckHARDWARE_ChipEnable( - hardware, - gcvNULL, - 0, - &chipEnableBytes - )); - - bytes += chipEnableBytes * 2; -#endif - /* Total bytes need to execute. */ executeBytes = bytes; @@ -1778,17 +1657,6 @@ gckEVENT_Submit( reservedBuffer = buffer; #endif -#if gcdMULTI_GPU - gcmkONERROR(gckHARDWARE_ChipEnable( - hardware, - buffer, - ChipEnable, - &chipEnableBytes - )); - - buffer = (gctUINT8_PTR)buffer + chipEnableBytes; -#endif - /* Set the flush in the command queue. */ gcmkONERROR(gckHARDWARE_Flush( hardware, @@ -1812,15 +1680,6 @@ gckEVENT_Submit( /* Advance to next command. */ buffer = (gctUINT8_PTR)buffer + bytes; -#if gcdMULTI_GPU - gcmkONERROR(gckHARDWARE_ChipEnable( - hardware, - buffer, - gcvCORE_3D_ALL_MASK, - &chipEnableBytes - )); -#endif - #if gcdSECURITY gckKERNEL_SecurityExecute( Event->kernel, @@ -1896,20 +1755,11 @@ gckEVENT_Submit( ** ** Nothing. */ -#if gcdMULTI_GPU -gceSTATUS -gckEVENT_Commit( - IN gckEVENT Event, - IN gcsQUEUE_PTR Queue, - IN gceCORE_3D_MASK ChipEnable - ) -#else gceSTATUS gckEVENT_Commit( IN gckEVENT Event, IN gcsQUEUE_PTR Queue ) -#endif { gceSTATUS status; gcsQUEUE_PTR record = gcvNULL, next; @@ -1978,11 +1828,7 @@ gckEVENT_Commit( } /* Submit the event list. */ -#if gcdMULTI_GPU - gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE, ChipEnable)); -#else gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); -#endif /* Success */ gcmkFOOTER_NO(); @@ -2041,11 +1887,7 @@ gckEVENT_Compose( gcmkVERIFY_ARGUMENT(Info != gcvNULL); /* Allocate an event ID. */ -#if gcdMULTI_GPU - gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL, gcvCORE_3D_ALL_MASK)); -#else gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL)); -#endif /* Get process ID. */ gcmkONERROR(gckOS_GetProcessID(&processID)); @@ -2057,10 +1899,6 @@ gckEVENT_Compose( /* Initialize the record. */ tempRecord->info.command = gcvHAL_SIGNAL; tempRecord->info.u.Signal.process = Info->process; -#ifdef __QNXNTO__ - tempRecord->info.u.Signal.coid = Info->coid; - tempRecord->info.u.Signal.rcvid = Info->rcvid; -#endif tempRecord->info.u.Signal.signal = Info->signal; tempRecord->info.u.Signal.auxSignal = 0; tempRecord->next = gcvNULL; @@ -2077,10 +1915,6 @@ gckEVENT_Compose( /* Initialize the record. */ tempRecord->info.command = gcvHAL_SIGNAL; tempRecord->info.u.Signal.process = Info->userProcess; -#ifdef __QNXNTO__ - tempRecord->info.u.Signal.coid = Info->coid; - tempRecord->info.u.Signal.rcvid = Info->rcvid; -#endif tempRecord->info.u.Signal.signal = Info->userSignal1; tempRecord->info.u.Signal.auxSignal = 0; tempRecord->next = gcvNULL; @@ -2097,10 +1931,6 @@ gckEVENT_Compose( /* Initialize the record. */ tempRecord->info.command = gcvHAL_SIGNAL; tempRecord->info.u.Signal.process = Info->userProcess; -#ifdef __QNXNTO__ - tempRecord->info.u.Signal.coid = Info->coid; - tempRecord->info.u.Signal.rcvid = Info->rcvid; -#endif tempRecord->info.u.Signal.signal = Info->userSignal2; tempRecord->info.u.Signal.auxSignal = 0; tempRecord->next = gcvNULL; @@ -2148,17 +1978,9 @@ gckEVENT_Compose( gceSTATUS gckEVENT_Interrupt( IN gckEVENT Event, -#if gcdMULTI_GPU - IN gctUINT CoreId, -#endif IN gctUINT32 Data ) { -#if gcdMULTI_GPU -#if defined(WIN32) - gctUINT32 i; -#endif -#endif gcmkHEADER_ARG("Event=0x%x Data=0x%x", Event, Data); /* Verify the arguments. */ @@ -2170,79 +1992,34 @@ gckEVENT_Interrupt( gctUINT32 idle; Data &= ~0x20000000; -#if gcdMULTI_GPU - if (Event->kernel->core == gcvCORE_MAJOR) -#endif - { - /* Get first entry information. */ - gcmkVERIFY_OK( - gckENTRYQUEUE_Dequeue(&Event->kernel->command->queue, &data)); + /* Get first entry information. */ + gcmkVERIFY_OK( + gckENTRYQUEUE_Dequeue(&Event->kernel->command->queue, &data)); - /* Make sure FE is idle. */ - do - { - gcmkVERIFY_OK(gckOS_ReadRegisterEx( - Event->os, - Event->kernel->core, - 0x4, - &idle)); - } - while (idle != 0x7FFFFFFF); - - /* Start Command Parser. */ - gcmkVERIFY_OK(gckHARDWARE_Execute( - Event->kernel->hardware, - data->physical, - data->bytes - )); + /* Make sure FE is idle. */ + do + { + gcmkVERIFY_OK(gckOS_ReadRegisterEx( + Event->os, + Event->kernel->core, + 0x4, + &idle)); } + while (idle != 0x7FFFFFFF); + + /* Start Command Parser. */ + gcmkVERIFY_OK(gckHARDWARE_Execute( + Event->kernel->hardware, + data->physical, + data->bytes + )); } /* Combine current interrupt status with pending flags. */ #if gcdSMP -#if gcdMULTI_GPU - if (Event->kernel->core == gcvCORE_MAJOR) - { - gckOS_AtomSetMask(Event->pending3D[CoreId], Data); - } - else -#endif - { - gckOS_AtomSetMask(Event->pending, Data); - } -#elif defined(__QNXNTO__) -#if gcdMULTI_GPU - if (Event->kernel->core == gcvCORE_MAJOR) - { - atomic_set(&Event->pending3D[CoreId], Data); - } - else -#endif - { - atomic_set(&Event->pending, Data); - } -#else -#if gcdMULTI_GPU -#if defined(WIN32) - if (Event->kernel->core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - Event->pending3D[i] |= Data; - } - } - else + gckOS_AtomSetMask(Event->pending, Data); #else - if (Event->kernel->core == gcvCORE_MAJOR) - { - Event->pending3D[CoreId] |= Data; - } - else -#endif -#endif - { - Event->pending |= Data; - } + Event->pending |= Data; #endif #if gcdINTERRUPT_STATISTIC @@ -2297,12 +2074,6 @@ gckEVENT_Notify( gctSIGNAL signal; gctUINT pending = 0; gckKERNEL kernel = Event->kernel; -#if gcdMULTI_GPU - gceCORE core = Event->kernel->core; - gctUINT32 busy; - gctUINT32 oldValue; - gctUINT pendingMask; -#endif #if !gcdSMP gctBOOL suspended = gcvFALSE; #endif @@ -2338,23 +2109,9 @@ gckEVENT_Notify( } ); -#if gcdMULTI_GPU - /* Set busy flag. */ - gckOS_AtomicExchange(Event->os, &Event->busy, 1, &busy); - if (busy) - { - /* Another thread is already busy - abort. */ - goto OnSuccess; - } -#endif - for (;;) { gcsEVENT_PTR record; -#if gcdMULTI_GPU - gctUINT32 pend[gcdMULTI_GPU]; - gctUINT32 pendMask[gcdMULTI_GPU]; -#endif /* Grab the mutex queue. */ gcmkONERROR(gckOS_AcquireMutex(Event->os, @@ -2363,79 +2120,18 @@ gckEVENT_Notify( acquired = gcvTRUE; #if gcdSMP -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - /* Get current interrupts. */ - for (i = 0; i < gcdMULTI_GPU; i++) - { - gckOS_AtomGet(Event->os, Event->pending3D[i], (gctINT32_PTR)&pend[i]); - gckOS_AtomGet(Event->os, Event->pending3DMask[i], (gctINT32_PTR)&pendMask[i]); - } - - gckOS_AtomGet(Event->os, Event->pendingMask, (gctINT32_PTR)&pendingMask); - } - else -#endif - { - gckOS_AtomGet(Event->os, Event->pending, (gctINT32_PTR)&pending); - } + gckOS_AtomGet(Event->os, Event->pending, (gctINT32_PTR)&pending); #else /* Suspend interrupts. */ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); suspended = gcvTRUE; -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - /* Get current interrupts. */ - pend[i] = Event->pending3D[i]; - pendMask[i] = Event->pending3DMask[i]; - } - - pendingMask = Event->pendingMask; - } - else -#endif - { - pending = Event->pending; - } + pending = Event->pending; /* Resume interrupts. */ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); suspended = gcvFALSE; #endif - -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - gctUINT32 bad_pend = (pend[i] & ~pendMask[i]); - - if (bad_pend != 0) - { - gcmkTRACE_ZONE_N( - gcvLEVEL_ERROR, gcvZONE_EVENT, - gcmSIZEOF(bad_pend) + gcmSIZEOF(i), - "Interrupts 0x%x are not unexpected for Core%d.", - bad_pend, i - ); - - gckOS_AtomClearMask(Event->pending3D[i], bad_pend); - - pend[i] &= pendMask[i]; - } - } - - pending = (pend[0] & pend[1] & pendingMask) /* Check combined events on both GPUs */ - | (pend[0] & ~pendingMask) /* Check individual events on GPU 0 */ - | (pend[1] & ~pendingMask); /* Check individual events on GPU 1 */ - } -#endif - if (pending == 0) { /* Release the mutex queue. */ @@ -2518,61 +2214,13 @@ gckEVENT_Notify( ); #if gcdSMP -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - /* Mark pending interrupts as handled. */ - for (i = 0; i < gcdMULTI_GPU; i++) - { - gckOS_AtomClearMask(Event->pending3D[i], pending); - gckOS_AtomClearMask(Event->pending3DMask[i], pending); - } - - gckOS_AtomClearMask(Event->pendingMask, pending); - } - else -#endif - { - gckOS_AtomClearMask(Event->pending, pending); - } - -#elif defined(__QNXNTO__) -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - atomic_clr((gctUINT32_PTR)&Event->pending3D[i], pending); - atomic_clr((gctUINT32_PTR)&Event->pending3DMask[i], pending); - } - - atomic_clr((gctUINT32_PTR)&Event->pendingMask, pending); - } - else -#endif - { - atomic_clr((gctUINT32_PTR)&Event->pending, pending); - } + gckOS_AtomClearMask(Event->pending, pending); #else /* Suspend interrupts. */ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); suspended = gcvTRUE; -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - /* Mark pending interrupts as handled. */ - Event->pending3D[i] &= ~pending; - Event->pending3DMask[i] &= ~pending; - } - } - else -#endif - { - Event->pending &= ~pending; - } + Event->pending &= ~pending; /* Resume interrupts. */ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); @@ -2591,9 +2239,6 @@ gckEVENT_Notify( if ((Event->queues[i].head != gcvNULL) && (Event->queues[i].stamp < queue->stamp) && (Event->queues[i].source <= queue->source) -#if gcdMULTI_GPU - && (Event->queues[i].chipEnable == queue->chipEnable) -#endif ) { gcmkTRACE_N( @@ -2622,63 +2267,13 @@ gckEVENT_Notify( } #if gcdSMP -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - /* Mark pending interrupt as handled. */ - gckOS_AtomClearMask(Event->pending3D[i], mask); - gckOS_AtomClearMask(Event->pending3DMask[i], mask); - } - - gckOS_AtomClearMask(Event->pendingMask, mask); - } - else -#endif - { - gckOS_AtomClearMask(Event->pending, mask); - } - -#elif defined(__QNXNTO__) -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - atomic_clr(&Event->pending3D[i], mask); - atomic_clr(&Event->pending3DMask[i], mask); - } - - atomic_clr(&Event->pendingMask, mask); - } - else -#endif - { - atomic_clr(&Event->pending, mask); - } + gckOS_AtomClearMask(Event->pending, mask); #else /* Suspend interrupts. */ gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); suspended = gcvTRUE; -#if gcdMULTI_GPU - if (core == gcvCORE_MAJOR) - { - for (i = 0; i < gcdMULTI_GPU; i++) - { - /* Mark pending interrupt as handled. */ - Event->pending3D[i] &= ~mask; - Event->pending3DMask[i] &= ~mask; - } - - Event->pendingMask &= ~mask; - } - else -#endif - { - Event->pending &= ~mask; - } + Event->pending &= ~mask; /* Resume interrupts. */ gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); @@ -2702,9 +2297,7 @@ gckEVENT_Notify( while (record != gcvNULL) { gcsEVENT_PTR recordNext; -#ifndef __QNXNTO__ gctPOINTER logical; -#endif #if gcdSECURE_USER gctSIZE_T bytes; #endif @@ -2712,13 +2305,6 @@ gckEVENT_Notify( /* Grab next record. */ recordNext = record->next; -#ifdef __QNXNTO__ - /* Assign record->processID as the pid for this galcore thread. - * Used in OS calls like gckOS_UnlockMemory() which do not take a pid. - */ - drv_thread_specific_key_assign(record->processID, 0, Event->kernel->core); -#endif - #if gcdSECURE_USER /* Get the cache that belongs to this process. */ gcmkONERROR(gckKERNEL_GetProcessDBCache(Event->kernel, @@ -2787,7 +2373,6 @@ gckEVENT_Notify( break; case gcvHAL_WRITE_DATA: -#ifndef __QNXNTO__ /* Convert physical into logical address. */ gcmkERR_BREAK( gckOS_MapPhysical(Event->os, @@ -2806,14 +2391,6 @@ gckEVENT_Notify( gckOS_UnmapPhysical(Event->os, logical, gcmSIZEOF(gctUINT32))); -#else - /* Write data. */ - gcmkERR_BREAK( - gckOS_WriteMemory(Event->os, - (gctPOINTER) - record->info.u.WriteData.address, - record->info.u.WriteData.data)); -#endif break; case gcvHAL_UNLOCK_VIDEO_MEMORY: @@ -2875,27 +2452,6 @@ gckEVENT_Notify( "gcvHAL_SIGNAL: 0x%x", signal); -#ifdef __QNXNTO__ - if ((record->info.u.Signal.coid == 0) - && (record->info.u.Signal.rcvid == 0) - ) - { - /* Kernel signal. */ - gcmkERR_BREAK( - gckOS_Signal(Event->os, - signal, - gcvTRUE)); - } - else - { - /* User signal. */ - gcmkERR_BREAK( - gckOS_UserSignal(Event->os, - signal, - record->info.u.Signal.rcvid, - record->info.u.Signal.coid)); - } -#else /* Set signal. */ if (gcmUINT64_TO_PTR(record->info.u.Signal.process) == gcvNULL) { @@ -2915,7 +2471,6 @@ gckEVENT_Notify( } gcmkASSERT(record->info.u.Signal.auxSignal == 0); -#endif break; case gcvHAL_UNMAP_USER_MEMORY: @@ -3044,19 +2599,11 @@ gckEVENT_Notify( "Handled interrupt 0x%x", mask); } -#if gcdMULTI_GPU - /* Clear busy flag. */ - gckOS_AtomicExchange(Event->os, &Event->busy, 0, &oldValue); -#endif - if (IDs == 0) { gcmkONERROR(_TryToIdleGPU(Event)); } -#if gcdMULTI_GPU -OnSuccess: -#endif /* Success. */ gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -3250,16 +2797,8 @@ gckEVENT_Stop( gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); /* Submit the current event queue. */ -#if gcdMULTI_GPU - gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE, gcvCORE_3D_ALL_MASK)); -#else gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); -#endif -#if gcdMULTI_GPU - gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL, gcvCORE_3D_ALL_MASK)); -#else gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL)); -#endif /* Allocate a record. */ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &record)); @@ -3269,10 +2808,6 @@ gckEVENT_Stop( record->processID = ProcessID; record->info.command = gcvHAL_SIGNAL; record->info.u.Signal.signal = gcmPTR_TO_UINT64(Signal); -#ifdef __QNXNTO__ - record->info.u.Signal.coid = 0; - record->info.u.Signal.rcvid = 0; -#endif record->info.u.Signal.auxSignal = 0; record->info.u.Signal.process = 0; diff --git a/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c b/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c index 5716223b9807b6..3bb92a8633f684 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c +++ b/drivers/gpu/galcore/gc_hal_kernel_interrupt_vg.c @@ -760,19 +760,10 @@ gckVGINTERRUPT_Disable( ** Nothing. */ -#ifndef __QNXNTO__ gceSTATUS gckVGINTERRUPT_Enque( IN gckVGINTERRUPT Interrupt ) -#else -gceSTATUS -gckVGINTERRUPT_Enque( - IN gckVGINTERRUPT Interrupt, - OUT gckOS *Os, - OUT gctSEMAPHORE *Semaphore - ) -#endif { gceSTATUS status; gctUINT32 triggered; @@ -782,11 +773,6 @@ gckVGINTERRUPT_Enque( /* Verify the arguments. */ gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT); -#ifdef __QNXNTO__ - *Os = gcvNULL; - *Semaphore = gcvNULL; -#endif - do { /* Read interrupt status register. */ @@ -833,15 +819,10 @@ gckVGINTERRUPT_Enque( /* Set the new value. */ Interrupt->fifo[Interrupt->head] = triggered; -#ifndef __QNXNTO__ /* Increment the FIFO semaphore. */ gcmkERR_BREAK(gckOS_IncrementSemaphore( Interrupt->os, Interrupt->fifoValid )); -#else - *Os = Interrupt->os; - *Semaphore = Interrupt->fifoValid; -#endif /* Windows kills our threads prematurely when the application exists. Verify here that the thread is still alive. */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_linux.c b/drivers/gpu/galcore/gc_hal_kernel_linux.c index 344f05827a866c..e1af24fbee2f70 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_linux.c +++ b/drivers/gpu/galcore/gc_hal_kernel_linux.c @@ -430,9 +430,6 @@ gckKERNEL_MapVideoMemory( gceSTATUS gckKERNEL_Notify( IN gckKERNEL Kernel, -#if gcdMULTI_GPU - IN gctUINT CoreId, -#endif IN gceNOTIFY Notification, IN gctBOOL Data ) @@ -454,9 +451,6 @@ gckKERNEL_Notify( status = gckINTERRUPT_Notify(Kernel->interrupt, Data); #else status = gckHARDWARE_Interrupt(Kernel->hardware, -#if gcdMULTI_GPU - CoreId, -#endif Data); #endif break; diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index e8dcfd632fd8ad..225ed6ff1f991b 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -490,16 +490,7 @@ _AllowAccess( return gcvTRUE; } -#if gcdMULTI_GPU - if (Core == gcvCORE_MAJOR) - { - data = readl((gctUINT8 *)Os->device->registerBases[gcvCORE_3D_0_ID] + 0x0); - } - else -#endif - { - data = readl((gctUINT8 *)Os->device->registerBases[Core] + 0x0); - } + data = readl((gctUINT8 *)Os->device->registerBases[Core] + 0x0); if ((data & 0x3) == 0x3) { @@ -1893,9 +1884,7 @@ gckOS_ReadRegisterEx( /* Verify the arguments. */ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); -#if !gcdMULTI_GPU gcmkVERIFY_ARGUMENT(Address < Os->device->requestedRegisterMemSizes[Core]); -#endif gcmkVERIFY_ARGUMENT(Data != gcvNULL); if (!in_interrupt()) @@ -1905,16 +1894,7 @@ gckOS_ReadRegisterEx( BUG_ON(!_AllowAccess(Os, Core, Address)); -#if gcdMULTI_GPU - if (Core == gcvCORE_MAJOR) - { - *Data = readl((gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_0_ID] + Address); - } - else -#endif - { - *Data = readl((gctUINT8 *)Os->device->registerBases[Core] + Address); - } + *Data = readl((gctUINT8 *)Os->device->registerBases[Core] + Address); if (!in_interrupt()) { @@ -1926,31 +1906,6 @@ gckOS_ReadRegisterEx( return gcvSTATUS_OK; } -#if gcdMULTI_GPU -gceSTATUS -gckOS_ReadRegisterByCoreId( - IN gckOS Os, - IN gceCORE Core, - IN gctUINT32 CoreId, - IN gctUINT32 Address, - OUT gctUINT32 * Data - ) -{ - gcmkHEADER_ARG("Os=0x%X Core=%d CoreId=%d Address=0x%X", - Os, Core, CoreId, Address); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); - gcmkVERIFY_ARGUMENT(Data != gcvNULL); - - *Data = readl((gctUINT8 *)Os->device->registerBase3D[CoreId] + Address); - - /* Success. */ - gcmkFOOTER_ARG("*Data=0x%08x", *Data); - return gcvSTATUS_OK; -} -#endif - /******************************************************************************* ** ** gckOS_WriteRegister @@ -1992,9 +1947,7 @@ gckOS_WriteRegisterEx( { gcmkHEADER_ARG("Os=0x%X Core=%d Address=0x%X Data=0x%08x", Os, Core, Address, Data); -#if !gcdMULTI_GPU gcmkVERIFY_ARGUMENT(Address < Os->device->requestedRegisterMemSizes[Core]); -#endif if (!in_interrupt()) { @@ -2003,19 +1956,7 @@ gckOS_WriteRegisterEx( BUG_ON(!_AllowAccess(Os, Core, Address)); -#if gcdMULTI_GPU - if (Core == gcvCORE_MAJOR) - { - writel(Data, (gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_0_ID] + Address); -#if gcdMULTI_GPU > 1 - writel(Data, (gctUINT8 *)Os->device->registerBase3D[gcvCORE_3D_1_ID] + Address); -#endif - } - else -#endif - { - writel(Data, (gctUINT8 *)Os->device->registerBases[Core] + Address); - } + writel(Data, (gctUINT8 *)Os->device->registerBases[Core] + Address); if (!in_interrupt()) { @@ -2027,27 +1968,6 @@ gckOS_WriteRegisterEx( return gcvSTATUS_OK; } -#if gcdMULTI_GPU -gceSTATUS -gckOS_WriteRegisterByCoreId( - IN gckOS Os, - IN gceCORE Core, - IN gctUINT32 CoreId, - IN gctUINT32 Address, - IN gctUINT32 Data - ) -{ - gcmkHEADER_ARG("Os=0x%X Core=%d CoreId=%d Address=0x%X Data=0x%08x", - Os, Core, CoreId, Address, Data); - - writel(Data, (gctUINT8 *)Os->device->registerBase3D[CoreId] + Address); - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} -#endif - /******************************************************************************* ** ** gckOS_GetPageSize @@ -5547,32 +5467,6 @@ gckOS_SuspendInterrupt( return gckOS_SuspendInterruptEx(Os, gcvCORE_MAJOR); } -#if gcdMULTI_GPU -gceSTATUS -gckOS_SuspendInterruptEx( - IN gckOS Os, - IN gceCORE Core - ) -{ - gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); - - if (Core == gcvCORE_MAJOR) - { - disable_irq(Os->device->irqLine3D[gcvCORE_3D_0_ID]); - disable_irq(Os->device->irqLine3D[gcvCORE_3D_1_ID]); - } - else - { - disable_irq(Os->device->irqLines[Core]); - } - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} -#else gceSTATUS gckOS_SuspendInterruptEx( IN gckOS Os, @@ -5589,7 +5483,6 @@ gckOS_SuspendInterruptEx( gcmkFOOTER_NO(); return gcvSTATUS_OK; } -#endif gceSTATUS gckOS_ResumeInterrupt( @@ -5599,32 +5492,6 @@ gckOS_ResumeInterrupt( return gckOS_ResumeInterruptEx(Os, gcvCORE_MAJOR); } -#if gcdMULTI_GPU -gceSTATUS -gckOS_ResumeInterruptEx( - IN gckOS Os, - IN gceCORE Core - ) -{ - gcmkHEADER_ARG("Os=0x%X Core=%d", Os, Core); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); - - if (Core == gcvCORE_MAJOR) - { - enable_irq(Os->device->irqLine3D[gcvCORE_3D_0_ID]); - enable_irq(Os->device->irqLine3D[gcvCORE_3D_1_ID]); - } - else - { - enable_irq(Os->device->irqLines[Core]); - } - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} -#else gceSTATUS gckOS_ResumeInterruptEx( IN gckOS Os, @@ -5641,7 +5508,6 @@ gckOS_ResumeInterruptEx( gcmkFOOTER_NO(); return gcvSTATUS_OK; } -#endif gceSTATUS gckOS_MemCopy( diff --git a/drivers/gpu/galcore/gc_hal_kernel_platform.h b/drivers/gpu/galcore/gc_hal_kernel_platform.h index 23b7178d83ba6f..d3be8dfd24d754 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_platform.h +++ b/drivers/gpu/galcore/gc_hal_kernel_platform.h @@ -25,18 +25,9 @@ typedef struct _gcsMODULE_PARAMETERS { -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - gctINT irqLine3D0; - gctUINT registerMemBase3D0; - gctUINT registerMemSize3D0; - gctINT irqLine3D1; - gctUINT registerMemBase3D1; - gctUINT registerMemSize3D1; -#else gctINT irqLine; gctUINT registerMemBase; gctUINT registerMemSize; -#endif gctINT irqLine2D; gctUINT registerMemBase2D; gctUINT registerMemSize2D; diff --git a/drivers/gpu/galcore/gc_hal_kernel_probe.c b/drivers/gpu/galcore/gc_hal_kernel_probe.c index cd5f8ed6e51f93..10098c3ef2c8ac 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_probe.c +++ b/drivers/gpu/galcore/gc_hal_kernel_probe.c @@ -25,9 +25,7 @@ #include "gc_hal_kernel_linux.h" #include "gc_hal_driver.h" -#if USE_PLATFORM_DRIVER -# include -#endif +#include #ifdef CONFIG_PXA_DVFM # include @@ -50,25 +48,6 @@ static gckGALDEVICE galDevice; static uint major = 199; module_param(major, uint, 0644); -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY -static int irqLine3D0 = -1; -module_param(irqLine3D0, int, 0644); - -static ulong registerMemBase3D0 = 0; -module_param(registerMemBase3D0, ulong, 0644); - -static ulong registerMemSize3D0 = 2 << 10; -module_param(registerMemSize3D0, ulong, 0644); - -static int irqLine3D1 = -1; -module_param(irqLine3D1, int, 0644); - -static ulong registerMemBase3D1 = 0; -module_param(registerMemBase3D1, ulong, 0644); - -static ulong registerMemSize3D1 = 2 << 10; -module_param(registerMemSize3D1, ulong, 0644); -#else static int irqLine = -1; module_param(irqLine, int, 0644); @@ -77,7 +56,6 @@ module_param(registerMemBase, ulong, 0644); static ulong registerMemSize = 2 << 10; module_param(registerMemSize, ulong, 0644); -#endif static int irqLine2D = -1; module_param(irqLine2D, int, 0644); @@ -190,12 +168,9 @@ _UpdateModuleParam( gcsMODULE_PARAMETERS *Param ) { -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY -#else irqLine = Param->irqLine ; registerMemBase = Param->registerMemBase; registerMemSize = Param->registerMemSize; -#endif irqLine2D = Param->irqLine2D ; registerMemBase2D = Param->registerMemBase2D; registerMemSize2D = Param->registerMemSize2D; @@ -226,22 +201,9 @@ gckOS_DumpParam( ) { printk("Galcore options:\n"); -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - printk(" irqLine3D0 = %d\n", irqLine3D0); - printk(" registerMemBase3D0 = 0x%08lX\n", registerMemBase3D0); - printk(" registerMemSize3D0 = 0x%08lX\n", registerMemSize3D0); - - if (irqLine3D1 != -1) - { - printk(" irqLine3D1 = %d\n", irqLine3D1); - printk(" registerMemBase3D1 = 0x%08lX\n", registerMemBase3D1); - printk(" registerMemSize3D1 = 0x%08lX\n", registerMemSize3D1); - } -#else printk(" irqLine = %d\n", irqLine); printk(" registerMemBase = 0x%08lX\n", registerMemBase); printk(" registerMemSize = 0x%08lX\n", registerMemSize); -#endif if (irqLine2D != -1) { @@ -796,11 +758,7 @@ static int drv_mmap( } -#if !USE_PLATFORM_DRIVER -static int __init drv_init(void) -#else static int drv_init(void) -#endif { int ret; int result = -EINVAL; @@ -842,15 +800,8 @@ static int drv_init(void) /* Create the GAL device. */ status = gckGALDEVICE_Construct( -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - irqLine3D0, - registerMemBase3D0, registerMemSize3D0, - irqLine3D1, - registerMemBase3D1, registerMemSize3D1, -#else irqLine, registerMemBase, registerMemSize, -#endif irqLine2D, registerMemBase2D, registerMemSize2D, irqLineVG, @@ -922,21 +873,12 @@ static int drv_init(void) galDevice = device; gpuClass = device_class; -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY - gcmkTRACE_ZONE( - gcvLEVEL_INFO, gcvZONE_DRIVER, - "%s(%d): irqLine3D0=%d, contiguousSize=%lu, memBase3D0=0x%lX\n", - __FUNCTION__, __LINE__, - irqLine3D0, contiguousSize, registerMemBase3D0 - ); -#else gcmkTRACE_ZONE( gcvLEVEL_INFO, gcvZONE_DRIVER, "%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n", __FUNCTION__, __LINE__, irqLine, contiguousSize, registerMemBase ); -#endif /* Success. */ gcmkFOOTER_NO(); @@ -960,11 +902,7 @@ static int drv_init(void) return result; } -#if !USE_PLATFORM_DRIVER -static void __exit drv_exit(void) -#else static void drv_exit(void) -#endif { gcmkHEADER(); @@ -985,21 +923,13 @@ static void drv_exit(void) gcmkFOOTER_NO(); } -#if !USE_PLATFORM_DRIVER - module_init(drv_init); - module_exit(drv_exit); -#else - static int gpu_probe(struct platform_device *pdev) { int ret = -ENODEV; gcsMODULE_PARAMETERS moduleParam = { -#if gcdMULTI_GPU || gcdMULTI_GPU_AFFINITY -#else .irqLine = irqLine, .registerMemBase = registerMemBase, .registerMemSize = registerMemSize, -#endif .irqLine2D = irqLine2D, .registerMemBase2D = registerMemBase2D, .registerMemSize2D = registerMemSize2D, @@ -1320,5 +1250,3 @@ static void __exit gpu_exit(void) module_init(gpu_init); module_exit(gpu_exit); - -#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c index 237b856749066c..80240631541a47 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c +++ b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c @@ -90,10 +90,6 @@ _Split( node->VidMem.memory = Node->VidMem.memory; node->VidMem.pool = Node->VidMem.pool; node->VidMem.physical = Node->VidMem.physical; -#ifdef __QNXNTO__ - node->VidMem.processID = 0; - node->VidMem.logical = gcvNULL; -#endif /* Insert node behind specified node. */ node->VidMem.next = Node->VidMem.next; @@ -440,11 +436,6 @@ gckVIDMEM_Construct( node->VidMem.locked = 0; -#ifdef __QNXNTO__ - node->VidMem.processID = 0; - node->VidMem.logical = gcvNULL; -#endif - #if gcdENABLE_VG node->VidMem.kernelVirtual = gcvNULL; #endif @@ -962,10 +953,6 @@ gckVIDMEM_AllocateLinear( /* Fill in the information. */ node->VidMem.alignment = alignment; node->VidMem.memory = Memory; -#ifdef __QNXNTO__ - node->VidMem.logical = gcvNULL; - gcmkONERROR(gckOS_GetProcessID(&node->VidMem.processID)); -#endif /* Adjust the number of free bytes. */ Memory->freeBytes -= node->VidMem.bytes; @@ -1054,87 +1041,65 @@ gckVIDMEM_Free( mutexAcquired = gcvTRUE; -#ifdef __QNXNTO__ - /* Unmap the video memory. */ - if (Node->VidMem.logical != gcvNULL) - { - gckKERNEL_UnmapVideoMemory( - Kernel, - Node->VidMem.logical, - Node->VidMem.processID, - Node->VidMem.bytes); - Node->VidMem.logical = gcvNULL; - } - - /* Reset. */ - Node->VidMem.processID = 0; - - /* Don't try to re-free an already freed node. */ - if ((Node->VidMem.nextFree == gcvNULL) - && (Node->VidMem.prevFree == gcvNULL) - ) -#endif - { #if gcdENABLE_VG - if (Node->VidMem.kernelVirtual) - { - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, - "%s(%d) Unmap %x from kernel space.", - __FUNCTION__, __LINE__, - Node->VidMem.kernelVirtual); + if (Node->VidMem.kernelVirtual) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, + "%s(%d) Unmap %x from kernel space.", + __FUNCTION__, __LINE__, + Node->VidMem.kernelVirtual); - gcmkVERIFY_OK( - gckOS_UnmapPhysical(memory->os, - Node->VidMem.kernelVirtual, - Node->VidMem.bytes)); + gcmkVERIFY_OK( + gckOS_UnmapPhysical(memory->os, + Node->VidMem.kernelVirtual, + Node->VidMem.bytes)); - Node->VidMem.kernelVirtual = gcvNULL; - } + Node->VidMem.kernelVirtual = gcvNULL; + } #endif - /* Check if Node is already freed. */ - if (Node->VidMem.nextFree) - { - /* Node is alread freed. */ - gcmkONERROR(gcvSTATUS_INVALID_DATA); - } + /* Check if Node is already freed. */ + if (Node->VidMem.nextFree) + { + /* Node is alread freed. */ + gcmkONERROR(gcvSTATUS_INVALID_DATA); + } - /* Update the number of free bytes. */ - memory->freeBytes += Node->VidMem.bytes; + /* Update the number of free bytes. */ + memory->freeBytes += Node->VidMem.bytes; - /* Find the next free node. */ - for (node = Node->VidMem.next; - node != gcvNULL && node->VidMem.nextFree == gcvNULL; - node = node->VidMem.next) ; + /* Find the next free node. */ + for (node = Node->VidMem.next; + node != gcvNULL && node->VidMem.nextFree == gcvNULL; + node = node->VidMem.next) ; - /* Insert this node in the free list. */ - Node->VidMem.nextFree = node; - Node->VidMem.prevFree = node->VidMem.prevFree; + /* Insert this node in the free list. */ + Node->VidMem.nextFree = node; + Node->VidMem.prevFree = node->VidMem.prevFree; - Node->VidMem.prevFree->VidMem.nextFree = - node->VidMem.prevFree = Node; + Node->VidMem.prevFree->VidMem.nextFree = + node->VidMem.prevFree = Node; - /* Is the next node a free node and not the sentinel? */ - if ((Node->VidMem.next == Node->VidMem.nextFree) - && (Node->VidMem.next->VidMem.bytes != 0) - ) - { - /* Merge this node with the next node. */ - gcmkONERROR(_Merge(memory->os, node = Node)); - gcmkASSERT(node->VidMem.nextFree != node); - gcmkASSERT(node->VidMem.prevFree != node); - } + /* Is the next node a free node and not the sentinel? */ + if ((Node->VidMem.next == Node->VidMem.nextFree) + && (Node->VidMem.next->VidMem.bytes != 0) + ) + { + /* Merge this node with the next node. */ + gcmkONERROR(_Merge(memory->os, node = Node)); + gcmkASSERT(node->VidMem.nextFree != node); + gcmkASSERT(node->VidMem.prevFree != node); + } - /* Is the previous node a free node and not the sentinel? */ - if ((Node->VidMem.prev == Node->VidMem.prevFree) - && (Node->VidMem.prev->VidMem.bytes != 0) - ) - { - /* Merge this node with the previous node. */ - gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev)); - gcmkASSERT(node->VidMem.nextFree != node); - gcmkASSERT(node->VidMem.prevFree != node); - } + /* Is the previous node a free node and not the sentinel? */ + if ((Node->VidMem.prev == Node->VidMem.prevFree) + && (Node->VidMem.prev->VidMem.bytes != 0) + ) + { + /* Merge this node with the previous node. */ + gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev)); + gcmkASSERT(node->VidMem.nextFree != node); + gcmkASSERT(node->VidMem.prevFree != node); } /* Release the mutex. */ diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h index f4b7d991128294..92636c5530e394 100644 --- a/drivers/gpu/galcore/inc/gc_hal.h +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -187,17 +187,10 @@ typedef enum _gceCORE gcvCORE_MAJOR = 0x0, gcvCORE_2D = 0x1, gcvCORE_VG = 0x2, -#if gcdMULTI_GPU_AFFINITY - gcvCORE_OCL = 0x3, -#endif } gceCORE; -#if gcdMULTI_GPU_AFFINITY -#define gcdMAX_GPU_COUNT 4 -#else #define gcdMAX_GPU_COUNT 3 -#endif #define gcdMAX_SURF_LAYER 4 @@ -550,26 +543,6 @@ gckOS_WriteRegisterEx( IN gctUINT32 Data ); -#if gcdMULTI_GPU -gceSTATUS -gckOS_ReadRegisterByCoreId( - IN gckOS Os, - IN gceCORE Core, - IN gctUINT32 CoreId, - IN gctUINT32 Address, - OUT gctUINT32 * Data - ); - -gceSTATUS -gckOS_WriteRegisterByCoreId( - IN gckOS Os, - IN gceCORE Core, - IN gctUINT32 CoreId, - IN gctUINT32 Address, - IN gctUINT32 Data - ); -#endif - /* Write data to a 32-bit memory location. */ gceSTATUS gckOS_WriteMemory( @@ -1281,22 +1254,12 @@ gckOS_SignalUserSignal( #endif /* USE_NEW_LINUX_SIGNAL */ /* Set a signal owned by a process. */ -#if defined(__QNXNTO__) -gceSTATUS -gckOS_UserSignal( - IN gckOS Os, - IN gctSIGNAL Signal, - IN gctINT Recvid, - IN gctINT Coid - ); -#else gceSTATUS gckOS_UserSignal( IN gckOS Os, IN gctSIGNAL Signal, IN gctHANDLE Process ); -#endif /******************************************************************************\ ** Cache Support @@ -1733,17 +1696,11 @@ typedef enum _gceKERNEL_FLUSH gcvFLUSH_DEPTH = 0x02, gcvFLUSH_TEXTURE = 0x04, gcvFLUSH_2D = 0x08, -#if gcdMULTI_GPU - gcvFLUSH_L2 = 0x10, -#endif gcvFLUSH_TILE_STATUS = 0x20, gcvFLUSH_ALL = gcvFLUSH_COLOR | gcvFLUSH_DEPTH | gcvFLUSH_TEXTURE | gcvFLUSH_2D -#if gcdMULTI_GPU - | gcvFLUSH_L2 -#endif | gcvFLUSH_TILE_STATUS } gceKERNEL_FLUSH; @@ -1836,10 +1793,6 @@ gckKERNEL_MapVideoMemory( IN gckKERNEL Kernel, IN gctBOOL InUserSpace, IN gctUINT32 Address, -#ifdef __QNXNTO__ - IN gctUINT32 Pid, - IN gctUINT32 Bytes, -#endif OUT gctPOINTER * Logical ); @@ -1850,24 +1803,9 @@ gckKERNEL_MapVideoMemoryEx( IN gceCORE Core, IN gctBOOL InUserSpace, IN gctUINT32 Address, -#ifdef __QNXNTO__ - IN gctUINT32 Pid, - IN gctUINT32 Bytes, -#endif OUT gctPOINTER * Logical ); -#ifdef __QNXNTO__ -/* Unmap video memory. */ -gceSTATUS -gckKERNEL_UnmapVideoMemory( - IN gckKERNEL Kernel, - IN gctPOINTER Logical, - IN gctUINT32 Pid, - IN gctUINT32 Bytes - ); -#endif - /* Map memory. */ gceSTATUS gckKERNEL_MapMemory( @@ -1890,9 +1828,6 @@ gckKERNEL_UnmapMemory( gceSTATUS gckKERNEL_Notify( IN gckKERNEL Kernel, -#if gcdMULTI_GPU - IN gctUINT CoreId, -#endif IN gceNOTIFY Notifcation, IN gctBOOL Data ); @@ -2051,16 +1986,6 @@ gckHARDWARE_End( IN OUT gctUINT32 * Bytes ); -#if gcdMULTI_GPU -gceSTATUS -gckHARDWARE_ChipEnable( - IN gckHARDWARE Hardware, - IN gctPOINTER Logical, - IN gceCORE_3D_MASK ChipEnable, - IN OUT gctSIZE_T * Bytes - ); -#endif - /* Add a NOP command in the command queue. */ gceSTATUS gckHARDWARE_Nop( @@ -2158,9 +2083,6 @@ gckHARDWARE_ConvertLogical( gceSTATUS gckHARDWARE_Interrupt( IN gckHARDWARE Hardware, -#if gcdMULTI_GPU - IN gctUINT CoreId, -#endif IN gctBOOL InterruptValid ); @@ -2443,16 +2365,6 @@ gckEVENT_Destroy( ); /* Reserve the next available hardware event. */ -#if gcdMULTI_GPU -gceSTATUS -gckEVENT_GetEvent( - IN gckEVENT Event, - IN gctBOOL Wait, - OUT gctUINT8 * EventID, - IN gceKERNEL_WHERE Source, - IN gceCORE_3D_MASK ChipEnable - ); -#else gceSTATUS gckEVENT_GetEvent( IN gckEVENT Event, @@ -2460,7 +2372,6 @@ gckEVENT_GetEvent( OUT gctUINT8 * EventID, IN gceKERNEL_WHERE Source ); -#endif /* Add a new event to the list of events. */ gceSTATUS @@ -2533,37 +2444,18 @@ gckEVENT_DestroyVirtualCommandBuffer( IN gceKERNEL_WHERE FromWhere ); -#if gcdMULTI_GPU -gceSTATUS -gckEVENT_Submit( - IN gckEVENT Event, - IN gctBOOL Wait, - IN gctBOOL FromPower, - IN gceCORE_3D_MASK ChipEnable - ); -#else gceSTATUS gckEVENT_Submit( IN gckEVENT Event, IN gctBOOL Wait, IN gctBOOL FromPower ); -#endif -#if gcdMULTI_GPU -gceSTATUS -gckEVENT_Commit( - IN gckEVENT Event, - IN gcsQUEUE_PTR Queue, - IN gceCORE_3D_MASK ChipEnable - ); -#else gceSTATUS gckEVENT_Commit( IN gckEVENT Event, IN gcsQUEUE_PTR Queue ); -#endif /* Schedule a composition event. */ gceSTATUS @@ -2583,9 +2475,6 @@ gckEVENT_Notify( gceSTATUS gckEVENT_Interrupt( IN gckEVENT Event, -#if gcdMULTI_GPU - IN gctUINT CoreId, -#endif IN gctUINT32 IDs ); @@ -2639,19 +2528,6 @@ gckCOMMAND_Stop( IN gctBOOL FromRecovery ); -#if gcdMULTI_GPU -/* Commit a buffer to the command queue. */ -gceSTATUS -gckCOMMAND_Commit( - IN gckCOMMAND Command, - IN gckCONTEXT Context, - IN gcoCMDBUF CommandBuffer, - IN gcsSTATE_DELTA_PTR StateDelta, - IN gcsQUEUE_PTR EventQueue, - IN gctUINT32 ProcessID, - IN gceCORE_3D_MASK ChipEnable - ); -#else gceSTATUS gckCOMMAND_Commit( IN gckCOMMAND Command, @@ -2661,7 +2537,6 @@ gckCOMMAND_Commit( IN gcsQUEUE_PTR EventQueue, IN gctUINT32 ProcessID ); -#endif /* Reserve space in the command buffer. */ gceSTATUS @@ -2680,20 +2555,11 @@ gckCOMMAND_Execute( ); /* Stall the command queue. */ -#if gcdMULTI_GPU -gceSTATUS -gckCOMMAND_Stall( - IN gckCOMMAND Command, - IN gctBOOL FromPower, - IN gceCORE_3D_MASK ChipEnable - ); -#else gceSTATUS gckCOMMAND_Stall( IN gckCOMMAND Command, IN gctBOOL FromPower ); -#endif /* Attach user process. */ gceSTATUS diff --git a/drivers/gpu/galcore/inc/gc_hal_base.h b/drivers/gpu/galcore/inc/gc_hal_base.h index b798152523af33..239d4208b3d0c8 100644 --- a/drivers/gpu/galcore/inc/gc_hal_base.h +++ b/drivers/gpu/galcore/inc/gc_hal_base.h @@ -1717,20 +1717,8 @@ gcoOS_Delay( /*----------------------------------------------------------------------------*/ /*----- Threads --------------------------------------------------------------*/ -#ifdef _WIN32 -/* Cannot include windows.h here becuase "near" and "far" - * which are used in gcsDEPTH_INFO, are defined to nothing in WinDef.h. - * So, use the real value of DWORD and WINAPI, instead. - * DWORD is unsigned long, and WINAPI is __stdcall. - * If these two are change in WinDef.h, the following two typdefs - * need to be changed, too. - */ -typedef unsigned long gctTHREAD_RETURN; -typedef unsigned long (__stdcall * gcTHREAD_ROUTINE)(void * Argument); -#else typedef void * gctTHREAD_RETURN; typedef void * (* gcTHREAD_ROUTINE)(void *); -#endif /* Create a new thread. */ gceSTATUS diff --git a/drivers/gpu/galcore/inc/gc_hal_driver.h b/drivers/gpu/galcore/inc/gc_hal_driver.h index 345b4eaca17b7f..fa54d891bee3d8 100644 --- a/drivers/gpu/galcore/inc/gc_hal_driver.h +++ b/drivers/gpu/galcore/inc/gc_hal_driver.h @@ -274,11 +274,6 @@ typedef struct _gcsHAL_QUERY_CHIP_IDENTITY /* Supertile layout style in hardware */ gctUINT32 superTileMode; -#if gcdMULTI_GPU - /* Number of 3D GPUs */ - gctUINT32 gpuCoreCount; -#endif - /* Special control bits for 2D chip. */ gctUINT32 chip2DControl; @@ -305,14 +300,6 @@ typedef struct _gcsHAL_COMPOSE gctUINT64 userProcess; gctUINT64 userSignal1; gctUINT64 userSignal2; - -#if defined(__QNXNTO__) - /* Client pulse side-channel connection ID. */ - gctINT32 coid; - - /* Set by server. */ - gctINT32 rcvid; -#endif } gcsHAL_COMPOSE; @@ -453,15 +440,6 @@ typedef struct _gcsHAL_INTERFACE { /* Allocated video memory. */ IN gctUINT32 node; - -#ifdef __QNXNTO__ -/* TODO: This is part of the unlock - why is it here? */ - /* Mapped logical address to unmap in user space. */ - OUT gctUINT64 memory; - - /* Number of bytes to allocated. */ - OUT gctUINT64 bytes; -#endif } ReleaseVideoMemory; @@ -565,12 +543,6 @@ typedef struct _gcsHAL_INTERFACE { /* Event queue in gcsQUEUE. */ IN gctUINT64 queue; - -#if gcdMULTI_GPU - IN gceCORE_3D_MASK chipEnable; - - IN gceMULTI_GPU_MODE gpuMode; -#endif } Event; @@ -588,12 +560,6 @@ typedef struct _gcsHAL_INTERFACE /* Event queue in gcsQUEUE. */ IN gctUINT64 queue; - -#if gcdMULTI_GPU - IN gceCORE_3D_MASK chipEnable; - - IN gceMULTI_GPU_MODE gpuMode; -#endif } Commit; @@ -667,14 +633,6 @@ typedef struct _gcsHAL_INTERFACE /* Process owning the signal gctHANDLE. */ IN gctUINT64 process; - -#if defined(__QNXNTO__) - /* Client pulse side-channel connection ID. Set by client in gcoOS_CreateSignal. */ - IN gctINT32 coid; - - /* Set by server. */ - IN gctINT32 rcvid; -#endif /* Event generated from where of pipeline */ IN gceKERNEL_WHERE fromWhere; } @@ -744,34 +702,6 @@ typedef struct _gcsHAL_INTERFACE } WriteRegisterData; -#if gcdMULTI_GPU - /* gcvHAL_READ_REGISTER_EX */ - struct _gcsHAL_READ_REGISTER_EX - { - /* Logical address of memory to write data to. */ - IN gctUINT32 address; - - IN gctUINT32 coreSelect; - - /* Data read. */ - OUT gctUINT32 data[gcdMULTI_GPU]; - } - ReadRegisterDataEx; - - /* gcvHAL_WRITE_REGISTER_EX */ - struct _gcsHAL_WRITE_REGISTER_EX - { - /* Logical address of memory to write data to. */ - IN gctUINT32 address; - - IN gctUINT32 coreSelect; - - /* Data read. */ - IN gctUINT32 data[gcdMULTI_GPU]; - } - WriteRegisterDataEx; -#endif - #if VIVANTE_PROFILER /* gcvHAL_GET_PROFILE_SETTING */ struct _gcsHAL_GET_PROFILE_SETTING diff --git a/drivers/gpu/galcore/inc/gc_hal_driver_vg.h b/drivers/gpu/galcore/inc/gc_hal_driver_vg.h index d4e4b3eb67c7af..bbced7ca23df2e 100644 --- a/drivers/gpu/galcore/inc/gc_hal_driver_vg.h +++ b/drivers/gpu/galcore/inc/gc_hal_driver_vg.h @@ -175,11 +175,6 @@ typedef struct _gcsTASK_SIGNAL /* Signal handle to signal. */ IN gctSIGNAL signal; - -#if defined(__QNXNTO__) - IN gctINT32 coid; - IN gctINT32 rcvid; -#endif } gcsTASK_SIGNAL; diff --git a/drivers/gpu/galcore/inc/gc_hal_eglplatform.h b/drivers/gpu/galcore/inc/gc_hal_eglplatform.h index 1f35873601d234..73d84fb8c42227 100644 --- a/drivers/gpu/galcore/inc/gc_hal_eglplatform.h +++ b/drivers/gpu/galcore/inc/gc_hal_eglplatform.h @@ -30,26 +30,13 @@ extern "C" { #endif - -#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) -/* Win32 and Windows CE platforms. */ -#include -typedef HDC HALNativeDisplayType; -typedef HWND HALNativeWindowType; -typedef HBITMAP HALNativePixmapType; - -typedef struct __BITFIELDINFO{ - BITMAPINFO bmi; - RGBQUAD bmiColors[2]; -} BITFIELDINFO; - -#elif defined(LINUX) && defined(EGL_API_DFB) && !defined(__APPLE__) +#if defined(LINUX) && defined(EGL_API_DFB) #include typedef struct _DFBDisplay * HALNativeDisplayType; typedef struct _DFBWindow * HALNativeWindowType; typedef struct _DFBPixmap * HALNativePixmapType; -#elif defined(LINUX) && defined(EGL_API_FB) && !defined(__APPLE__) +#elif defined(LINUX) && defined(EGL_API_FB) #if defined(EGL_API_WL) @@ -147,7 +134,7 @@ struct egl_native_pixmap_t; typedef void* HALNativeDisplayType; #endif -#elif defined(LINUX) || defined(__APPLE__) +#elif defined(LINUX) /* X11 platform. */ #include #include @@ -175,14 +162,6 @@ typedef Pixmap HALNativePixmapType; # define XCurrentTime 0 #endif -#elif defined(__QNXNTO__) -#include - -/* VOID */ -typedef int HALNativeDisplayType; -typedef screen_window_t HALNativeWindowType; -typedef screen_pixmap_t HALNativePixmapType; - #else #error "Platform not recognized" diff --git a/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h b/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h index 10da3af527de20..409f7a8b7812b3 100644 --- a/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h +++ b/drivers/gpu/galcore/inc/gc_hal_eglplatform_type.h @@ -258,12 +258,6 @@ typedef struct _halDISPLAY_INFO int wrapFB; /* true if compositor, false otherwise. */ -#ifndef __QNXNTO__ - /* 355_FB_MULTI_BUFFER */ - int multiBuffer; - int backBufferY; -#endif - /* The color info of the display. */ unsigned int alphaLength; unsigned int alphaOffset; diff --git a/drivers/gpu/galcore/inc/gc_hal_enum.h b/drivers/gpu/galcore/inc/gc_hal_enum.h index d20f00774b9871..39d8ca3333912c 100644 --- a/drivers/gpu/galcore/inc/gc_hal_enum.h +++ b/drivers/gpu/galcore/inc/gc_hal_enum.h @@ -1224,9 +1224,6 @@ typedef enum _gceHARDWARE_TYPE gcvHARDWARE_3D = 0x01, gcvHARDWARE_2D = 0x02, gcvHARDWARE_VG = 0x04, -#if gcdMULTI_GPU_AFFINITY - gcvHARDWARE_OCL = 0x05, -#endif gcvHARDWARE_3D2D = gcvHARDWARE_3D | gcvHARDWARE_2D } gceHARDWARE_TYPE; diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h index b93015d32f923e..5056509fd996ea 100644 --- a/drivers/gpu/galcore/inc/gc_hal_options.h +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -699,27 +699,6 @@ # define gcdENABLE_INFINITE_SPEED_HW 0 #endif -/* - gcdMULTI_GPU - - Enable/disable multi-GPU support. - 0 : Disable multi-GPU support - 1 : Enable one of the 3D cores - [2..X] : Number of 3D GPU Cores -*/ -#ifndef gcdMULTI_GPU -# define gcdMULTI_GPU 0 -#endif - -/* - gcdMULTI_GPU_AFFINITY - - Enable/disable the binding of a context to one GPU -*/ -#ifndef gcdMULTI_GPU_AFFINITY -# define gcdMULTI_GPU_AFFINITY 0 -#endif - /* gcdPOWEROFF_TIMEOUT @@ -731,13 +710,6 @@ # define gcdPOWEROFF_TIMEOUT 300 #endif -/* - QNX_SINGLE_THREADED_DEBUGGING -*/ -#ifndef QNX_SINGLE_THREADED_DEBUGGING -# define QNX_SINGLE_THREADED_DEBUGGING 0 -#endif - /* gcdRENDER_THREADS @@ -759,12 +731,8 @@ */ #ifndef gcdSMP -#ifdef __APPLE__ -# define gcdSMP 1 -#else # define gcdSMP 0 #endif -#endif /* gcdSHARED_RESOLVE_BUFFER_ENABLED diff --git a/drivers/gpu/galcore/inc/gc_hal_types.h b/drivers/gpu/galcore/inc/gc_hal_types.h index d6fc3949945d0b..7134165df8a9d0 100644 --- a/drivers/gpu/galcore/inc/gc_hal_types.h +++ b/drivers/gpu/galcore/inc/gc_hal_types.h @@ -29,15 +29,6 @@ #if defined(__KERNEL__) #include "linux/version.h" #include "linux/types.h" -#elif defined(UNDER_CE) -#include -#elif defined(_MSC_VER) && (_MSC_VER <= 1500) -#include -#include "vadefs.h" -#elif defined(__QNXNTO__) -#define _QNX_SOURCE -#include -#include #else #include #include @@ -45,18 +36,6 @@ #endif #endif -#ifdef _WIN32 -#pragma warning(disable:4127) /* Conditional expression is constant (do { } - ** while(0)). */ -#pragma warning(disable:4100) /* Unreferenced formal parameter. */ -#pragma warning(disable:4204) /* Non-constant aggregate initializer (C99). */ -#pragma warning(disable:4131) /* Uses old-style declarator (for Bison and - ** Flex generated files). */ -#pragma warning(disable:4206) /* Translation unit is empty. */ -#pragma warning(disable:4214) /* Nonstandard extension used : - ** bit field types other than int. */ -#endif - #ifdef __cplusplus extern "C" { #endif @@ -86,7 +65,7 @@ extern "C" { \******************************************************************************/ #if defined(ANDROID) && defined(__BIONIC_FORTIFY) # define gcmINLINE __inline__ __attribute__ ((always_inline)) __attribute__ ((gnu_inline)) __attribute__ ((artificial)) -#elif ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__APPLE__)) +#elif ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))) # define gcmINLINE inline /* C99 keyword. */ #elif defined(__GNUC__) # define gcmINLINE __inline__ /* GNU keyword. */ diff --git a/drivers/gpu/galcore/inc/gc_hal_vg.h b/drivers/gpu/galcore/inc/gc_hal_vg.h index 09e8b9647423d7..781cb5ab7c3c1f 100644 --- a/drivers/gpu/galcore/inc/gc_hal_vg.h +++ b/drivers/gpu/galcore/inc/gc_hal_vg.h @@ -35,19 +35,9 @@ extern "C" { #if gcdENABLE_VG /* Thread routine type. */ -#if defined(LINUX) - typedef gctINT gctTHREADFUNCRESULT; - typedef gctPOINTER gctTHREADFUNCPARAMETER; -# define gctTHREADFUNCTYPE -#elif defined(WIN32) - typedef gctUINT gctTHREADFUNCRESULT; - typedef gctPOINTER gctTHREADFUNCPARAMETER; -# define gctTHREADFUNCTYPE __stdcall -#elif defined(__QNXNTO__) - typedef void * gctTHREADFUNCRESULT; - typedef gctPOINTER gctTHREADFUNCPARAMETER; -# define gctTHREADFUNCTYPE -#endif +typedef gctINT gctTHREADFUNCRESULT; +typedef gctPOINTER gctTHREADFUNCPARAMETER; +#define gctTHREADFUNCTYPE typedef gctTHREADFUNCRESULT (gctTHREADFUNCTYPE * gctTHREADFUNC) ( gctTHREADFUNCPARAMETER ThreadParameter @@ -665,11 +655,6 @@ typedef struct _gcsVGCONTEXT /* Completion signal. */ gctHANDLE process; gctSIGNAL signal; - -#if defined(__QNXNTO__) - gctINT32 coid; - gctINT32 rcvid; -#endif } gcsVGCONTEXT; @@ -709,11 +694,6 @@ typedef struct _gcsTASK_MASTER_TABLE /* The total size of event data in bytes. */ gctUINT size; - -#if defined(__QNXNTO__) - gctINT32 coid; - gctINT32 rcvid; -#endif } gcsTASK_MASTER_TABLE; @@ -751,24 +731,11 @@ gckVGINTERRUPT_Disable( IN gctINT32 Id ); -#ifndef __QNXNTO__ - gceSTATUS gckVGINTERRUPT_Enque( IN gckVGINTERRUPT Interrupt ); -#else - -gceSTATUS -gckVGINTERRUPT_Enque( - IN gckVGINTERRUPT Interrupt, - OUT gckOS *Os, - OUT gctSEMAPHORE *Semaphore - ); - -#endif - gceSTATUS gckVGINTERRUPT_DumpState( IN gckVGINTERRUPT Interrupt From b4a2ac5f61e439b1b9aec724a90a766ad073f77b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 27 Jun 2015 14:52:36 +0200 Subject: [PATCH 0406/1983] gpu: galcore: Remove gc_hal_kernel_heap.c This code is not used anywhere so remove it. --- drivers/gpu/galcore/gc_hal_kernel_heap.c | 858 ----------------------- 1 file changed, 858 deletions(-) delete mode 100644 drivers/gpu/galcore/gc_hal_kernel_heap.c diff --git a/drivers/gpu/galcore/gc_hal_kernel_heap.c b/drivers/gpu/galcore/gc_hal_kernel_heap.c deleted file mode 100644 index be0a60eb6da93e..00000000000000 --- a/drivers/gpu/galcore/gc_hal_kernel_heap.c +++ /dev/null @@ -1,858 +0,0 @@ -/**************************************************************************** -* -* Copyright (C) 2005 - 2014 by Vivante Corp. -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - - -/** -** @file -** gckHEAP object for kernel HAL layer. The heap implemented here is an arena- -** based memory allocation. An arena-based memory heap allocates data quickly -** from specified arenas and reduces memory fragmentation. -** -*/ -#include "gc_hal_kernel_precomp.h" - -#define _GC_OBJ_ZONE gcvZONE_HEAP - -/******************************************************************************* -***** Structures *************************************************************** -*******************************************************************************/ -#define gcdIN_USE ((gcskNODE_PTR)gcvMAXUINTPTR_T) - -typedef struct _gcskNODE * gcskNODE_PTR; -typedef struct _gcskNODE -{ - /* Number of byets in node. */ - gctSIZE_T bytes; - - /* Pointer to next free node, or gcvNULL to mark the node as freed, or - ** gcdIN_USE to mark the node as used. */ - gcskNODE_PTR next; - -#if gcmIS_DEBUG(gcdDEBUG_CODE) - /* Time stamp of allocation. */ - gctUINT64 timeStamp; -#endif -} -gcskNODE; - -typedef struct _gcskHEAP * gcskHEAP_PTR; -typedef struct _gcskHEAP -{ - /* Linked list. */ - gcskHEAP_PTR next; - gcskHEAP_PTR prev; - - /* Heap size. */ - gctSIZE_T size; - - /* Free list. */ - gcskNODE_PTR freeList; -} -gcskHEAP; - -struct _gckHEAP -{ - /* Object. */ - gcsOBJECT object; - - /* Pointer to a gckOS object. */ - gckOS os; - - /* Locking mutex. */ - gctPOINTER mutex; - - /* Allocation parameters. */ - gctSIZE_T allocationSize; - - /* Heap list. */ - gcskHEAP_PTR heap; -#if gcmIS_DEBUG(gcdDEBUG_CODE) - gctUINT64 timeStamp; -#endif - -#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) - /* Profile information. */ - gctUINT32 allocCount; - gctUINT64 allocBytes; - gctUINT64 allocBytesMax; - gctUINT64 allocBytesTotal; - gctUINT32 heapCount; - gctUINT32 heapCountMax; - gctUINT64 heapMemory; - gctUINT64 heapMemoryMax; -#endif -}; - -/******************************************************************************* -***** Static Support Functions ************************************************* -*******************************************************************************/ - -#if gcmIS_DEBUG(gcdDEBUG_CODE) -static gctSIZE_T -_DumpHeap( - IN gcskHEAP_PTR Heap - ) -{ - gctPOINTER p; - gctSIZE_T leaked = 0; - - /* Start at first node. */ - for (p = Heap + 1;;) - { - /* Convert the pointer. */ - gcskNODE_PTR node = (gcskNODE_PTR) p; - - /* Check if this is a used node. */ - if (node->next == gcdIN_USE) - { - /* Print the leaking node. */ - gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_HEAP, - "Detected leaking: node=0x%x bytes=%lu timeStamp=%llu " - "(%08X %c%c%c%c)", - node, node->bytes, node->timeStamp, - ((gctUINT32_PTR) (node + 1))[0], - gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[0]), - gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[1]), - gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[2]), - gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[3])); - - /* Add leaking byte count. */ - leaked += node->bytes; - } - - /* Test for end of heap. */ - if (node->bytes == 0) - { - break; - } - - else - { - /* Move to next node. */ - p = (gctUINT8_PTR) node + node->bytes; - } - } - - /* Return the number of leaked bytes. */ - return leaked; -} -#endif - -static gceSTATUS -_CompactKernelHeap( - IN gckHEAP Heap - ) -{ - gcskHEAP_PTR heap, next; - gctPOINTER p; - gcskHEAP_PTR freeList = gcvNULL; - - gcmkHEADER_ARG("Heap=0x%x", Heap); - - /* Walk all the heaps. */ - for (heap = Heap->heap; heap != gcvNULL; heap = next) - { - gcskNODE_PTR lastFree = gcvNULL; - - /* Zero out the free list. */ - heap->freeList = gcvNULL; - - /* Start at the first node. */ - for (p = (gctUINT8_PTR) (heap + 1);;) - { - /* Convert the pointer. */ - gcskNODE_PTR node = (gcskNODE_PTR) p; - - gcmkASSERT(p <= (gctPOINTER) ((gctUINT8_PTR) (heap + 1) + heap->size)); - - /* Test if this node not used. */ - if (node->next != gcdIN_USE) - { - /* Test if this is the end of the heap. */ - if (node->bytes == 0) - { - break; - } - - /* Test of this is the first free node. */ - else if (lastFree == gcvNULL) - { - /* Initialzie the free list. */ - heap->freeList = node; - lastFree = node; - } - - else - { - /* Test if this free node is contiguous with the previous - ** free node. */ - if ((gctUINT8_PTR) lastFree + lastFree->bytes == p) - { - /* Just increase the size of the previous free node. */ - lastFree->bytes += node->bytes; - } - else - { - /* Add to linked list. */ - lastFree->next = node; - lastFree = node; - } - } - } - - /* Move to next node. */ - p = (gctUINT8_PTR) node + node->bytes; - } - - /* Mark the end of the chain. */ - if (lastFree != gcvNULL) - { - lastFree->next = gcvNULL; - } - - /* Get next heap. */ - next = heap->next; - - /* Check if the entire heap is free. */ - if ((heap->freeList != gcvNULL) - && (heap->freeList->bytes == heap->size - gcmSIZEOF(gcskNODE)) - ) - { - /* Remove the heap from the linked list. */ - if (heap->prev == gcvNULL) - { - Heap->heap = next; - } - else - { - heap->prev->next = next; - } - - if (heap->next != gcvNULL) - { - heap->next->prev = heap->prev; - } - -#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) - /* Update profiling. */ - Heap->heapCount -= 1; - Heap->heapMemory -= heap->size + gcmSIZEOF(gcskHEAP); -#endif - - /* Add this heap to the list of heaps that need to be freed. */ - heap->next = freeList; - freeList = heap; - } - } - - if (freeList != gcvNULL) - { - /* Release the mutex, remove any chance for a dead lock. */ - gcmkVERIFY_OK( - gckOS_ReleaseMutex(Heap->os, Heap->mutex)); - - /* Free all heaps in the free list. */ - for (heap = freeList; heap != gcvNULL; heap = next) - { - /* Get pointer to the next heap. */ - next = heap->next; - - /* Free the heap. */ - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP, - "Freeing heap 0x%x (%lu bytes)", - heap, heap->size + gcmSIZEOF(gcskHEAP)); - gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap)); - } - - /* Acquire the mutex again. */ - gcmkVERIFY_OK( - gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); - } - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} - -/******************************************************************************* -***** gckHEAP API Code ********************************************************* -*******************************************************************************/ - -/******************************************************************************* -** -** gckHEAP_Construct -** -** Construct a new gckHEAP object. -** -** INPUT: -** -** gckOS Os -** Pointer to a gckOS object. -** -** gctSIZE_T AllocationSize -** Minimum size per arena. -** -** OUTPUT: -** -** gckHEAP * Heap -** Pointer to a variable that will hold the pointer to the gckHEAP -** object. -*/ -gceSTATUS -gckHEAP_Construct( - IN gckOS Os, - IN gctSIZE_T AllocationSize, - OUT gckHEAP * Heap - ) -{ - gceSTATUS status; - gckHEAP heap = gcvNULL; - gctPOINTER pointer = gcvNULL; - - gcmkHEADER_ARG("Os=0x%x AllocationSize=%lu", Os, AllocationSize); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); - gcmkVERIFY_ARGUMENT(Heap != gcvNULL); - - /* Allocate the gckHEAP object. */ - gcmkONERROR(gckOS_AllocateMemory(Os, - gcmSIZEOF(struct _gckHEAP), - &pointer)); - - heap = pointer; - - /* Initialize the gckHEAP object. */ - heap->object.type = gcvOBJ_HEAP; - heap->os = Os; - heap->allocationSize = AllocationSize; - heap->heap = gcvNULL; -#if gcmIS_DEBUG(gcdDEBUG_CODE) - heap->timeStamp = 0; -#endif - -#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) - /* Zero the counters. */ - heap->allocCount = 0; - heap->allocBytes = 0; - heap->allocBytesMax = 0; - heap->allocBytesTotal = 0; - heap->heapCount = 0; - heap->heapCountMax = 0; - heap->heapMemory = 0; - heap->heapMemoryMax = 0; -#endif - - /* Create the mutex. */ - gcmkONERROR(gckOS_CreateMutex(Os, &heap->mutex)); - - /* Return the pointer to the gckHEAP object. */ - *Heap = heap; - - /* Success. */ - gcmkFOOTER_ARG("*Heap=0x%x", *Heap); - return gcvSTATUS_OK; - -OnError: - /* Roll back. */ - if (heap != gcvNULL) - { - /* Free the heap structure. */ - gcmkVERIFY_OK(gckOS_FreeMemory(Os, heap)); - } - - /* Return the status. */ - gcmkFOOTER(); - return status; -} - -/******************************************************************************* -** -** gckHEAP_Destroy -** -** Destroy a gckHEAP object. -** -** INPUT: -** -** gckHEAP Heap -** Pointer to a gckHEAP object to destroy. -** -** OUTPUT: -** -** Nothing. -*/ -gceSTATUS -gckHEAP_Destroy( - IN gckHEAP Heap - ) -{ - gcskHEAP_PTR heap; -#if gcmIS_DEBUG(gcdDEBUG_CODE) - gctSIZE_T leaked = 0; -#endif - - gcmkHEADER_ARG("Heap=0x%x", Heap); - - for (heap = Heap->heap; heap != gcvNULL; heap = Heap->heap) - { - /* Unlink heap from linked list. */ - Heap->heap = heap->next; - -#if gcmIS_DEBUG(gcdDEBUG_CODE) - /* Check for leaked memory. */ - leaked += _DumpHeap(heap); -#endif - - /* Free the heap. */ - gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap)); - } - - /* Free the mutex. */ - gcmkVERIFY_OK(gckOS_DeleteMutex(Heap->os, Heap->mutex)); - - /* Free the heap structure. */ - gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, Heap)); - - /* Success. */ -#if gcmIS_DEBUG(gcdDEBUG_CODE) - gcmkFOOTER_ARG("leaked=%lu", leaked); -#else - gcmkFOOTER_NO(); -#endif - return gcvSTATUS_OK; -} - -/******************************************************************************* -** -** gckHEAP_Allocate -** -** Allocate data from the heap. -** -** INPUT: -** -** gckHEAP Heap -** Pointer to a gckHEAP object. -** -** IN gctSIZE_T Bytes -** Number of byte to allocate. -** -** OUTPUT: -** -** gctPOINTER * Memory -** Pointer to a variable that will hold the address of the allocated -** memory. -*/ -gceSTATUS -gckHEAP_Allocate( - IN gckHEAP Heap, - IN gctSIZE_T Bytes, - OUT gctPOINTER * Memory - ) -{ - gctBOOL acquired = gcvFALSE; - gcskHEAP_PTR heap; - gceSTATUS status; - gctSIZE_T bytes; - gcskNODE_PTR node, used, prevFree = gcvNULL; - gctPOINTER memory = gcvNULL; - - gcmkHEADER_ARG("Heap=0x%x Bytes=%lu", Heap, Bytes); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); - gcmkVERIFY_ARGUMENT(Bytes > 0); - gcmkVERIFY_ARGUMENT(Memory != gcvNULL); - - /* Determine number of bytes required for a node. */ - bytes = gcmALIGN(Bytes + gcmSIZEOF(gcskNODE), 8); - - /* Acquire the mutex. */ - gcmkONERROR( - gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); - - acquired = gcvTRUE; - - /* Check if this allocation is bigger than the default allocation size. */ - if (bytes > Heap->allocationSize - gcmSIZEOF(gcskHEAP) - gcmSIZEOF(gcskNODE)) - { - /* Adjust allocation size. */ - Heap->allocationSize = bytes * 2; - } - - else if (Heap->heap != gcvNULL) - { - gctINT i; - - /* 2 retries, since we might need to compact. */ - for (i = 0; i < 2; ++i) - { - /* Walk all the heaps. */ - for (heap = Heap->heap; heap != gcvNULL; heap = heap->next) - { - /* Check if this heap has enough bytes to hold the request. */ - if (bytes <= heap->size - gcmSIZEOF(gcskNODE)) - { - prevFree = gcvNULL; - - /* Walk the chain of free nodes. */ - for (node = heap->freeList; - node != gcvNULL; - node = node->next - ) - { - gcmkASSERT(node->next != gcdIN_USE); - - /* Check if this free node has enough bytes. */ - if (node->bytes >= bytes) - { - /* Use the node. */ - goto UseNode; - } - - /* Save current free node for linked list management. */ - prevFree = node; - } - } - } - - if (i == 0) - { - /* Compact the heap. */ - gcmkVERIFY_OK(_CompactKernelHeap(Heap)); - -#if gcmIS_DEBUG(gcdDEBUG_CODE) - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "===== KERNEL HEAP ====="); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Number of allocations : %12u", - Heap->allocCount); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Number of bytes allocated : %12llu", - Heap->allocBytes); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Maximum allocation size : %12llu", - Heap->allocBytesMax); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Total number of bytes allocated : %12llu", - Heap->allocBytesTotal); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Number of heaps : %12u", - Heap->heapCount); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Heap memory in bytes : %12llu", - Heap->heapMemory); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Maximum number of heaps : %12u", - Heap->heapCountMax); - gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP, - "Maximum heap memory in bytes : %12llu", - Heap->heapMemoryMax); -#endif - } - } - } - - /* Release the mutex. */ - gcmkONERROR( - gckOS_ReleaseMutex(Heap->os, Heap->mutex)); - - acquired = gcvFALSE; - - /* Allocate a new heap. */ - gcmkONERROR( - gckOS_AllocateMemory(Heap->os, - Heap->allocationSize, - &memory)); - - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP, - "Allocated heap 0x%x (%lu bytes)", - memory, Heap->allocationSize); - - /* Acquire the mutex. */ - gcmkONERROR( - gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); - - acquired = gcvTRUE; - - /* Use the allocated memory as the heap. */ - heap = (gcskHEAP_PTR) memory; - - /* Insert this heap to the head of the chain. */ - heap->next = Heap->heap; - heap->prev = gcvNULL; - heap->size = Heap->allocationSize - gcmSIZEOF(gcskHEAP); - - if (heap->next != gcvNULL) - { - heap->next->prev = heap; - } - Heap->heap = heap; - - /* Mark the end of the heap. */ - node = (gcskNODE_PTR) ( (gctUINT8_PTR) heap - + Heap->allocationSize - - gcmSIZEOF(gcskNODE) - ); - node->bytes = 0; - node->next = gcvNULL; - - /* Create a free list. */ - node = (gcskNODE_PTR) (heap + 1); - heap->freeList = node; - - /* Initialize the free list. */ - node->bytes = heap->size - gcmSIZEOF(gcskNODE); - node->next = gcvNULL; - - /* No previous free. */ - prevFree = gcvNULL; - -#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) - /* Update profiling. */ - Heap->heapCount += 1; - Heap->heapMemory += Heap->allocationSize; - - if (Heap->heapCount > Heap->heapCountMax) - { - Heap->heapCountMax = Heap->heapCount; - } - if (Heap->heapMemory > Heap->heapMemoryMax) - { - Heap->heapMemoryMax = Heap->heapMemory; - } -#endif - -UseNode: - /* Verify some stuff. */ - gcmkASSERT(heap != gcvNULL); - gcmkASSERT(node != gcvNULL); - gcmkASSERT(node->bytes >= bytes); - - if (heap->prev != gcvNULL) - { - /* Unlink the heap from the linked list. */ - heap->prev->next = heap->next; - if (heap->next != gcvNULL) - { - heap->next->prev = heap->prev; - } - - /* Move the heap to the front of the list. */ - heap->next = Heap->heap; - heap->prev = gcvNULL; - Heap->heap = heap; - heap->next->prev = heap; - } - - /* Check if there is enough free space left after usage for another free - ** node. */ - if (node->bytes - bytes >= gcmSIZEOF(gcskNODE)) - { - /* Allocated used space from the back of the free list. */ - used = (gcskNODE_PTR) ((gctUINT8_PTR) node + node->bytes - bytes); - - /* Adjust the number of free bytes. */ - node->bytes -= bytes; - gcmkASSERT(node->bytes >= gcmSIZEOF(gcskNODE)); - } - else - { - /* Remove this free list from the chain. */ - if (prevFree == gcvNULL) - { - heap->freeList = node->next; - } - else - { - prevFree->next = node->next; - } - - /* Consume the entire free node. */ - used = (gcskNODE_PTR) node; - bytes = node->bytes; - } - - /* Mark node as used. */ - used->bytes = bytes; - used->next = gcdIN_USE; -#if gcmIS_DEBUG(gcdDEBUG_CODE) - used->timeStamp = ++Heap->timeStamp; -#endif - -#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) - /* Update profile counters. */ - Heap->allocCount += 1; - Heap->allocBytes += bytes; - Heap->allocBytesMax = gcmMAX(Heap->allocBytes, Heap->allocBytesMax); - Heap->allocBytesTotal += bytes; -#endif - - /* Release the mutex. */ - gcmkVERIFY_OK( - gckOS_ReleaseMutex(Heap->os, Heap->mutex)); - - /* Return pointer to memory. */ - *Memory = used + 1; - - /* Success. */ - gcmkFOOTER_ARG("*Memory=0x%x", *Memory); - return gcvSTATUS_OK; - -OnError: - if (acquired) - { - /* Release the mutex. */ - gcmkVERIFY_OK( - gckOS_ReleaseMutex(Heap->os, Heap->mutex)); - } - - if (memory != gcvNULL) - { - /* Free the heap memory. */ - gckOS_FreeMemory(Heap->os, memory); - } - - /* Return the status. */ - gcmkFOOTER(); - return status; -} - -/******************************************************************************* -** -** gckHEAP_Free -** -** Free allocated memory from the heap. -** -** INPUT: -** -** gckHEAP Heap -** Pointer to a gckHEAP object. -** -** IN gctPOINTER Memory -** Pointer to memory to free. -** -** OUTPUT: -** -** NOTHING. -*/ -gceSTATUS -gckHEAP_Free( - IN gckHEAP Heap, - IN gctPOINTER Memory - ) -{ - gcskNODE_PTR node; - gceSTATUS status; - - gcmkHEADER_ARG("Heap=0x%x Memory=0x%x", Heap, Memory); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); - gcmkVERIFY_ARGUMENT(Memory != gcvNULL); - - /* Acquire the mutex. */ - gcmkONERROR( - gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE)); - - /* Pointer to structure. */ - node = (gcskNODE_PTR) Memory - 1; - - /* Mark the node as freed. */ - node->next = gcvNULL; - -#if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE) - /* Update profile counters. */ - Heap->allocBytes -= node->bytes; -#endif - - /* Release the mutex. */ - gcmkVERIFY_OK( - gckOS_ReleaseMutex(Heap->os, Heap->mutex)); - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - /* Return the status. */ - gcmkFOOTER(); - return status; -} - -#if VIVANTE_PROFILER -gceSTATUS -gckHEAP_ProfileStart( - IN gckHEAP Heap - ) -{ - gcmkHEADER_ARG("Heap=0x%x", Heap); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); - - /* Zero the counters. */ - Heap->allocCount = 0; - Heap->allocBytes = 0; - Heap->allocBytesMax = 0; - Heap->allocBytesTotal = 0; - Heap->heapCount = 0; - Heap->heapCountMax = 0; - Heap->heapMemory = 0; - Heap->heapMemoryMax = 0; - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} - -gceSTATUS -gckHEAP_ProfileEnd( - IN gckHEAP Heap, - IN gctCONST_STRING Title - ) -{ - gcmkHEADER_ARG("Heap=0x%x Title=0x%x", Heap, Title); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP); - gcmkVERIFY_ARGUMENT(Title != gcvNULL); - - gcmkPRINT(""); - gcmkPRINT("=====[ HEAP - %s ]=====", Title); - gcmkPRINT("Number of allocations : %12u", Heap->allocCount); - gcmkPRINT("Number of bytes allocated : %12llu", Heap->allocBytes); - gcmkPRINT("Maximum allocation size : %12llu", Heap->allocBytesMax); - gcmkPRINT("Total number of bytes allocated : %12llu", Heap->allocBytesTotal); - gcmkPRINT("Number of heaps : %12u", Heap->heapCount); - gcmkPRINT("Heap memory in bytes : %12llu", Heap->heapMemory); - gcmkPRINT("Maximum number of heaps : %12u", Heap->heapCountMax); - gcmkPRINT("Maximum heap memory in bytes : %12llu", Heap->heapMemoryMax); - gcmkPRINT("=============================================="); - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} -#endif /* VIVANTE_PROFILER */ - -/******************************************************************************* -***** Test Code **************************************************************** -*******************************************************************************/ - From 290c850b548176fe84fd86a6025aca63b0f2d898 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 27 Jun 2015 15:56:14 +0200 Subject: [PATCH 0407/1983] gpu: galcore: remove unused security code I have never seen this code enabled in the userspace binaries so I remove it here. --- drivers/gpu/galcore/Kbuild | 9 - .../gpu/galcore/arch/gc_hal_kernel_context.c | 58 - .../gpu/galcore/arch/gc_hal_kernel_context.h | 5 - .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 5 - drivers/gpu/galcore/gc_hal_kernel.c | 582 -------- drivers/gpu/galcore/gc_hal_kernel.h | 108 -- drivers/gpu/galcore/gc_hal_kernel_command.c | 177 --- drivers/gpu/galcore/gc_hal_kernel_db.c | 99 -- drivers/gpu/galcore/gc_hal_kernel_drv.c | 1252 +++++++++++++++++ drivers/gpu/galcore/gc_hal_kernel_event.c | 84 -- drivers/gpu/galcore/gc_hal_kernel_os.c | 275 ---- drivers/gpu/galcore/gc_hal_kernel_security.c | 239 ---- .../galcore/gc_hal_kernel_security_channel.c | 385 ----- .../gpu/galcore/gc_hal_kernel_video_memory.c | 38 - drivers/gpu/galcore/inc/gc_hal.h | 38 - drivers/gpu/galcore/inc/gc_hal_base.h | 33 - .../gpu/galcore/inc/gc_hal_kernel_buffer.h | 7 - drivers/gpu/galcore/inc/gc_hal_options.h | 20 - .../galcore/inc/gc_hal_security_interface.h | 137 -- 19 files changed, 1252 insertions(+), 2299 deletions(-) create mode 100644 drivers/gpu/galcore/gc_hal_kernel_drv.c delete mode 100644 drivers/gpu/galcore/gc_hal_kernel_security.c delete mode 100644 drivers/gpu/galcore/gc_hal_kernel_security_channel.c delete mode 100644 drivers/gpu/galcore/inc/gc_hal_security_interface.h diff --git a/drivers/gpu/galcore/Kbuild b/drivers/gpu/galcore/Kbuild index 24fe8c1c688f62..318375da6ec87f 100644 --- a/drivers/gpu/galcore/Kbuild +++ b/drivers/gpu/galcore/Kbuild @@ -100,11 +100,6 @@ EXTRA_CFLAGS += -Idrivers/staging/android OBJS += gc_hal_kernel_sync.o endif -ifeq ($(SECURITY), 1) -OBJS += gc_hal_kernel_security_channel.o \ - gc_hal_kernel_security.o -endif - ifneq ($(CUSTOMER_ALLOCATOR_OBJS),) OBJS += $(CUSTOMER_ALLOCATOR_OBJS) endif @@ -235,10 +230,6 @@ else EXTRA_CFLAGS += -DgcdFPGA_BUILD=0 endif -ifeq ($(SECURITY), 1) -EXTRA_CFLAGS += -DgcdSECURITY=1 -endif - EXTRA_CFLAGS += -I$(AQROOT) EXTRA_CFLAGS += -I$(AQROOT)/arch EXTRA_CFLAGS += -I$(AQROOT)/inc diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c index 0c5d4df85961ce..0b7961c41263d6 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c @@ -495,14 +495,6 @@ _State( /* Set index in state mapping table. */ Context->map[Address + i].index = (gctUINT)Index + 1 + i; - -#if gcdSECURE_USER - /* Save hint. */ - if (Context->hint != gcvNULL) - { - Context->hint[Address + i] = Hinted; - } -#endif } } @@ -531,14 +523,6 @@ _State( /* Set index in state mapping table. */ Context->map[Address + i].index = (gctUINT)Index + i; - -#if gcdSECURE_USER - /* Save hint. */ - if (Context->hint != gcvNULL) - { - Context->hint[Address + i] = Hinted; - } -#endif } } @@ -1321,13 +1305,6 @@ _DestroyContext( Context->buffer = next; } -#if gcdSECURE_USER - /* Free the hint array. */ - if (Context->hint != gcvNULL) - { - gcmkONERROR(gcmkOS_SAFE_FREE(Context->os, Context->hint)); - } -#endif /* Free record array copy. */ #if REMOVE_DUPLICATED_COPY_FROM_USER if (Context->recordArrayMap != gcvNULL) @@ -1499,21 +1476,6 @@ gckCONTEXT_Construct( gcmkONERROR(gckOS_ZeroMemory( context->map, gcmSIZEOF(gcsSTATE_MAP) * context->stateCount )); - - - /**************************************************************************/ - /* Allocate the hint array. ***********************************************/ - -#if gcdSECURE_USER - /* Allocate hints. */ - gcmkONERROR(gckOS_Allocate( - Os, - gcmSIZEOF(gctBOOL) * context->stateCount, - &pointer - )); - - context->hint = pointer; -#endif } /**************************************************************************/ @@ -1794,10 +1756,6 @@ gckCONTEXT_Update( gctUINT index; gctUINT i, j; -#if gcdSECURE_USER - gcskSECURE_CACHE_PTR cache; -#endif - gcmkHEADER_ARG( "Context=0x%08X ProcessID=%d StateDelta=0x%08X", Context, ProcessID, StateDelta @@ -1863,11 +1821,6 @@ gckCONTEXT_Update( Context->os, buffer->signal, gcvINFINITE )); -#if gcdSECURE_USER - /* Get the cache form the database. */ - gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache)); -#endif - #if gcmIS_DEBUG(gcdDEBUG_CODE) && 1 && gcdENABLE_3D /* Update current context token. */ buffer->logical[Context->map[0x0E14].index] @@ -2012,17 +1965,6 @@ gckCONTEXT_Update( data = ((((gctUINT32) (data)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1))))))) << (0 ? 13:13))) | (((gctUINT32) (0x0 & ((gctUINT32) ((((1 ? 13:13) - (0 ? 13:13) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 13:13) - (0 ? 13:13) + 1))))))) << (0 ? 13:13))); } -#if gcdSECURE_USER - /* Do we need to convert the logical address? */ - if (Context->hint[address]) - { - /* Map handle into physical address. */ - gcmkONERROR(gckKERNEL_MapLogicalToPhysical( - kernel, cache, (gctPOINTER) &data - )); - } -#endif - /* Set new data. */ buffer->logical[index] = data; } diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.h b/drivers/gpu/galcore/arch/gc_hal_kernel_context.h index 29e6a5321e0da0..93d172fad745b1 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_context.h +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.h @@ -156,11 +156,6 @@ struct _gckCONTEXT gctUINT32 pipeSelectBytes; - /* Hint array. */ -#if gcdSECURE_USER - gctBOOL_PTR hint; -#endif - #if VIVANTE_PROFILER_CONTEXT gcsPROFILER_COUNTERS latestProfiler; gcsPROFILER_COUNTERS histroyProfiler; diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 583e8449f89d22..d4240056b390c8 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -5385,10 +5385,6 @@ gckHARDWARE_QueryIdle( else { -#if gcdSECURITY - isIdle = gcvTRUE; - address = 0; -#else /* Read the current FE address. */ gcmkONERROR(gckOS_ReadRegisterEx(Hardware->os, Hardware->core, @@ -5408,7 +5404,6 @@ gckHARDWARE_QueryIdle( /* FE is not in WAIT/LINK yet. */ isIdle = gcvFALSE; } -#endif } } diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c index a4801b490dab08..ee033772785b58 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.c +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -431,10 +431,6 @@ gckKERNEL_Construct( /* Initialize virtual command buffer. */ kernel->virtualCommandBuffer = gcvTRUE; -#if gcdSECURITY - kernel->virtualCommandBuffer = gcvFALSE; -#endif - /* Construct the gckCOMMAND object. */ gcmkONERROR( gckCOMMAND_Construct(kernel, &kernel->command)); @@ -484,11 +480,6 @@ gckKERNEL_Construct( gcmkONERROR( gckOS_CreateMutex(Os, (gctPOINTER)&kernel->virtualBufferLock)); -#if gcdSECURITY - /* Connect to security service for this GPU. */ - gcmkONERROR(gckKERNEL_SecurityOpen(kernel, kernel->core, &kernel->securityChannel)); -#endif - #if gcdGPU_TIMEOUT && gcdINTERRUPT_STATISTIC if (kernel->timeOut) { @@ -723,10 +714,6 @@ gckKERNEL_Destroy( gcmkVERIFY_OK(gckOS_DestroySyncTimeline(Kernel->os, Kernel->timeline)); #endif -#if gcdSECURITY - gcmkVERIFY_OK(gckKERNEL_SecurityClose(Kernel->securityChannel)); -#endif - if (Kernel->monitorTimer) { gcmkVERIFY_OK(gckOS_StopTimer(Kernel->os, Kernel->monitorTimer)); @@ -1189,11 +1176,6 @@ gckKERNEL_LockVideoMemory( #endif -#if gcdSECURE_USER - /* Return logical address as physical address. */ - Interface->u.LockVideoMemory.address = - (gctUINT32)(Interface->u.LockVideoMemory.memory); -#endif gcmkONERROR( gckKERNEL_AddProcessDB(Kernel, ProcessID, gcvDB_VIDEO_MEMORY_LOCKED, @@ -1281,21 +1263,6 @@ gckKERNEL_UnlockVideoMemory( node = nodeObject->node; - /* Unlock video memory. */ -#if gcdSECURE_USER - /* Save node information before it disappears. */ - if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) - { - logical = gcvNULL; - bytes = 0; - } - else - { - logical = node->Virtual.logical; - bytes = node->Virtual.bytes; - } -#endif - /* Unlock video memory. */ gcmkONERROR(gckVIDMEM_Unlock( Kernel, @@ -1303,17 +1270,6 @@ gckKERNEL_UnlockVideoMemory( Interface->u.UnlockVideoMemory.type, &Interface->u.UnlockVideoMemory.asynchroneous)); -#if gcdSECURE_USER - /* Flush the translation cache for virtual surfaces. */ - if (logical != gcvNULL) - { - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache(Kernel, - cache, - logical, - bytes)); - } -#endif - gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -1480,10 +1436,6 @@ gckKERNEL_Dispatch( gckKERNEL kernel = Kernel; gctUINT32 address; gctUINT32 processID; -#if gcdSECURE_USER - gcskSECURE_CACHE_PTR cache; - gctPOINTER logical; -#endif gctUINT32 paddr = gcvINVALID_ADDRESS; #if !USE_NEW_LINUX_SIGNAL gctSIGNAL signal; @@ -1509,10 +1461,6 @@ gckKERNEL_Dispatch( /* Get the current process ID. */ gcmkONERROR(gckOS_GetProcessID(&processID)); -#if gcdSECURE_USER - gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache)); -#endif - /* Dispatch on command. */ switch (Interface->command) { @@ -1640,14 +1588,6 @@ gckKERNEL_Dispatch( physical, gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical))); -#if gcdSECURE_USER - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Kernel, - cache, - gcmUINT64_TO_PTR(Interface->u.FreeNonPagedMemory.logical), - (gctSIZE_T) Interface->u.FreeNonPagedMemory.bytes)); -#endif - gcmRELEASE_NAME(Interface->u.FreeNonPagedMemory.physical); break; @@ -1701,14 +1641,6 @@ gckKERNEL_Dispatch( gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical), (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes)); -#if gcdSECURE_USER - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Kernel, - cache, - gcmUINT64_TO_PTR(Interface->u.FreeContiguousMemory.logical), - (gctSIZE_T) Interface->u.FreeContiguousMemory.bytes)); -#endif - gcmRELEASE_NAME(Interface->u.FreeContiguousMemory.physical); break; @@ -1811,14 +1743,6 @@ gckKERNEL_Dispatch( info, address)); -#if gcdSECURE_USER - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Kernel, - cache, - gcmUINT64_TO_PTR(Interface->u.UnmapUserMemory.memory), - (gctSIZE_T) Interface->u.UnmapUserMemory.size)); -#endif - gcmRELEASE_NAME(Interface->u.UnmapUserMemory.info); break; @@ -2737,501 +2661,6 @@ gckKERNEL_AttachProcessEx( return status; } -#if gcdSECURE_USER -gceSTATUS -gckKERNEL_MapLogicalToPhysical( - IN gckKERNEL Kernel, - IN gcskSECURE_CACHE_PTR Cache, - IN OUT gctPOINTER * Data - ) -{ - gceSTATUS status; - static gctBOOL baseAddressValid = gcvFALSE; - static gctUINT32 baseAddress; - gctBOOL needBase; - gcskLOGICAL_CACHE_PTR slot; - - gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x *Data=0x%x", - Kernel, Cache, gcmOPT_POINTER(Data)); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); - - if (!baseAddressValid) - { - /* Get base address. */ - gcmkONERROR(gckHARDWARE_GetBaseAddress(Kernel->hardware, &baseAddress)); - - baseAddressValid = gcvTRUE; - } - - /* Does this state load need a base address? */ - gcmkONERROR(gckHARDWARE_NeedBaseAddress(Kernel->hardware, - ((gctUINT32_PTR) Data)[-1], - &needBase)); - -#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU - { - gcskLOGICAL_CACHE_PTR next; - gctINT i; - - /* Walk all used cache slots. */ - for (i = 1, slot = Cache->cache[0].next, next = gcvNULL; - (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); - ++i, slot = slot->next - ) - { - if (slot->logical == *Data) - { - /* Bail out. */ - next = slot; - break; - } - } - - /* See if we had a miss. */ - if (next == gcvNULL) - { - /* Use the tail of the cache. */ - slot = Cache->cache[0].prev; - - /* Initialize the cache line. */ - slot->logical = *Data; - - /* Map the logical address to a DMA address. */ - gcmkONERROR( - gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); - } - - /* Move slot to head of list. */ - if (slot != Cache->cache[0].next) - { - /* Unlink. */ - slot->prev->next = slot->next; - slot->next->prev = slot->prev; - - /* Move to head of chain. */ - slot->prev = &Cache->cache[0]; - slot->next = Cache->cache[0].next; - slot->prev->next = slot; - slot->next->prev = slot; - } - } -#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR - { - gctINT i; - gcskLOGICAL_CACHE_PTR next = gcvNULL; - gcskLOGICAL_CACHE_PTR oldestSlot = gcvNULL; - slot = gcvNULL; - - if (Cache->cacheIndex != gcvNULL) - { - /* Walk the cache forwards. */ - for (i = 1, slot = Cache->cacheIndex; - (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); - ++i, slot = slot->next) - { - if (slot->logical == *Data) - { - /* Bail out. */ - next = slot; - break; - } - - /* Determine age of this slot. */ - if ((oldestSlot == gcvNULL) - || (oldestSlot->stamp > slot->stamp) - ) - { - oldestSlot = slot; - } - } - - if (next == gcvNULL) - { - /* Walk the cache backwards. */ - for (slot = Cache->cacheIndex->prev; - (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); - ++i, slot = slot->prev) - { - if (slot->logical == *Data) - { - /* Bail out. */ - next = slot; - break; - } - - /* Determine age of this slot. */ - if ((oldestSlot == gcvNULL) - || (oldestSlot->stamp > slot->stamp) - ) - { - oldestSlot = slot; - } - } - } - } - - /* See if we had a miss. */ - if (next == gcvNULL) - { - if (Cache->cacheFree != 0) - { - slot = &Cache->cache[Cache->cacheFree]; - gcmkASSERT(slot->logical == gcvNULL); - - ++ Cache->cacheFree; - if (Cache->cacheFree >= gcmCOUNTOF(Cache->cache)) - { - Cache->cacheFree = 0; - } - } - else - { - /* Use the oldest cache slot. */ - gcmkASSERT(oldestSlot != gcvNULL); - slot = oldestSlot; - - /* Unlink from the chain. */ - slot->prev->next = slot->next; - slot->next->prev = slot->prev; - - /* Append to the end. */ - slot->prev = Cache->cache[0].prev; - slot->next = &Cache->cache[0]; - slot->prev->next = slot; - slot->next->prev = slot; - } - - /* Initialize the cache line. */ - slot->logical = *Data; - - /* Map the logical address to a DMA address. */ - gcmkONERROR( - gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); - } - - /* Save time stamp. */ - slot->stamp = ++ Cache->cacheStamp; - - /* Save current slot for next lookup. */ - Cache->cacheIndex = slot; - } -#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH - { - gctINT i; - gctUINT32 data = gcmPTR2INT32(*Data); - gctUINT32 key, index; - gcskLOGICAL_CACHE_PTR hash; - - /* Generate a hash key. */ - key = (data >> 24) + (data >> 16) + (data >> 8) + data; - index = key % gcmCOUNTOF(Cache->hash); - - /* Get the hash entry. */ - hash = &Cache->hash[index]; - - for (slot = hash->nextHash, i = 0; - (slot != gcvNULL) && (i < gcdSECURE_CACHE_SLOTS); - slot = slot->nextHash, ++i - ) - { - if (slot->logical == (*Data)) - { - break; - } - } - - if (slot == gcvNULL) - { - /* Grab from the tail of the cache. */ - slot = Cache->cache[0].prev; - - /* Unlink slot from any hash table it is part of. */ - if (slot->prevHash != gcvNULL) - { - slot->prevHash->nextHash = slot->nextHash; - } - if (slot->nextHash != gcvNULL) - { - slot->nextHash->prevHash = slot->prevHash; - } - - /* Initialize the cache line. */ - slot->logical = *Data; - - /* Map the logical address to a DMA address. */ - gcmkONERROR( - gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); - - if (hash->nextHash != gcvNULL) - { - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_KERNEL, - "Hash Collision: logical=0x%x key=0x%08x", - *Data, key); - } - - /* Insert the slot at the head of the hash list. */ - slot->nextHash = hash->nextHash; - if (slot->nextHash != gcvNULL) - { - slot->nextHash->prevHash = slot; - } - slot->prevHash = hash; - hash->nextHash = slot; - } - - /* Move slot to head of list. */ - if (slot != Cache->cache[0].next) - { - /* Unlink. */ - slot->prev->next = slot->next; - slot->next->prev = slot->prev; - - /* Move to head of chain. */ - slot->prev = &Cache->cache[0]; - slot->next = Cache->cache[0].next; - slot->prev->next = slot; - slot->next->prev = slot; - } - } -#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE - { - gctUINT32 index = (gcmPTR2INT32(*Data) % gcdSECURE_CACHE_SLOTS) + 1; - - /* Get cache slot. */ - slot = &Cache->cache[index]; - - /* Check for cache miss. */ - if (slot->logical != *Data) - { - /* Initialize the cache line. */ - slot->logical = *Data; - - /* Map the logical address to a DMA address. */ - gcmkONERROR( - gckOS_GetPhysicalAddress(Kernel->os, *Data, &slot->dma)); - } - } -#endif - - /* Return DMA address. */ - *Data = gcmINT2PTR(slot->dma + (needBase ? baseAddress : 0)); - - /* Success. */ - gcmkFOOTER_ARG("*Data=0x%08x", *Data); - return gcvSTATUS_OK; - -OnError: - /* Return the status. */ - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckKERNEL_FlushTranslationCache( - IN gckKERNEL Kernel, - IN gcskSECURE_CACHE_PTR Cache, - IN gctPOINTER Logical, - IN gctSIZE_T Bytes - ) -{ - gctINT i; - gcskLOGICAL_CACHE_PTR slot; - gctUINT8_PTR ptr; - - gcmkHEADER_ARG("Kernel=0x%x Cache=0x%x Logical=0x%x Bytes=%lu", - Kernel, Cache, Logical, Bytes); - - /* Do we need to flush the entire cache? */ - if (Logical == gcvNULL) - { - /* Clear all cache slots. */ - for (i = 1; i <= gcdSECURE_CACHE_SLOTS; ++i) - { - Cache->cache[i].logical = gcvNULL; - -#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH - Cache->cache[i].nextHash = gcvNULL; - Cache->cache[i].prevHash = gcvNULL; -#endif -} - -#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH - /* Zero the hash table. */ - for (i = 0; i < gcmCOUNTOF(Cache->hash); ++i) - { - Cache->hash[i].nextHash = gcvNULL; - } -#endif - - /* Reset the cache functionality. */ - Cache->cacheIndex = gcvNULL; - Cache->cacheFree = 1; - Cache->cacheStamp = 0; - } - - else - { - gctUINT8_PTR low = (gctUINT8_PTR) Logical; - gctUINT8_PTR high = low + Bytes; - -#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LRU - gcskLOGICAL_CACHE_PTR next; - - /* Walk all used cache slots. */ - for (i = 1, slot = Cache->cache[0].next; - (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); - ++i, slot = next - ) - { - /* Save pointer to next slot. */ - next = slot->next; - - /* Test if this slot falls within the range to flush. */ - ptr = (gctUINT8_PTR) slot->logical; - if ((ptr >= low) && (ptr < high)) - { - /* Unlink slot. */ - slot->prev->next = slot->next; - slot->next->prev = slot->prev; - - /* Append slot to tail of cache. */ - slot->prev = Cache->cache[0].prev; - slot->next = &Cache->cache[0]; - slot->prev->next = slot; - slot->next->prev = slot; - - /* Mark slot as empty. */ - slot->logical = gcvNULL; - } - } - -#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_LINEAR - gcskLOGICAL_CACHE_PTR next; - - for (i = 1, slot = Cache->cache[0].next; - (i <= gcdSECURE_CACHE_SLOTS) && (slot->logical != gcvNULL); - ++i, slot = next) - { - /* Save pointer to next slot. */ - next = slot->next; - - /* Test if this slot falls within the range to flush. */ - ptr = (gctUINT8_PTR) slot->logical; - if ((ptr >= low) && (ptr < high)) - { - /* Test if this slot is the current slot. */ - if (slot == Cache->cacheIndex) - { - /* Move to next or previous slot. */ - Cache->cacheIndex = (slot->next->logical != gcvNULL) - ? slot->next - : (slot->prev->logical != gcvNULL) - ? slot->prev - : gcvNULL; - } - - /* Unlink slot from cache. */ - slot->prev->next = slot->next; - slot->next->prev = slot->prev; - - /* Insert slot to head of cache. */ - slot->prev = &Cache->cache[0]; - slot->next = Cache->cache[0].next; - slot->prev->next = slot; - slot->next->prev = slot; - - /* Mark slot as empty. */ - slot->logical = gcvNULL; - slot->stamp = 0; - } - } - -#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH - gctINT j; - gcskLOGICAL_CACHE_PTR hash, next; - - /* Walk all hash tables. */ - for (i = 0, hash = Cache->hash; - i < gcmCOUNTOF(Cache->hash); - ++i, ++hash) - { - /* Walk all slots in the hash. */ - for (j = 0, slot = hash->nextHash; - (j < gcdSECURE_CACHE_SLOTS) && (slot != gcvNULL); - ++j, slot = next) - { - /* Save pointer to next slot. */ - next = slot->next; - - /* Test if this slot falls within the range to flush. */ - ptr = (gctUINT8_PTR) slot->logical; - if ((ptr >= low) && (ptr < high)) - { - /* Unlink slot from hash table. */ - if (slot->prevHash == hash) - { - hash->nextHash = slot->nextHash; - } - else - { - slot->prevHash->nextHash = slot->nextHash; - } - - if (slot->nextHash != gcvNULL) - { - slot->nextHash->prevHash = slot->prevHash; - } - - /* Unlink slot from cache. */ - slot->prev->next = slot->next; - slot->next->prev = slot->prev; - - /* Append slot to tail of cache. */ - slot->prev = Cache->cache[0].prev; - slot->next = &Cache->cache[0]; - slot->prev->next = slot; - slot->next->prev = slot; - - /* Mark slot as empty. */ - slot->logical = gcvNULL; - slot->prevHash = gcvNULL; - slot->nextHash = gcvNULL; - } - } - } - -#elif gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_TABLE - gctUINT32 index; - - /* Loop while inside the range. */ - for (i = 1; (low < high) && (i <= gcdSECURE_CACHE_SLOTS); ++i) - { - /* Get index into cache for this range. */ - index = (gcmPTR2INT32(low) % gcdSECURE_CACHE_SLOTS) + 1; - slot = &Cache->cache[index]; - - /* Test if this slot falls within the range to flush. */ - ptr = (gctUINT8_PTR) slot->logical; - if ((ptr >= low) && (ptr < high)) - { - /* Remove entry from cache. */ - slot->logical = gcvNULL; - } - - /* Next block. */ - low += gcdSECURE_CACHE_SLOTS; - } -#endif - } - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} -#endif - /******************************************************************************* ** ** gckKERNEL_Recovery @@ -3255,10 +2684,6 @@ gckKERNEL_Recovery( gceSTATUS status; gckEVENT eventObj; gckHARDWARE hardware; -#if gcdSECURE_USER - gctUINT32 processID; - gcskSECURE_CACHE_PTR cache; -#endif gctUINT32 mask = 0; gckCOMMAND command; gckENTRYDATA data; @@ -3284,13 +2709,6 @@ gckKERNEL_Recovery( command = Kernel->command; gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND); -#if gcdSECURE_USER - /* Flush the secure mapping cache. */ - gcmkONERROR(gckOS_GetProcessID(&processID)); - gcmkONERROR(gckKERNEL_GetProcessDBCache(Kernel, processID, &cache)); - gcmkONERROR(gckKERNEL_FlushTranslationCache(Kernel, cache, gcvNULL, 0)); -#endif - if (Kernel->stuckDump == gcdSTUCK_DUMP_MINIMAL) { gcmkPRINT("[galcore]: GPU[%d] hang, automatic recovery.", Kernel->core); diff --git a/drivers/gpu/galcore/gc_hal_kernel.h b/drivers/gpu/galcore/gc_hal_kernel.h index 613104e2c00954..879546e0cd7b36 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.h +++ b/drivers/gpu/galcore/gc_hal_kernel.h @@ -30,10 +30,6 @@ #include "gc_hal_kernel_vg.h" #endif -#if gcdSECURITY -#include "gc_hal_security_interface.h" -#endif - #ifdef __cplusplus extern "C" { #endif @@ -224,11 +220,6 @@ typedef struct _gcsDATABASE /* Pointer to database. */ gcsDATABASE_RECORD_PTR list[48]; -#if gcdSECURE_USER - /* Secure cache. */ - gcskSECURE_CACHE cache; -#endif - gctPOINTER handleDatabase; gctPOINTER handleDatabaseMutex; @@ -415,16 +406,6 @@ gckKERNEL_DeleteName( IN gctUINT32 Name ); -#if gcdSECURE_USER -/* Get secure cache from the process database. */ -gceSTATUS -gckKERNEL_GetProcessDBCache( - IN gckKERNEL Kernel, - IN gctUINT32 ProcessID, - OUT gcskSECURE_CACHE_PTR * Cache - ); -#endif - /******************************************************************************* ********* Timer Management ****************************************************/ typedef struct _gcsTIMER * gcsTIMER_PTR; @@ -554,10 +535,6 @@ struct _gckKERNEL /* Level of dump information after stuck. */ gctUINT stuckDump; -#if gcdSECURITY - gctUINT32 securityChannel; -#endif - /* Timer to monitor GPU stuck. */ gctPOINTER monitorTimer; @@ -673,13 +650,6 @@ struct _gckCOMMAND /* End Event signal. */ gctSIGNAL endEventSignal; -#if gcdSECURE_USER - /* Hint array copy buffer. */ - gctBOOL hintArrayAllocated; - gctUINT hintArraySize; - gctUINT32_PTR hintArray; -#endif - #if gcdPROCESS_ADDRESS_SPACE gckMMU currentMmu; #endif @@ -1224,90 +1194,12 @@ gckKERNEL_AttachProcessEx( IN gctUINT32 PID ); -#if gcdSECURE_USER -gceSTATUS -gckKERNEL_MapLogicalToPhysical( - IN gckKERNEL Kernel, - IN gcskSECURE_CACHE_PTR Cache, - IN OUT gctPOINTER * Data - ); - -gceSTATUS -gckKERNEL_FlushTranslationCache( - IN gckKERNEL Kernel, - IN gcskSECURE_CACHE_PTR Cache, - IN gctPOINTER Logical, - IN gctSIZE_T Bytes - ); -#endif - gceSTATUS gckHARDWARE_QueryIdle( IN gckHARDWARE Hardware, OUT gctBOOL_PTR IsIdle ); -#if gcdSECURITY -gceSTATUS -gckKERNEL_SecurityOpen( - IN gckKERNEL Kernel, - IN gctUINT32 GPU, - OUT gctUINT32 *Channel - ); - -/* -** Close a security service channel -*/ -gceSTATUS -gckKERNEL_SecurityClose( - IN gctUINT32 Channel - ); - -/* -** Security service interface. -*/ -gceSTATUS -gckKERNEL_SecurityCallService( - IN gctUINT32 Channel, - IN OUT gcsTA_INTERFACE * Interface - ); - -gceSTATUS -gckKERNEL_SecurityStartCommand( - IN gckKERNEL Kernel - ); - -gceSTATUS -gckKERNEL_SecurityAllocateSecurityMemory( - IN gckKERNEL Kernel, - IN gctUINT32 Bytes, - OUT gctUINT32 * Handle - ); - -gceSTATUS -gckKERNEL_SecurityExecute( - IN gckKERNEL Kernel, - IN gctPOINTER Buffer, - IN gctUINT32 Bytes - ); - -gceSTATUS -gckKERNEL_SecurityMapMemory( - IN gckKERNEL Kernel, - IN gctUINT32 *PhysicalArray, - IN gctUINT32 PageCount, - OUT gctUINT32 * GPUAddress - ); - -gceSTATUS -gckKERNEL_SecurityUnmapMemory( - IN gckKERNEL Kernel, - IN gctUINT32 GPUAddress, - IN gctUINT32 PageCount - ); - -#endif - gceSTATUS gckKERNEL_CreateShBuffer( IN gckKERNEL Kernel, diff --git a/drivers/gpu/galcore/gc_hal_kernel_command.c b/drivers/gpu/galcore/gc_hal_kernel_command.c index 3208979316ec97..fe83bb0c13b546 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_command.c +++ b/drivers/gpu/galcore/gc_hal_kernel_command.c @@ -191,140 +191,11 @@ _IncrementCommitAtom( return status; } -#if gcdSECURE_USER -static gceSTATUS -_ProcessHints( - IN gckCOMMAND Command, - IN gctUINT32 ProcessID, - IN gcoCMDBUF CommandBuffer - ) -{ - gceSTATUS status = gcvSTATUS_OK; - gckKERNEL kernel; - gctBOOL needCopy = gcvFALSE; - gcskSECURE_CACHE_PTR cache; - gctUINT8_PTR commandBufferLogical; - gctUINT8_PTR hintedData; - gctUINT32_PTR hintArray; - gctUINT i, hintCount; - - gcmkHEADER_ARG( - "Command=0x%08X ProcessID=%d CommandBuffer=0x%08X", - Command, ProcessID, CommandBuffer - ); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Command, gcvOBJ_COMMAND); - - /* Reset state array pointer. */ - hintArray = gcvNULL; - - /* Get the kernel object. */ - kernel = Command->kernel; - - /* Get the cache form the database. */ - gcmkONERROR(gckKERNEL_GetProcessDBCache(kernel, ProcessID, &cache)); - - /* Determine the start of the command buffer. */ - commandBufferLogical - = (gctUINT8_PTR) CommandBuffer->logical - + CommandBuffer->startOffset; - - /* Determine the number of records in the state array. */ - hintCount = CommandBuffer->hintArrayTail - CommandBuffer->hintArray; - - /* Check wehther we need to copy the structures or not. */ - gcmkONERROR(gckOS_QueryNeedCopy(Command->os, ProcessID, &needCopy)); - - /* Get access to the state array. */ - if (needCopy) - { - gctUINT copySize; - - if (Command->hintArrayAllocated && - (Command->hintArraySize < CommandBuffer->hintArraySize)) - { - gcmkONERROR(gcmkOS_SAFE_FREE(Command->os, gcmUINT64_TO_PTR(Command->hintArray))); - Command->hintArraySize = gcvFALSE; - } - - if (!Command->hintArrayAllocated) - { - gctPOINTER pointer = gcvNULL; - - gcmkONERROR(gckOS_Allocate( - Command->os, - CommandBuffer->hintArraySize, - &pointer - )); - - Command->hintArray = gcmPTR_TO_UINT64(pointer); - Command->hintArrayAllocated = gcvTRUE; - Command->hintArraySize = CommandBuffer->hintArraySize; - } - - hintArray = gcmUINT64_TO_PTR(Command->hintArray); - copySize = hintCount * gcmSIZEOF(gctUINT32); - - gcmkONERROR(gckOS_CopyFromUserData( - Command->os, - hintArray, - gcmUINT64_TO_PTR(CommandBuffer->hintArray), - copySize - )); - } - else - { - gctPOINTER pointer = gcvNULL; - - gcmkONERROR(gckOS_MapUserPointer( - Command->os, - gcmUINT64_TO_PTR(CommandBuffer->hintArray), - CommandBuffer->hintArraySize, - &pointer - )); - - hintArray = pointer; - } - - /* Scan through the buffer. */ - for (i = 0; i < hintCount; i += 1) - { - /* Determine the location of the hinted data. */ - hintedData = commandBufferLogical + hintArray[i]; - - /* Map handle into physical address. */ - gcmkONERROR(gckKERNEL_MapLogicalToPhysical( - kernel, cache, (gctPOINTER) hintedData - )); - } - -OnError: - /* Get access to the state array. */ - if (!needCopy && (hintArray != gcvNULL)) - { - gcmkVERIFY_OK(gckOS_UnmapUserPointer( - Command->os, - gcmUINT64_TO_PTR(CommandBuffer->hintArray), - CommandBuffer->hintArraySize, - hintArray - )); - } - - /* Return the status. */ - gcmkFOOTER(); - return status; -} -#endif - static gceSTATUS _FlushMMU( IN gckCOMMAND Command ) { -#if gcdSECURITY - return gcvSTATUS_OK; -#else gceSTATUS status; gctUINT32 oldValue; gckHARDWARE hardware = Command->kernel->hardware; @@ -407,7 +278,6 @@ _FlushMMU( return gcvSTATUS_OK; OnError: return status; -#endif } static void @@ -767,15 +637,6 @@ gckCOMMAND_Destroy( /* Destroy the commit atom. */ gcmkVERIFY_OK(gckOS_AtomDestroy(Command->os, Command->atomCommit)); -#if gcdSECURE_USER - /* Free state array. */ - if (Command->hintArrayAllocated) - { - gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Command->os, gcmUINT64_TO_PTR(Command->hintArray))); - Command->hintArrayAllocated = gcvFALSE; - } -#endif - #if gcdRECORD_COMMAND gckRECORDER_Destory(Command->os, Command->recorder); #endif @@ -1010,19 +871,12 @@ gckCOMMAND_Start( Command->offset = waitLinkBytes; Command->newQueue = gcvFALSE; -#if gcdSECURITY - /* Start FE by calling security service. */ - gckKERNEL_SecurityStartCommand( - Command->kernel - ); -#else /* Enable command processor. */ gcmkONERROR(gckHARDWARE_Execute( hardware, Command->address, waitLinkBytes )); -#endif /* Command queue is running. */ Command->running = gcvTRUE; @@ -1104,12 +958,6 @@ gckCOMMAND_Stop( hardware, Command->waitLogical, &Command->waitSize )); -#if gcdSECURITY - gcmkONERROR(gckKERNEL_SecurityExecute( - Command->kernel, Command->waitLogical, 8 - )); -#endif - /* Update queue tail pointer. */ gcmkONERROR(gckHARDWARE_UpdateQueueTail(Command->kernel->hardware, Command->logical, @@ -1787,15 +1635,6 @@ gckCOMMAND_Commit( contextDumpBytes = entryBytes; #endif -#if gcdSECURITY - /* Commit context buffer to trust zone. */ - gckKERNEL_SecurityExecute( - Command->kernel, - entryLogical, - entryBytes - 8 - ); -#endif - #if gcdRECORD_COMMAND gckRECORDER_Record( Command->recorder, @@ -2037,11 +1876,6 @@ gckCOMMAND_Commit( bufferDumpBytes = commandBufferSize - offset; #endif -#if gcdSECURE_USER - /* Process user hints. */ - gcmkONERROR(_ProcessHints(Command, ProcessID, commandBufferObject)); -#endif - /* Determine the location to jump to for the command buffer being ** scheduled. */ if (Command->newQueue) @@ -2102,7 +1936,6 @@ gckCOMMAND_Commit( /* Generate a LINK from the end of the command buffer being scheduled back to the kernel command queue. */ -#if !gcdSECURITY gcmkONERROR(gckHARDWARE_Link( hardware, commandBufferLink, @@ -2110,7 +1943,6 @@ gckCOMMAND_Commit( exitBytes, &linkBytes )); -#endif #if gcdNONPAGED_MEMORY_CACHEABLE /* Flush the command buffer cache. */ @@ -2138,14 +1970,6 @@ gckCOMMAND_Commit( Command->commitStamp++; #endif -#if gcdSECURITY - /* Submit command buffer to trust zone. */ - gckKERNEL_SecurityExecute( - Command->kernel, - commandBufferLogical + offset, - commandBufferSize - offset - 8 - ); -#else /* Generate a LINK from the previous WAIT/LINK command sequence to the entry determined above (either the context or the command buffer). This LINK replaces the WAIT instruction from the previous WAIT/LINK @@ -2158,7 +1982,6 @@ gckCOMMAND_Commit( entryBytes, &Command->waitSize )); -#endif #if gcdNONPAGED_MEMORY_CACHEABLE /* Flush the cache for the link. */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_db.c b/drivers/gpu/galcore/gc_hal_kernel_db.c index 021f6338d1ef6e..44850e75fd35bd 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_db.c +++ b/drivers/gpu/galcore/gc_hal_kernel_db.c @@ -740,53 +740,6 @@ gckKERNEL_CreateProcessDB( gckMMU_Construct(Kernel, gcdMMU_SIZE, &database->mmu)); #endif -#if gcdSECURE_USER - { - gctINT slot; - gcskSECURE_CACHE * cache = &database->cache; - - /* Setup the linked list of cache nodes. */ - for (slot = 1; slot <= gcdSECURE_CACHE_SLOTS; ++slot) - { - cache->cache[slot].logical = gcvNULL; - -#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE - cache->cache[slot].prev = &cache->cache[slot - 1]; - cache->cache[slot].next = &cache->cache[slot + 1]; -# endif -#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH - cache->cache[slot].nextHash = gcvNULL; - cache->cache[slot].prevHash = gcvNULL; -# endif - } - -#if gcdSECURE_CACHE_METHOD != gcdSECURE_CACHE_TABLE - /* Setup the head and tail of the cache. */ - cache->cache[0].next = &cache->cache[1]; - cache->cache[0].prev = &cache->cache[gcdSECURE_CACHE_SLOTS]; - cache->cache[0].logical = gcvNULL; - - /* Fix up the head and tail pointers. */ - cache->cache[0].next->prev = &cache->cache[0]; - cache->cache[0].prev->next = &cache->cache[0]; -# endif - -#if gcdSECURE_CACHE_METHOD == gcdSECURE_CACHE_HASH - /* Zero out the hash table. */ - for (slot = 0; slot < gcmCOUNTOF(cache->hash); ++slot) - { - cache->hash[slot].logical = gcvNULL; - cache->hash[slot].nextHash = gcvNULL; - } -# endif - - /* Initialize cache index. */ - cache->cacheIndex = gcvNULL; - cache->cacheFree = 1; - cache->cacheStamp = 0; - } -#endif - /* Reset idle timer. */ Kernel->db->lastIdle = 0; @@ -1691,58 +1644,6 @@ gckKERNEL_GetProcessMMU( } #endif -#if gcdSECURE_USER -/******************************************************************************* -** gckKERNEL_GetProcessDBCache -** -** Get teh secure cache from a process database. -** -** INPUT: -** -** gckKERNEL Kernel -** Pointer to a gckKERNEL object. -** -** gctUINT32 ProcessID -** Process ID used to identify the database. -** -** OUTPUT: -** -** gcskSECURE_CACHE_PTR * Cache -** Pointer to a variable that receives the secure cache pointer. -*/ -gceSTATUS -gckKERNEL_GetProcessDBCache( - IN gckKERNEL Kernel, - IN gctUINT32 ProcessID, - OUT gcskSECURE_CACHE_PTR * Cache - ) -{ - gceSTATUS status; - gcsDATABASE_PTR database; - - gcmkHEADER_ARG("Kernel=0x%x ProcessID=%d", Kernel, ProcessID); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL); - gcmkVERIFY_ARGUMENT(Cache != gcvNULL); - - /* Find the database. */ - gcmkONERROR(gckKERNEL_FindDatabase(Kernel, ProcessID, gcvFALSE, &database)); - - /* Return the pointer to the cache. */ - *Cache = &database->cache; - - /* Success. */ - gcmkFOOTER_ARG("*Cache=0x%x", *Cache); - return gcvSTATUS_OK; - -OnError: - /* Return the status. */ - gcmkFOOTER(); - return status; -} -#endif - gceSTATUS gckKERNEL_DumpProcessDB( IN gckKERNEL Kernel diff --git a/drivers/gpu/galcore/gc_hal_kernel_drv.c b/drivers/gpu/galcore/gc_hal_kernel_drv.c new file mode 100644 index 00000000000000..10098c3ef2c8ac --- /dev/null +++ b/drivers/gpu/galcore/gc_hal_kernel_drv.c @@ -0,0 +1,1252 @@ +/**************************************************************************** +* +* Copyright (C) 2005 - 2014 by Vivante Corp. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + + +#include +#include + +#include "gc_hal_kernel_linux.h" +#include "gc_hal_driver.h" + +#include + +#ifdef CONFIG_PXA_DVFM +# include +# include +#endif + + +/* Zone used for header/footer. */ +#define _GC_OBJ_ZONE gcvZONE_DRIVER + +MODULE_DESCRIPTION("Vivante Graphics Driver"); +MODULE_LICENSE("GPL"); + +static struct class* gpuClass; + +static gcsPLATFORM platform; + +static gckGALDEVICE galDevice; + +static uint major = 199; +module_param(major, uint, 0644); + +static int irqLine = -1; +module_param(irqLine, int, 0644); + +static ulong registerMemBase = 0x80000000; +module_param(registerMemBase, ulong, 0644); + +static ulong registerMemSize = 2 << 10; +module_param(registerMemSize, ulong, 0644); + +static int irqLine2D = -1; +module_param(irqLine2D, int, 0644); + +static ulong registerMemBase2D = 0x00000000; +module_param(registerMemBase2D, ulong, 0644); + +static ulong registerMemSize2D = 2 << 10; +module_param(registerMemSize2D, ulong, 0644); + +static int irqLineVG = -1; +module_param(irqLineVG, int, 0644); + +static ulong registerMemBaseVG = 0x00000000; +module_param(registerMemBaseVG, ulong, 0644); + +static ulong registerMemSizeVG = 2 << 10; +module_param(registerMemSizeVG, ulong, 0644); + +#ifndef gcdDEFAULT_CONTIGUOUS_SIZE +#define gcdDEFAULT_CONTIGUOUS_SIZE (4 << 20) +#endif +static ulong contiguousSize = gcdDEFAULT_CONTIGUOUS_SIZE; +module_param(contiguousSize, ulong, 0644); + +static ulong contiguousBase = 0; +module_param(contiguousBase, ulong, 0644); + +static ulong bankSize = 0; +module_param(bankSize, ulong, 0644); + +static int fastClear = -1; +module_param(fastClear, int, 0644); + +static int compression = -1; +module_param(compression, int, 0644); + +static int powerManagement = -1; +module_param(powerManagement, int, 0644); + +static int gpuProfiler = 0; +module_param(gpuProfiler, int, 0644); + +static int signal = 48; +module_param(signal, int, 0644); + +static ulong baseAddress = 0; +module_param(baseAddress, ulong, 0644); + +static ulong physSize = 0; +module_param(physSize, ulong, 0644); + +static uint logFileSize = 0; +module_param(logFileSize,uint, 0644); + +static uint recovery = 1; +module_param(recovery, uint, 0644); +MODULE_PARM_DESC(recovery, "Recover GPU from stuck (1: Enable, 0: Disable)"); + +/* Middle needs about 40KB buffer, Maximal may need more than 200KB buffer. */ +static uint stuckDump = 1; +module_param(stuckDump, uint, 0644); +MODULE_PARM_DESC(stuckDump, "Level of stuck dump content (1: Minimal, 2: Middle, 3: Maximal)"); + +static int showArgs = 0; +module_param(showArgs, int, 0644); + +static int mmu = 1; +module_param(mmu, int, 0644); + +static int gpu3DMinClock = 1; + +static int contiguousRequested = 0; + +static int drv_open( + struct inode* inode, + struct file* filp + ); + +static int drv_release( + struct inode* inode, + struct file* filp + ); + +static long drv_ioctl( + struct file* filp, + unsigned int ioctlCode, + unsigned long arg + ); + +static int drv_mmap( + struct file* filp, + struct vm_area_struct* vma + ); + +static struct file_operations driver_fops = +{ + .owner = THIS_MODULE, + .open = drv_open, + .release = drv_release, + .unlocked_ioctl = drv_ioctl, +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = drv_ioctl, +#endif + .mmap = drv_mmap, +}; + +void +_UpdateModuleParam( + gcsMODULE_PARAMETERS *Param + ) +{ + irqLine = Param->irqLine ; + registerMemBase = Param->registerMemBase; + registerMemSize = Param->registerMemSize; + irqLine2D = Param->irqLine2D ; + registerMemBase2D = Param->registerMemBase2D; + registerMemSize2D = Param->registerMemSize2D; + irqLineVG = Param->irqLineVG; + registerMemBaseVG = Param->registerMemBaseVG; + registerMemSizeVG = Param->registerMemSizeVG; + contiguousSize = Param->contiguousSize; + contiguousBase = Param->contiguousBase; + bankSize = Param->bankSize; + fastClear = Param->fastClear; + compression = Param->compression; + powerManagement = Param->powerManagement; + gpuProfiler = Param->gpuProfiler; + signal = Param->signal; + baseAddress = Param->baseAddress; + physSize = Param->physSize; + logFileSize = Param->logFileSize; + recovery = Param->recovery; + stuckDump = Param->stuckDump; + showArgs = Param->showArgs; + contiguousRequested = Param->contiguousRequested; + gpu3DMinClock = Param->gpu3DMinClock; +} + +void +gckOS_DumpParam( + void + ) +{ + printk("Galcore options:\n"); + printk(" irqLine = %d\n", irqLine); + printk(" registerMemBase = 0x%08lX\n", registerMemBase); + printk(" registerMemSize = 0x%08lX\n", registerMemSize); + + if (irqLine2D != -1) + { + printk(" irqLine2D = %d\n", irqLine2D); + printk(" registerMemBase2D = 0x%08lX\n", registerMemBase2D); + printk(" registerMemSize2D = 0x%08lX\n", registerMemSize2D); + } + + if (irqLineVG != -1) + { + printk(" irqLineVG = %d\n", irqLineVG); + printk(" registerMemBaseVG = 0x%08lX\n", registerMemBaseVG); + printk(" registerMemSizeVG = 0x%08lX\n", registerMemSizeVG); + } + + printk(" contiguousSize = %ld\n", contiguousSize); + printk(" contiguousBase = 0x%08lX\n", contiguousBase); + printk(" bankSize = 0x%08lX\n", bankSize); + printk(" fastClear = %d\n", fastClear); + printk(" compression = %d\n", compression); + printk(" signal = %d\n", signal); + printk(" powerManagement = %d\n", powerManagement); + printk(" baseAddress = 0x%08lX\n", baseAddress); + printk(" physSize = 0x%08lX\n", physSize); + printk(" logFileSize = %d KB \n", logFileSize); + printk(" recovery = %d\n", recovery); + printk(" stuckDump = %d\n", stuckDump); + printk(" gpuProfiler = %d\n", gpuProfiler); +} + +int drv_open( + struct inode* inode, + struct file* filp + ) +{ + gceSTATUS status; + gctBOOL attached = gcvFALSE; + gcsHAL_PRIVATE_DATA_PTR data = gcvNULL; + gctINT i; + + gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = kmalloc(sizeof(gcsHAL_PRIVATE_DATA), GFP_KERNEL | __GFP_NOWARN); + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + data->device = galDevice; + data->mappedMemory = gcvNULL; + data->contiguousLogical = gcvNULL; + gcmkONERROR(gckOS_GetProcessID(&data->pidOpen)); + + /* Attached the process. */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (galDevice->kernels[i] != gcvNULL) + { + gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE)); + } + } + attached = gcvTRUE; + + if (!galDevice->contiguousMapped) + { + if (galDevice->contiguousPhysical != gcvNULL) + { + gcmkONERROR(gckOS_MapMemory( + galDevice->os, + galDevice->contiguousPhysical, + galDevice->contiguousSize, + &data->contiguousLogical + )); + } + } + + filp->private_data = data; + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + if (data != gcvNULL) + { + if (data->contiguousLogical != gcvNULL) + { + gcmkVERIFY_OK(gckOS_UnmapMemory( + galDevice->os, + galDevice->contiguousPhysical, + galDevice->contiguousSize, + data->contiguousLogical + )); + } + + kfree(data); + } + + if (attached) + { + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (galDevice->kernels[i] != gcvNULL) + { + gcmkVERIFY_OK(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvFALSE)); + } + } + } + + gcmkFOOTER(); + return -ENOTTY; +} + +int drv_release( + struct inode* inode, + struct file* filp + ) +{ + gceSTATUS status; + gcsHAL_PRIVATE_DATA_PTR data; + gckGALDEVICE device; + gctINT i; + + gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = filp->private_data; + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + device = data->device; + + if (device == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): device is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (!device->contiguousMapped) + { + if (data->contiguousLogical != gcvNULL) + { + gcmkONERROR(gckOS_UnmapMemoryEx( + galDevice->os, + galDevice->contiguousPhysical, + galDevice->contiguousSize, + data->contiguousLogical, + data->pidOpen + )); + + data->contiguousLogical = gcvNULL; + } + } + + /* A process gets detached. */ + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (galDevice->kernels[i] != gcvNULL) + { + gcmkONERROR(gckKERNEL_AttachProcessEx(galDevice->kernels[i], gcvFALSE, data->pidOpen)); + } + } + + kfree(data); + filp->private_data = NULL; + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + gcmkFOOTER(); + return -ENOTTY; +} + +long drv_ioctl( + struct file* filp, + unsigned int ioctlCode, + unsigned long arg + ) +{ + gceSTATUS status; + gcsHAL_INTERFACE iface; + gctUINT32 copyLen; + DRIVER_ARGS drvArgs; + gckGALDEVICE device; + gcsHAL_PRIVATE_DATA_PTR data; + gctINT32 i, count; + gckVIDMEM_NODE nodeObject; + + gcmkHEADER_ARG( + "filp=0x%08X ioctlCode=0x%08X arg=0x%08X", + filp, ioctlCode, arg + ); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = filp->private_data; + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + device = data->device; + + if (device == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): device is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if ((ioctlCode != IOCTL_GCHAL_INTERFACE) + && (ioctlCode != IOCTL_GCHAL_KERNEL_INTERFACE) + ) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): unknown command %d\n", + __FUNCTION__, __LINE__, + ioctlCode + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Get the drvArgs. */ + copyLen = copy_from_user( + &drvArgs, (void *) arg, sizeof(DRIVER_ARGS) + ); + + if (copyLen != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): error copying of the input arguments.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Now bring in the gcsHAL_INTERFACE structure. */ + if ((drvArgs.InputBufferSize != sizeof(gcsHAL_INTERFACE)) + || (drvArgs.OutputBufferSize != sizeof(gcsHAL_INTERFACE)) + ) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): input or/and output structures are invalid.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + copyLen = copy_from_user( + &iface, gcmUINT64_TO_PTR(drvArgs.InputBuffer), sizeof(gcsHAL_INTERFACE) + ); + + if (copyLen != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): error copying of input HAL interface.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + if (iface.command == gcvHAL_CHIP_INFO) + { + count = 0; + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) + { +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + iface.u.ChipInfo.types[count] = gcvHARDWARE_VG; + } + else +#endif + { + gcmkVERIFY_OK(gckHARDWARE_GetType(device->kernels[i]->hardware, + &iface.u.ChipInfo.types[count])); + } + count++; + } + } + + iface.u.ChipInfo.count = count; + iface.status = status = gcvSTATUS_OK; + } + else + { + if (iface.hardwareType > 7) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): unknown hardwareType %d\n", + __FUNCTION__, __LINE__, + iface.hardwareType + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +#if gcdENABLE_VG + if (device->coreMapping[iface.hardwareType] == gcvCORE_VG) + { + status = gckVGKERNEL_Dispatch(device->kernels[gcvCORE_VG], + (ioctlCode == IOCTL_GCHAL_INTERFACE), + &iface); + } + else +#endif + { + status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]], + (ioctlCode == IOCTL_GCHAL_INTERFACE), + &iface); + } + } + + /* Redo system call after pending signal is handled. */ + if (status == gcvSTATUS_INTERRUPTED) + { + gcmkFOOTER(); + return -ERESTARTSYS; + } + + if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY)) + { + gcuVIDMEM_NODE_PTR node; + gctUINT32 processID; + + gckOS_GetProcessID(&processID); + + gcmkONERROR(gckVIDMEM_HANDLE_Lookup(device->kernels[device->coreMapping[iface.hardwareType]], + processID, + (gctUINT32)iface.u.LockVideoMemory.node, + &nodeObject)); + node = nodeObject->node; + + /* Special case for mapped memory. */ + if ((data->mappedMemory != gcvNULL) + && (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) + ) + { + /* Compute offset into mapped memory. */ + gctUINT32 offset + = (gctUINT8 *) gcmUINT64_TO_PTR(iface.u.LockVideoMemory.memory) + - (gctUINT8 *) device->contiguousBase; + + /* Compute offset into user-mapped region. */ + iface.u.LockVideoMemory.memory = + gcmPTR_TO_UINT64((gctUINT8 *) data->mappedMemory + offset); + } + } + + /* Copy data back to the user. */ + copyLen = copy_to_user( + gcmUINT64_TO_PTR(drvArgs.OutputBuffer), &iface, sizeof(gcsHAL_INTERFACE) + ); + + if (copyLen != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): error copying of output HAL interface.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + gcmkFOOTER(); + return -ENOTTY; +} + +static int drv_mmap( + struct file* filp, + struct vm_area_struct* vma + ) +{ + gceSTATUS status = gcvSTATUS_OK; + gcsHAL_PRIVATE_DATA_PTR data; + gckGALDEVICE device; + + gcmkHEADER_ARG("filp=0x%08X vma=0x%08X", filp, vma); + + if (filp == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): filp is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + data = filp->private_data; + + if (data == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): private_data is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + device = data->device; + + if (device == gcvNULL) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): device is NULL\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + +#if !gcdPAGED_MEMORY_CACHEABLE + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_flags |= gcdVM_FLAGS; +#endif + vma->vm_pgoff = 0; + + if (device->contiguousMapped) + { + unsigned long size = vma->vm_end - vma->vm_start; + int ret = 0; + + if (size > device->contiguousSize) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Invalid mapping size.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); + } + + ret = io_remap_pfn_range( + vma, + vma->vm_start, + device->requestedContiguousBase >> PAGE_SHIFT, + size, + vma->vm_page_prot + ); + + if (ret != 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): io_remap_pfn_range failed %d\n", + __FUNCTION__, __LINE__, + ret + ); + + data->mappedMemory = gcvNULL; + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + data->mappedMemory = (gctPOINTER) vma->vm_start; + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + } + +OnError: + gcmkFOOTER(); + return -ENOTTY; +} + + +static int drv_init(void) +{ + int ret; + int result = -EINVAL; + gceSTATUS status; + gckGALDEVICE device = gcvNULL; + struct class* device_class = gcvNULL; + + gcsDEVICE_CONSTRUCT_ARGS args = { + .recovery = recovery, + .stuckDump = stuckDump, + .gpu3DMinClock = gpu3DMinClock, + .contiguousRequested = contiguousRequested, + .platform = &platform, + .mmu = mmu, + }; + + gcmkHEADER(); + + printk(KERN_INFO "Galcore version %d.%d.%d.%d\n", + gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD); + +#if !VIVANTE_PROFILER_PM + /* when enable gpu profiler, we need to turn off gpu powerMangement */ + if (gpuProfiler) + { + powerManagement = 0; + } +#endif + + if (showArgs) + { + gckOS_DumpParam(); + } + + if (logFileSize != 0) + { + gckDEBUGFS_Initialize(); + } + + /* Create the GAL device. */ + status = gckGALDEVICE_Construct( + irqLine, + registerMemBase, registerMemSize, + irqLine2D, + registerMemBase2D, registerMemSize2D, + irqLineVG, + registerMemBaseVG, registerMemSizeVG, + contiguousBase, contiguousSize, + bankSize, fastClear, compression, baseAddress, physSize, signal, + logFileSize, + powerManagement, + gpuProfiler, + &args, + &device + ); + + if (gcmIS_ERROR(status)) + { + gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to create the GAL device: status=%d\n", + __FUNCTION__, __LINE__, status); + + goto OnError; + } + + /* Start the GAL device. */ + gcmkONERROR(gckGALDEVICE_Start(device)); + + if ((physSize != 0) + && (device->kernels[gcvCORE_MAJOR] != gcvNULL) + && (device->kernels[gcvCORE_MAJOR]->hardware->mmuVersion != 0)) + { + /* Reset the base address */ + device->baseAddress = 0; + } + + /* Register the character device. */ + ret = register_chrdev(major, DEVICE_NAME, &driver_fops); + + if (ret < 0) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Could not allocate major number for mmap.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); + } + + if (major == 0) + { + major = ret; + } + + /* Create the device class. */ + device_class = class_create(THIS_MODULE, "graphics_class"); + + if (IS_ERR(device_class)) + { + gcmkTRACE_ZONE( + gcvLEVEL_ERROR, gcvZONE_DRIVER, + "%s(%d): Failed to create the class.\n", + __FUNCTION__, __LINE__ + ); + + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + } + + device_create(device_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); + + galDevice = device; + gpuClass = device_class; + + gcmkTRACE_ZONE( + gcvLEVEL_INFO, gcvZONE_DRIVER, + "%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n", + __FUNCTION__, __LINE__, + irqLine, contiguousSize, registerMemBase + ); + + /* Success. */ + gcmkFOOTER_NO(); + return 0; + +OnError: + /* Roll back. */ + if (device_class != gcvNULL) + { + device_destroy(device_class, MKDEV(major, 0)); + class_destroy(device_class); + } + + if (device != gcvNULL) + { + gcmkVERIFY_OK(gckGALDEVICE_Stop(device)); + gcmkVERIFY_OK(gckGALDEVICE_Destroy(device)); + } + + gcmkFOOTER(); + return result; +} + +static void drv_exit(void) +{ + gcmkHEADER(); + + gcmkASSERT(gpuClass != gcvNULL); + device_destroy(gpuClass, MKDEV(major, 0)); + class_destroy(gpuClass); + + unregister_chrdev(major, DEVICE_NAME); + + gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice)); + gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice)); + + if(gckDEBUGFS_IsEnabled()) + { + gckDEBUGFS_Terminate(); + } + + gcmkFOOTER_NO(); +} + +static int gpu_probe(struct platform_device *pdev) +{ + int ret = -ENODEV; + gcsMODULE_PARAMETERS moduleParam = { + .irqLine = irqLine, + .registerMemBase = registerMemBase, + .registerMemSize = registerMemSize, + .irqLine2D = irqLine2D, + .registerMemBase2D = registerMemBase2D, + .registerMemSize2D = registerMemSize2D, + .irqLineVG = irqLineVG, + .registerMemBaseVG = registerMemBaseVG, + .registerMemSizeVG = registerMemSizeVG, + .contiguousSize = contiguousSize, + .contiguousBase = contiguousBase, + .bankSize = bankSize, + .fastClear = fastClear, + .compression = compression, + .powerManagement = powerManagement, + .gpuProfiler = gpuProfiler, + .signal = signal, + .baseAddress = baseAddress, + .physSize = physSize, + .logFileSize = logFileSize, + .recovery = recovery, + .stuckDump = stuckDump, + .showArgs = showArgs, + .gpu3DMinClock = gpu3DMinClock, + }; + + gcmkHEADER(); + + platform.device = pdev; + + if (platform.ops->getPower) + { + if (gcmIS_ERROR(platform.ops->getPower(&platform))) + { + gcmkFOOTER_NO(); + return ret; + } + } + + if (platform.ops->adjustParam) + { + /* Override default module param. */ + platform.ops->adjustParam(&platform, &moduleParam); + + /* Update module param because drv_init() uses them directly. */ + _UpdateModuleParam(&moduleParam); + } + + ret = drv_init(); + + if (!ret) + { + platform_set_drvdata(pdev, galDevice); + + gcmkFOOTER_NO(); + return ret; + } + + gcmkFOOTER_ARG(KERN_INFO "Failed to register gpu driver: %d\n", ret); + return ret; +} + +static int gpu_remove(struct platform_device *pdev) +{ + gcmkHEADER(); + + drv_exit(); + + if (platform.ops->putPower) + { + platform.ops->putPower(&platform); + } + + gcmkFOOTER_NO(); + return 0; +} + +static int gpu_suspend(struct platform_device *dev, pm_message_t state) +{ + gceSTATUS status; + gckGALDEVICE device; + gctINT i; + + device = platform_get_drvdata(dev); + + if (!device) + { + return -1; + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) + { + /* Store states. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_QueryPowerManagementState(device->kernels[i]->vg->hardware, &device->statesStored[i]); + } + else +#endif + { + status = gckHARDWARE_QueryPowerManagementState(device->kernels[i]->hardware, &device->statesStored[i]); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_OFF); + } + else +#endif + { + status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + + } + } + + return 0; +} + +static int gpu_resume(struct platform_device *dev) +{ + gceSTATUS status; + gckGALDEVICE device; + gctINT i; + gceCHIPPOWERSTATE statesStored; + + device = platform_get_drvdata(dev); + + if (!device) + { + return -1; + } + + for (i = 0; i < gcdMAX_GPU_COUNT; i++) + { + if (device->kernels[i] != gcvNULL) + { +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_ON); + } + else +#endif + { + status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_ON); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + + /* Convert global state to crossponding internal state. */ + switch(device->statesStored[i]) + { + case gcvPOWER_OFF: + statesStored = gcvPOWER_OFF_BROADCAST; + break; + case gcvPOWER_IDLE: + statesStored = gcvPOWER_IDLE_BROADCAST; + break; + case gcvPOWER_SUSPEND: + statesStored = gcvPOWER_SUSPEND_BROADCAST; + break; + case gcvPOWER_ON: + statesStored = gcvPOWER_ON_AUTO; + break; + default: + statesStored = device->statesStored[i]; + break; + } + + /* Restore states. */ +#if gcdENABLE_VG + if (i == gcvCORE_VG) + { + status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, statesStored); + } + else +#endif + { + status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, statesStored); + } + + if (gcmIS_ERROR(status)) + { + return -1; + } + } + } + + return 0; +} + +#if defined(CONFIG_PM) +static int gpu_runtime_suspend(struct device *dev) +{ + pm_message_t state={0}; + return gpu_suspend(to_platform_device(dev), state); +} + +static int gpu_runtime_resume(struct device *dev) +{ + return gpu_resume(to_platform_device(dev)); +} + +static const struct dev_pm_ops gpu_pm_ops = { + SET_RUNTIME_PM_OPS(gpu_runtime_suspend, gpu_runtime_resume, NULL) +}; +#endif + +static struct platform_driver gpu_driver = { + .probe = gpu_probe, + .remove = gpu_remove, + + .driver = { + .name = DEVICE_NAME, + .pm = &gpu_pm_ops, + } +}; + +static int __init gpu_init(void) +{ + int ret = 0; + + memset(&platform, 0, sizeof(gcsPLATFORM)); + + gckPLATFORM_QueryOperations(&platform.ops); + + if (platform.ops == gcvNULL) + { + printk(KERN_ERR "galcore: No platform specific operations.\n"); + ret = -ENODEV; + goto out; + } + + if (platform.ops->allocPriv) + { + /* Allocate platform private data. */ + if (gcmIS_ERROR(platform.ops->allocPriv(&platform))) + { + ret = -ENOMEM; + goto out; + } + } + + if (platform.ops->needAddDevice + && platform.ops->needAddDevice(&platform)) + { + /* Allocate device */ + platform.device = platform_device_alloc(DEVICE_NAME, -1); + if (!platform.device) + { + printk(KERN_ERR "galcore: platform_device_alloc failed.\n"); + ret = -ENOMEM; + goto out; + } + + /* Add device */ + ret = platform_device_add(platform.device); + if (ret) + { + printk(KERN_ERR "galcore: platform_device_add failed.\n"); + goto put_dev; + } + } + + platform.driver = &gpu_driver; + + if (platform.ops->adjustDriver) + { + /* Override default platform_driver struct. */ + platform.ops->adjustDriver(&platform); + } + + ret = platform_driver_register(&gpu_driver); + if (!ret) + { + goto out; + } + + platform_device_del(platform.device); +put_dev: + platform_device_put(platform.device); + +out: + return ret; +} + +static void __exit gpu_exit(void) +{ + platform_driver_unregister(&gpu_driver); + + if (platform.ops->needAddDevice + && platform.ops->needAddDevice(&platform)) + { + platform_device_unregister(platform.device); + } + + if (platform.priv) + { + /* Free platform private data. */ + platform.ops->freePriv(&platform); + } +} + +module_init(gpu_init); +module_exit(gpu_exit); diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index 603fa653e8ab10..8e8fdc45c5c8de 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -1549,10 +1549,6 @@ gckEVENT_Submit( gctINT32 oldValue; #endif -#if gcdSECURITY - gctPOINTER reservedBuffer; -#endif - gctUINT32 flushBytes; gctUINT32 executeBytes; gckHARDWARE hardware; @@ -1653,9 +1649,6 @@ gckEVENT_Submit( /* Reserve space in the command queue. */ gcmkONERROR(gckCOMMAND_Reserve(command, bytes, &buffer, &bytes)); -#if gcdSECURITY - reservedBuffer = buffer; -#endif /* Set the flush in the command queue. */ gcmkONERROR(gckHARDWARE_Flush( @@ -1680,16 +1673,8 @@ gckEVENT_Submit( /* Advance to next command. */ buffer = (gctUINT8_PTR)buffer + bytes; -#if gcdSECURITY - gckKERNEL_SecurityExecute( - Event->kernel, - reservedBuffer, - executeBytes - ); -#else /* Execute the hardware event. */ gcmkONERROR(gckCOMMAND_Execute(command, executeBytes)); -#endif #endif } @@ -2081,9 +2066,6 @@ gckEVENT_Notify( gctINT eventNumber = 0; #endif gctINT32 free; -#if gcdSECURE_USER - gcskSECURE_CACHE_PTR cache; -#endif gckVIDMEM_NODE nodeObject; gcuVIDMEM_NODE_PTR node; @@ -2298,20 +2280,10 @@ gckEVENT_Notify( { gcsEVENT_PTR recordNext; gctPOINTER logical; -#if gcdSECURE_USER - gctSIZE_T bytes; -#endif /* Grab next record. */ recordNext = record->next; -#if gcdSECURE_USER - /* Get the cache that belongs to this process. */ - gcmkONERROR(gckKERNEL_GetProcessDBCache(Event->kernel, - record->processID, - &cache)); -#endif - gcmkTRACE_ZONE_N( gcvLEVEL_INFO, gcvZONE_EVENT, gcmSIZEOF(record->info.command), @@ -2333,16 +2305,6 @@ gckEVENT_Notify( gcmNAME_TO_PTR(record->info.u.FreeNonPagedMemory.physical), gcmUINT64_TO_PTR(record->info.u.FreeNonPagedMemory.logical)); - if (gcmIS_SUCCESS(status)) - { -#if gcdSECURE_USER - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Event->kernel, - cache, - gcmUINT64_TO_PTR(record->record.u.FreeNonPagedMemory.logical), - (gctSIZE_T) record->record.u.FreeNonPagedMemory.bytes)); -#endif - } gcmRELEASE_NAME(record->info.u.FreeNonPagedMemory.physical); break; @@ -2359,16 +2321,6 @@ gckEVENT_Notify( gcmUINT64_TO_PTR(record->info.u.FreeContiguousMemory.logical), (gctSIZE_T) record->info.u.FreeContiguousMemory.bytes); - if (gcmIS_SUCCESS(status)) - { -#if gcdSECURE_USER - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Event->kernel, - cache, - gcmUINT64_TO_PTR(event->event.u.FreeContiguousMemory.logical), - (gctSIZE_T) event->event.u.FreeContiguousMemory.bytes)); -#endif - } gcmRELEASE_NAME(record->info.u.FreeContiguousMemory.physical); break; @@ -2402,21 +2354,6 @@ gckEVENT_Notify( node = nodeObject->node; - /* Save node information before it disappears. */ -#if gcdSECURE_USER - node = event->event.u.UnlockVideoMemory.node; - if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) - { - logical = gcvNULL; - bytes = 0; - } - else - { - logical = node->Virtual.logical; - bytes = node->Virtual.bytes; - } -#endif - /* Unlock. */ status = gckVIDMEM_Unlock( Event->kernel, @@ -2424,17 +2361,6 @@ gckEVENT_Notify( record->info.u.UnlockVideoMemory.type, gcvNULL); -#if gcdSECURE_USER - if (gcmIS_SUCCESS(status) && (logical != gcvNULL)) - { - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Event->kernel, - cache, - logical, - bytes)); - } -#endif - #if gcdPROCESS_ADDRESS_SPACE gcmkVERIFY_OK(gckVIDMEM_NODE_Unlock( Event->kernel, @@ -2488,16 +2414,6 @@ gckEVENT_Notify( info, record->info.u.UnmapUserMemory.address); -#if gcdSECURE_USER - if (gcmIS_SUCCESS(status)) - { - gcmkVERIFY_OK(gckKERNEL_FlushTranslationCache( - Event->kernel, - cache, - gcmUINT64_TO_PTR(record->info.u.UnmapUserMemory.memory), - (gctSIZE_T) record->info.u.UnmapUserMemory.size)); - } -#endif gcmRELEASE_NAME(record->info.u.UnmapUserMemory.info); break; diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index 225ed6ff1f991b..91f0e05264966d 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -1632,9 +1632,7 @@ gckOS_AllocateNonPagedMemory( gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); } #else -#if !gcdSECURITY mdlMap->vma->vm_page_prot = gcmkNONPAGED_MEMROY_PROT(mdlMap->vma->vm_page_prot); -#endif mdlMap->vma->vm_flags |= gcdVM_FLAGS; mdlMap->vma->vm_pgoff = 0; @@ -1662,11 +1660,7 @@ gckOS_AllocateNonPagedMemory( } else { -#if gcdSECURITY - *Logical = (gctPOINTER)mdl->kaddr; -#else *Logical = (gctPOINTER)mdl->addr; -#endif } /* @@ -2091,91 +2085,6 @@ gceSTATUS gckOS_UserLogicalToPhysical( return gckOS_GetPhysicalAddress(Os, Logical, Address); } -#if gcdSECURE_USER -static gceSTATUS -gckOS_AddMapping( - IN gckOS Os, - IN gctUINT32 Physical, - IN gctPOINTER Logical, - IN gctSIZE_T Bytes - ) -{ - gceSTATUS status; - gcsUSER_MAPPING_PTR map; - - gcmkHEADER_ARG("Os=0x%X Physical=0x%X Logical=0x%X Bytes=%lu", - Os, Physical, Logical, Bytes); - - gcmkONERROR(gckOS_Allocate(Os, - gcmSIZEOF(gcsUSER_MAPPING), - (gctPOINTER *) &map)); - - map->next = Os->userMap; - map->physical = Physical - Os->device->baseAddress; - map->logical = Logical; - map->bytes = Bytes; - map->start = (gctINT8_PTR) Logical; - map->end = map->start + Bytes; - - Os->userMap = map; - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -static gceSTATUS -gckOS_RemoveMapping( - IN gckOS Os, - IN gctPOINTER Logical, - IN gctSIZE_T Bytes - ) -{ - gceSTATUS status; - gcsUSER_MAPPING_PTR map, prev; - - gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", Os, Logical, Bytes); - - for (map = Os->userMap, prev = gcvNULL; map != gcvNULL; map = map->next) - { - if ((map->logical == Logical) - && (map->bytes == Bytes) - ) - { - break; - } - - prev = map; - } - - if (map == gcvNULL) - { - gcmkONERROR(gcvSTATUS_INVALID_ADDRESS); - } - - if (prev == gcvNULL) - { - Os->userMap = map->next; - } - else - { - prev->next = map->next; - } - - gcmkONERROR(gcmkOS_SAFE_FREE(Os, map)); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} -#endif - gceSTATUS _ConvertLogical2Physical( IN gckOS Os, @@ -2190,11 +2099,7 @@ _ConvertLogical2Physical( PLINUX_MDL_MAP map; gcsUSER_MAPPING_PTR userMap; -#if gcdSECURITY - base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->kaddr; -#else base = (Mdl == gcvNULL) ? gcvNULL : (gctINT8_PTR) Mdl->addr; -#endif /* Check for the logical address match. */ if ((base != gcvNULL) @@ -4696,17 +4601,6 @@ gckOS_MapUserMemory( gcmkHEADER_ARG("Os=0x%x Core=%d Memory=0x%x Size=%lu", Os, Core, Memory, Size); -#if gcdSECURE_USER - gcmkONERROR(gckOS_AddMapping(Os, *Address, Memory, Size)); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -#else -{ gctSIZE_T pageCount, i, j; gctUINT32_PTR pageTable; gctUINT32 address = 0, physical = ~0U; @@ -4942,51 +4836,6 @@ gckOS_MapUserMemory( info->extraPage = 1; } -#if gcdSECURITY - { - gctPHYS_ADDR physicalArrayPhysical; - gctPOINTER physicalArrayLogical; - gctUINT32_PTR logical; - gctSIZE_T bytes = pageCount * gcmSIZEOF(gctUINT32); - pageTable = gcvNULL; - - gcmkONERROR(gckOS_AllocateNonPagedMemory( - Os, - gcvFALSE, - &bytes, - &physicalArrayPhysical, - &physicalArrayLogical - )); - - logical = physicalArrayLogical; - - /* Fill the page table. */ - for (i = 0; i < pageCount; i++) - { - gctUINT32 phys; - phys = page_to_phys(pages[i]); - - logical[i] = phys; - } - j = 0; - - - gcmkONERROR(gckKERNEL_SecurityMapMemory( - Os->device->kernels[Core], - physicalArrayLogical, - pageCount, - &address - )); - - gcmkONERROR(gckOS_FreeNonPagedMemory( - Os, - 1, - physicalArrayPhysical, - physicalArrayLogical - )); - } - -#else #if gcdENABLE_VG if (Core == gcvCORE_VG) { @@ -5094,7 +4943,6 @@ gckOS_MapUserMemory( gcmkONERROR(gckMMU_Flush(Os->device->kernels[Core]->mmu, gcvSURF_TYPE_UNKNOWN)); #endif } -#endif info->address = address; /* Save pointer to page table. */ @@ -5203,8 +5051,6 @@ gckOS_MapUserMemory( return status; } -#endif -} /******************************************************************************* ** @@ -5246,17 +5092,6 @@ gckOS_UnmapUserMemory( gcmkHEADER_ARG("Os=0x%X Core=%d Memory=0x%X Size=%lu Info=0x%X Address0x%08x", Os, Core, Memory, Size, Info, Address); -#if gcdSECURE_USER - gcmkONERROR(gckOS_RemoveMapping(Os, Memory, Size)); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -#else -{ gctUINTPTR_T memory, start, end; gcsPageInfo_PTR info; gctSIZE_T pageCount, i; @@ -5311,29 +5146,13 @@ gckOS_UnmapUserMemory( MEMORY_MAP_LOCK(Os); -#if !gcdSECURITY gcmkASSERT(info->pageTable != gcvNULL); -#endif if (info->extraPage) { pageCount += 1; } -#if gcdSECURITY - if (info->address > 0x80000000) - { - gckKERNEL_SecurityUnmapMemory( - Os->device->kernels[Core], - info->address, - pageCount - ); - } - else - { - gcmkPRINT("Wrong address %s(%d) %x", __FUNCTION__, __LINE__, info->address); - } -#else #if gcdENABLE_VG if (Core == gcvCORE_VG) { @@ -5366,7 +5185,6 @@ gckOS_UnmapUserMemory( info->address )); } -#endif if (info->extraPage) { @@ -5420,8 +5238,6 @@ gckOS_UnmapUserMemory( gcmkFOOTER(); return status; } -#endif -} /******************************************************************************* ** @@ -8237,93 +8053,6 @@ gckOS_CreateNativeFence( } #endif -#if gcdSECURITY -gceSTATUS -gckOS_AllocatePageArray( - IN gckOS Os, - IN gctPHYS_ADDR Physical, - IN gctSIZE_T PageCount, - OUT gctPOINTER * PageArrayLogical, - OUT gctPHYS_ADDR * PageArrayPhysical - ) -{ - gceSTATUS status = gcvSTATUS_OK; - PLINUX_MDL mdl; - gctUINT32* table; - gctUINT32 offset; - gctSIZE_T bytes; - gckALLOCATOR allocator; - - gcmkHEADER_ARG("Os=0x%X Physical=0x%X PageCount=%u", - Os, Physical, PageCount); - - /* Verify the arguments. */ - gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); - gcmkVERIFY_ARGUMENT(Physical != gcvNULL); - gcmkVERIFY_ARGUMENT(PageCount > 0); - - bytes = PageCount * gcmSIZEOF(gctUINT32); - gcmkONERROR(gckOS_AllocateNonPagedMemory( - Os, - gcvFALSE, - &bytes, - PageArrayPhysical, - PageArrayLogical - )); - - table = *PageArrayLogical; - - /* Convert pointer to MDL. */ - mdl = (PLINUX_MDL)Physical; - - allocator = mdl->allocator; - - /* Get all the physical addresses and store them in the page table. */ - - offset = 0; - PageCount = PageCount / (PAGE_SIZE / 4096); - - /* Try to get the user pages so DMA can happen. */ - while (PageCount-- > 0) - { - unsigned long phys = ~0; - - if (mdl->pagedMem && !mdl->contiguous) - { - if (allocator) - { - gctUINT32 phys_addr; - allocator->ops->Physical(allocator, mdl, offset, &phys_addr); - phys = (unsigned long)phys_addr; - } - } - else - { - if (!mdl->pagedMem) - { - gcmkTRACE_ZONE( - gcvLEVEL_INFO, gcvZONE_OS, - "%s(%d): we should not get this call for Non Paged Memory!", - __FUNCTION__, __LINE__ - ); - } - - phys = page_to_phys(nth_page(mdl->u.contiguousPages, offset)); - } - - table[offset] = phys; - - offset += 1; - } - -OnError: - - /* Return the status. */ - gcmkFOOTER(); - return status; -} -#endif - gceSTATUS gckOS_CPUPhysicalToGPUPhysical( IN gckOS Os, @@ -8404,11 +8133,7 @@ gckOS_QueryOption( } else if (!strcmp(Option, "mmu")) { -#if gcdSECURITY - *Value = 0; -#else *Value = device->mmu; -#endif return gcvSTATUS_OK; } diff --git a/drivers/gpu/galcore/gc_hal_kernel_security.c b/drivers/gpu/galcore/gc_hal_kernel_security.c deleted file mode 100644 index 54e5ce39f44ce3..00000000000000 --- a/drivers/gpu/galcore/gc_hal_kernel_security.c +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -* -* Copyright (C) 2005 - 2014 by Vivante Corp. -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - - -#include "gc_hal_kernel_precomp.h" - - - - -#define _GC_OBJ_ZONE gcvZONE_KERNEL - -#if gcdSECURITY - -/* -** Open a security service channel. -*/ -gceSTATUS -gckKERNEL_SecurityOpen( - IN gckKERNEL Kernel, - IN gctUINT32 GPU, - OUT gctUINT32 *Channel - ) -{ - gceSTATUS status; - - gcmkONERROR(gckOS_OpenSecurityChannel(Kernel->os, Kernel->core, Channel)); - gcmkONERROR(gckOS_InitSecurityChannel(*Channel)); - - return gcvSTATUS_OK; - -OnError: - return status; -} - -/* -** Close a security service channel -*/ -gceSTATUS -gckKERNEL_SecurityClose( - IN gctUINT32 Channel - ) -{ - return gcvSTATUS_OK; -} - -/* -** Security service interface. -*/ -gceSTATUS -gckKERNEL_SecurityCallService( - IN gctUINT32 Channel, - IN OUT gcsTA_INTERFACE * Interface -) -{ - gceSTATUS status; - gcmkHEADER(); - - gcmkVERIFY_ARGUMENT(Interface != gcvNULL); - - gckOS_CallSecurityService(Channel, Interface); - - status = Interface->result; - - gcmkONERROR(status); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckKERNEL_SecurityStartCommand( - IN gckKERNEL Kernel - ) -{ - gceSTATUS status; - gcsTA_INTERFACE iface; - - gcmkHEADER(); - - iface.command = KERNEL_START_COMMAND; - iface.u.StartCommand.gpu = Kernel->core; - - gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckKERNEL_SecurityAllocateSecurityMemory( - IN gckKERNEL Kernel, - IN gctUINT32 Bytes, - OUT gctUINT32 * Handle - ) -{ - gceSTATUS status; - gcsTA_INTERFACE iface; - - gcmkHEADER(); - - iface.command = KERNEL_ALLOCATE_SECRUE_MEMORY; - iface.u.AllocateSecurityMemory.bytes = Bytes; - - gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); - - *Handle = iface.u.AllocateSecurityMemory.memory_handle; - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckKERNEL_SecurityExecute( - IN gckKERNEL Kernel, - IN gctPOINTER Buffer, - IN gctUINT32 Bytes - ) -{ - gceSTATUS status; - gcsTA_INTERFACE iface; - - gcmkHEADER(); - - iface.command = KERNEL_EXECUTE; - iface.u.Execute.command_buffer = (gctUINT32 *)Buffer; - iface.u.Execute.gpu = Kernel->core; - iface.u.Execute.command_buffer_length = Bytes; - -#if defined(LINUX) - gcmkONERROR(gckOS_GetPhysicalAddress(Kernel->os, Buffer, - (gctUINT32 *)&iface.u.Execute.command_buffer)); -#endif - - gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); - - /* Update queue tail pointer. */ - gcmkONERROR(gckHARDWARE_UpdateQueueTail( - Kernel->hardware, 0, 0 - )); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckKERNEL_SecurityMapMemory( - IN gckKERNEL Kernel, - IN gctUINT32 *PhysicalArray, - IN gctUINT32 PageCount, - OUT gctUINT32 * GPUAddress - ) -{ - gceSTATUS status; - gcsTA_INTERFACE iface; - - gcmkHEADER(); - - iface.command = KERNEL_MAP_MEMORY; - -#if defined(LINUX) - gcmkONERROR(gckOS_GetPhysicalAddress(Kernel->os, PhysicalArray, - (gctUINT32 *)&iface.u.MapMemory.physicals)); -#endif - - iface.u.MapMemory.pageCount = PageCount; - - gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); - - *GPUAddress = iface.u.MapMemory.gpuAddress; - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckKERNEL_SecurityUnmapMemory( - IN gckKERNEL Kernel, - IN gctUINT32 GPUAddress, - IN gctUINT32 PageCount - ) -{ - gceSTATUS status; - gcsTA_INTERFACE iface; - - gcmkHEADER(); - - iface.command = KERNEL_UNMAP_MEMORY; - - iface.u.UnmapMemory.gpuAddress = GPUAddress; - iface.u.UnmapMemory.pageCount = PageCount; - - gcmkONERROR(gckKERNEL_SecurityCallService(Kernel->securityChannel, &iface)); - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -#endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_security_channel.c b/drivers/gpu/galcore/gc_hal_kernel_security_channel.c deleted file mode 100644 index e745a6f7c199ca..00000000000000 --- a/drivers/gpu/galcore/gc_hal_kernel_security_channel.c +++ /dev/null @@ -1,385 +0,0 @@ -/**************************************************************************** -* -* Copyright (C) 2005 - 2014 by Vivante Corp. -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - - -#include "gc_hal_kernel_linux.h" -#include - -#include "tee_client_api.h" - -#define _GC_OBJ_ZONE gcvZONE_OS - -#define GPU3D_UUID { 0xcc9f80ea, 0xa836, 0x11e3, { 0x9b, 0x07, 0x78, 0x2b, 0xcb, 0x5c, 0xf3, 0xe3 } } - -static const TEEC_UUID gpu3d_uuid = GPU3D_UUID; -TEEC_Context teecContext; - -typedef struct _gcsSecurityChannel { - gckOS os; - TEEC_Session session; - int * virtual; - TEEC_SharedMemory inputBuffer; - gctUINT32 bytes; - gctPOINTER mutex; -} gcsSecurityChannel; - -TEEC_SharedMemory * -gpu3d_allocate_secure_mem( - gckOS Os, - unsigned int size - ) -{ - TEEC_Result result; - TEEC_Context *context = &teecContext; - TEEC_SharedMemory *shm = NULL; - void *handle = NULL; - unsigned int phyAddr = 0xFFFFFFFF; - gceSTATUS status; - gctSIZE_T bytes = size; - - shm = kmalloc(sizeof(TEEC_SharedMemory), GFP_KERNEL); - - if (NULL == shm) - { - return NULL; - } - - memset(shm, 0, sizeof(TEEC_SharedMemory)); - - status = gckOS_AllocatePagedMemoryEx( - Os, - gcvALLOC_FLAG_SECURITY, - bytes, - gcvNULL, - (gctPHYS_ADDR *)&handle); - - if (gcmIS_ERROR(status)) - { - kfree(shm); - return NULL; - } - - status = gckOS_PhysicalToPhysicalAddress( - Os, - handle, - &phyAddr); - - if (gcmIS_ERROR(status)) - { - kfree(shm); - return NULL; - } - - /* record the handle into shm->user_data */ - shm->userdata = handle; - - /* [b] Bulk input buffer. */ - shm->size = size; - shm->flags = TEEC_MEM_INPUT; - - /* Use TEE Client API to register the underlying memory buffer. */ - shm->phyAddr = (void *)phyAddr; - - result = TEEC_RegisterSharedMemory( - context, - shm); - - if (result != TEEC_SUCCESS) - { - gckOS_FreePagedMemory(Os, (gctPHYS_ADDR)handle, shm->size); - kfree(shm); - return NULL; - } - - return shm; -} - -void gpu3d_release_secure_mem( - gckOS Os, - void *shm_handle - ) -{ - TEEC_SharedMemory *shm = shm_handle; - void * handle; - - if (!shm) - { - return; - } - - handle = shm->userdata; - - TEEC_ReleaseSharedMemory(shm); - gckOS_FreePagedMemory(Os, (gctPHYS_ADDR)handle, shm->size); - - kfree(shm); - - return; -} - -static TEEC_Result gpu3d_session_callback( - TEEC_Session* session, - uint32_t commandID, - TEEC_Operation* operation, - void* userdata - ) -{ - gcsSecurityChannel *channel = userdata; - - if (channel == gcvNULL) - { - return TEEC_ERROR_BAD_PARAMETERS; - } - - switch(commandID) - { - case gcvTA_CALLBACK_ALLOC_SECURE_MEM: - { - uint32_t size = operation->params[0].value.a; - TEEC_SharedMemory *shm = NULL; - - shm = gpu3d_allocate_secure_mem(channel->os, size); - - /* use the value to save the pointer in client side */ - operation->params[0].value.a = (uint32_t)shm; - operation->params[0].value.b = (uint32_t)shm->phyAddr; - - break; - } - case gcvTA_CALLBACK_FREE_SECURE_MEM: - { - TEEC_SharedMemory *shm = (TEEC_SharedMemory *)operation->params[0].value.a; - - gpu3d_release_secure_mem(channel->os, shm); - break; - } - default: - break; - } - - return TEEC_SUCCESS; -} - -gceSTATUS -gckOS_OpenSecurityChannel( - IN gckOS Os, - IN gceCORE GPU, - OUT gctUINT32 *Channel - ) -{ - gceSTATUS status; - TEEC_Result result; - static bool initialized = gcvFALSE; - gcsSecurityChannel *channel = gcvNULL; - - TEEC_Operation operation = {0}; - - /* Connect to TEE. */ - if (initialized == gcvFALSE) - { - result = TEEC_InitializeContext(NULL, &teecContext); - - if (result != TEEC_SUCCESS) { - gcmkONERROR(gcvSTATUS_CHIP_NOT_READY); - } - - initialized = gcvTRUE; - } - - /* Construct channel. */ - gcmkONERROR( - gckOS_Allocate(Os, gcmSIZEOF(*channel), (gctPOINTER *)&channel)); - - gckOS_ZeroMemory(channel, gcmSIZEOF(gcsSecurityChannel)); - - channel->os = Os; - - gcmkONERROR(gckOS_CreateMutex(Os, &channel->mutex)); - - /* Allocate shared memory for passing gcTA_INTERFACE. */ - channel->bytes = gcmSIZEOF(gcsTA_INTERFACE); - channel->virtual = kmalloc(channel->bytes, GFP_KERNEL | __GFP_NOWARN); - - if (!channel->virtual) - { - gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); - } - - channel->inputBuffer.size = channel->bytes; - channel->inputBuffer.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; - channel->inputBuffer.phyAddr = (void *)virt_to_phys(channel->virtual); - - result = TEEC_RegisterSharedMemory(&teecContext, &channel->inputBuffer); - - if (result != TEEC_SUCCESS) - { - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } - - operation.paramTypes = TEEC_PARAM_TYPES( - TEEC_VALUE_INPUT, - TEEC_NONE, - TEEC_NONE, - TEEC_NONE); - - operation.params[0].value.a = GPU; - - /* Open session with TEE application. */ - result = TEEC_OpenSession( - &teecContext, - &channel->session, - &gpu3d_uuid, - TEEC_LOGIN_USER, - NULL, - &operation, - NULL); - - /* Prepare callback. */ - TEEC_RegisterCallback(&channel->session, gpu3d_session_callback, channel); - - *Channel = (gctUINT32)channel; - - return gcvSTATUS_OK; - -OnError: - if (channel) - { - if (channel->virtual) - { - } - - if (channel->mutex) - { - gcmkVERIFY_OK(gckOS_DeleteMutex(Os, channel->mutex)); - } - - gcmkVERIFY_OK(gckOS_Free(Os, channel)); - } - - return status; -} - -gceSTATUS -gckOS_CloseSecurityChannel( - IN gctUINT32 Channel - ) -{ - /* TODO . */ - return gcvSTATUS_OK; -} - -gceSTATUS -gckOS_CallSecurityService( - IN gctUINT32 Channel, - IN gcsTA_INTERFACE *Interface - ) -{ - gceSTATUS status; - TEEC_Result result; - gcsSecurityChannel *channel = (gcsSecurityChannel *)Channel; - TEEC_Operation operation = {0}; - - gcmkHEADER(); - gcmkVERIFY_ARGUMENT(Channel != 0); - - gckOS_AcquireMutex(channel->os, channel->mutex, gcvINFINITE); - - gckOS_MemCopy(channel->virtual, Interface, channel->bytes); - - operation.paramTypes = TEEC_PARAM_TYPES( - TEEC_MEMREF_PARTIAL_INPUT, - TEEC_NONE, - TEEC_NONE, - TEEC_NONE); - - /* Note: we use the updated size in the MemRef output by the encryption. */ - operation.params[0].memref.parent = &channel->inputBuffer; - operation.params[0].memref.offset = 0; - operation.params[0].memref.size = sizeof(gcsTA_INTERFACE); - operation.started = true; - - /* Start the commit command within the TEE application. */ - result = TEEC_InvokeCommand( - &channel->session, - gcvTA_COMMAND_DISPATCH, - &operation, - NULL); - - gckOS_MemCopy(Interface, channel->virtual, channel->bytes); - - gckOS_ReleaseMutex(channel->os, channel->mutex); - - if (result != TEEC_SUCCESS) - { - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckOS_InitSecurityChannel( - IN gctUINT32 Channel - ) -{ - gceSTATUS status; - TEEC_Result result; - gcsSecurityChannel *channel = (gcsSecurityChannel *)Channel; - TEEC_Operation operation = {0}; - - gcmkHEADER(); - gcmkVERIFY_ARGUMENT(Channel != 0); - - operation.paramTypes = TEEC_PARAM_TYPES( - TEEC_MEMREF_PARTIAL_INPUT, - TEEC_NONE, - TEEC_NONE, - TEEC_NONE); - - /* Note: we use the updated size in the MemRef output by the encryption. */ - operation.params[0].memref.parent = &channel->inputBuffer; - operation.params[0].memref.offset = 0; - operation.params[0].memref.size = gcmSIZEOF(gcsTA_INTERFACE); - operation.started = true; - - /* Start the commit command within the TEE application. */ - result = TEEC_InvokeCommand( - &channel->session, - gcvTA_COMMAND_INIT, - &operation, - NULL); - - if (result != TEEC_SUCCESS) - { - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} diff --git a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c index 80240631541a47..a255d7d5fd29e2 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c +++ b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c @@ -1529,32 +1529,6 @@ gckVIDMEM_Lock( } else { -#if gcdSECURITY - gctPHYS_ADDR physicalArrayPhysical; - gctPOINTER physicalArrayLogical; - - gcmkONERROR(gckOS_AllocatePageArray( - os, - node->Virtual.physical, - node->Virtual.pageCount, - &physicalArrayLogical, - &physicalArrayPhysical - )); - - gcmkONERROR(gckKERNEL_SecurityMapMemory( - Kernel, - physicalArrayLogical, - node->Virtual.pageCount, - &node->Virtual.addresses[Kernel->core] - )); - - gcmkONERROR(gckOS_FreeNonPagedMemory( - os, - 1, - physicalArrayPhysical, - physicalArrayLogical - )); -#else #if gcdENABLE_VG if (Kernel->vg != gcvNULL) { @@ -1598,7 +1572,6 @@ gckVIDMEM_Lock( { gcmkONERROR(gckMMU_Flush(Kernel->mmu, node->Virtual.type)); } -#endif } gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, "Mapped virtual node 0x%x to 0x%08X", @@ -1781,16 +1754,6 @@ gckVIDMEM_Unlock( /* See if we can unlock the resources. */ if (node->Virtual.lockeds[Kernel->core] == 0) { -#if gcdSECURITY - if (node->Virtual.addresses[Kernel->core] > 0x80000000) - { - gcmkONERROR(gckKERNEL_SecurityUnmapMemory( - Kernel, - node->Virtual.addresses[Kernel->core], - node->Virtual.pageCount - )); - } -#else /* Free the page table. */ if (node->Virtual.pageTables[Kernel->core] != gcvNULL) { @@ -1821,7 +1784,6 @@ gckVIDMEM_Unlock( node->Virtual.pageTables[Kernel->core] = gcvNULL; node->Virtual.lockKernels[Kernel->core] = gcvNULL; } -#endif } gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM, diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h index 92636c5530e394..b31ae35704b57d 100644 --- a/drivers/gpu/galcore/inc/gc_hal.h +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -32,10 +32,6 @@ #include "gc_hal_statistics.h" #endif -#if gcdSECURITY -#include "gc_hal_security_interface.h" -#endif - #ifdef __cplusplus extern "C" { #endif @@ -1058,40 +1054,6 @@ gckOS_GetThreadID( OUT gctUINT32_PTR ThreadID ); -#if gcdSECURITY -gceSTATUS -gckOS_OpenSecurityChannel( - IN gckOS Os, - IN gceCORE Core, - OUT gctUINT32 *Channel - ); - -gceSTATUS -gckOS_CloseSecurityChannel( - IN gctUINT32 Channel - ); - -gceSTATUS -gckOS_CallSecurityService( - IN gctUINT32 Channel, - IN gcsTA_INTERFACE * Interface - ); - -gceSTATUS -gckOS_InitSecurityChannel( - OUT gctUINT32 Channel - ); - -gceSTATUS -gckOS_AllocatePageArray( - IN gckOS Os, - IN gctPHYS_ADDR Physical, - IN gctSIZE_T PageCount, - OUT gctPOINTER * PageArrayLogical, - OUT gctPHYS_ADDR * PageArrayPhysical - ); -#endif - /******************************************************************************\ ********************************** Signal Object ********************************* \******************************************************************************/ diff --git a/drivers/gpu/galcore/inc/gc_hal_base.h b/drivers/gpu/galcore/inc/gc_hal_base.h index 239d4208b3d0c8..0b070d8be8f2b6 100644 --- a/drivers/gpu/galcore/inc/gc_hal_base.h +++ b/drivers/gpu/galcore/inc/gc_hal_base.h @@ -4820,39 +4820,6 @@ struct _gcoOS_SymbolsList #endif -#if gcdSECURE_USER - -# define gcmDEFINESECUREUSER() \ - gctUINT __secure_user_offset__; \ - gctUINT32_PTR __secure_user_hintArray__; - -# define gcmBEGINSECUREUSER() \ - __secure_user_offset__ = reserve->lastOffset; \ - \ - __secure_user_hintArray__ = gcmUINT64_TO_PTR(reserve->hintArrayTail) - -# define gcmENDSECUREUSER() \ - reserve->hintArrayTail = gcmPTR_TO_UINT64(__secure_user_hintArray__) - -# define gcmSKIPSECUREUSER() \ - __secure_user_offset__ += gcmSIZEOF(gctUINT32) - -# define gcmUPDATESECUREUSER() \ - *__secure_user_hintArray__ = __secure_user_offset__; \ - \ - __secure_user_offset__ += gcmSIZEOF(gctUINT32); \ - __secure_user_hintArray__ += 1 - -#else - -# define gcmDEFINESECUREUSER() -# define gcmBEGINSECUREUSER() -# define gcmENDSECUREUSER() -# define gcmSKIPSECUREUSER() -# define gcmUPDATESECUREUSER() - -#endif - /*----------------------------------------------------------------------------*/ #if gcdDUMP diff --git a/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h b/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h index 84283421f1dd70..b7ca67af4cfe33 100644 --- a/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h +++ b/drivers/gpu/galcore/inc/gc_hal_kernel_buffer.h @@ -154,13 +154,6 @@ struct _gcoCMDBUF gctUINT64 lastReserve; gctUINT32 lastOffset; -#if gcdSECURE_USER - /* Hint array for the current command buffer. */ - gctUINT hintArraySize; - gctUINT64 hintArray; - gctUINT64 hintArrayTail; -#endif - #if gcmIS_DEBUG(gcdDEBUG_CODE) /* Last load state command location and hardware address. */ gctUINT64 lastLoadStatePtr; diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h index 5056509fd996ea..fa2c229ed2c218 100644 --- a/drivers/gpu/galcore/inc/gc_hal_options.h +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -21,14 +21,6 @@ #ifndef __gc_hal_options_h_ #define __gc_hal_options_h_ -/* - gcdSECURITY - -*/ -#ifndef gcdSECURITY -# define gcdSECURITY 0 -#endif - /* gcdPRINT_VERSION @@ -400,18 +392,6 @@ #endif #endif -/* - gcdSECURE_USER - - Use logical addresses instead of physical addresses in user land. In - this case a hint table is created for both command buffers and context - buffers, and that hint table will be used to patch up those buffers in - the kernel when they are ready to submit. -*/ -#ifndef gcdSECURE_USER -# define gcdSECURE_USER 0 -#endif - /* gcdSECURE_CACHE_SLOTS diff --git a/drivers/gpu/galcore/inc/gc_hal_security_interface.h b/drivers/gpu/galcore/inc/gc_hal_security_interface.h deleted file mode 100644 index cb86d767c60ae0..00000000000000 --- a/drivers/gpu/galcore/inc/gc_hal_security_interface.h +++ /dev/null @@ -1,137 +0,0 @@ -/**************************************************************************** -* -* Copyright (C) 2005 - 2014 by Vivante Corp. -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - - -#ifndef _GC_HAL_SECURITY_INTERFACE_H_ -#define _GC_HAL_SECURITY_INTERFACE_H_ -/*! - @brief Command codes between kernel module and TrustZone - @discussion - Critical services must be done in TrustZone to avoid sensitive content leak. Most of kernel module is kept in non-Secure os to minimize - code in TrustZone. - */ -typedef enum kernel_packet_command { - KERNEL_START_COMMAND, - KERNEL_SUBMIT, - KERNEL_MAP_MEMORY, /* */ - KERNEL_UNMAP_MEMORY, - KERNEL_ALLOCATE_SECRUE_MEMORY, /*! Security memory management. */ - KERNEL_FREE_SECURE_MEMORY, - KERNEL_EXECUTE, /* Execute a command buffer. */ -} kernel_packet_command_t; - -/*! - @brief gckCOMMAND Object requests TrustZone to start FE. - @discussion - DMA enabled register can only be written in TrustZone to avoid GPU from jumping to a hacked code. - Kernel module need use these command to ask TrustZone start command parser. - */ -struct kernel_start_command { - kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ - gctUINT8 gpu; /*! Which GPU. */ -}; - -/*! - @brief gckCOMMAND Object requests TrustZone to submit command buffer. - @discussion - Code in trustzone will check content of command buffer after copying command buffer to TrustZone. - */ -struct kernel_submit { - kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ - gctUINT8 gpu; /*! Which GPU. */ - gctUINT8 kernel_command; /*! Whether it is a kernel command. */ - gctUINT32 command_buffer_handle; /*! Handle to command buffer. */ - gctUINT32 offset; /* Offset in command buffer. */ - gctUINT32 * command_buffer; /*! Content of command buffer need to be submit. */ - gctUINT32 command_buffer_length; /*! Length of command buffer. */ -}; - - -/*! - @brief gckVIDMEM Object requests TrustZone to allocate security memory. - @discussion - Allocate a buffer from security GPU memory. - */ -struct kernel_allocate_security_memory { - kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ - gctUINT32 bytes; /*! Requested bytes. */ - gctUINT32 memory_handle; /*! Handle of allocated memory. */ -}; - -/*! - @brief gckVIDMEM Object requests TrustZone to allocate security memory. - @discussion - Free a video memory buffer from security GPU memory. - */ -struct kernel_free_security_memory { - kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ - gctUINT32 memory_handle; /*! Handle of allocated memory. */ -}; - -struct kernel_execute { - kernel_packet_command_t command; /*! The command (always needs to be the first entry in a structure). */ - gctUINT8 gpu; /*! Which GPU. */ - gctUINT8 kernel_command; /*! Whether it is a kernel command. */ - gctUINT32 * command_buffer; /*! Content of command buffer need to be submit. */ - gctUINT32 command_buffer_length; /*! Length of command buffer. */ -}; - -typedef struct kernel_map_scatter_gather { - gctUINT32 bytes; - gctUINT32 physical; - struct kernel_map_scatter_gather *next; -} -kernel_map_scatter_gather_t; - -struct kernel_map_memory { - kernel_packet_command_t command; - kernel_map_scatter_gather_t *scatter; - gctUINT32 *physicals; - gctUINT32 pageCount; - gctUINT32 gpuAddress; -}; - -struct kernel_unmap_memory { - gctUINT32 gpuAddress; - gctUINT32 pageCount; -}; - -typedef struct _gcsTA_INTERFACE { - kernel_packet_command_t command; - union { - struct kernel_submit Submit; - struct kernel_start_command StartCommand; - struct kernel_allocate_security_memory AllocateSecurityMemory; - struct kernel_execute Execute; - struct kernel_map_memory MapMemory; - struct kernel_unmap_memory UnmapMemory; - } u; - gceSTATUS result; -} gcsTA_INTERFACE; - -enum { - gcvTA_COMMAND_INIT, - gcvTA_COMMAND_DISPATCH, - - gcvTA_CALLBACK_ALLOC_SECURE_MEM, - gcvTA_CALLBACK_FREE_SECURE_MEM, -}; - -#endif From a87ff07fbcb1e0fa488db575ad4e862084e01b43 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 28 Jun 2015 09:00:17 +0200 Subject: [PATCH 0408/1983] gpu: galcore: Remove COMMAND_PROCESSOR_VERSION This is always set to 1 so remove logic around it. --- drivers/gpu/galcore/gc_hal_kernel_device.c | 6 ------ drivers/gpu/galcore/gc_hal_kernel_linux.c | 4 ---- drivers/gpu/galcore/inc/gc_hal_options.h | 7 ------- 3 files changed, 17 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index ff7b28e0b81e61..c1d7cd788994e4 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -880,10 +880,8 @@ gckGALDEVICE_Construct( device->kernels[gcvCORE_MAJOR], Args->recovery, Args->stuckDump )); -#if COMMAND_PROCESSOR_VERSION == 1 /* Start the command queue. */ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_MAJOR]->command)); -#endif } else { @@ -966,10 +964,8 @@ gckGALDEVICE_Construct( device->kernels[gcvCORE_2D], Args->recovery, Args->stuckDump )); -#if COMMAND_PROCESSOR_VERSION == 1 /* Start the command queue. */ gcmkONERROR(gckCOMMAND_Start(device->kernels[gcvCORE_2D]->command)); -#endif } else { @@ -1011,8 +1007,6 @@ gckGALDEVICE_Construct( gcvTRUE )); } - - #endif } else diff --git a/drivers/gpu/galcore/gc_hal_kernel_linux.c b/drivers/gpu/galcore/gc_hal_kernel_linux.c index e1af24fbee2f70..4922d095e6f29a 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_linux.c +++ b/drivers/gpu/galcore/gc_hal_kernel_linux.c @@ -447,12 +447,8 @@ gckKERNEL_Notify( { case gcvNOTIFY_INTERRUPT: /* Process the interrupt. */ -#if COMMAND_PROCESSOR_VERSION > 1 - status = gckINTERRUPT_Notify(Kernel->interrupt, Data); -#else status = gckHARDWARE_Interrupt(Kernel->hardware, Data); -#endif break; default: diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h index fa2c229ed2c218..c7ed3293c1ad8b 100644 --- a/drivers/gpu/galcore/inc/gc_hal_options.h +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -114,13 +114,6 @@ # define PROFILE_SHADER_COUNTERS 1 #endif -/* - COMMAND_PROCESSOR_VERSION - - The version of the command buffer and task manager. -*/ -#define COMMAND_PROCESSOR_VERSION 1 - /* gcdDUMP_KEY From a6c0520efd0295a80877dfac92b5275b52f6e590 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 28 Jun 2015 09:05:30 +0200 Subject: [PATCH 0409/1983] gpu: galcore: merge down with heap removal --- drivers/gpu/galcore/Kbuild | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/galcore/Kbuild b/drivers/gpu/galcore/Kbuild index 318375da6ec87f..56cb46aa0d9471 100644 --- a/drivers/gpu/galcore/Kbuild +++ b/drivers/gpu/galcore/Kbuild @@ -72,7 +72,6 @@ OBJS += gc_hal_kernel.o \ gc_hal_kernel_db.o \ gc_hal_kernel_debug.o \ gc_hal_kernel_event.o \ - gc_hal_kernel_heap.o \ gc_hal_kernel_mmu.o \ gc_hal_kernel_video_memory.o \ gc_hal_kernel_power.o \ From d3334db4bcce80ed011b7b9d6f3bd8319b8b7b10 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 28 Jun 2015 09:06:32 +0200 Subject: [PATCH 0410/1983] gpu: galcore: Merge down with initial commit. --- drivers/gpu/galcore/gc_hal_kernel_drv.c | 1252 ----------------- .../gc_hal_kernel_platform_imx6q14.c | 17 +- 2 files changed, 14 insertions(+), 1255 deletions(-) delete mode 100644 drivers/gpu/galcore/gc_hal_kernel_drv.c diff --git a/drivers/gpu/galcore/gc_hal_kernel_drv.c b/drivers/gpu/galcore/gc_hal_kernel_drv.c deleted file mode 100644 index 10098c3ef2c8ac..00000000000000 --- a/drivers/gpu/galcore/gc_hal_kernel_drv.c +++ /dev/null @@ -1,1252 +0,0 @@ -/**************************************************************************** -* -* Copyright (C) 2005 - 2014 by Vivante Corp. -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -*****************************************************************************/ - - -#include -#include - -#include "gc_hal_kernel_linux.h" -#include "gc_hal_driver.h" - -#include - -#ifdef CONFIG_PXA_DVFM -# include -# include -#endif - - -/* Zone used for header/footer. */ -#define _GC_OBJ_ZONE gcvZONE_DRIVER - -MODULE_DESCRIPTION("Vivante Graphics Driver"); -MODULE_LICENSE("GPL"); - -static struct class* gpuClass; - -static gcsPLATFORM platform; - -static gckGALDEVICE galDevice; - -static uint major = 199; -module_param(major, uint, 0644); - -static int irqLine = -1; -module_param(irqLine, int, 0644); - -static ulong registerMemBase = 0x80000000; -module_param(registerMemBase, ulong, 0644); - -static ulong registerMemSize = 2 << 10; -module_param(registerMemSize, ulong, 0644); - -static int irqLine2D = -1; -module_param(irqLine2D, int, 0644); - -static ulong registerMemBase2D = 0x00000000; -module_param(registerMemBase2D, ulong, 0644); - -static ulong registerMemSize2D = 2 << 10; -module_param(registerMemSize2D, ulong, 0644); - -static int irqLineVG = -1; -module_param(irqLineVG, int, 0644); - -static ulong registerMemBaseVG = 0x00000000; -module_param(registerMemBaseVG, ulong, 0644); - -static ulong registerMemSizeVG = 2 << 10; -module_param(registerMemSizeVG, ulong, 0644); - -#ifndef gcdDEFAULT_CONTIGUOUS_SIZE -#define gcdDEFAULT_CONTIGUOUS_SIZE (4 << 20) -#endif -static ulong contiguousSize = gcdDEFAULT_CONTIGUOUS_SIZE; -module_param(contiguousSize, ulong, 0644); - -static ulong contiguousBase = 0; -module_param(contiguousBase, ulong, 0644); - -static ulong bankSize = 0; -module_param(bankSize, ulong, 0644); - -static int fastClear = -1; -module_param(fastClear, int, 0644); - -static int compression = -1; -module_param(compression, int, 0644); - -static int powerManagement = -1; -module_param(powerManagement, int, 0644); - -static int gpuProfiler = 0; -module_param(gpuProfiler, int, 0644); - -static int signal = 48; -module_param(signal, int, 0644); - -static ulong baseAddress = 0; -module_param(baseAddress, ulong, 0644); - -static ulong physSize = 0; -module_param(physSize, ulong, 0644); - -static uint logFileSize = 0; -module_param(logFileSize,uint, 0644); - -static uint recovery = 1; -module_param(recovery, uint, 0644); -MODULE_PARM_DESC(recovery, "Recover GPU from stuck (1: Enable, 0: Disable)"); - -/* Middle needs about 40KB buffer, Maximal may need more than 200KB buffer. */ -static uint stuckDump = 1; -module_param(stuckDump, uint, 0644); -MODULE_PARM_DESC(stuckDump, "Level of stuck dump content (1: Minimal, 2: Middle, 3: Maximal)"); - -static int showArgs = 0; -module_param(showArgs, int, 0644); - -static int mmu = 1; -module_param(mmu, int, 0644); - -static int gpu3DMinClock = 1; - -static int contiguousRequested = 0; - -static int drv_open( - struct inode* inode, - struct file* filp - ); - -static int drv_release( - struct inode* inode, - struct file* filp - ); - -static long drv_ioctl( - struct file* filp, - unsigned int ioctlCode, - unsigned long arg - ); - -static int drv_mmap( - struct file* filp, - struct vm_area_struct* vma - ); - -static struct file_operations driver_fops = -{ - .owner = THIS_MODULE, - .open = drv_open, - .release = drv_release, - .unlocked_ioctl = drv_ioctl, -#ifdef HAVE_COMPAT_IOCTL - .compat_ioctl = drv_ioctl, -#endif - .mmap = drv_mmap, -}; - -void -_UpdateModuleParam( - gcsMODULE_PARAMETERS *Param - ) -{ - irqLine = Param->irqLine ; - registerMemBase = Param->registerMemBase; - registerMemSize = Param->registerMemSize; - irqLine2D = Param->irqLine2D ; - registerMemBase2D = Param->registerMemBase2D; - registerMemSize2D = Param->registerMemSize2D; - irqLineVG = Param->irqLineVG; - registerMemBaseVG = Param->registerMemBaseVG; - registerMemSizeVG = Param->registerMemSizeVG; - contiguousSize = Param->contiguousSize; - contiguousBase = Param->contiguousBase; - bankSize = Param->bankSize; - fastClear = Param->fastClear; - compression = Param->compression; - powerManagement = Param->powerManagement; - gpuProfiler = Param->gpuProfiler; - signal = Param->signal; - baseAddress = Param->baseAddress; - physSize = Param->physSize; - logFileSize = Param->logFileSize; - recovery = Param->recovery; - stuckDump = Param->stuckDump; - showArgs = Param->showArgs; - contiguousRequested = Param->contiguousRequested; - gpu3DMinClock = Param->gpu3DMinClock; -} - -void -gckOS_DumpParam( - void - ) -{ - printk("Galcore options:\n"); - printk(" irqLine = %d\n", irqLine); - printk(" registerMemBase = 0x%08lX\n", registerMemBase); - printk(" registerMemSize = 0x%08lX\n", registerMemSize); - - if (irqLine2D != -1) - { - printk(" irqLine2D = %d\n", irqLine2D); - printk(" registerMemBase2D = 0x%08lX\n", registerMemBase2D); - printk(" registerMemSize2D = 0x%08lX\n", registerMemSize2D); - } - - if (irqLineVG != -1) - { - printk(" irqLineVG = %d\n", irqLineVG); - printk(" registerMemBaseVG = 0x%08lX\n", registerMemBaseVG); - printk(" registerMemSizeVG = 0x%08lX\n", registerMemSizeVG); - } - - printk(" contiguousSize = %ld\n", contiguousSize); - printk(" contiguousBase = 0x%08lX\n", contiguousBase); - printk(" bankSize = 0x%08lX\n", bankSize); - printk(" fastClear = %d\n", fastClear); - printk(" compression = %d\n", compression); - printk(" signal = %d\n", signal); - printk(" powerManagement = %d\n", powerManagement); - printk(" baseAddress = 0x%08lX\n", baseAddress); - printk(" physSize = 0x%08lX\n", physSize); - printk(" logFileSize = %d KB \n", logFileSize); - printk(" recovery = %d\n", recovery); - printk(" stuckDump = %d\n", stuckDump); - printk(" gpuProfiler = %d\n", gpuProfiler); -} - -int drv_open( - struct inode* inode, - struct file* filp - ) -{ - gceSTATUS status; - gctBOOL attached = gcvFALSE; - gcsHAL_PRIVATE_DATA_PTR data = gcvNULL; - gctINT i; - - gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp); - - if (filp == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): filp is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - data = kmalloc(sizeof(gcsHAL_PRIVATE_DATA), GFP_KERNEL | __GFP_NOWARN); - - if (data == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): private_data is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); - } - - data->device = galDevice; - data->mappedMemory = gcvNULL; - data->contiguousLogical = gcvNULL; - gcmkONERROR(gckOS_GetProcessID(&data->pidOpen)); - - /* Attached the process. */ - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (galDevice->kernels[i] != gcvNULL) - { - gcmkONERROR(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvTRUE)); - } - } - attached = gcvTRUE; - - if (!galDevice->contiguousMapped) - { - if (galDevice->contiguousPhysical != gcvNULL) - { - gcmkONERROR(gckOS_MapMemory( - galDevice->os, - galDevice->contiguousPhysical, - galDevice->contiguousSize, - &data->contiguousLogical - )); - } - } - - filp->private_data = data; - - /* Success. */ - gcmkFOOTER_NO(); - return 0; - -OnError: - if (data != gcvNULL) - { - if (data->contiguousLogical != gcvNULL) - { - gcmkVERIFY_OK(gckOS_UnmapMemory( - galDevice->os, - galDevice->contiguousPhysical, - galDevice->contiguousSize, - data->contiguousLogical - )); - } - - kfree(data); - } - - if (attached) - { - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (galDevice->kernels[i] != gcvNULL) - { - gcmkVERIFY_OK(gckKERNEL_AttachProcess(galDevice->kernels[i], gcvFALSE)); - } - } - } - - gcmkFOOTER(); - return -ENOTTY; -} - -int drv_release( - struct inode* inode, - struct file* filp - ) -{ - gceSTATUS status; - gcsHAL_PRIVATE_DATA_PTR data; - gckGALDEVICE device; - gctINT i; - - gcmkHEADER_ARG("inode=0x%08X filp=0x%08X", inode, filp); - - if (filp == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): filp is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - data = filp->private_data; - - if (data == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): private_data is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - device = data->device; - - if (device == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): device is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - if (!device->contiguousMapped) - { - if (data->contiguousLogical != gcvNULL) - { - gcmkONERROR(gckOS_UnmapMemoryEx( - galDevice->os, - galDevice->contiguousPhysical, - galDevice->contiguousSize, - data->contiguousLogical, - data->pidOpen - )); - - data->contiguousLogical = gcvNULL; - } - } - - /* A process gets detached. */ - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (galDevice->kernels[i] != gcvNULL) - { - gcmkONERROR(gckKERNEL_AttachProcessEx(galDevice->kernels[i], gcvFALSE, data->pidOpen)); - } - } - - kfree(data); - filp->private_data = NULL; - - /* Success. */ - gcmkFOOTER_NO(); - return 0; - -OnError: - gcmkFOOTER(); - return -ENOTTY; -} - -long drv_ioctl( - struct file* filp, - unsigned int ioctlCode, - unsigned long arg - ) -{ - gceSTATUS status; - gcsHAL_INTERFACE iface; - gctUINT32 copyLen; - DRIVER_ARGS drvArgs; - gckGALDEVICE device; - gcsHAL_PRIVATE_DATA_PTR data; - gctINT32 i, count; - gckVIDMEM_NODE nodeObject; - - gcmkHEADER_ARG( - "filp=0x%08X ioctlCode=0x%08X arg=0x%08X", - filp, ioctlCode, arg - ); - - if (filp == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): filp is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - data = filp->private_data; - - if (data == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): private_data is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - device = data->device; - - if (device == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): device is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - if ((ioctlCode != IOCTL_GCHAL_INTERFACE) - && (ioctlCode != IOCTL_GCHAL_KERNEL_INTERFACE) - ) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): unknown command %d\n", - __FUNCTION__, __LINE__, - ioctlCode - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - /* Get the drvArgs. */ - copyLen = copy_from_user( - &drvArgs, (void *) arg, sizeof(DRIVER_ARGS) - ); - - if (copyLen != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): error copying of the input arguments.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - /* Now bring in the gcsHAL_INTERFACE structure. */ - if ((drvArgs.InputBufferSize != sizeof(gcsHAL_INTERFACE)) - || (drvArgs.OutputBufferSize != sizeof(gcsHAL_INTERFACE)) - ) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): input or/and output structures are invalid.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - copyLen = copy_from_user( - &iface, gcmUINT64_TO_PTR(drvArgs.InputBuffer), sizeof(gcsHAL_INTERFACE) - ); - - if (copyLen != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): error copying of input HAL interface.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - if (iface.command == gcvHAL_CHIP_INFO) - { - count = 0; - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (device->kernels[i] != gcvNULL) - { -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - iface.u.ChipInfo.types[count] = gcvHARDWARE_VG; - } - else -#endif - { - gcmkVERIFY_OK(gckHARDWARE_GetType(device->kernels[i]->hardware, - &iface.u.ChipInfo.types[count])); - } - count++; - } - } - - iface.u.ChipInfo.count = count; - iface.status = status = gcvSTATUS_OK; - } - else - { - if (iface.hardwareType > 7) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): unknown hardwareType %d\n", - __FUNCTION__, __LINE__, - iface.hardwareType - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - -#if gcdENABLE_VG - if (device->coreMapping[iface.hardwareType] == gcvCORE_VG) - { - status = gckVGKERNEL_Dispatch(device->kernels[gcvCORE_VG], - (ioctlCode == IOCTL_GCHAL_INTERFACE), - &iface); - } - else -#endif - { - status = gckKERNEL_Dispatch(device->kernels[device->coreMapping[iface.hardwareType]], - (ioctlCode == IOCTL_GCHAL_INTERFACE), - &iface); - } - } - - /* Redo system call after pending signal is handled. */ - if (status == gcvSTATUS_INTERRUPTED) - { - gcmkFOOTER(); - return -ERESTARTSYS; - } - - if (gcmIS_SUCCESS(status) && (iface.command == gcvHAL_LOCK_VIDEO_MEMORY)) - { - gcuVIDMEM_NODE_PTR node; - gctUINT32 processID; - - gckOS_GetProcessID(&processID); - - gcmkONERROR(gckVIDMEM_HANDLE_Lookup(device->kernels[device->coreMapping[iface.hardwareType]], - processID, - (gctUINT32)iface.u.LockVideoMemory.node, - &nodeObject)); - node = nodeObject->node; - - /* Special case for mapped memory. */ - if ((data->mappedMemory != gcvNULL) - && (node->VidMem.memory->object.type == gcvOBJ_VIDMEM) - ) - { - /* Compute offset into mapped memory. */ - gctUINT32 offset - = (gctUINT8 *) gcmUINT64_TO_PTR(iface.u.LockVideoMemory.memory) - - (gctUINT8 *) device->contiguousBase; - - /* Compute offset into user-mapped region. */ - iface.u.LockVideoMemory.memory = - gcmPTR_TO_UINT64((gctUINT8 *) data->mappedMemory + offset); - } - } - - /* Copy data back to the user. */ - copyLen = copy_to_user( - gcmUINT64_TO_PTR(drvArgs.OutputBuffer), &iface, sizeof(gcsHAL_INTERFACE) - ); - - if (copyLen != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): error copying of output HAL interface.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - /* Success. */ - gcmkFOOTER_NO(); - return 0; - -OnError: - gcmkFOOTER(); - return -ENOTTY; -} - -static int drv_mmap( - struct file* filp, - struct vm_area_struct* vma - ) -{ - gceSTATUS status = gcvSTATUS_OK; - gcsHAL_PRIVATE_DATA_PTR data; - gckGALDEVICE device; - - gcmkHEADER_ARG("filp=0x%08X vma=0x%08X", filp, vma); - - if (filp == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): filp is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - data = filp->private_data; - - if (data == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): private_data is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - device = data->device; - - if (device == gcvNULL) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): device is NULL\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - -#if !gcdPAGED_MEMORY_CACHEABLE - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - vma->vm_flags |= gcdVM_FLAGS; -#endif - vma->vm_pgoff = 0; - - if (device->contiguousMapped) - { - unsigned long size = vma->vm_end - vma->vm_start; - int ret = 0; - - if (size > device->contiguousSize) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Invalid mapping size.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); - } - - ret = io_remap_pfn_range( - vma, - vma->vm_start, - device->requestedContiguousBase >> PAGE_SHIFT, - size, - vma->vm_page_prot - ); - - if (ret != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): io_remap_pfn_range failed %d\n", - __FUNCTION__, __LINE__, - ret - ); - - data->mappedMemory = gcvNULL; - - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } - - data->mappedMemory = (gctPOINTER) vma->vm_start; - - /* Success. */ - gcmkFOOTER_NO(); - return 0; - } - -OnError: - gcmkFOOTER(); - return -ENOTTY; -} - - -static int drv_init(void) -{ - int ret; - int result = -EINVAL; - gceSTATUS status; - gckGALDEVICE device = gcvNULL; - struct class* device_class = gcvNULL; - - gcsDEVICE_CONSTRUCT_ARGS args = { - .recovery = recovery, - .stuckDump = stuckDump, - .gpu3DMinClock = gpu3DMinClock, - .contiguousRequested = contiguousRequested, - .platform = &platform, - .mmu = mmu, - }; - - gcmkHEADER(); - - printk(KERN_INFO "Galcore version %d.%d.%d.%d\n", - gcvVERSION_MAJOR, gcvVERSION_MINOR, gcvVERSION_PATCH, gcvVERSION_BUILD); - -#if !VIVANTE_PROFILER_PM - /* when enable gpu profiler, we need to turn off gpu powerMangement */ - if (gpuProfiler) - { - powerManagement = 0; - } -#endif - - if (showArgs) - { - gckOS_DumpParam(); - } - - if (logFileSize != 0) - { - gckDEBUGFS_Initialize(); - } - - /* Create the GAL device. */ - status = gckGALDEVICE_Construct( - irqLine, - registerMemBase, registerMemSize, - irqLine2D, - registerMemBase2D, registerMemSize2D, - irqLineVG, - registerMemBaseVG, registerMemSizeVG, - contiguousBase, contiguousSize, - bankSize, fastClear, compression, baseAddress, physSize, signal, - logFileSize, - powerManagement, - gpuProfiler, - &args, - &device - ); - - if (gcmIS_ERROR(status)) - { - gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Failed to create the GAL device: status=%d\n", - __FUNCTION__, __LINE__, status); - - goto OnError; - } - - /* Start the GAL device. */ - gcmkONERROR(gckGALDEVICE_Start(device)); - - if ((physSize != 0) - && (device->kernels[gcvCORE_MAJOR] != gcvNULL) - && (device->kernels[gcvCORE_MAJOR]->hardware->mmuVersion != 0)) - { - /* Reset the base address */ - device->baseAddress = 0; - } - - /* Register the character device. */ - ret = register_chrdev(major, DEVICE_NAME, &driver_fops); - - if (ret < 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not allocate major number for mmap.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY); - } - - if (major == 0) - { - major = ret; - } - - /* Create the device class. */ - device_class = class_create(THIS_MODULE, "graphics_class"); - - if (IS_ERR(device_class)) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Failed to create the class.\n", - __FUNCTION__, __LINE__ - ); - - gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); - } - - device_create(device_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); - - galDevice = device; - gpuClass = device_class; - - gcmkTRACE_ZONE( - gcvLEVEL_INFO, gcvZONE_DRIVER, - "%s(%d): irqLine=%d, contiguousSize=%lu, memBase=0x%lX\n", - __FUNCTION__, __LINE__, - irqLine, contiguousSize, registerMemBase - ); - - /* Success. */ - gcmkFOOTER_NO(); - return 0; - -OnError: - /* Roll back. */ - if (device_class != gcvNULL) - { - device_destroy(device_class, MKDEV(major, 0)); - class_destroy(device_class); - } - - if (device != gcvNULL) - { - gcmkVERIFY_OK(gckGALDEVICE_Stop(device)); - gcmkVERIFY_OK(gckGALDEVICE_Destroy(device)); - } - - gcmkFOOTER(); - return result; -} - -static void drv_exit(void) -{ - gcmkHEADER(); - - gcmkASSERT(gpuClass != gcvNULL); - device_destroy(gpuClass, MKDEV(major, 0)); - class_destroy(gpuClass); - - unregister_chrdev(major, DEVICE_NAME); - - gcmkVERIFY_OK(gckGALDEVICE_Stop(galDevice)); - gcmkVERIFY_OK(gckGALDEVICE_Destroy(galDevice)); - - if(gckDEBUGFS_IsEnabled()) - { - gckDEBUGFS_Terminate(); - } - - gcmkFOOTER_NO(); -} - -static int gpu_probe(struct platform_device *pdev) -{ - int ret = -ENODEV; - gcsMODULE_PARAMETERS moduleParam = { - .irqLine = irqLine, - .registerMemBase = registerMemBase, - .registerMemSize = registerMemSize, - .irqLine2D = irqLine2D, - .registerMemBase2D = registerMemBase2D, - .registerMemSize2D = registerMemSize2D, - .irqLineVG = irqLineVG, - .registerMemBaseVG = registerMemBaseVG, - .registerMemSizeVG = registerMemSizeVG, - .contiguousSize = contiguousSize, - .contiguousBase = contiguousBase, - .bankSize = bankSize, - .fastClear = fastClear, - .compression = compression, - .powerManagement = powerManagement, - .gpuProfiler = gpuProfiler, - .signal = signal, - .baseAddress = baseAddress, - .physSize = physSize, - .logFileSize = logFileSize, - .recovery = recovery, - .stuckDump = stuckDump, - .showArgs = showArgs, - .gpu3DMinClock = gpu3DMinClock, - }; - - gcmkHEADER(); - - platform.device = pdev; - - if (platform.ops->getPower) - { - if (gcmIS_ERROR(platform.ops->getPower(&platform))) - { - gcmkFOOTER_NO(); - return ret; - } - } - - if (platform.ops->adjustParam) - { - /* Override default module param. */ - platform.ops->adjustParam(&platform, &moduleParam); - - /* Update module param because drv_init() uses them directly. */ - _UpdateModuleParam(&moduleParam); - } - - ret = drv_init(); - - if (!ret) - { - platform_set_drvdata(pdev, galDevice); - - gcmkFOOTER_NO(); - return ret; - } - - gcmkFOOTER_ARG(KERN_INFO "Failed to register gpu driver: %d\n", ret); - return ret; -} - -static int gpu_remove(struct platform_device *pdev) -{ - gcmkHEADER(); - - drv_exit(); - - if (platform.ops->putPower) - { - platform.ops->putPower(&platform); - } - - gcmkFOOTER_NO(); - return 0; -} - -static int gpu_suspend(struct platform_device *dev, pm_message_t state) -{ - gceSTATUS status; - gckGALDEVICE device; - gctINT i; - - device = platform_get_drvdata(dev); - - if (!device) - { - return -1; - } - - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (device->kernels[i] != gcvNULL) - { - /* Store states. */ -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_QueryPowerManagementState(device->kernels[i]->vg->hardware, &device->statesStored[i]); - } - else -#endif - { - status = gckHARDWARE_QueryPowerManagementState(device->kernels[i]->hardware, &device->statesStored[i]); - } - - if (gcmIS_ERROR(status)) - { - return -1; - } - -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_OFF); - } - else -#endif - { - status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_OFF); - } - - if (gcmIS_ERROR(status)) - { - return -1; - } - - } - } - - return 0; -} - -static int gpu_resume(struct platform_device *dev) -{ - gceSTATUS status; - gckGALDEVICE device; - gctINT i; - gceCHIPPOWERSTATE statesStored; - - device = platform_get_drvdata(dev); - - if (!device) - { - return -1; - } - - for (i = 0; i < gcdMAX_GPU_COUNT; i++) - { - if (device->kernels[i] != gcvNULL) - { -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, gcvPOWER_ON); - } - else -#endif - { - status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, gcvPOWER_ON); - } - - if (gcmIS_ERROR(status)) - { - return -1; - } - - /* Convert global state to crossponding internal state. */ - switch(device->statesStored[i]) - { - case gcvPOWER_OFF: - statesStored = gcvPOWER_OFF_BROADCAST; - break; - case gcvPOWER_IDLE: - statesStored = gcvPOWER_IDLE_BROADCAST; - break; - case gcvPOWER_SUSPEND: - statesStored = gcvPOWER_SUSPEND_BROADCAST; - break; - case gcvPOWER_ON: - statesStored = gcvPOWER_ON_AUTO; - break; - default: - statesStored = device->statesStored[i]; - break; - } - - /* Restore states. */ -#if gcdENABLE_VG - if (i == gcvCORE_VG) - { - status = gckVGHARDWARE_SetPowerManagementState(device->kernels[i]->vg->hardware, statesStored); - } - else -#endif - { - status = gckHARDWARE_SetPowerManagementState(device->kernels[i]->hardware, statesStored); - } - - if (gcmIS_ERROR(status)) - { - return -1; - } - } - } - - return 0; -} - -#if defined(CONFIG_PM) -static int gpu_runtime_suspend(struct device *dev) -{ - pm_message_t state={0}; - return gpu_suspend(to_platform_device(dev), state); -} - -static int gpu_runtime_resume(struct device *dev) -{ - return gpu_resume(to_platform_device(dev)); -} - -static const struct dev_pm_ops gpu_pm_ops = { - SET_RUNTIME_PM_OPS(gpu_runtime_suspend, gpu_runtime_resume, NULL) -}; -#endif - -static struct platform_driver gpu_driver = { - .probe = gpu_probe, - .remove = gpu_remove, - - .driver = { - .name = DEVICE_NAME, - .pm = &gpu_pm_ops, - } -}; - -static int __init gpu_init(void) -{ - int ret = 0; - - memset(&platform, 0, sizeof(gcsPLATFORM)); - - gckPLATFORM_QueryOperations(&platform.ops); - - if (platform.ops == gcvNULL) - { - printk(KERN_ERR "galcore: No platform specific operations.\n"); - ret = -ENODEV; - goto out; - } - - if (platform.ops->allocPriv) - { - /* Allocate platform private data. */ - if (gcmIS_ERROR(platform.ops->allocPriv(&platform))) - { - ret = -ENOMEM; - goto out; - } - } - - if (platform.ops->needAddDevice - && platform.ops->needAddDevice(&platform)) - { - /* Allocate device */ - platform.device = platform_device_alloc(DEVICE_NAME, -1); - if (!platform.device) - { - printk(KERN_ERR "galcore: platform_device_alloc failed.\n"); - ret = -ENOMEM; - goto out; - } - - /* Add device */ - ret = platform_device_add(platform.device); - if (ret) - { - printk(KERN_ERR "galcore: platform_device_add failed.\n"); - goto put_dev; - } - } - - platform.driver = &gpu_driver; - - if (platform.ops->adjustDriver) - { - /* Override default platform_driver struct. */ - platform.ops->adjustDriver(&platform); - } - - ret = platform_driver_register(&gpu_driver); - if (!ret) - { - goto out; - } - - platform_device_del(platform.device); -put_dev: - platform_device_put(platform.device); - -out: - return ret; -} - -static void __exit gpu_exit(void) -{ - platform_driver_unregister(&gpu_driver); - - if (platform.ops->needAddDevice - && platform.ops->needAddDevice(&platform)) - { - platform_device_unregister(platform.device); - } - - if (platform.priv) - { - /* Free platform private data. */ - platform.ops->freePriv(&platform); - } -} - -module_init(gpu_init); -module_exit(gpu_exit); diff --git a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c index 06738c3aeb9718..0d55089e66dfea 100644 --- a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c +++ b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c @@ -286,7 +286,7 @@ static DRIVER_ATTR(gpu3DMinClock, S_IRUGO | S_IWUSR, show_gpu3DMinClock, update_ static const struct of_device_id mxs_gpu_dt_ids[] = { { .compatible = "fsl,imx6q-gpu", }, - { .compatible = "vivante,gc", }, + { .compatible = "fsl,imx-gpu-subsystem", }, {/* sentinel */} }; MODULE_DEVICE_TABLE(of, mxs_gpu_dt_ids); @@ -612,7 +612,6 @@ _SetClock( return gcvSTATUS_OK; } -#if 0 #ifdef CONFIG_PM static int gpu_runtime_suspend(struct device *dev) { @@ -632,7 +631,6 @@ static int gpu_runtime_resume(struct device *dev) static struct dev_pm_ops gpu_pm_ops; #endif -#endif gceSTATUS _AdjustDriver( @@ -642,6 +640,19 @@ _AdjustDriver( struct platform_driver * driver = Platform->driver; driver->driver.of_match_table = mxs_gpu_dt_ids; + /* Fill local structure with original value. */ + memcpy(&gpu_pm_ops, driver->driver.pm, sizeof(struct dev_pm_ops)); + + /* Add runtime PM callback. */ +#ifdef CONFIG_PM + gpu_pm_ops.runtime_suspend = gpu_runtime_suspend; + gpu_pm_ops.runtime_resume = gpu_runtime_resume; + gpu_pm_ops.runtime_idle = NULL; + + /* Replace callbacks. */ + driver->driver.pm = &gpu_pm_ops; +#endif + return gcvSTATUS_OK; } From cc539a3203bf451c94a2075d1372d8b84c7a5efc Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 28 Jun 2015 09:10:51 +0200 Subject: [PATCH 0411/1983] gpu: galcore: remove USE_NEW_LINUX_SIGNAL Another unused option. --- drivers/gpu/galcore/Kbuild | 6 ------ drivers/gpu/galcore/gc_hal_kernel.c | 4 ---- drivers/gpu/galcore/gc_hal_kernel_db.c | 4 ---- drivers/gpu/galcore/gc_hal_kernel_vg.c | 2 -- drivers/gpu/galcore/inc/gc_hal.h | 2 -- drivers/gpu/galcore/inc/gc_hal_driver.h | 2 -- drivers/gpu/galcore/inc/gc_hal_options.h | 9 --------- 7 files changed, 29 deletions(-) diff --git a/drivers/gpu/galcore/Kbuild b/drivers/gpu/galcore/Kbuild index 56cb46aa0d9471..540a78fca7da4e 100644 --- a/drivers/gpu/galcore/Kbuild +++ b/drivers/gpu/galcore/Kbuild @@ -149,12 +149,6 @@ else EXTRA_CFLAGS += -DENABLE_GPU_CLOCK_BY_DRIVER=0 endif -ifeq ($(USE_NEW_LINUX_SIGNAL), 1) -EXTRA_CFLAGS += -DUSE_NEW_LINUX_SIGNAL=1 -else -EXTRA_CFLAGS += -DUSE_NEW_LINUX_SIGNAL=0 -endif - ifeq ($(FORCE_ALL_VIDEO_MEMORY_CACHED), 1) EXTRA_CFLAGS += -DgcdPAGED_MEMORY_CACHEABLE=1 else diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c index ee033772785b58..a3e967b042c0d6 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.c +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -1437,9 +1437,7 @@ gckKERNEL_Dispatch( gctUINT32 address; gctUINT32 processID; gctUINT32 paddr = gcvINVALID_ADDRESS; -#if !USE_NEW_LINUX_SIGNAL gctSIGNAL signal; -#endif gckVIRTUAL_COMMAND_BUFFER_PTR buffer; gckVIDMEM_NODE nodeObject; @@ -1746,7 +1744,6 @@ gckKERNEL_Dispatch( gcmRELEASE_NAME(Interface->u.UnmapUserMemory.info); break; -#if !USE_NEW_LINUX_SIGNAL case gcvHAL_USER_SIGNAL: /* Dispatch depends on the user signal subcommands. */ switch(Interface->u.UserSignal.command) @@ -1826,7 +1823,6 @@ gckKERNEL_Dispatch( gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); } break; -#endif case gcvHAL_SET_POWER_MANAGEMENT_STATE: /* Set the power management state. */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_db.c b/drivers/gpu/galcore/gc_hal_kernel_db.c index 44850e75fd35bd..40591ef2181efe 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_db.c +++ b/drivers/gpu/galcore/gc_hal_kernel_db.c @@ -1298,13 +1298,9 @@ gckKERNEL_DestroyProcessDB( break; case gcvDB_SIGNAL: -#if USE_NEW_LINUX_SIGNAL - status = gcvSTATUS_NOT_SUPPORTED; -#else /* Free the user signal. */ status = gckOS_DestroyUserSignal(Kernel->os, gcmPTR2INT32(record->data)); -#endif /* USE_NEW_LINUX_SIGNAL */ gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_DATABASE, "DB: SIGNAL %d (status=%d)", diff --git a/drivers/gpu/galcore/gc_hal_kernel_vg.c b/drivers/gpu/galcore/gc_hal_kernel_vg.c index eac565cd65f928..8888d0ef8f2d6f 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_vg.c +++ b/drivers/gpu/galcore/gc_hal_kernel_vg.c @@ -594,7 +594,6 @@ gceSTATUS gckVGKERNEL_Dispatch( break; case gcvHAL_USER_SIGNAL: -#if !USE_NEW_LINUX_SIGNAL /* Dispatch depends on the user signal subcommands. */ switch(Interface->u.UserSignal.command) { @@ -645,7 +644,6 @@ gceSTATUS gckVGKERNEL_Dispatch( /* Invalid user signal command. */ gcmkERR_BREAK(gcvSTATUS_INVALID_ARGUMENT); } -#endif break; case gcvHAL_COMMIT: diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h index b31ae35704b57d..d5970a9dd592e8 100644 --- a/drivers/gpu/galcore/inc/gc_hal.h +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -1182,7 +1182,6 @@ gckOS_CreateNativeFence( OUT gctINT * FenceFD ); -#if !USE_NEW_LINUX_SIGNAL /* Create signal to be used in the user space. */ gceSTATUS gckOS_CreateUserSignal( @@ -1213,7 +1212,6 @@ gckOS_SignalUserSignal( IN gctINT SignalID, IN gctBOOL State ); -#endif /* USE_NEW_LINUX_SIGNAL */ /* Set a signal owned by a process. */ gceSTATUS diff --git a/drivers/gpu/galcore/inc/gc_hal_driver.h b/drivers/gpu/galcore/inc/gc_hal_driver.h index fa54d891bee3d8..1507ccdfbb5c79 100644 --- a/drivers/gpu/galcore/inc/gc_hal_driver.h +++ b/drivers/gpu/galcore/inc/gc_hal_driver.h @@ -600,7 +600,6 @@ typedef struct _gcsHAL_INTERFACE IN gctUINT32 address; } UnmapUserMemory; -#if !USE_NEW_LINUX_SIGNAL /* gcsHAL_USER_SIGNAL */ struct _gcsHAL_USER_SIGNAL { @@ -620,7 +619,6 @@ typedef struct _gcsHAL_INTERFACE IN gctBOOL state; } UserSignal; -#endif /* gcvHAL_SIGNAL. */ struct _gcsHAL_SIGNAL diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h index c7ed3293c1ad8b..1bd3feb38aff5f 100644 --- a/drivers/gpu/galcore/inc/gc_hal_options.h +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -30,15 +30,6 @@ # define gcdPRINT_VERSION 0 #endif -/* - USE_NEW_LINUX_SIGNAL - - This define enables the Linux kernel signaling between kernel and user. -*/ -#ifndef USE_NEW_LINUX_SIGNAL -# define USE_NEW_LINUX_SIGNAL 0 -#endif - /* VIVANTE_PROFILER From b03fe59673aa6174ed5eac13f7297b2e68630a37 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 28 Jun 2015 09:16:22 +0200 Subject: [PATCH 0412/1983] gpu: galcore: re-order definitions to clean up some warnings Re-order code to remove "error: ISO C90 forbids mixed declarations and code" warnings/errors. --- drivers/gpu/galcore/gc_hal_kernel_os.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index 91f0e05264966d..3d441d1ed122b9 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -4598,9 +4598,6 @@ gckOS_MapUserMemory( ) { gceSTATUS status; - - gcmkHEADER_ARG("Os=0x%x Core=%d Memory=0x%x Size=%lu", Os, Core, Memory, Size); - gctSIZE_T pageCount, i, j; gctUINT32_PTR pageTable; gctUINT32 address = 0, physical = ~0U; @@ -4614,6 +4611,8 @@ gckOS_MapUserMemory( gcsPageInfo_PTR info = gcvNULL; struct page **pages = gcvNULL; + gcmkHEADER_ARG("Os=0x%x Core=%d Memory=0x%x Size=%lu", Os, Core, Memory, Size); + /* Verify the arguments. */ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); gcmkVERIFY_ARGUMENT(Memory != gcvNULL || Physical != ~0U); @@ -5088,15 +5087,14 @@ gckOS_UnmapUserMemory( ) { gceSTATUS status; - - gcmkHEADER_ARG("Os=0x%X Core=%d Memory=0x%X Size=%lu Info=0x%X Address0x%08x", - Os, Core, Memory, Size, Info, Address); - gctUINTPTR_T memory, start, end; gcsPageInfo_PTR info; gctSIZE_T pageCount, i; struct page **pages; + gcmkHEADER_ARG("Os=0x%X Core=%d Memory=0x%X Size=%lu Info=0x%X Address0x%08x", + Os, Core, Memory, Size, Info, Address); + /* Verify the arguments. */ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); gcmkVERIFY_ARGUMENT(Memory != gcvNULL); From 302e68f556bee8cbb0364393f35954ac2beaabb7 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 12 Dec 2013 10:15:42 +0100 Subject: [PATCH 0413/1983] mxc: gpu_viv: Optimize PM routine We are trying to manage multiple threads that are all trying to put the GPU in the proper powerstate. It is possible that while the hardware is trying to power on the GPU the idle timer will kick off and first suspend the chip only to wake it back up. This commit does two things 1) If the chip is already in the state we are trying to achieve bail out and do nothing. 2) If we are powering the chip on or off kill the IDLE timer right away as we know we don't care about the suspend state because we are already processing a different state command that is not suspend. --- .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index d4240056b390c8..d890f6824cfb7f 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4454,7 +4454,14 @@ gckHARDWARE_SetPowerManagementState( gcmkONERROR(gckOS_AcquireMutex(os, Hardware->powerMutex, gcvINFINITE)); } - /* Get time until mtuex acquired. */ + /* Before we grab locks see if this is actually a needed change */ + if (State == Hardware->chipPowerState) + { + gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + return gcvSTATUS_OK; + } + + /* Get time until mutex acquired. */ gcmkPROFILE_QUERY(time, mutexTime); Hardware->powerProcess = process; @@ -4518,6 +4525,14 @@ gckHARDWARE_SetPowerManagementState( "Power Off GPU[%d] at %u [supposed to be at %u]", Hardware->core, currentTime, Hardware->powerOffTime); } + + if (State == gcvPOWER_ON || State == gcvPOWER_OFF) + { + gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "Cancel powerOfftimer"); + + /* Cancel running timer when GPU enters ON or OFF. */ + gcmkVERIFY_OK(gckOS_StopTimer(os, Hardware->powerOffTimer)); + } #endif if (flag == 0) @@ -4943,25 +4958,16 @@ gckHARDWARE_SetPowerManagementState( #endif #if gcdPOWEROFF_TIMEOUT - /* Reset power off time */ - gcmkONERROR(gckOS_GetTicks(¤tTime)); - - Hardware->powerOffTime = currentTime + Hardware->powerOffTimeout; - if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND) { + gcmkONERROR(gckOS_GetTicks(¤tTime)); + + Hardware->powerOffTime = currentTime + Hardware->powerOffTimeout; /* Start a timer to power off GPU when GPU enters IDLE or SUSPEND. */ gcmkVERIFY_OK(gckOS_StartTimer(os, Hardware->powerOffTimer, Hardware->powerOffTimeout)); } - else - { - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, "Cancel powerOfftimer"); - - /* Cancel running timer when GPU enters ON or OFF. */ - gcmkVERIFY_OK(gckOS_StopTimer(os, Hardware->powerOffTimer)); - } #endif /* Release the power mutex. */ From b42b8190fbd45620a4e957a3bfd06d1b8039e3b3 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 6 Aug 2014 14:10:02 +0200 Subject: [PATCH 0414/1983] mxc: gpu_viv: Fixup interrupt initialization There is no reason to tear down and setup the interrupt constantly. This breaks the routines up to Setup/Remove and Enable/Disable, it also consolidates the code to take Core as a parameter so there aren't similar functions for the various Core types. --- .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 6 +- drivers/gpu/galcore/gc_hal_kernel_device.c | 180 ++++++------------ drivers/gpu/galcore/gc_hal_kernel_device.h | 27 ++- drivers/gpu/galcore/inc/gc_hal.h | 2 +- 4 files changed, 76 insertions(+), 139 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index d890f6824cfb7f..4a266a94df5f03 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4777,7 +4777,7 @@ gckHARDWARE_SetPowerManagementState( /* Stop the Isr. */ if (Hardware->stopIsr) { - gcmkONERROR(Hardware->stopIsr(Hardware->isrContext)); + gcmkONERROR(Hardware->stopIsr(Hardware->isrContext, Hardware->core)); } } @@ -4913,7 +4913,7 @@ gckHARDWARE_SetPowerManagementState( if (Hardware->startIsr) { /* Start the Isr. */ - gcmkONERROR(Hardware->startIsr(Hardware->isrContext)); + gcmkONERROR(Hardware->startIsr(Hardware->isrContext, Hardware->core)); isrStarted = gcvTRUE; } } @@ -4996,7 +4996,7 @@ gckHARDWARE_SetPowerManagementState( if (isrStarted) { - gcmkVERIFY_OK(Hardware->stopIsr(Hardware->isrContext)); + gcmkVERIFY_OK(Hardware->stopIsr(Hardware->isrContext, Hardware->core)); } if (commitEntered) diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index c1d7cd788994e4..cc756e147d2ed3 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -835,8 +835,8 @@ gckGALDEVICE_Construct( /* Setup the ISR manager. */ gcmkONERROR(gckHARDWARE_SetIsrManager( device->kernels[gcvCORE_MAJOR]->hardware, - (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR, - (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR, + (gctISRMANAGERFUNC) gckGALDEVICE_Enable_ISR, + (gctISRMANAGERFUNC) gckGALDEVICE_Disable_ISR, device )); @@ -927,8 +927,8 @@ gckGALDEVICE_Construct( /* Setup the ISR manager. */ gcmkONERROR(gckHARDWARE_SetIsrManager( device->kernels[gcvCORE_2D]->hardware, - (gctISRMANAGERFUNC) gckGALDEVICE_Setup_ISR_2D, - (gctISRMANAGERFUNC) gckGALDEVICE_Release_ISR_2D, + (gctISRMANAGERFUNC) gckGALDEVICE_Enable_ISR, + (gctISRMANAGERFUNC) gckGALDEVICE_Disable_ISR, device )); @@ -1461,26 +1461,45 @@ gckGALDEVICE_Destroy( */ gceSTATUS gckGALDEVICE_Setup_ISR( - IN gckGALDEVICE Device + IN gckGALDEVICE Device, + IN gceCORE Core ) { gceSTATUS status; gctINT ret = 0; - gcmkHEADER_ARG("Device=0x%x", Device); + gcmkHEADER_ARG("Device=0x%x Core=%d", Device, Core); gcmkVERIFY_ARGUMENT(Device != NULL); - if (Device->irqLines[gcvCORE_MAJOR] < 0) + if (Device->irqLines[Core] < 0) { gcmkONERROR(gcvSTATUS_GENERIC_IO); } /* Hook up the isr based on the irq line. */ - ret = request_irq( - Device->irqLines[gcvCORE_MAJOR], isrRoutine, 0, - "galcore interrupt service", Device - ); + switch (Core) { + case gcvCORE_MAJOR: + ret = request_irq( + Device->irqLines[Core], isrRoutine, 0, + "galcore interrupt service", Device + ); + break; + case gcvCORE_2D: + ret = request_irq( + Device->irqLines[Core], isrRoutine2D, 0, + "galcore 2D interrupt service", Device + ); + break; + case gcvCORE_VG: + ret = request_irq( + Device->irqLines[Core], isrRoutineVG, 0, + "galcore VG interrupt service", Device + ); + break; + default: + break; + } if (ret != 0) { @@ -1495,53 +1514,7 @@ gckGALDEVICE_Setup_ISR( } /* Mark ISR as initialized. */ - Device->isrInitializeds[gcvCORE_MAJOR] = gcvTRUE; - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - gcmkFOOTER(); - return status; -} - -gceSTATUS -gckGALDEVICE_Setup_ISR_2D( - IN gckGALDEVICE Device - ) -{ - gceSTATUS status; - gctINT ret; - - gcmkHEADER_ARG("Device=0x%x", Device); - - gcmkVERIFY_ARGUMENT(Device != NULL); - - if (Device->irqLines[gcvCORE_2D] < 0) - { - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - /* Hook up the isr based on the irq line. */ - ret = request_irq( - Device->irqLines[gcvCORE_2D], isrRoutine2D, 0, - "galcore interrupt service for 2D", Device - ); - - if (ret != 0) - { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not register irq line %d (error=%d)\n", - __FUNCTION__, __LINE__, - Device->irqLines[gcvCORE_2D], ret - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); - } - - /* Mark ISR as initialized. */ - Device->isrInitializeds[gcvCORE_2D] = gcvTRUE; + Device->isrInitializeds[Core] = gcvTRUE; gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -1552,43 +1525,29 @@ gckGALDEVICE_Setup_ISR_2D( } gceSTATUS -gckGALDEVICE_Setup_ISR_VG( - IN gckGALDEVICE Device +gckGALDEVICE_Enable_ISR( + IN gckGALDEVICE Device, + IN gceCORE Core ) { gceSTATUS status; - gctINT ret; - gcmkHEADER_ARG("Device=0x%x", Device); + gcmkHEADER_ARG("Device=0x%x Core=%d", Device, Core); gcmkVERIFY_ARGUMENT(Device != NULL); - if (Device->irqLines[gcvCORE_VG] < 0) + if (Device->irqLines[Core] < 0) { gcmkONERROR(gcvSTATUS_GENERIC_IO); } - /* Hook up the isr based on the irq line. */ - ret = request_irq( - Device->irqLines[gcvCORE_VG], isrRoutineVG, 0, - "galcore interrupt service for 2D", Device - ); - - if (ret != 0) + if (Device->isrEnabled[Core] == gcvFALSE) { - gcmkTRACE_ZONE( - gcvLEVEL_ERROR, gcvZONE_DRIVER, - "%s(%d): Could not register irq line %d (error=%d)\n", - __FUNCTION__, __LINE__, - Device->irqLines[gcvCORE_VG], ret - ); - - gcmkONERROR(gcvSTATUS_GENERIC_IO); + enable_irq(Device->irqLines[Core]); + /* Mark ISR as initialized. */ + Device->isrEnabled[Core] = gcvTRUE; } - /* Mark ISR as initialized. */ - Device->isrInitializeds[gcvCORE_VG] = gcvTRUE; - gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -1618,39 +1577,19 @@ gckGALDEVICE_Setup_ISR_VG( */ gceSTATUS gckGALDEVICE_Release_ISR( - IN gckGALDEVICE Device - ) -{ - gcmkHEADER_ARG("Device=0x%x", Device); - - gcmkVERIFY_ARGUMENT(Device != NULL); - - /* release the irq */ - if (Device->isrInitializeds[gcvCORE_MAJOR]) - { - free_irq(Device->irqLines[gcvCORE_MAJOR], Device); - Device->isrInitializeds[gcvCORE_MAJOR] = gcvFALSE; - } - - gcmkFOOTER_NO(); - return gcvSTATUS_OK; -} - -gceSTATUS -gckGALDEVICE_Release_ISR_2D( - IN gckGALDEVICE Device + IN gckGALDEVICE Device, + IN gceCORE Core ) { - gcmkHEADER_ARG("Device=0x%x", Device); + gcmkHEADER_ARG("Device=0x%x Core=%d", Device, Core); gcmkVERIFY_ARGUMENT(Device != NULL); /* release the irq */ - if (Device->isrInitializeds[gcvCORE_2D]) + if (Device->isrInitializeds[Core]) { - free_irq(Device->irqLines[gcvCORE_2D], Device); - - Device->isrInitializeds[gcvCORE_2D] = gcvFALSE; + free_irq(Device->irqLines[Core], Device); + Device->isrInitializeds[Core] = gcvFALSE; } gcmkFOOTER_NO(); @@ -1658,19 +1597,20 @@ gckGALDEVICE_Release_ISR_2D( } gceSTATUS -gckGALDEVICE_Release_ISR_VG( - IN gckGALDEVICE Device +gckGALDEVICE_Disable_ISR( + IN gckGALDEVICE Device, + IN gceCORE Core ) { - gcmkHEADER_ARG("Device=0x%x", Device); + gcmkHEADER_ARG("Device=0x%x Core=%d", Device, Core); gcmkVERIFY_ARGUMENT(Device != NULL); - /* release the irq */ - if (Device->isrInitializeds[gcvCORE_VG]) + /* disable the irq */ + if (Device->isrEnabled[Core]) { - free_irq(Device->irqLines[gcvCORE_VG], Device); - Device->isrInitializeds[gcvCORE_VG] = gcvFALSE; + disable_irq(Device->irqLines[Core]); + Device->isrEnabled[Core] = gcvFALSE; } gcmkFOOTER_NO(); @@ -1872,7 +1812,7 @@ gckGALDEVICE_Start( if (Device->kernels[gcvCORE_MAJOR] != gcvNULL) { /* Setup the ISR routine. */ - gcmkONERROR(gckGALDEVICE_Setup_ISR(Device)); + gcmkONERROR(gckGALDEVICE_Setup_ISR(Device, gcvCORE_MAJOR)); /* Switch to SUSPEND power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( @@ -1883,7 +1823,7 @@ gckGALDEVICE_Start( if (Device->kernels[gcvCORE_2D] != gcvNULL) { /* Setup the ISR routine. */ - gcmkONERROR(gckGALDEVICE_Setup_ISR_2D(Device)); + gcmkONERROR(gckGALDEVICE_Setup_ISR(Device, gcvCORE_2D)); /* Switch to SUSPEND power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( @@ -1894,7 +1834,7 @@ gckGALDEVICE_Start( if (Device->kernels[gcvCORE_VG] != gcvNULL) { /* Setup the ISR routine. */ - gcmkONERROR(gckGALDEVICE_Setup_ISR_VG(Device)); + gcmkONERROR(gckGALDEVICE_Setup_ISR(Device, gcvCORE_VG)); #if gcdENABLE_VG /* Switch to SUSPEND power state. */ @@ -1951,13 +1891,13 @@ gckGALDEVICE_Stop( )); /* Remove the ISR routine. */ - gcmkONERROR(gckGALDEVICE_Release_ISR(Device)); + gcmkONERROR(gckGALDEVICE_Release_ISR(Device, gcvCORE_MAJOR)); } if (Device->kernels[gcvCORE_2D] != gcvNULL) { /* Setup the ISR routine. */ - gcmkONERROR(gckGALDEVICE_Release_ISR_2D(Device)); + gcmkONERROR(gckGALDEVICE_Release_ISR(Device, gcvCORE_2D)); /* Switch to OFF power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( @@ -1968,7 +1908,7 @@ gckGALDEVICE_Stop( if (Device->kernels[gcvCORE_VG] != gcvNULL) { /* Setup the ISR routine. */ - gcmkONERROR(gckGALDEVICE_Release_ISR_VG(Device)); + gcmkONERROR(gckGALDEVICE_Release_ISR(Device, gcvCORE_VG)); #if gcdENABLE_VG /* Switch to OFF power state. */ diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.h b/drivers/gpu/galcore/gc_hal_kernel_device.h index a05aa928d8300d..c94d67ccf76276 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.h +++ b/drivers/gpu/galcore/gc_hal_kernel_device.h @@ -71,6 +71,7 @@ typedef struct _gckGALDEVICE /* IRQ management. */ gctINT irqLines[gcdMAX_GPU_COUNT]; gctBOOL isrInitializeds[gcdMAX_GPU_COUNT]; + gctINT isrEnabled[gcdMAX_GPU_COUNT]; /* Thread management. */ struct task_struct *threadCtxts[gcdMAX_GPU_COUNT]; @@ -116,28 +117,24 @@ typedef struct _gcsDEVICE_CONSTRUCT_ARGS } gcsDEVICE_CONSTRUCT_ARGS; -gceSTATUS gckGALDEVICE_Setup_ISR( - IN gckGALDEVICE Device +gceSTATUS gckGALDEVICE_Enable_ISR( + IN gckGALDEVICE Device, + IN gceCORE Core ); -gceSTATUS gckGALDEVICE_Setup_ISR_2D( - IN gckGALDEVICE Device +gceSTATUS gckGALDEVICE_Disable_ISR( + IN gckGALDEVICE Device, + IN gceCORE Core ); -gceSTATUS gckGALDEVICE_Setup_ISR_VG( - IN gckGALDEVICE Device +gceSTATUS gckGALDEVICE_Setup_ISR( + IN gckGALDEVICE Device, + IN gceCORE Core ); gceSTATUS gckGALDEVICE_Release_ISR( - IN gckGALDEVICE Device - ); - -gceSTATUS gckGALDEVICE_Release_ISR_2D( - IN gckGALDEVICE Device - ); - -gceSTATUS gckGALDEVICE_Release_ISR_VG( - IN gckGALDEVICE Device + IN gckGALDEVICE Device, + IN gceCORE Core ); gceSTATUS gckGALDEVICE_Start_Threads( diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h index d5970a9dd592e8..a2d44eae58419c 100644 --- a/drivers/gpu/galcore/inc/gc_hal.h +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -2199,7 +2199,7 @@ gckHARDWARE_Reset( IN gckHARDWARE Hardware ); -typedef gceSTATUS (*gctISRMANAGERFUNC)(gctPOINTER Context); +typedef gceSTATUS (*gctISRMANAGERFUNC)(gctPOINTER Context, gceCORE Core); gceSTATUS gckHARDWARE_SetIsrManager( From 955efec574ef49c53401f29b679240685a61f113 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 6 Aug 2014 14:12:03 +0200 Subject: [PATCH 0415/1983] mxc: gpu_viv: Mark interrupts enabled Interrupts are enabled at this point so mark them so. This avoids unbalanced irq warnings later on. --- drivers/gpu/galcore/gc_hal_kernel_device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index cc756e147d2ed3..00784e987657c7 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -1513,6 +1513,8 @@ gckGALDEVICE_Setup_ISR( gcmkONERROR(gcvSTATUS_GENERIC_IO); } + Device->isrEnabled[Core] = 1; + /* Mark ISR as initialized. */ Device->isrInitializeds[Core] = gcvTRUE; From b73c4885c796774f454ce0a0c4487533334e2631 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 6 Aug 2014 14:21:10 +0200 Subject: [PATCH 0416/1983] mxc: gpu_viv: Wrap interrupt handling in a spinlock Make sure that multiple threads are enabling/disabling irq's when we are trying to read events pending. --- drivers/gpu/galcore/gc_hal_kernel.c | 2 + drivers/gpu/galcore/gc_hal_kernel.h | 4 ++ drivers/gpu/galcore/gc_hal_kernel_device.c | 4 ++ drivers/gpu/galcore/gc_hal_kernel_event.c | 46 ++++++---------------- 4 files changed, 21 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c index a3e967b042c0d6..d188a635343fd1 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.c +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -461,6 +461,8 @@ gckKERNEL_Construct( #endif } + spin_lock_init(&kernel->irq_lock); + #if VIVANTE_PROFILER /* Initialize profile setting */ kernel->profileEnable = gcvFALSE; diff --git a/drivers/gpu/galcore/gc_hal_kernel.h b/drivers/gpu/galcore/gc_hal_kernel.h index 879546e0cd7b36..4e3652252332c2 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.h +++ b/drivers/gpu/galcore/gc_hal_kernel.h @@ -22,6 +22,8 @@ #ifndef __gc_hal_kernel_h_ #define __gc_hal_kernel_h_ +#include + #include "gc_hal.h" #include "gc_hal_kernel_hardware.h" #include "gc_hal_driver.h" @@ -547,6 +549,8 @@ struct _gckKERNEL gctUINT32 timer; gctUINT32 restoreAddress; gctUINT32 restoreMask; + + spinlock_t irq_lock; }; struct _FrequencyHistory diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index 00784e987657c7..327cd1eef6f21e 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -1543,12 +1543,14 @@ gckGALDEVICE_Enable_ISR( gcmkONERROR(gcvSTATUS_GENERIC_IO); } + spin_lock(&Device->kernels[Core]->irq_lock); if (Device->isrEnabled[Core] == gcvFALSE) { enable_irq(Device->irqLines[Core]); /* Mark ISR as initialized. */ Device->isrEnabled[Core] = gcvTRUE; } + spin_unlock(&Device->kernels[Core]->irq_lock); gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -1609,11 +1611,13 @@ gckGALDEVICE_Disable_ISR( gcmkVERIFY_ARGUMENT(Device != NULL); /* disable the irq */ + spin_lock(&Device->kernels[Core]->irq_lock); if (Device->isrEnabled[Core]) { disable_irq(Device->irqLines[Core]); Device->isrEnabled[Core] = gcvFALSE; } + spin_unlock(&Device->kernels[Core]->irq_lock); gcmkFOOTER_NO(); return gcvSTATUS_OK; diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index 8e8fdc45c5c8de..3f72e2430e4eb6 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -1966,6 +1966,7 @@ gckEVENT_Interrupt( IN gctUINT32 Data ) { + unsigned long flags; gcmkHEADER_ARG("Event=0x%x Data=0x%x", Event, Data); /* Verify the arguments. */ @@ -2001,11 +2002,13 @@ gckEVENT_Interrupt( } /* Combine current interrupt status with pending flags. */ + spin_lock_irqsave(&Event->kernel->irq_lock, flags); #if gcdSMP gckOS_AtomSetMask(Event->pending, Data); #else Event->pending |= Data; #endif + spin_unlock_irqrestore(&Event->kernel->irq_lock, flags); #if gcdINTERRUPT_STATISTIC { @@ -2059,15 +2062,13 @@ gckEVENT_Notify( gctSIGNAL signal; gctUINT pending = 0; gckKERNEL kernel = Event->kernel; -#if !gcdSMP - gctBOOL suspended = gcvFALSE; -#endif #if gcmIS_DEBUG(gcdDEBUG_TRACE) gctINT eventNumber = 0; #endif gctINT32 free; gckVIDMEM_NODE nodeObject; gcuVIDMEM_NODE_PTR node; + unsigned long flags; gcmkHEADER_ARG("Event=0x%x IDs=0x%x", Event, IDs); @@ -2101,19 +2102,14 @@ gckEVENT_Notify( gcvINFINITE)); acquired = gcvTRUE; + spin_lock_irqsave(&Event->kernel->irq_lock, flags); #if gcdSMP gckOS_AtomGet(Event->os, Event->pending, (gctINT32_PTR)&pending); #else - /* Suspend interrupts. */ - gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); - suspended = gcvTRUE; - pending = Event->pending; - - /* Resume interrupts. */ - gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); - suspended = gcvFALSE; #endif + spin_unlock_irqrestore(&Event->kernel->irq_lock, flags); + if (pending == 0) { /* Release the mutex queue. */ @@ -2195,19 +2191,13 @@ gckEVENT_Notify( pending ); + spin_lock_irqsave(&Event->kernel->irq_lock, flags); #if gcdSMP gckOS_AtomClearMask(Event->pending, pending); #else - /* Suspend interrupts. */ - gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); - suspended = gcvTRUE; - Event->pending &= ~pending; - - /* Resume interrupts. */ - gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); - suspended = gcvFALSE; #endif + spin_unlock_irqrestore(&Event->kernel->irq_lock, flags); /* Release the mutex queue. */ gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); @@ -2248,19 +2238,13 @@ gckEVENT_Notify( #endif } + spin_lock_irqsave(&Event->kernel->irq_lock, flags); #if gcdSMP gckOS_AtomClearMask(Event->pending, mask); #else - /* Suspend interrupts. */ - gcmkONERROR(gckOS_SuspendInterruptEx(Event->os, Event->kernel->core)); - suspended = gcvTRUE; - Event->pending &= ~mask; - - /* Resume interrupts. */ - gcmkONERROR(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); - suspended = gcvFALSE; #endif + spin_unlock_irqrestore(&Event->kernel->irq_lock, flags); /* Grab the event head. */ record = queue->head; @@ -2531,14 +2515,6 @@ gckEVENT_Notify( gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); } -#if !gcdSMP - if (suspended) - { - /* Resume interrupts. */ - gcmkVERIFY_OK(gckOS_ResumeInterruptEx(Event->os, Event->kernel->core)); - } -#endif - /* Return the status. */ gcmkFOOTER(); return status; From 958d3dc1ba1bc85cdca66846889fe97d08cb9b03 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 6 Aug 2014 14:27:24 +0200 Subject: [PATCH 0417/1983] GALCORE: refcount interrupt handling to prevent shutdown race This fixes the following scenario. First thread comes in with a command queue, it takes the powersave mutex and then enables interrupts if they are disabled, submits the command queue and releases the powersave mutex. Then the program doing the drawing exits and releases the driver, which takes the powersave mutex and issues a powerdown which disables irqs. This can cause an interrupts pending softirq crash. Therefore we refcount all the calls to enable interrupts and don't disable them until we are back down to 0. --- drivers/gpu/galcore/gc_hal_kernel_device.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index 327cd1eef6f21e..f39bf8f668ebdb 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -1544,12 +1544,13 @@ gckGALDEVICE_Enable_ISR( } spin_lock(&Device->kernels[Core]->irq_lock); - if (Device->isrEnabled[Core] == gcvFALSE) + if (Device->isrEnabled[Core] == 0) { enable_irq(Device->irqLines[Core]); /* Mark ISR as initialized. */ Device->isrEnabled[Core] = gcvTRUE; } + Device->isrEnabled[Core]++; spin_unlock(&Device->kernels[Core]->irq_lock); gcmkFOOTER_NO(); @@ -1612,10 +1613,11 @@ gckGALDEVICE_Disable_ISR( /* disable the irq */ spin_lock(&Device->kernels[Core]->irq_lock); - if (Device->isrEnabled[Core]) + if (Device->isrEnabled[Core] > 0) { - disable_irq(Device->irqLines[Core]); - Device->isrEnabled[Core] = gcvFALSE; + Device->isrEnabled[Core]--; + if (Device->isrEnabled[Core] == 0) + disable_irq(Device->irqLines[Core]); } spin_unlock(&Device->kernels[Core]->irq_lock); From a6fa552f48f2edb6d257f1273f366095f6bc92b3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 12 Dec 2013 12:40:10 +0100 Subject: [PATCH 0418/1983] GALCORE: fix race in _GetEvent() Two _GetEvent()s can race, as it finds an event with a NULL head, and then drops the lock: Thread 0 Thread 1 Calls _GetEvent Calls _GetEvent Acquires mutexQueue Searches for a free entry by way of NULL queues[n].head Finds entry N Sets the stamp and source Drops mutexQueue lock returns event id N Acquires MutexQueue Searches for a free entry by way of NULL queues[n].head Finds entry N Sets the stamp and source Drops mutexQueue lock returns event id N Sets the Event->queues[N].head Sets Event->queues[N].head This allows another thread to come in and also pick the same event. Plug this race by holding the list lock over the call to _GetEvent, and assign the head within _GetEvent(). Signed-off-by: Russell King Adapted to the V4 codebase Signed-off-by: Jon Nettleton --- drivers/gpu/galcore/gc_hal_kernel_event.c | 20 +++++++++----------- drivers/gpu/galcore/inc/gc_hal.h | 9 --------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index 3f72e2430e4eb6..a2a6182c57a9ff 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -730,11 +730,12 @@ gckEVENT_Destroy( */ #define gcdINVALID_EVENT_PTR ((gcsEVENT_PTR)gcvMAXUINTPTR_T) -gceSTATUS +static gceSTATUS gckEVENT_GetEvent( IN gckEVENT Event, IN gctBOOL Wait, OUT gctUINT8 * EventID, + IN gcsEVENT_PTR Head, IN gceKERNEL_WHERE Source ) { @@ -743,7 +744,7 @@ gckEVENT_GetEvent( gctBOOL acquired = gcvFALSE; gctINT32 free; - gcmkHEADER_ARG("Event=0x%x Source=%d", Event, Source); + gcmkHEADER_ARG("Event=0x%x Head=%p Source=%d", Event, Head, Source); while (gcvTRUE) { @@ -769,6 +770,7 @@ gckEVENT_GetEvent( /* Save time stamp of event. */ Event->queues[id].head = gcdINVALID_EVENT_PTR; Event->queues[id].stamp = ++(Event->stamp); + Event->queues[id].head = Head; Event->queues[id].source = Source; gcmkONERROR(gckOS_AtomDecrement(Event->os, @@ -1585,7 +1587,7 @@ gckEVENT_Submit( queue = Event->queueHead; /* Allocate an event ID. */ - gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->source)); + gcmkONERROR(gckEVENT_GetEvent(Event, Wait, &id, queue->head, queue->source)); /* Copy event list to event ID queue. */ Event->queues[id].head = queue->head; @@ -1871,9 +1873,6 @@ gckEVENT_Compose( gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); gcmkVERIFY_ARGUMENT(Info != gcvNULL); - /* Allocate an event ID. */ - gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL)); - /* Get process ID. */ gcmkONERROR(gckOS_GetProcessID(&processID)); @@ -1922,8 +1921,8 @@ gckEVENT_Compose( tempRecord->processID = processID; } - /* Set the event list. */ - Event->queues[id].head = headRecord; + /* Allocate an event ID. */ + gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, headRecord, gcvKERNEL_PIXEL)); /* Start composition. */ gcmkONERROR(gckHARDWARE_Compose( @@ -2690,7 +2689,6 @@ gckEVENT_Stop( /* Submit the current event queue. */ gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); - gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, gcvKERNEL_PIXEL)); /* Allocate a record. */ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &record)); @@ -2703,8 +2701,8 @@ gckEVENT_Stop( record->info.u.Signal.auxSignal = 0; record->info.u.Signal.process = 0; - /* Append the record. */ - Event->queues[id].head = record; + + gcmkONERROR(gckEVENT_GetEvent(Event, gcvTRUE, &id, record, gcvKERNEL_PIXEL)); /* Replace last WAIT with END. */ gcmkONERROR(gckHARDWARE_End( diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h index a2d44eae58419c..f4a0ba5c8b3d25 100644 --- a/drivers/gpu/galcore/inc/gc_hal.h +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -2324,15 +2324,6 @@ gckEVENT_Destroy( IN gckEVENT Event ); -/* Reserve the next available hardware event. */ -gceSTATUS -gckEVENT_GetEvent( - IN gckEVENT Event, - IN gctBOOL Wait, - OUT gctUINT8 * EventID, - IN gceKERNEL_WHERE Source - ); - /* Add a new event to the list of events. */ gceSTATUS gckEVENT_AddList( From 79fd83056476ff0350ab13609f76f633889738d2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 12 Dec 2013 13:33:06 +0100 Subject: [PATCH 0419/1983] GALCORE: fix race in _IsEmpty() The following race is possible: Thread 0 Thread 1 Read Event->queues[0].head Finds that it's NULL Acquires Event->mutexQueue Sets Event->queues[0].head Releases Event->mutexQueue Acquires Event->mutexQueue Releases Event->mutexQueue IsEmpty = True Therefore, it is possible that this function returns with IsEmpty=TRUE even though the event queues may not be empty. The converse is also possible: Thread 0 Thread 1 (event thread) Read Event->queues[0].head Finds that it's non-NULL Receives interrupt Acquires Event->mutexQueue Clears Event->queues[0].head Releases Event->mutexQueue Acquires Event->mutexQueue Releases Event->mutexQueue IsEmpty = False Here, the queues are empty, but we've returned that they not empty. Fix this by reading the Event queue pointers beneath the lock. Signed-off-by: Russell King Adapted to the V4 codebase Signed-off-by: Jon Nettleton --- drivers/gpu/galcore/gc_hal_kernel_event.c | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index a2a6182c57a9ff..f4be42efbd8c06 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -151,18 +151,6 @@ gckEVENT_IsEmpty( /* Assume the event queue is empty. */ *IsEmpty = gcvTRUE; - /* Walk the event queue. */ - for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) - { - /* Check whether this event is in use. */ - if (Event->queues[i].head != gcvNULL) - { - /* The event is in use, hence the queue is not empty. */ - *IsEmpty = gcvFALSE; - break; - } - } - /* Try acquiring the mutex. */ status = gckOS_AcquireMutex(Event->os, Event->eventQueueMutex, 0); if (status == gcvSTATUS_TIMEOUT) @@ -175,6 +163,18 @@ gckEVENT_IsEmpty( /* Bail out on error. */ gcmkONERROR(status); + /* Walk the event queue. */ + for (i = 0; i < gcmCOUNTOF(Event->queues); ++i) + { + /* Check whether this event is in use. */ + if (Event->queues[i].head != gcvNULL) + { + /* The event is in use, hence the queue is not empty. */ + *IsEmpty = gcvFALSE; + break; + } + } + /* Release the mutex. */ gcmkVERIFY_OK(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); } From ca09c2548cf2880c97c25e17b7e25c4004840579 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 12 Dec 2013 13:35:42 +0100 Subject: [PATCH 0420/1983] GALCORE: fix LINK fetch size The size of the destination command buffer was being padded to 64 bytes, whereas only 64 bits is required (as is the case for every other LINK command.) Make this consistent. Signed-off-by: Russell King Updated to the V4 codebase Signed-off-by: Jon Nettleton --- drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 4a266a94df5f03..97f1b9c88eb118 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -2585,7 +2585,7 @@ gckHARDWARE_Link( gckOS_MemoryBarrier(Hardware->os, logical + 1)); /* Compute number of 64-byte aligned bytes to fetch. */ - bytes = gcmALIGN(FetchAddress + FetchSize, 64) - FetchAddress; + bytes = gcmALIGN(FetchAddress + FetchSize, 8) - FetchAddress; /* Append LINK(bytes / 8), FetchAddress. */ link = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x08 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) From f02644704aca7505ee11a7b2a10dca49544fe2a1 Mon Sep 17 00:00:00 2001 From: CruX Date: Tue, 21 Oct 2014 08:43:19 +0200 Subject: [PATCH 0421/1983] fix fec initialization problems This fixes fec initialization problems observed on the wandboard quad. Without this fix, the boot process did not work reliably. Fix proposed by rmk, thanks. --- drivers/net/ethernet/freescale/fec_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 90ec0bb33621c8..85ebdbe1edda84 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3434,6 +3434,8 @@ fec_probe(struct platform_device *pdev) failed_init: if (fep->reg_phy) regulator_disable(fep->reg_phy); + if (fep->ptp_clock) + ptp_clock_unregister(fep->ptp_clock); failed_regulator: fec_enet_clk_enable(ndev, false); failed_clk: From c31e7e65f51cbc003928db63a9eb1533a59d622c Mon Sep 17 00:00:00 2001 From: CurlyMoo Date: Tue, 16 Sep 2014 15:56:07 +0200 Subject: [PATCH 0422/1983] Support for gpio connected ir senders/receivers This module allows users to use a GPIO connected IR receiver and/or sender with LIRC. The module uses the sysfs gpio kernel functions to interact with the gpio. You can configure the module from inside the device tree like this: lirc_gpio { compatible = "lirc_gpio"; gpios = <&gpio3 6 1 &gpio3 7 2>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_gpio3_6>; pinctrl-1 = <&pinctrl_hummingboard_gpio3_7>; linux,sense = <-1>; linux,softcarrier = <1>; linux,validgpios = <1 73 72 71 70 194 195 67>; }; It should therefor work on all platforms. --- drivers/staging/media/lirc/Kconfig | 6 + drivers/staging/media/lirc/lirc_gpio.c | 782 +++++++++++++++++++++++++ 2 files changed, 788 insertions(+) create mode 100755 drivers/staging/media/lirc/lirc_gpio.c diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig index e60a59fc252bd2..2432475d014adf 100644 --- a/drivers/staging/media/lirc/Kconfig +++ b/drivers/staging/media/lirc/Kconfig @@ -38,6 +38,12 @@ config LIRC_PARALLEL help Driver for Homebrew Parallel Port Receivers +config LIRC_GPIO + tristate "Homebrew GPIO Port Receiver/Transmitter" + depends on LIRC + help + Driver for Homebrew GPIO Port Receiver/Transmitter + config LIRC_SASEM tristate "Sasem USB IR Remote" depends on LIRC && USB diff --git a/drivers/staging/media/lirc/lirc_gpio.c b/drivers/staging/media/lirc/lirc_gpio.c new file mode 100755 index 00000000000000..2d34c08eedd393 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_gpio.c @@ -0,0 +1,782 @@ +/* + * lirc_gpio.c + * + * lirc_gpio - Device driver that records pulse- and pause-lengths + * (space-lengths) (just like the lirc_serial driver does) + * between GPIO interrupt events on GPIO capable devices. + * Lots of code has been taken from the lirc_serial and the + * lirc_rpi modules so I would like say thanks to the authors. + * + * Copyright (C) 2014 CurlyMo + * Aron Robert Szabo , + * Michael Bishop + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + lirc_gpio { + compatible = "lirc_gpio"; + gpios = <&gpio3 6 1 &gpio3 7 2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard_gpio3_6>; + pinctrl-1 = <&pinctrl_hummingboard_gpio3_7>; + linux,sense = <-1>; + linux,softcarrier = <1>; + linux,validgpios = <1 73 72 71 70 194 195 67>; + }; + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIRC_DRIVER_NAME "lirc_gpio" +#define RBUF_LEN 256 +#define LIRC_TRANSMITTER_LATENCY 256 + +#ifndef MAX_UDELAY_MS +#define MAX_UDELAY_US 5000 +#else +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) +#endif + +static ssize_t lirc_write(struct file *file, const char *buf, size_t n, loff_t *ppos); +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +static int set_use_inc(void *data); +static void set_use_dec(void *data); +static int lirc_gpio_probe(struct platform_device *pdev); +static int lirc_gpio_remove(struct platform_device *pdev); + +struct lirc_gpio_platform_data { + int gpio_rx_nr; + int gpio_tx_nr; + bool active_rx_low; + bool active_tx_low; + u64 allowed_rx_protos; + u64 allowed_tx_protos; + int sense; + int softcarrier; + int validgpios[255]; +}; + +struct lirc_gpio_dev { + int gpio_rx_nr; + int gpio_tx_nr; + int sense; + int softcarrier; + int validgpios[255]; +}; + +struct lirc_gpio_dev *gpio_dev; + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .write = lirc_write, + .unlocked_ioctl = lirc_ioctl, + .read = lirc_dev_fop_read, + .poll = lirc_dev_fop_poll, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, + .llseek = no_llseek, +}; + +struct irq_chip *irqchip; +struct irq_data *irqdata; + +static struct timeval lasttv = { 0, 0 }; +static struct lirc_buffer rbuf; +static spinlock_t lock; + +/* set the default GPIO input pin */ +static int gpio_in_pin = -1; +/* set the default GPIO output pin */ +static int gpio_out_pin = -1; +/* -1 = auto, 0 = active high, 1 = active low */ +static int sense = -2; +/* use softcarrier by default */ +static int softcarrier = -1; + +/* initialized/set in init_timing_params() */ +static unsigned int freq = 38000; +static unsigned int duty_cycle = 50; +static unsigned long period; +static unsigned long pulse_width; +static unsigned long space_width; + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .rbuf = &rbuf, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static struct of_device_id lirc_gpio_of_match[] = { + { .compatible = "lirc_gpio", }, + {} +}; + +static struct platform_driver lirc_gpio_driver = { + .probe = lirc_gpio_probe, + .remove = lirc_gpio_remove, + .driver = { + .name = LIRC_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = lirc_gpio_of_match, + }, +}; + +static void safe_udelay(unsigned long usecs) { + while (usecs > MAX_UDELAY_US) { + udelay(MAX_UDELAY_US); + usecs -= MAX_UDELAY_US; + } + udelay(usecs); +} + +static int init_timing_params(unsigned int new_duty_cycle, unsigned int new_freq) { + /* + * period, pulse/space width are kept with 8 binary places - + * IE multiplied by 256. + */ + if(256 * 1000000L / new_freq * new_duty_cycle / 100 <= + LIRC_TRANSMITTER_LATENCY) + return -EINVAL; + if(256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= + LIRC_TRANSMITTER_LATENCY) + return -EINVAL; + duty_cycle = new_duty_cycle; + freq = new_freq; + period = 256 * 1000000L / freq; + pulse_width = period * duty_cycle / 100; + space_width = period - pulse_width; + return 0; +} + + +static long send_pulse_softcarrier(unsigned long length) { + int flag; + unsigned long actual, target, d; + + if(gpio_dev->gpio_tx_nr >= 0) { + length <<= 8; + + actual = 0; target = 0; flag = 0; + while(actual < length) { + if(flag) { + gpio_set_value(gpio_dev->gpio_tx_nr, 0); + target += space_width; + } else { + gpio_set_value(gpio_dev->gpio_tx_nr, 1); + target += pulse_width; + } + d = (target - actual - LIRC_TRANSMITTER_LATENCY + 128) >> 8; + /* + * Note - we've checked in ioctl that the pulse/space + * widths are big enough so that d is > 0 + */ + udelay(d); + actual += (d << 8) + LIRC_TRANSMITTER_LATENCY; + flag = !flag; + } + return (actual-length) >> 8; + } + return 0; +} + +static long send_pulse(unsigned long length) { + if(length <= 0) + return 0; + + if(gpio_dev->gpio_tx_nr >= 0) { + if(gpio_dev->softcarrier) { + return send_pulse_softcarrier(length); + } else { + gpio_set_value(gpio_dev->gpio_tx_nr, 1); + safe_udelay(length); + return 0; + } + } + return 0; +} + +static void send_space(long length) { + if(gpio_dev->gpio_tx_nr >= 0) { + gpio_set_value(gpio_dev->gpio_tx_nr, 0); + if(length <= 0) + return; + safe_udelay(length); + } +} + +static void rbwrite(int l) { + if (lirc_buffer_full(&rbuf)) { + /* no new signals will be accepted */ + return; + } + lirc_buffer_write(&rbuf, (void *)&l); +} + +static void frbwrite(int l) { + /* simple noise filter */ + static int pulse, space; + static unsigned int ptr; + + if(ptr > 0 && (l & PULSE_BIT)) { + pulse += l & PULSE_MASK; + if(pulse > 250) { + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + return; + } + if(!(l & PULSE_BIT)) { + if(ptr == 0) { + if (l > 20000) { + space = l; + ptr++; + return; + } + } else { + if(l > 20000) { + space += pulse; + if (space > PULSE_MASK) + space = PULSE_MASK; + space += l; + if (space > PULSE_MASK) + space = PULSE_MASK; + pulse = 0; + return; + } + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + } + rbwrite(l); +} + +static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs) { + struct timeval tv; + long deltv; + int data; + int signal; + + /* use the GPIO signal level */ + signal = gpio_get_value(gpio_dev->gpio_rx_nr); + + /* unmask the irq */ + irqchip->irq_unmask(irqdata); + + if(gpio_dev->sense != -1) { + /* The HB GPIO input acts like it is an analogue input. + Therefor a high signal is 256 and a low signal is 1. + For Lirc to properly interpret the spaces and pulses, + we need to transform these to ones and zeros. To be + on the safe side, every signal higher then 128 will + be interpreted as a high and vice versa. */ + if (signal > 128) { + signal = 1; + } else { + signal = 0; + } + /* get current time */ + do_gettimeofday(&tv); + + /* calc time since last interrupt in microseconds */ + deltv = tv.tv_sec-lasttv.tv_sec; + if(tv.tv_sec < lasttv.tv_sec || + (tv.tv_sec == lasttv.tv_sec && + tv.tv_usec < lasttv.tv_usec)) { + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: your clock just jumped backwards\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": %d %d %lx %lx %lx %lx\n", signal, gpio_dev->sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + data = PULSE_MASK; + } else if (deltv > 15) { + data = PULSE_MASK; /* really long time */ + if(!(signal^gpio_dev->sense)) { + /* sanity check */ + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: %d %d %lx %lx %lx %lx\n", + signal, gpio_dev->sense, tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + /* + * detecting pulse while this + * MUST be a space! + */ + gpio_dev->sense = gpio_dev->sense ? 0 : 1; + } + } else { + data = (int) (deltv*1000000 + + (tv.tv_usec - lasttv.tv_usec)); + } + frbwrite(signal^gpio_dev->sense ? data : (data|PULSE_BIT)); + lasttv = tv; + wake_up_interruptible(&rbuf.wait_poll); + } + + return IRQ_HANDLED; +} + +// called when the character device is opened +static int set_use_inc(void *data) { + int result; + unsigned long flags; + + /* initialize timestamp */ + do_gettimeofday(&lasttv); + + if(gpio_dev->gpio_rx_nr >= 0) { + result = request_irq(gpio_to_irq(gpio_dev->gpio_rx_nr), + (irq_handler_t) irq_handler, 0, + LIRC_DRIVER_NAME, (void*) 0); + + switch (result) { + case -EBUSY: + printk(KERN_ERR LIRC_DRIVER_NAME + ": IRQ %d is busy\n", + gpio_to_irq(gpio_dev->gpio_rx_nr)); + return -EBUSY; + case -EINVAL: + printk(KERN_ERR LIRC_DRIVER_NAME + ": Bad irq number or handler\n"); + return -EINVAL; + default: + break; + }; + + /* initialize pulse/space widths */ + init_timing_params(duty_cycle, freq); + + spin_lock_irqsave(&lock, flags); + + /* GPIO Pin Falling/Rising Edge Detect Enable */ + irqchip->irq_set_type(irqdata, + IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING); + + /* unmask the irq */ + irqchip->irq_unmask(irqdata); + + spin_unlock_irqrestore(&lock, flags); + } + + return 0; +} + +static void set_use_dec(void *data) { + unsigned long flags; + if(gpio_dev->gpio_rx_nr >= 0) { + spin_lock_irqsave(&lock, flags); + + /* GPIO Pin Falling/Rising Edge Detect Disable */ + irqchip->irq_set_type(irqdata, 0); + irqchip->irq_mask(irqdata); + + spin_unlock_irqrestore(&lock, flags); + + free_irq(gpio_to_irq(gpio_dev->gpio_rx_nr), (void *) 0); + } +} + +static ssize_t lirc_write(struct file *file, const char *buf, size_t n, loff_t *ppos) { + int i, count; + unsigned long flags; + long delta = 0; + int *wbuf; + + if(gpio_dev->gpio_tx_nr >= 0) { + count = n / sizeof(int); + if(n % sizeof(int) || count % 2 == 0) + return -EINVAL; + wbuf = memdup_user(buf, n); + if(IS_ERR(wbuf)) + return PTR_ERR(wbuf); + spin_lock_irqsave(&lock, flags); + + for(i = 0; i < count; i++) { + if(i%2) + send_space(wbuf[i] - delta); + else + delta = send_pulse(wbuf[i]); + } + gpio_set_value(gpio_dev->gpio_tx_nr, 0); + + spin_unlock_irqrestore(&lock, flags); + kfree(wbuf); + return n; + } + return 0; +} + + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { + int result; + __u32 value; + + switch(cmd) { + case LIRC_GET_SEND_MODE: + return -ENOIOCTLCMD; + break; + + case LIRC_SET_SEND_MODE: + result = get_user(value, (__u32 *) arg); + if(result) + return result; + /* only LIRC_MODE_PULSE supported */ + if(value != LIRC_MODE_PULSE) + return -ENOSYS; + break; + + case LIRC_GET_LENGTH: + return -ENOSYS; + break; + + case LIRC_SET_SEND_DUTY_CYCLE: + result = get_user(value, (__u32 *) arg); + if (result) + return result; + if (value <= 0 || value > 100) + return -EINVAL; + return init_timing_params(value, freq); + break; + + case LIRC_SET_SEND_CARRIER: + result = get_user(value, (__u32 *) arg); + if(result) + return result; + if(value > 500000 || value < 20000) + return -EINVAL; + return init_timing_params(duty_cycle, value); + break; + + default: + return lirc_dev_fop_ioctl(filep, cmd, arg); + } + return 0; +} + +static int lirc_gpio_get_devtree_pdata(struct device *dev, struct lirc_gpio_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + struct property *prop; + const __be32 *cur; + int gpio = -1; + int ret = 0; + int i = 0; + + if(np) { + gpio = of_get_gpio_flags(np, 0, &flags); + if(gpio < 0) { + if(gpio != -EPROBE_DEFER) + dev_err(dev, "RX gpio not defined (%d)\n", gpio); + + pdata->gpio_rx_nr = -1; + pdata->active_rx_low = 0; + pdata->allowed_rx_protos = 0; + } else { + pdata->gpio_rx_nr = gpio; + pdata->active_rx_low = (flags & OF_GPIO_ACTIVE_LOW); + pdata->allowed_rx_protos = 0; + } + + gpio = of_get_gpio_flags(np, 1, &flags); + if(gpio < 0) { + if(gpio != -EPROBE_DEFER) + dev_err(dev, "TX gpio not defined (%d)\n", gpio); + + pdata->gpio_tx_nr = -1; + pdata->active_tx_low = 0; + pdata->allowed_tx_protos = 0; + } else { + pdata->gpio_tx_nr = gpio; + pdata->active_tx_low = (flags & OF_GPIO_ACTIVE_LOW); + pdata->allowed_tx_protos = 0; + } + ret = of_property_read_u32(np, "linux,sense", &pdata->sense); + if(ret) { + pdata->sense = -1; + } + ret = of_property_read_u32(np, "linux,softcarrier", &pdata->softcarrier); + if(ret) { + pdata->softcarrier = 1; + } + i = 0; + printk(KERN_DEBUG LIRC_DRIVER_NAME ": valid gpios"); + of_property_for_each_u32(np, "linux,validgpios", prop, cur, gpio) { + printk(" %d", gpio); + pdata->validgpios[i++] = gpio; + } + printk("\n"); + pdata->validgpios[i] = -1; + } + + return 0; +} + +static int init_port(void) { + int i, nlow, nhigh, ret, irq; + + if(gpio_dev->gpio_tx_nr >= 0) { + if(gpio_request(gpio_dev->gpio_tx_nr, LIRC_DRIVER_NAME " ir/out")) { + printk(KERN_ALERT LIRC_DRIVER_NAME ": cant claim gpio pin %d\n", gpio_dev->gpio_tx_nr); + ret = -ENODEV; + goto exit_init_port; + } + } + + if(gpio_dev->gpio_rx_nr >= 0) { + if(gpio_request(gpio_dev->gpio_rx_nr, LIRC_DRIVER_NAME " ir/in")) { + printk(KERN_ALERT LIRC_DRIVER_NAME ": cant claim gpio pin %d\n", gpio_dev->gpio_rx_nr); + ret = -ENODEV; + goto exit_gpio_free_out_pin; + } + } + + if(gpio_dev->gpio_rx_nr >= 0) { + gpio_direction_input(gpio_dev->gpio_rx_nr); + } + if(gpio_dev->gpio_tx_nr >= 0) { + gpio_direction_output(gpio_dev->gpio_tx_nr, 1); + gpio_set_value(gpio_dev->gpio_tx_nr, 0); + } + + if(gpio_dev->gpio_rx_nr >= 0) { + irq = gpio_to_irq(gpio_dev->gpio_rx_nr); + irqdata = irq_get_irq_data(irq); + + if(irqdata && irqdata->chip) { + irqchip = irqdata->chip; + } else { + ret = -ENODEV; + goto exit_gpio_free_in_pin; + } + + /* if pin is high, then this must be an active low receiver. */ + if(gpio_dev->sense == -1) { + /* wait 1/2 sec for the power supply */ + msleep(500); + + /* + * probe 9 times every 0.04s, collect "votes" for + * active high/low + */ + nlow = 0; + nhigh = 0; + for(i = 0; i < 9; i++) { + if(gpio_get_value(gpio_dev->gpio_rx_nr)) + nlow++; + else + nhigh++; + msleep(40); + } + gpio_dev->sense = (nlow >= nhigh ? 1 : 0); + printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active %s receiver on GPIO pin %d\n", + gpio_dev->sense ? "low" : "high", gpio_dev->gpio_rx_nr); + } else { + printk(KERN_INFO LIRC_DRIVER_NAME ": manually using active %s receiver on GPIO pin %d\n", + gpio_dev->sense ? "low" : "high", gpio_dev->gpio_rx_nr); + } + } + + return 0; + +exit_gpio_free_in_pin: + gpio_free(gpio_dev->gpio_rx_nr); + +exit_gpio_free_out_pin: + gpio_free(gpio_dev->gpio_tx_nr); + +exit_init_port: + return ret; +} + +static void lirc_gpio_exit(void) { + if(gpio_dev->gpio_tx_nr >= 0) { + gpio_free(gpio_dev->gpio_tx_nr); + } + if(gpio_dev->gpio_rx_nr >= 0) { + gpio_free(gpio_dev->gpio_rx_nr); + } + + lirc_unregister_driver(driver.minor); + lirc_buffer_free(&rbuf); +} + +static int lirc_gpio_probe(struct platform_device *pdev) { + const struct lirc_gpio_platform_data *pdata = + pdev->dev.platform_data; + int rc; + int result = 0; + int match = 0; + int i = 0; + + if(pdev->dev.of_node) { + struct lirc_gpio_platform_data *dtpdata = devm_kzalloc(&pdev->dev, sizeof(*dtpdata), GFP_KERNEL); + if(!dtpdata) + return -ENOMEM; + rc = lirc_gpio_get_devtree_pdata(&pdev->dev, dtpdata); + if(rc) + return rc; + pdata = dtpdata; + } + + if(!pdata) + return -EINVAL; + + gpio_dev = kzalloc(sizeof(struct lirc_gpio_dev), GFP_KERNEL); + if(!gpio_dev) + return -ENOMEM; + + gpio_dev->gpio_rx_nr = pdata->gpio_rx_nr; + gpio_dev->gpio_tx_nr = pdata->gpio_tx_nr; + gpio_dev->sense = pdata->sense; + gpio_dev->softcarrier = pdata->softcarrier; + memcpy(gpio_dev->validgpios, pdata->validgpios, 255); + + if(gpio_in_pin != gpio_out_pin) { + match = 0; + for(i = 0; (i < ARRAY_SIZE(gpio_dev->validgpios)) && (!match) && (gpio_dev->validgpios[i] != -1); i++) { + if(gpio_in_pin == gpio_dev->validgpios[i]) { + match = 1; + break; + } + } + if(gpio_in_pin > -1) { + if(!match) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": invalid RX GPIO pin specified!\n"); + return -EINVAL; + } else { + gpio_dev->gpio_rx_nr = gpio_in_pin; + } + } + match = 0; + for(i = 0; (i < ARRAY_SIZE(gpio_dev->validgpios)) && (!match) && (gpio_dev->validgpios[i] != -1); i++) { + if(gpio_out_pin == gpio_dev->validgpios[i]) { + match = 1; + break; + } + } + if(gpio_out_pin > -1) { + if(!match) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": invalid TX GPIO pin specified!\n"); + return -EINVAL; + } else { + gpio_dev->gpio_tx_nr = gpio_out_pin; + } + } + } + if(sense > -2) { + gpio_dev->sense = sense; + } + if(softcarrier >= 0) { + gpio_dev->softcarrier = softcarrier; + } + + printk(KERN_DEBUG LIRC_DRIVER_NAME ": rx %d, tx %d, sense %d, softcarrier %d\n", + gpio_dev->gpio_rx_nr, gpio_dev->gpio_tx_nr, gpio_dev->sense, gpio_dev->softcarrier); + + platform_set_drvdata(pdev, gpio_dev); + + result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); + if(result < 0) + return -ENOMEM; + + driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | + LIRC_CAN_REC_MODE2; + + driver.dev = &pdev->dev; + driver.minor = lirc_register_driver(&driver); + + if(driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME ": device registration failed with %d\n", result); + result = -EIO; + goto exit_gpio; + } + + result = init_port(); + if(result < 0) + goto exit_gpio; + + return 0; + +exit_gpio: + lirc_gpio_exit(); + + return result; +} + +static int lirc_gpio_remove(struct platform_device *pdev) { + struct lirc_gpio_dev *gpio_dev = platform_get_drvdata(pdev); + + lirc_gpio_exit(); + + kfree(gpio_dev); + + return 0; +} + +MODULE_DEVICE_TABLE(of, lirc_gpio_of_match); +module_platform_driver(lirc_gpio_driver); + +MODULE_DESCRIPTION("Infra-red GPIO receiver and blaster driver."); +MODULE_AUTHOR("CurlyMo "); +MODULE_AUTHOR("Aron Robert Szabo "); +MODULE_AUTHOR("Michael Bishop "); +MODULE_LICENSE("GPL"); + +module_param(gpio_out_pin, int, S_IRUGO); +MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number"); + +module_param(gpio_in_pin, int, S_IRUGO); +MODULE_PARM_DESC(gpio_in_pin, "GPIO input/receiver pin number."); + +module_param(sense, int, S_IRUGO); +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" + " (0 = active high, 1 = active low )"); + +module_param(softcarrier, int, S_IRUGO); +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); \ No newline at end of file From a2ff03cad05522d8623070d5c9ea7d1eca52e249 Mon Sep 17 00:00:00 2001 From: CurlyMoo Date: Tue, 16 Sep 2014 16:09:48 +0200 Subject: [PATCH 0423/1983] Added newline at end of file --- drivers/staging/media/lirc/lirc_gpio.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_gpio.c b/drivers/staging/media/lirc/lirc_gpio.c index 2d34c08eedd393..2b7bb568a5eb82 100755 --- a/drivers/staging/media/lirc/lirc_gpio.c +++ b/drivers/staging/media/lirc/lirc_gpio.c @@ -38,7 +38,7 @@ }; */ - + #include #include #include @@ -494,8 +494,7 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) return 0; } -static int lirc_gpio_get_devtree_pdata(struct device *dev, struct lirc_gpio_platform_data *pdata) -{ +static int lirc_gpio_get_devtree_pdata(struct device *dev, struct lirc_gpio_platform_data *pdata) { struct device_node *np = dev->of_node; enum of_gpio_flags flags; struct property *prop; @@ -572,7 +571,7 @@ static int init_port(void) { } } - if(gpio_dev->gpio_rx_nr >= 0) { + if(gpio_dev->gpio_rx_nr >= 0) { gpio_direction_input(gpio_dev->gpio_rx_nr); } if(gpio_dev->gpio_tx_nr >= 0) { @@ -705,7 +704,7 @@ static int lirc_gpio_probe(struct platform_device *pdev) { } else { gpio_dev->gpio_tx_nr = gpio_out_pin; } - } + } } if(sense > -2) { gpio_dev->sense = sense; @@ -713,8 +712,8 @@ static int lirc_gpio_probe(struct platform_device *pdev) { if(softcarrier >= 0) { gpio_dev->softcarrier = softcarrier; } - - printk(KERN_DEBUG LIRC_DRIVER_NAME ": rx %d, tx %d, sense %d, softcarrier %d\n", + + printk(KERN_DEBUG LIRC_DRIVER_NAME ": rx %d, tx %d, sense %d, softcarrier %d\n", gpio_dev->gpio_rx_nr, gpio_dev->gpio_tx_nr, gpio_dev->sense, gpio_dev->softcarrier); platform_set_drvdata(pdev, gpio_dev); @@ -729,7 +728,7 @@ static int lirc_gpio_probe(struct platform_device *pdev) { LIRC_CAN_REC_MODE2; driver.dev = &pdev->dev; - driver.minor = lirc_register_driver(&driver); + driver.minor = lirc_register_driver(&driver); if(driver.minor < 0) { printk(KERN_ERR LIRC_DRIVER_NAME ": device registration failed with %d\n", result); @@ -752,8 +751,8 @@ static int lirc_gpio_probe(struct platform_device *pdev) { static int lirc_gpio_remove(struct platform_device *pdev) { struct lirc_gpio_dev *gpio_dev = platform_get_drvdata(pdev); - lirc_gpio_exit(); - + lirc_gpio_exit(); + kfree(gpio_dev); return 0; @@ -779,4 +778,5 @@ MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" " (0 = active high, 1 = active low )"); module_param(softcarrier, int, S_IRUGO); -MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); \ No newline at end of file +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); + From 53258c3cba08e276d82fe465e23451ed54742233 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 2 May 2014 17:06:19 +0100 Subject: [PATCH 0424/1983] ARM: 8047/1: rwsem: use asm-generic rwsem implementation Upstream-commit: 8a87411b649c6082aebea613e84dcb3f174f72fb asm-generic offers an atomic-add based rwsem implementation, which can avoid the need for heavier, spinlock-based synchronisation on the fast path. This patch makes use of the optimised implementation for ARM CPUs. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/Kconfig | 5 +---- arch/arm/include/asm/Kbuild | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index af00d12b4803cf..ff4f55b70d52a1 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -168,12 +168,9 @@ config TRACE_IRQFLAGS_SUPPORT bool default y -config RWSEM_GENERIC_SPINLOCK - bool - default y - config RWSEM_XCHGADD_ALGORITHM bool + default y config ARCH_HAS_ILOG2_U32 bool diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild index 3278afe2c3ab90..072e3aa7e8c202 100644 --- a/arch/arm/include/asm/Kbuild +++ b/arch/arm/include/asm/Kbuild @@ -18,6 +18,7 @@ generic-y += param.h generic-y += parport.h generic-y += poll.h generic-y += resource.h +generic-y += rwsem.h generic-y += sections.h generic-y += segment.h generic-y += sembuf.h From 1b41524667b8c1b77308fe64af91ba69d32300cb Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 21 Feb 2014 17:01:48 +0100 Subject: [PATCH 0425/1983] ARM: 7984/1: prefetch: add prefetchw invocations for barriered atomics Upstream-commit: c32ffce0f66e5d1d4856254516e24f5ef275cd00 After a bunch of benchmarking on the interaction between dmb and pldw, it turns out that issuing the pldw *after* the dmb instruction can give modest performance gains (~3% atomic_add_return improvement on a dual A15). This patch adds prefetchw invocations to our barriered atomic operations including cmpxchg, test_and_xxx and futexes. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/include/asm/atomic.h | 9 +++++++++ arch/arm/include/asm/cmpxchg.h | 6 ++++++ arch/arm/include/asm/futex.h | 3 +++ arch/arm/lib/bitops.h | 5 +++++ 4 files changed, 23 insertions(+) diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h index 62d2cb53b06935..6e410090896e54 100644 --- a/arch/arm/include/asm/atomic.h +++ b/arch/arm/include/asm/atomic.h @@ -60,6 +60,7 @@ static inline int atomic_add_return(int i, atomic_t *v) int result; smp_mb(); + prefetchw(&v->counter); __asm__ __volatile__("@ atomic_add_return\n" "1: ldrex %0, [%3]\n" @@ -99,6 +100,7 @@ static inline int atomic_sub_return(int i, atomic_t *v) int result; smp_mb(); + prefetchw(&v->counter); __asm__ __volatile__("@ atomic_sub_return\n" "1: ldrex %0, [%3]\n" @@ -121,6 +123,7 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) unsigned long res; smp_mb(); + prefetchw(&ptr->counter); do { __asm__ __volatile__("@ atomic_cmpxchg\n" @@ -299,6 +302,7 @@ static inline long long atomic64_add_return(long long i, atomic64_t *v) unsigned long tmp; smp_mb(); + prefetchw(&v->counter); __asm__ __volatile__("@ atomic64_add_return\n" "1: ldrexd %0, %H0, [%3]\n" @@ -340,6 +344,7 @@ static inline long long atomic64_sub_return(long long i, atomic64_t *v) unsigned long tmp; smp_mb(); + prefetchw(&v->counter); __asm__ __volatile__("@ atomic64_sub_return\n" "1: ldrexd %0, %H0, [%3]\n" @@ -364,6 +369,7 @@ static inline long long atomic64_cmpxchg(atomic64_t *ptr, long long old, unsigned long res; smp_mb(); + prefetchw(&ptr->counter); do { __asm__ __volatile__("@ atomic64_cmpxchg\n" @@ -388,6 +394,7 @@ static inline long long atomic64_xchg(atomic64_t *ptr, long long new) unsigned long tmp; smp_mb(); + prefetchw(&ptr->counter); __asm__ __volatile__("@ atomic64_xchg\n" "1: ldrexd %0, %H0, [%3]\n" @@ -409,6 +416,7 @@ static inline long long atomic64_dec_if_positive(atomic64_t *v) unsigned long tmp; smp_mb(); + prefetchw(&v->counter); __asm__ __volatile__("@ atomic64_dec_if_positive\n" "1: ldrexd %0, %H0, [%3]\n" @@ -436,6 +444,7 @@ static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u) int ret = 1; smp_mb(); + prefetchw(&v->counter); __asm__ __volatile__("@ atomic64_add_unless\n" "1: ldrexd %0, %H0, [%4]\n" diff --git a/arch/arm/include/asm/cmpxchg.h b/arch/arm/include/asm/cmpxchg.h index df2fbba7efc80d..abb2c3769b014e 100644 --- a/arch/arm/include/asm/cmpxchg.h +++ b/arch/arm/include/asm/cmpxchg.h @@ -2,6 +2,7 @@ #define __ASM_ARM_CMPXCHG_H #include +#include #include #if defined(CONFIG_CPU_SA1100) || defined(CONFIG_CPU_SA110) @@ -35,6 +36,7 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size #endif smp_mb(); + prefetchw((const void *)ptr); switch (size) { #if __LINUX_ARM_ARCH__ >= 6 @@ -138,6 +140,8 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, { unsigned long oldval, res; + prefetchw((const void *)ptr); + switch (size) { #ifndef CONFIG_CPU_V6 /* min ARCH >= ARMv6K */ case 1: @@ -230,6 +234,8 @@ static inline unsigned long long __cmpxchg64(unsigned long long *ptr, unsigned long long oldval; unsigned long res; + prefetchw(ptr); + __asm__ __volatile__( "1: ldrexd %1, %H1, [%3]\n" " teq %1, %4\n" diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 2aff798fbef40b..53e69dae796f32 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -23,6 +23,7 @@ #define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \ smp_mb(); \ + prefetchw(uaddr); \ __asm__ __volatile__( \ "1: ldrex %1, [%3]\n" \ " " insn "\n" \ @@ -46,6 +47,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, return -EFAULT; smp_mb(); + /* Prefetching cannot fault */ + prefetchw(uaddr); __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" "1: ldrex %1, [%4]\n" " teq %1, %2\n" diff --git a/arch/arm/lib/bitops.h b/arch/arm/lib/bitops.h index 52886b89706caf..9f12ed1eea860f 100644 --- a/arch/arm/lib/bitops.h +++ b/arch/arm/lib/bitops.h @@ -37,6 +37,11 @@ UNWIND( .fnstart ) add r1, r1, r0, lsl #2 @ Get word offset mov r3, r2, lsl r3 @ create mask smp_dmb +#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) + .arch_extension mp + ALT_SMP(W(pldw) [r1]) + ALT_UP(W(nop)) +#endif 1: ldrex r2, [r1] ands r0, r2, r3 @ save old value of bit \instr r2, r2, r3 @ toggle bit From cbc01f8814f1adc8e82fd5d7cb29ec3720ae8f45 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 21 Feb 2014 17:01:48 +0100 Subject: [PATCH 0426/1983] ARM: 7983/1: atomics: implement a better __atomic_add_unless for v6+ Upstream-commit: db38ee874c48713d0723221d08332242e0088970 Looking at perf profiles of multi-threaded hackbench runs, a significant performance hit appears to manifest from the cmpxchg loop used to implement the 32-bit atomic_add_unless function. This can be mitigated by writing a direct implementation of __atomic_add_unless which doesn't require iteration outside of the atomic operation. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/include/asm/atomic.h | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h index 6e410090896e54..9a92fd7864a841 100644 --- a/arch/arm/include/asm/atomic.h +++ b/arch/arm/include/asm/atomic.h @@ -141,6 +141,33 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new) return oldval; } +static inline int __atomic_add_unless(atomic_t *v, int a, int u) +{ + int oldval, newval; + unsigned long tmp; + + smp_mb(); + prefetchw(&v->counter); + + __asm__ __volatile__ ("@ atomic_add_unless\n" +"1: ldrex %0, [%4]\n" +" teq %0, %5\n" +" beq 2f\n" +" add %1, %0, %6\n" +" strex %2, %1, [%4]\n" +" teq %2, #0\n" +" bne 1b\n" +"2:" + : "=&r" (oldval), "=&r" (newval), "=&r" (tmp), "+Qo" (v->counter) + : "r" (&v->counter), "r" (u), "r" (a) + : "cc"); + + if (oldval != u) + smp_mb(); + + return oldval; +} + #else /* ARM_ARCH_6 */ #ifdef CONFIG_SMP @@ -189,10 +216,6 @@ static inline int atomic_cmpxchg(atomic_t *v, int old, int new) return ret; } -#endif /* __LINUX_ARM_ARCH__ */ - -#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) - static inline int __atomic_add_unless(atomic_t *v, int a, int u) { int c, old; @@ -203,6 +226,10 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u) return c; } +#endif /* __LINUX_ARM_ARCH__ */ + +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + #define atomic_inc(v) atomic_add(1, v) #define atomic_dec(v) atomic_sub(1, v) From bd3c8f60119691984ecbd83609d5b67194f5fe36 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 29 Aug 2014 12:52:46 +0200 Subject: [PATCH 0427/1983] mxc: gpu_viv: Fix poweroff at boot problems When issuing a poweroff command at boot it is possible for a race to occur with the interrupt handler (mostly with the VG core) that results in the poweroff command failing. Looking at the code there is already a specific mode to fix this, gcvPOWER_OFF_ATPOWERON. Why not use this so our unused cores are fully powered off if they aren't used? --- drivers/gpu/galcore/gc_hal_kernel_device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index f39bf8f668ebdb..e99d29f3763f33 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -1824,7 +1824,7 @@ gckGALDEVICE_Start( /* Switch to SUSPEND power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( - Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF_BROADCAST + Device->kernels[gcvCORE_MAJOR]->hardware, gcvPOWER_OFF_ATPOWERON )); } @@ -1835,7 +1835,7 @@ gckGALDEVICE_Start( /* Switch to SUSPEND power state. */ gcmkONERROR(gckHARDWARE_SetPowerManagementState( - Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF_BROADCAST + Device->kernels[gcvCORE_2D]->hardware, gcvPOWER_OFF_ATPOWERON )); } @@ -1847,7 +1847,7 @@ gckGALDEVICE_Start( #if gcdENABLE_VG /* Switch to SUSPEND power state. */ gcmkONERROR(gckVGHARDWARE_SetPowerManagementState( - Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF_BROADCAST + Device->kernels[gcvCORE_VG]->vg->hardware, gcvPOWER_OFF_ATPOWERON )); #endif } From b26a5fffbb03dc5e80cd1ab8ac6dc64ad2af6822 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 21 Oct 2014 22:06:26 +0200 Subject: [PATCH 0428/1983] gpu: galcore: Put gckOS mutexs in lockdep classes The galcore Mutex wrapper causes all Mutex's to look the same when CONFIG_DEBUG_MUTEX is enabled. Adding them to lockdep classes and giving them names will fix this problem and make them easier to debug. --- drivers/gpu/galcore/gc_hal_kernel_os.c | 59 ++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index 3d441d1ed122b9..a77dfa06e3e946 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -32,6 +32,8 @@ #include #include #include +#include + #if gcdANDROID_NATIVE_FENCE_SYNC #include @@ -61,6 +63,22 @@ gcmkVERIFY_OK(gckOS_ReleaseMutex((os), (os)->memoryMapLock)) +/* Create a new mutex. */ +static gceSTATUS +gckOS_CreateNamedMutex( + IN gckOS Os, + IN struct lock_class_key *key, + IN const char *name, + OUT gctPOINTER * Mutex + ); + +static struct lock_class_key gckOS_key; +static struct lock_class_key gckOS_MM_key; +static struct lock_class_key gckOS_Signal_key; +#if gcdANDROID_NATIVE_FENCE_SYNC +static struct lock_class_key gckOS_SyncPoint_key; +#endif + /******************************************************************************\ ******************************* Private Functions ****************************** \******************************************************************************/ @@ -581,8 +599,8 @@ gckOS_Construct( atomic_set(&os->allocateCount, 0); /* Initialize the memory lock. */ - gcmkONERROR(gckOS_CreateMutex(os, &os->memoryLock)); - gcmkONERROR(gckOS_CreateMutex(os, &os->memoryMapLock)); + gcmkONERROR(gckOS_CreateNamedMutex(os, &gckOS_key, "memoryLock", &os->memoryLock)); + gcmkONERROR(gckOS_CreateNamedMutex(os, &gckOS_MM_key, "memoryMapLock", &os->memoryMapLock)); /* Create debug lock mutex. */ gcmkONERROR(gckOS_CreateMutex(os, &os->debugLock)); @@ -597,7 +615,7 @@ gckOS_Construct( */ /* Initialize mutex. */ - gcmkONERROR(gckOS_CreateMutex(os, &os->signalMutex)); + gcmkONERROR(gckOS_CreateNamedMutex(os, &gckOS_Signal_key, "signalMutex", &os->signalMutex)); /* Initialize signal id database lock. */ spin_lock_init(&os->signalDB.lock); @@ -611,7 +629,7 @@ gckOS_Construct( */ /* Initialize mutex. */ - gcmkONERROR(gckOS_CreateMutex(os, &os->syncPointMutex)); + gcmkONERROR(gckOS_CreateNamedMutex(os, &gckOS_SyncPoint_key, "syncPointMutex", &os->syncPointMutex)); /* Initialize sync point id database lock. */ spin_lock_init(&os->syncPointDB.lock); @@ -2548,6 +2566,39 @@ gckOS_CreateMutex( return status; } +static gceSTATUS +gckOS_CreateNamedMutex( + IN gckOS Os, + IN struct lock_class_key *key, + IN const char *name, + OUT gctPOINTER * Mutex + ) +{ + gceSTATUS status; + + gcmkHEADER_ARG("Os=0x%X", Os); + + /* Validate the arguments. */ + gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); + gcmkVERIFY_ARGUMENT(Mutex != gcvNULL); + + /* Allocate the mutex structure. */ + gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct mutex), Mutex)); + + /* Initialize the mutex. */ + mutex_init(*Mutex); + lockdep_set_class_and_name((struct mutex *)Mutex, key, name); + + /* Return status. */ + gcmkFOOTER_ARG("*Mutex=0x%X", *Mutex); + return gcvSTATUS_OK; + +OnError: + /* Return status. */ + gcmkFOOTER(); + return status; +} + /******************************************************************************* ** ** gckOS_DeleteMutex From 651561a9cd2bc6d3f6d2a487a4968bf1de599c20 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Nov 2014 18:47:32 +0100 Subject: [PATCH 0429/1983] gpu: galcore: Handle additional cooling information Rather than just scaling down the GPU's 3D core to the slowest speed at the first passive cooling trip point, we slowly scale back the core clock speed trying to achieve balance. If we reach the critical speed we scale all the way back to the lowest freq. *TEST* the divider number is completely guestimated and needs to be tested. --- .../freescale/gc_hal_kernel_platform_imx6q14.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c index 0d55089e66dfea..b5c83a87db277f 100644 --- a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c +++ b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c @@ -202,7 +202,7 @@ static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) { static gctUINT orgFscale, minFscale, maxFscale; - static gctBOOL bAlreadyTooHot = gcvFALSE; + static gctBOOL critical; gckHARDWARE hardware; gckGALDEVICE galDevice; @@ -225,15 +225,20 @@ static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } - if (event && !bAlreadyTooHot) { + if (event > 4) { + critical = gcvTRUE; gckHARDWARE_GetFscaleValue(hardware,&orgFscale,&minFscale, &maxFscale); gckHARDWARE_SetFscaleValue(hardware, minFscale); - bAlreadyTooHot = gcvTRUE; gckOS_Print("System is too hot. GPU3D will work at %d/64 clock.\n", minFscale); - } else if (!event && bAlreadyTooHot) { + } else if (event > 1) { + gckHARDWARE_GetFscaleValue(hardware,&orgFscale,&minFscale, &maxFscale); + gckHARDWARE_SetFscaleValue(hardware, maxFscale - (8 * event)); + } else if (orgFscale) { gckHARDWARE_SetFscaleValue(hardware, orgFscale); - gckOS_Print("Hot alarm is canceled. GPU3D clock will return to %d/64\n", orgFscale); - bAlreadyTooHot = gcvFALSE; + if (critical) { + gckOS_Print("Hot alarm is canceled. GPU3D clock will return to %d/64\n", orgFscale); + critical = gcvFALSE; + } } return NOTIFY_OK; } From 477db3d878097c74f984a81c879d746936b34e81 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [PATCH 0430/1983] [media] v4l: Add UYVY10_2X10 and VYUY10_2X10 media bus pixel codes Upstream-commit: aa9ba84b088a42f0ce522115675298e763815663 Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/subdev-formats.xml | 128 ++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 4 +- 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 7331ce116f4cc1..6fb58de9466ae6 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -1898,6 +1898,134 @@ y1 y0 + + V4L2_MBUS_FMT_UYVY10_2X10 + 0x2018 + + &dash-ent-22; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-22; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY10_2X10 + 0x2019 + + &dash-ent-22; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-22; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-22; + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + V4L2_MBUS_FMT_YUYV10_2X10 0x200b diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index b5c3aab6e82c7f..20a99b11c65cb7 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x2018 */ + /* YUV (including grey) - next is 0x201a */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -64,6 +64,8 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008, V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009, V4L2_MBUS_FMT_Y10_1X10 = 0x200a, + V4L2_MBUS_FMT_UYVY10_2X10 = 0x2018, + V4L2_MBUS_FMT_VYUY10_2X10 = 0x2019, V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, V4L2_MBUS_FMT_Y12_1X12 = 0x2013, From e03f7c663967f0cf97edb24d269c5aff382ae1e5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [PATCH 0431/1983] [media] v4l: Add UYVY10_1X20 and VYUY10_1X20 media bus pixel codes Upstream-commit: a678a198fdeab3368f1a3171453a7d43837e587b Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/subdev-formats.xml | 104 ++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 4 +- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 6fb58de9466ae6..e3cbbb4fbd53b0 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2436,6 +2436,110 @@ v1 v0 + + V4L2_MBUS_FMT_UYVY10_1X20 + 0x201a + + &dash-ent-12; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-12; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY10_1X20 + 0x201b + + &dash-ent-12; + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-12; + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + V4L2_MBUS_FMT_YUYV10_1X20 0x200d diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 20a99b11c65cb7..43707b2c17c196 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x201a */ + /* YUV (including grey) - next is 0x201c */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -74,6 +74,8 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014, + V4L2_MBUS_FMT_UYVY10_1X20 = 0x201a, + V4L2_MBUS_FMT_VYUY10_1X20 = 0x201b, V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, From a079810e7f2dc4f26c4035900da9ddaff4bbc94c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [PATCH 0432/1983] [media] v4l: Add 12-bit YUV 4:2:0 media bus pixel codes Upstream-commit: 39d39af6093038491137decdef855c703a09f983 Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/subdev-formats.xml | 288 ++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 6 +- 2 files changed, 293 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index e3cbbb4fbd53b0..a0fa7e0759c86c 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -2718,6 +2718,294 @@ v1 v0 + + V4L2_MBUS_FMT_UYVY12_2X12 + 0x201c + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY12_2X12 + 0x201d + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV12_2X12 + 0x201e + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU12_2X12 + 0x201f + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-20; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-20; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 43707b2c17c196..70a732b7e47b20 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x201c */ + /* YUV (including grey) - next is 0x2020 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -80,6 +80,10 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017, + V4L2_MBUS_FMT_UYVY12_2X12 = 0x201c, + V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d, + V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e, + V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, From b26fb2e0a0aaa3f5f82cd875f7a54482ed405a59 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 May 2013 12:32:46 -0300 Subject: [PATCH 0433/1983] [media] v4l: Add 12-bit YUV 4:2:2 media bus pixel codes Upstream-commit: 7ffd58ddab76969019098e97d687711451d32a3d Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/subdev-formats.xml | 240 ++++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 6 +- 2 files changed, 245 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index a0fa7e0759c86c..b2d5a0363cba2e 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -3006,6 +3006,246 @@ u1 u0 + + V4L2_MBUS_FMT_UYVY12_1X24 + 0x2020 + + &dash-ent-8; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-8; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_VYUY12_1X24 + 0x2021 + + &dash-ent-8; + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + + + + &dash-ent-8; + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + + + V4L2_MBUS_FMT_YUYV12_1X24 + 0x2022 + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + V4L2_MBUS_FMT_YVYU12_1X24 + 0x2023 + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v11 + v10 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + + + + + + &dash-ent-8; + y11 + y10 + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u11 + u10 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 70a732b7e47b20..1445e858854ff0 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -52,7 +52,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x2020 */ + /* YUV (including grey) - next is 0x2024 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -84,6 +84,10 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d, V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e, V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f, + V4L2_MBUS_FMT_UYVY12_1X24 = 0x2020, + V4L2_MBUS_FMT_VYUY12_1X24 = 0x2021, + V4L2_MBUS_FMT_YUYV12_1X24 = 0x2022, + V4L2_MBUS_FMT_YVYU12_1X24 = 0x2023, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, From d3db67c94857cb1232f91b8136c4203c07090560 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 10 Nov 2014 14:28:26 -0300 Subject: [PATCH 0434/1983] [media] Move mediabus format definition to a more standard place Upstream-commit: edcf58bc031e621f519c9dfce3c7e1ea6880c70a Define MEDIA_BUS_FMT macros (re-using the values defined in the v4l2_mbus_pixelcode enum) into a separate header file so that they can be used from the DRM/KMS subsystem without any reference to the V4L2 subsystem. Then set V4L2_MBUS_FMT definitions to the MEDIA_BUS_FMT values using the V4L2_MBUS_FROM_MEDIA_BUS_FMT macro. Signed-off-by: Boris Brezillon Acked-by: Guennadi Liakhovetski Acked-by: Hans Verkuil Acked-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/Kbuild | 1 + include/uapi/linux/media-bus-format.h | 125 +++++++++++++++++ include/uapi/linux/v4l2-mediabus.h | 184 +++++++++++--------------- 3 files changed, 206 insertions(+), 104 deletions(-) create mode 100644 include/uapi/linux/media-bus-format.h diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index b1a1c8c73f9dde..1ee3e867adeb3a 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -239,6 +239,7 @@ header-y += map_to_7segment.h header-y += matroxfb.h header-y += mdio.h header-y += media.h +header-y += media-bus-format.h header-y += mei.h header-y += mempolicy.h header-y += meye.h diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h new file mode 100644 index 00000000000000..23b40908be3061 --- /dev/null +++ b/include/uapi/linux/media-bus-format.h @@ -0,0 +1,125 @@ +/* + * Media Bus API header + * + * Copyright (C) 2009, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MEDIA_BUS_FORMAT_H +#define __LINUX_MEDIA_BUS_FORMAT_H + +/* + * These bus formats uniquely identify data formats on the data bus. Format 0 + * is reserved, MEDIA_BUS_FMT_FIXED shall be used by host-client pairs, where + * the data format is fixed. Additionally, "2X8" means that one pixel is + * transferred in two 8-bit samples, "BE" or "LE" specify in which order those + * samples are transferred over the bus: "LE" means that the least significant + * bits are transferred first, "BE" means that the most significant bits are + * transferred first, and "PADHI" and "PADLO" define which bits - low or high, + * in the incomplete high byte, are filled with padding bits. + * + * The bus formats are grouped by type, bus_width, bits per component, samples + * per pixel and order of subsamples. Numerical values are sorted using generic + * numerical sort order (8 thus comes before 10). + * + * As their value can't change when a new bus format is inserted in the + * enumeration, the bus formats are explicitly given a numerical value. The next + * free values for each category are listed below, update them when inserting + * new pixel codes. + */ + +#define MEDIA_BUS_FMT_FIXED 0x0001 + +/* RGB - next is 0x100e */ +#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 +#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 +#define MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE 0x1003 +#define MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE 0x1004 +#define MEDIA_BUS_FMT_BGR565_2X8_BE 0x1005 +#define MEDIA_BUS_FMT_BGR565_2X8_LE 0x1006 +#define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 +#define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 +#define MEDIA_BUS_FMT_RGB666_1X18 0x1009 +#define MEDIA_BUS_FMT_RGB888_1X24 0x100a +#define MEDIA_BUS_FMT_RGB888_2X12_BE 0x100b +#define MEDIA_BUS_FMT_RGB888_2X12_LE 0x100c +#define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d + +/* YUV (including grey) - next is 0x2024 */ +#define MEDIA_BUS_FMT_Y8_1X8 0x2001 +#define MEDIA_BUS_FMT_UV8_1X8 0x2015 +#define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002 +#define MEDIA_BUS_FMT_VYUY8_1_5X8 0x2003 +#define MEDIA_BUS_FMT_YUYV8_1_5X8 0x2004 +#define MEDIA_BUS_FMT_YVYU8_1_5X8 0x2005 +#define MEDIA_BUS_FMT_UYVY8_2X8 0x2006 +#define MEDIA_BUS_FMT_VYUY8_2X8 0x2007 +#define MEDIA_BUS_FMT_YUYV8_2X8 0x2008 +#define MEDIA_BUS_FMT_YVYU8_2X8 0x2009 +#define MEDIA_BUS_FMT_Y10_1X10 0x200a +#define MEDIA_BUS_FMT_UYVY10_2X10 0x2018 +#define MEDIA_BUS_FMT_VYUY10_2X10 0x2019 +#define MEDIA_BUS_FMT_YUYV10_2X10 0x200b +#define MEDIA_BUS_FMT_YVYU10_2X10 0x200c +#define MEDIA_BUS_FMT_Y12_1X12 0x2013 +#define MEDIA_BUS_FMT_UYVY8_1X16 0x200f +#define MEDIA_BUS_FMT_VYUY8_1X16 0x2010 +#define MEDIA_BUS_FMT_YUYV8_1X16 0x2011 +#define MEDIA_BUS_FMT_YVYU8_1X16 0x2012 +#define MEDIA_BUS_FMT_YDYUYDYV8_1X16 0x2014 +#define MEDIA_BUS_FMT_UYVY10_1X20 0x201a +#define MEDIA_BUS_FMT_VYUY10_1X20 0x201b +#define MEDIA_BUS_FMT_YUYV10_1X20 0x200d +#define MEDIA_BUS_FMT_YVYU10_1X20 0x200e +#define MEDIA_BUS_FMT_YUV10_1X30 0x2016 +#define MEDIA_BUS_FMT_AYUV8_1X32 0x2017 +#define MEDIA_BUS_FMT_UYVY12_2X12 0x201c +#define MEDIA_BUS_FMT_VYUY12_2X12 0x201d +#define MEDIA_BUS_FMT_YUYV12_2X12 0x201e +#define MEDIA_BUS_FMT_YVYU12_2X12 0x201f +#define MEDIA_BUS_FMT_UYVY12_1X24 0x2020 +#define MEDIA_BUS_FMT_VYUY12_1X24 0x2021 +#define MEDIA_BUS_FMT_YUYV12_1X24 0x2022 +#define MEDIA_BUS_FMT_YVYU12_1X24 0x2023 + +/* Bayer - next is 0x3019 */ +#define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001 +#define MEDIA_BUS_FMT_SGBRG8_1X8 0x3013 +#define MEDIA_BUS_FMT_SGRBG8_1X8 0x3002 +#define MEDIA_BUS_FMT_SRGGB8_1X8 0x3014 +#define MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8 0x3015 +#define MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8 0x3016 +#define MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8 0x3017 +#define MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8 0x3018 +#define MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 0x300b +#define MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 0x300c +#define MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 0x3009 +#define MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 0x300d +#define MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE 0x3003 +#define MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE 0x3004 +#define MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE 0x3005 +#define MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE 0x3006 +#define MEDIA_BUS_FMT_SBGGR10_1X10 0x3007 +#define MEDIA_BUS_FMT_SGBRG10_1X10 0x300e +#define MEDIA_BUS_FMT_SGRBG10_1X10 0x300a +#define MEDIA_BUS_FMT_SRGGB10_1X10 0x300f +#define MEDIA_BUS_FMT_SBGGR12_1X12 0x3008 +#define MEDIA_BUS_FMT_SGBRG12_1X12 0x3010 +#define MEDIA_BUS_FMT_SGRBG12_1X12 0x3011 +#define MEDIA_BUS_FMT_SRGGB12_1X12 0x3012 + +/* JPEG compressed formats - next is 0x4002 */ +#define MEDIA_BUS_FMT_JPEG_1X8 0x4001 + +/* Vendor specific formats - next is 0x5002 */ + +/* S5C73M3 sensor specific interleaved UYVY and JPEG */ +#define MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8 0x5001 + +/* HSV - next is 0x6002 */ +#define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001 + +#endif /* __LINUX_MEDIA_BUS_FORMAT_H */ diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 1445e858854ff0..26180844c6b845 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -11,120 +11,96 @@ #ifndef __LINUX_V4L2_MEDIABUS_H #define __LINUX_V4L2_MEDIABUS_H +#include #include #include -/* - * These pixel codes uniquely identify data formats on the media bus. Mostly - * they correspond to similarly named V4L2_PIX_FMT_* formats, format 0 is - * reserved, V4L2_MBUS_FMT_FIXED shall be used by host-client pairs, where the - * data format is fixed. Additionally, "2X8" means that one pixel is transferred - * in two 8-bit samples, "BE" or "LE" specify in which order those samples are - * transferred over the bus: "LE" means that the least significant bits are - * transferred first, "BE" means that the most significant bits are transferred - * first, and "PADHI" and "PADLO" define which bits - low or high, in the - * incomplete high byte, are filled with padding bits. - * - * The pixel codes are grouped by type, bus_width, bits per component, samples - * per pixel and order of subsamples. Numerical values are sorted using generic - * numerical sort order (8 thus comes before 10). - * - * As their value can't change when a new pixel code is inserted in the - * enumeration, the pixel codes are explicitly given a numerical value. The next - * free values for each category are listed below, update them when inserting - * new pixel codes. - */ -enum v4l2_mbus_pixelcode { - V4L2_MBUS_FMT_FIXED = 0x0001, +#define V4L2_MBUS_FROM_MEDIA_BUS_FMT(name) \ + V4L2_MBUS_FMT_ ## name = MEDIA_BUS_FMT_ ## name - /* RGB - next is 0x100e */ - V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001, - V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002, - V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003, - V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE = 0x1004, - V4L2_MBUS_FMT_BGR565_2X8_BE = 0x1005, - V4L2_MBUS_FMT_BGR565_2X8_LE = 0x1006, - V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, - V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, - V4L2_MBUS_FMT_RGB666_1X18 = 0x1009, - V4L2_MBUS_FMT_RGB888_1X24 = 0x100a, - V4L2_MBUS_FMT_RGB888_2X12_BE = 0x100b, - V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, - V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, +enum v4l2_mbus_pixelcode { + V4L2_MBUS_FROM_MEDIA_BUS_FMT(FIXED), - /* YUV (including grey) - next is 0x2024 */ - V4L2_MBUS_FMT_Y8_1X8 = 0x2001, - V4L2_MBUS_FMT_UV8_1X8 = 0x2015, - V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, - V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, - V4L2_MBUS_FMT_YUYV8_1_5X8 = 0x2004, - V4L2_MBUS_FMT_YVYU8_1_5X8 = 0x2005, - V4L2_MBUS_FMT_UYVY8_2X8 = 0x2006, - V4L2_MBUS_FMT_VYUY8_2X8 = 0x2007, - V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008, - V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009, - V4L2_MBUS_FMT_Y10_1X10 = 0x200a, - V4L2_MBUS_FMT_UYVY10_2X10 = 0x2018, - V4L2_MBUS_FMT_VYUY10_2X10 = 0x2019, - V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, - V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, - V4L2_MBUS_FMT_Y12_1X12 = 0x2013, - V4L2_MBUS_FMT_UYVY8_1X16 = 0x200f, - V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010, - V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, - V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, - V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014, - V4L2_MBUS_FMT_UYVY10_1X20 = 0x201a, - V4L2_MBUS_FMT_VYUY10_1X20 = 0x201b, - V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, - V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, - V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, - V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017, - V4L2_MBUS_FMT_UYVY12_2X12 = 0x201c, - V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d, - V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e, - V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f, - V4L2_MBUS_FMT_UYVY12_1X24 = 0x2020, - V4L2_MBUS_FMT_VYUY12_1X24 = 0x2021, - V4L2_MBUS_FMT_YUYV12_1X24 = 0x2022, - V4L2_MBUS_FMT_YVYU12_1X24 = 0x2023, + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB444_2X8_PADHI_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB444_2X8_PADHI_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB555_2X8_PADHI_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB555_2X8_PADHI_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(BGR565_2X8_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(BGR565_2X8_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB565_2X8_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB565_2X8_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB666_1X18), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB888_1X24), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB888_2X12_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB888_2X12_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(ARGB8888_1X32), - /* Bayer - next is 0x3019 */ - V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, - V4L2_MBUS_FMT_SGBRG8_1X8 = 0x3013, - V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, - V4L2_MBUS_FMT_SRGGB8_1X8 = 0x3014, - V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 = 0x3015, - V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8 = 0x3016, - V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 = 0x3017, - V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8 = 0x3018, - V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, - V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c, - V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, - V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 = 0x300d, - V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, - V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, - V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, - V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, - V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, - V4L2_MBUS_FMT_SGBRG10_1X10 = 0x300e, - V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, - V4L2_MBUS_FMT_SRGGB10_1X10 = 0x300f, - V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, - V4L2_MBUS_FMT_SGBRG12_1X12 = 0x3010, - V4L2_MBUS_FMT_SGRBG12_1X12 = 0x3011, - V4L2_MBUS_FMT_SRGGB12_1X12 = 0x3012, + V4L2_MBUS_FROM_MEDIA_BUS_FMT(Y8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UV8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY8_1_5X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY8_1_5X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV8_1_5X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU8_1_5X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY8_2X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY8_2X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV8_2X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU8_2X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(Y10_1X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY10_2X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY10_2X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV10_2X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU10_2X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(Y12_1X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY8_1X16), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY8_1X16), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV8_1X16), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU8_1X16), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YDYUYDYV8_1X16), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY10_1X20), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY10_1X20), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV10_1X20), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU10_1X20), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUV10_1X30), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(AYUV8_1X32), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY12_2X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY12_2X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV12_2X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU12_2X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY12_1X24), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY12_1X24), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV12_1X24), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU12_1X24), - /* JPEG compressed formats - next is 0x4002 */ - V4L2_MBUS_FMT_JPEG_1X8 = 0x4001, + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_ALAW8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG10_ALAW8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG10_ALAW8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB10_ALAW8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_DPCM8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG10_DPCM8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG10_DPCM8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB10_DPCM8_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADHI_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADHI_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADLO_BE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADLO_LE), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_1X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG10_1X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG10_1X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB10_1X10), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR12_1X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG12_1X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG12_1X12), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB12_1X12), - /* Vendor specific formats - next is 0x5002 */ + V4L2_MBUS_FROM_MEDIA_BUS_FMT(JPEG_1X8), - /* S5C73M3 sensor specific interleaved UYVY and JPEG */ - V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 = 0x5001, + V4L2_MBUS_FROM_MEDIA_BUS_FMT(S5C_UYVY_JPEG_1X8), - /* HSV - next is 0x6002 */ - V4L2_MBUS_FMT_AHSV8888_1X32 = 0x6001, + V4L2_MBUS_FROM_MEDIA_BUS_FMT(AHSV8888_1X32), }; /** From 74f2349feecce71b92b962bf5a4fd613b7855fe9 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Mon, 30 Mar 2015 13:10:45 +0200 Subject: [PATCH 0435/1983] Driver for Toshiba TC358743 CSI-2 to HDMI bridge The driver is tested on our hardware and all the implemented features works as expected. Missing features: - CEC support - HDCP repeater support - IR support Signed-off-by: Mats Randgaard Signed-off-by: Philipp Zabel --- MAINTAINERS | 6 + drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/tc358743.c | 1768 ++++++++++++++++++++++++++++ drivers/media/i2c/tc358743_regs.h | 670 +++++++++++ include/media/tc358743.h | 89 ++ include/uapi/linux/v4l2-controls.h | 4 + 7 files changed, 2550 insertions(+) create mode 100644 drivers/media/i2c/tc358743.c create mode 100644 drivers/media/i2c/tc358743_regs.h create mode 100644 include/media/tc358743.h diff --git a/MAINTAINERS b/MAINTAINERS index 17fa05705e391f..251a751ce0b272 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8878,6 +8878,12 @@ L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/toshiba_acpi.c +TOSHIBA TC358743 DRIVER +M: Mats Randgaard +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/tc358743* + TOSHIBA SMM DRIVER M: Jonathan Buzzard L: tlinux-users@tce.toshiba-dme.co.jp diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 4aa9c5311cc506..98988238b16757 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -293,6 +293,18 @@ config VIDEO_SAA7191 To compile this driver as a module, choose M here: the module will be called saa7191. +config VIDEO_TC358743 + tristate "Toshiba TC358743 decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge + + This is a Analog Devices Component/Graphics Digitizer + with 4:1 Multiplexed HDMI Receiver. + + To compile this driver as a module, choose M here: the + module will be called tc358743. + config VIDEO_TVP514X tristate "Texas Instruments TVP514x video decoder" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 48888ae876fb36..9e69faf89f264a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -76,3 +76,4 @@ obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o +obj-$(CONFIG_VIDEO_TC358743) += tc358743.o diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c new file mode 100644 index 00000000000000..a86cbe0c36233e --- /dev/null +++ b/drivers/media/i2c/tc358743.c @@ -0,0 +1,1768 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may 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. + * + * 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. 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. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tc358743_regs.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-3)"); + +MODULE_DESCRIPTION("Toshiba TC358743 HDMI to CSI-2 bridge driver"); +MODULE_AUTHOR("Ramakrishnan Muthukrishnan "); +MODULE_AUTHOR("Mikhail Khelik "); +MODULE_AUTHOR("Mats Randgaard "); +MODULE_LICENSE("GPL"); + +static const struct v4l2_dv_timings_cap tc358743_timings_cap = { + .type = V4L2_DV_BT_656_1120, + /* keep this initialization for compatibility with GCC < 4.4.6 */ + .reserved = { 0 }, + /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000, + V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM) +}; + +struct tc358743_state { + struct tc358743_platform_data pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + struct i2c_client *i2c_client; + + /* controls */ + struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *audio_sampling_rate_ctrl; + struct v4l2_ctrl *audio_present_ctrl; + + /* work queues */ + struct workqueue_struct *work_queues; + struct delayed_work delayed_work_enable_hotplug; + + /* edid */ + bool edid_written; + + struct v4l2_dv_timings timings; + u32 mbus_fmt_code; +}; + +static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tc358743_state, sd); +} + +/* --------------- I2C --------------- */ + +static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +{ + struct tc358743_state *state = to_state(sd); + struct i2c_client *client = state->i2c_client; + int err; + u8 buf[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = buf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = n, + .buf = values, + }, + }; + + err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) { + v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed\n", + __func__, reg, client->addr); + } +} + +static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) +{ + struct tc358743_state *state = to_state(sd); + struct i2c_client *client = state->i2c_client; + int err, i; + struct i2c_msg msg; + u8 data[2 + n]; + + msg.addr = client->addr; + msg.buf = data; + msg.len = 2 + n; + msg.flags = 0; + + data[0] = reg >> 8; + data[1] = reg & 0xff; + + for (i = 0; i < n; i++) + data[2 + i] = values[i]; + + err = i2c_transfer(client->adapter, &msg, 1); + if (err != 1) { + v4l2_err(sd, "%s: writing register 0x%x from 0x%x failed\n", + __func__, reg, client->addr); + return; + } + + if (debug < 3) + return; + + switch (n) { + case 1: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x", + reg, data[2]); + break; + case 2: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x", + reg, data[3], data[2]); + break; + case 4: + v4l2_info(sd, "I2C write 0x%04x = 0x%02x%02x%02x%02x", + reg, data[5], data[4], data[3], data[2]); + break; + default: + v4l2_info(sd, "I2C write %d bytes from address 0x%04x\n", + n, reg); + } +} + +static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +{ + u8 val; + + i2c_rd(sd, reg, &val, 1); + + return val; +} + +static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) +{ + i2c_wr(sd, reg, &val, 1); +} + +static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, + u8 mask, u8 val) +{ + i2c_wr8(sd, reg, (i2c_rd8(sd, reg) & mask) | val); +} + +static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) +{ + u16 val; + + i2c_rd(sd, reg, (u8 *)&val, 2); + + return val; +} + +static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + i2c_wr(sd, reg, (u8 *)&val, 2); +} + +static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) +{ + i2c_wr16(sd, reg, (i2c_rd16(sd, reg) & mask) | val); +} + +static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) +{ + u32 val; + + i2c_rd(sd, reg, (u8 *)&val, 4); + + return val; +} + +static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) +{ + i2c_wr(sd, reg, (u8 *)&val, 4); +} + +/* --------------- STATUS --------------- */ + +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, SYS_STATUS) & MASK_S_HDMI; +} + +static inline bool tx_5v_power_present(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, SYS_STATUS) & MASK_S_DDC5V; +} + +static inline bool no_signal(struct v4l2_subdev *sd) +{ + return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_TMDS); +} + +static inline bool no_sync(struct v4l2_subdev *sd) +{ + return !(i2c_rd8(sd, SYS_STATUS) & MASK_S_SYNC); +} + +static inline bool audio_present(struct v4l2_subdev *sd) +{ + return i2c_rd8(sd, AU_STATUS0) & MASK_S_A_SAMPLE; +} + +static int get_audio_sampling_rate(struct v4l2_subdev *sd) +{ + static const int code_to_rate[] = { + 44100, 0, 48000, 32000, 22050, 384000, 24000, 352800, + 88200, 768000, 96000, 705600, 176400, 0, 192000, 0 + }; + + /* Register FS_SET is not cleared when the cable is disconnected */ + if (no_signal(sd)) + return 0; + + return code_to_rate[i2c_rd8(sd, FS_SET) & MASK_FS]; +} + +static unsigned tc358743_num_csi_lanes_in_use(struct v4l2_subdev *sd) +{ + return ((i2c_rd32(sd, CSI_CONTROL) & MASK_NOL) >> 1) + 1; +} + +/* --------------- TIMINGS --------------- */ + +static inline unsigned fps(const struct v4l2_bt_timings *t) +{ + if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t)) + return 0; + + return DIV_ROUND_CLOSEST((unsigned)t->pixelclock, + V4L2_DV_BT_FRAME_HEIGHT(t) * V4L2_DV_BT_FRAME_WIDTH(t)); +} + +static int tc358743_get_detected_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + unsigned width, height, frame_width, frame_height, frame_interval, fps; + + memset(timings, 0, sizeof(struct v4l2_dv_timings)); + + if (no_signal(sd)) { + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return -ENOLINK; + } + if (no_sync(sd)) + v4l2_dbg(1, debug, sd, "%s: no sync on signal\n", __func__); + + timings->type = V4L2_DV_BT_656_1120; + bt->interlaced = i2c_rd8(sd, VI_STATUS1) & MASK_S_V_INTERLACE ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + + width = ((i2c_rd8(sd, DE_WIDTH_H_HI) & 0x1f) << 8) + + i2c_rd8(sd, DE_WIDTH_H_LO); + height = ((i2c_rd8(sd, DE_WIDTH_V_HI) & 0x1f) << 8) + + i2c_rd8(sd, DE_WIDTH_V_LO); + frame_width = ((i2c_rd8(sd, H_SIZE_HI) & 0x1f) << 8) + + i2c_rd8(sd, H_SIZE_LO); + frame_height = (((i2c_rd8(sd, V_SIZE_HI) & 0x3f) << 8) + + i2c_rd8(sd, V_SIZE_LO)) / 2; + /* frame interval in milliseconds * 10 + * Require SYS_FREQ0 and SYS_FREQ1 are precisely set */ + frame_interval = ((i2c_rd8(sd, FV_CNT_HI) & 0x3) << 8) + + i2c_rd8(sd, FV_CNT_LO); + fps = (frame_interval > 0) ? + DIV_ROUND_CLOSEST(10000, frame_interval) : 0; + + bt->width = width; + bt->height = height; + bt->vsync = frame_height - height; + bt->hsync = frame_width - width; + bt->pixelclock = frame_width * frame_height * fps; + + return 0; +} + +/* --------------- HOTPLUG / HDCP / EDID --------------- */ + +static void tc358743_delayed_work_enable_hotplug(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tc358743_state *state = container_of(dwork, + struct tc358743_state, delayed_work_enable_hotplug); + struct v4l2_subdev *sd = &state->sd; + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, MASK_HPD_OUT0); +} + +static void tc358743_set_hdmi_hdcp(struct v4l2_subdev *sd, bool enable) +{ + v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? + "enable" : "disable"); + + i2c_wr8_and_or(sd, HDCP_REG1, + ~(MASK_AUTH_UNAUTH_SEL | MASK_AUTH_UNAUTH), + MASK_AUTH_UNAUTH_SEL_16_FRAMES | MASK_AUTH_UNAUTH_AUTO); + + i2c_wr8_and_or(sd, HDCP_REG2, ~MASK_AUTO_P3_RESET, + SET_AUTO_P3_RESET_FRAMES(0x0f)); + + /* HDCP is disabled by configuring the receiver as HDCP repeater. + * The repeater mode require software support to work, so + * HDCP authentication will fail. Set Ready and MAX_EXCED bits + * to avoid problems on MacBook Pro gen.8. + */ + i2c_wr8_and_or(sd, HDCP_REG3, ~KEY_RD_CMD, enable ? KEY_RD_CMD : 0); + + i2c_wr8_and_or(sd, HDCP_MODE, ~(MASK_AUTO_CLR | MASK_MODE_RST_TN), + enable ? (MASK_AUTO_CLR | MASK_MODE_RST_TN) : 0); + + i2c_wr8_and_or(sd, BSTATUS1, ~MASK_MAX_EXCED, + enable ? 0 : MASK_MAX_EXCED); + + i2c_wr8_and_or(sd, BCAPS, ~(MASK_REPEATER | MASK_READY), + enable ? 0 : MASK_REPEATER | MASK_READY); +} + +static void tc358743_disable_edid(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + + /* DDC access to EDID is also disabled when hotplug is disabled. See + * register DDC_CTL */ + i2c_wr8_and_or(sd, HPD_CTL, ~MASK_HPD_OUT0, 0x0); +} + +static void tc358743_enable_edid(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + if (!state->edid_written) { + v4l2_dbg(2, debug, sd, "%s: no EDID -> no hotplug\n", __func__); + return; + } + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when + * hotplug is enabled. See register DDC_CTL */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 10); +} + +/* --------------- AVI --------------- */ +/* TODO move to common library */ + +struct aviInfoFrame { + u8 f17; + u8 y10; + u8 a0; + u8 b10; + u8 s10; + u8 c10; + u8 m10; + u8 r3210; + u8 itc; + u8 ec210; + u8 q10; + u8 sc10; + u8 f47; + u8 vic; + u8 yq10; + u8 cn10; + u8 pr3210; + u16 etb; + u16 sbb; + u16 elb; + u16 srb; +}; + +static const char *y10Text[4] = { + "RGB", + "YCbCr 4:2:2", + "YCbCr 4:4:4", + "Future", +}; + +static const char *c10Text[4] = { + "No Data", + "SMPTE 170M", + "ITU-R 709", + "Extended Colorimetry information valied", +}; + +static const char *itcText[2] = { + "No Data", + "IT content", +}; + +static const char *ec210Text[8] = { + "xvYCC601", + "xvYCC709", + "sYCC601", + "AdobeYCC601", + "AdobeRGB", + "5 reserved", + "6 reserved", + "7 reserved", +}; + +static const char *q10Text[4] = { + "Default", + "Limited Range", + "Full Range", + "Reserved", +}; + +static void parse_avi_infoframe(struct v4l2_subdev *sd, u8 *buf, + struct aviInfoFrame *avi) +{ + avi->f17 = (buf[1] >> 7) & 0x1; + avi->y10 = (buf[1] >> 5) & 0x3; + avi->a0 = (buf[1] >> 4) & 0x1; + avi->b10 = (buf[1] >> 2) & 0x3; + avi->s10 = buf[1] & 0x3; + avi->c10 = (buf[2] >> 6) & 0x3; + avi->m10 = (buf[2] >> 4) & 0x3; + avi->r3210 = buf[2] & 0xf; + avi->itc = (buf[3] >> 7) & 0x1; + avi->ec210 = (buf[3] >> 4) & 0x7; + avi->q10 = (buf[3] >> 2) & 0x3; + avi->sc10 = buf[3] & 0x3; + avi->f47 = (buf[4] >> 7) & 0x1; + avi->vic = buf[4] & 0x7f; + avi->yq10 = (buf[5] >> 6) & 0x3; + avi->cn10 = (buf[5] >> 4) & 0x3; + avi->pr3210 = buf[5] & 0xf; + avi->etb = buf[6] + 256 * buf[7]; + avi->sbb = buf[8] + 256 * buf[9]; + avi->elb = buf[10] + 256 * buf[11]; + avi->srb = buf[12] + 256 * buf[13]; +} + +static void print_avi_infoframe(struct v4l2_subdev *sd) +{ + u8 buf[14]; + u8 avi_len; + u8 avi_ver; + struct aviInfoFrame avi; + + if (!is_hdmi(sd)) { + v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); + return; + } + + avi_ver = i2c_rd8(sd, PK_AVI_1HEAD); + avi_len = i2c_rd8(sd, PK_AVI_2HEAD); + v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", avi_ver, avi_len); + + if (avi_ver != 0x02) + return; + + i2c_rd(sd, PK_AVI_0BYTE, buf, ARRAY_SIZE(buf)); + + v4l2_info(sd, "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], + buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], + buf[13]); + + parse_avi_infoframe(sd, buf, &avi); + + if (avi.vic) + v4l2_info(sd, "\tVIC: %d\n", avi.vic); + if (avi.itc) + v4l2_info(sd, "\t%s\n", itcText[avi.itc]); + + if (avi.y10) + v4l2_info(sd, "\t%s %s\n", y10Text[avi.y10], + !avi.c10 ? "" : + (avi.c10 == 0x3 ? ec210Text[avi.ec210] : + c10Text[avi.c10])); + else + v4l2_info(sd, "\t%s %s\n", y10Text[avi.y10], q10Text[avi.q10]); +} + +/* --------------- CTRLS --------------- */ + +static int tc358743_s_ctrl_detect_tx_5v(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, + tx_5v_power_present(sd)); +} + +static int tc358743_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->audio_sampling_rate_ctrl, + get_audio_sampling_rate(sd)); +} + +static int tc358743_s_audio_present_ctrl(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + return v4l2_ctrl_s_ctrl(state->audio_present_ctrl, + audio_present(sd)); +} + +static int tc358743_update_controls(struct v4l2_subdev *sd) +{ + int ret = 0; + + ret |= tc358743_s_ctrl_detect_tx_5v(sd); + ret |= tc358743_s_ctrl_audio_sampling_rate(sd); + ret |= tc358743_s_audio_present_ctrl(sd); + + return ret; +} + +/* --------------- INIT --------------- */ + +static void tc358743_reset_phy(struct v4l2_subdev *sd) +{ + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, 0); + i2c_wr8_and_or(sd, PHY_RST, ~MASK_RESET_CTRL, MASK_RESET_CTRL); +} + +static void tc358743_reset(struct v4l2_subdev *sd, uint16_t mask) +{ + u16 sysctl = i2c_rd16(sd, SYSCTL); + + i2c_wr16(sd, SYSCTL, sysctl | mask); + i2c_wr16(sd, SYSCTL, sysctl & ~mask); +} + +static inline void tc358743_sleep_mode(struct v4l2_subdev *sd, bool enable) +{ + i2c_wr16_and_or(sd, SYSCTL, ~MASK_SLEEP, + enable ? MASK_SLEEP : 0); +} + +static inline void enable_stream(struct v4l2_subdev *sd, bool enable) +{ + v4l2_dbg(3, debug, sd, "%s: %sable\n", + __func__, enable ? "en" : "dis"); + + i2c_wr16_and_or(sd, CONFCTL, ~(MASK_VBUFEN | MASK_ABUFEN), + enable ? (MASK_VBUFEN | MASK_ABUFEN) : 0x0); +} + +static void tc358743_set_pll(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + u16 pllctl0 = i2c_rd16(sd, PLLCTL0); + u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | + SET_PLL_FBD(pdata->pll_fbd); + + v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + /* Only rewrite when needed, since rewriting triggers another format + * change event. */ + if (pllctl0 != pllctl0_new) { + u32 hsck = (pdata->refclk_hz * pdata->pll_prd) / pdata->pll_fbd; + u16 pll_frs; + + if (hsck > 500000000) + pll_frs = 0x0; + else if (hsck > 250000000) + pll_frs = 0x1; + else if (hsck > 125000000) + pll_frs = 0x2; + else + pll_frs = 0x3; + + v4l2_dbg(1, debug, sd, "%s: updating PLL clock\n", __func__); + tc358743_sleep_mode(sd, true); + i2c_wr16(sd, PLLCTL0, pllctl0_new); + i2c_wr16_and_or(sd, PLLCTL1, + ~(MASK_PLL_FRS | MASK_RESETB | MASK_PLL_EN), + (SET_PLL_FRS(pll_frs) | MASK_RESETB | + MASK_PLL_EN)); + udelay(10); /* REF_02, Sheet "Source HDMI" */ + i2c_wr16_and_or(sd, PLLCTL1, ~MASK_CKEN, MASK_CKEN); + tc358743_sleep_mode(sd, false); + } +} + +static void tc358743_set_ref_clk(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + u32 sys_freq; + u32 lockdet_ref; + u16 fh_min; + u16 fh_max; + + BUG_ON(!(pdata->refclk_hz == 26000000 || + pdata->refclk_hz == 27000000 || + pdata->refclk_hz == 42000000)); + + sys_freq = pdata->refclk_hz / 10000; + i2c_wr8(sd, SYS_FREQ0, sys_freq & 0x00ff); + i2c_wr8(sd, SYS_FREQ1, (sys_freq & 0xff00) >> 8); + + i2c_wr8_and_or(sd, PHY_CTL0, ~MASK_PHY_SYSCLK_IND, + (pdata->refclk_hz == 42000000) ? + MASK_PHY_SYSCLK_IND : 0x0); + + fh_min = pdata->refclk_hz / 100000; + i2c_wr8(sd, FH_MIN0, fh_min & 0x00ff); + i2c_wr8(sd, FH_MIN1, (fh_min & 0xff00) >> 8); + + fh_max = (fh_min * 66) / 10; + i2c_wr8(sd, FH_MAX0, fh_max & 0x00ff); + i2c_wr8(sd, FH_MAX1, (fh_max & 0xff00) >> 8); + + lockdet_ref = pdata->refclk_hz / 100; + i2c_wr8(sd, LOCKDET_REF0, lockdet_ref & 0x0000ff); + i2c_wr8(sd, LOCKDET_REF1, (lockdet_ref & 0x00ff00) >> 8); + i2c_wr8(sd, LOCKDET_REF2, (lockdet_ref & 0x0f0000) >> 16); + + i2c_wr8_and_or(sd, NCO_F0_MOD, ~MASK_NCO_F0_MOD, + (pdata->refclk_hz == 27000000) ? + MASK_NCO_F0_MOD_27MHZ : 0x0); +} + +static void tc358743_set_csi_color_space(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + + switch (state->mbus_fmt_code) { + case MEDIA_BUS_FMT_UYVY8_1X16: + v4l2_dbg(2, debug, sd, "%s: YCbCr 422 16-bit\n", __func__); + i2c_wr8_and_or(sd, VOUT_SET2, + ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, + MASK_SEL422 | MASK_VOUT_422FIL_100); + i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, + MASK_VOUT_COLOR_601_YCBCR_LIMITED); + i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, + MASK_YCBCRFMT_422_8_BIT); + break; + case MEDIA_BUS_FMT_RGB888_1X24: + v4l2_dbg(2, debug, sd, "%s: RGB 888 24-bit\n", __func__); + i2c_wr8_and_or(sd, VOUT_SET2, + ~(MASK_SEL422 | MASK_VOUT_422FIL_100) & 0xff, + 0x00); + i2c_wr8_and_or(sd, VI_REP, ~MASK_VOUT_COLOR_SEL & 0xff, + MASK_VOUT_COLOR_RGB_FULL); + i2c_wr16_and_or(sd, CONFCTL, ~MASK_YCBCRFMT, 0); + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unsupported format code 0x%x\n", + __func__, state->mbus_fmt_code); + } +} + +static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_bt_timings *bt = &state->timings.bt; + u32 bits_pr_pixel = + (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; + u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; + + return DIV_ROUND_UP(bps, state->pdata.bps_pr_lane); +} + +static void tc358743_set_csi(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + unsigned lanes = tc358743_num_csi_lanes_needed(sd); + + v4l2_dbg(3, debug, sd, "%s:\n", __func__); + + tc358743_reset(sd, MASK_CTXRST); + + if (lanes < 1) + i2c_wr32(sd, CLW_CNTRL, MASK_CLW_LANEDISABLE); + if (lanes < 1) + i2c_wr32(sd, D0W_CNTRL, MASK_D0W_LANEDISABLE); + if (lanes < 2) + i2c_wr32(sd, D1W_CNTRL, MASK_D1W_LANEDISABLE); + if (lanes < 3) + i2c_wr32(sd, D2W_CNTRL, MASK_D2W_LANEDISABLE); + if (lanes < 4) + i2c_wr32(sd, D3W_CNTRL, MASK_D3W_LANEDISABLE); + + i2c_wr32(sd, LINEINITCNT, pdata->lineinitcnt); + i2c_wr32(sd, LPTXTIMECNT, pdata->lptxtimecnt); + i2c_wr32(sd, TCLK_HEADERCNT, pdata->tclk_headercnt); + i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); + i2c_wr32(sd, TWAKEUP, pdata->twakeup); + i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); + i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); + + i2c_wr32(sd, HSTXVREGEN, + ((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) | + ((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) | + ((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) | + ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | + ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); + + i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); + i2c_wr32(sd, STARTCNTRL, MASK_START); + i2c_wr32(sd, CSI_START, MASK_STRT); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_CONTROL | + MASK_CSI_MODE | + MASK_TXHSMD | + ((lanes == 4) ? MASK_NOL_4 : + (lanes == 3) ? MASK_NOL_3 : + (lanes == 2) ? MASK_NOL_2 : MASK_NOL_1)); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_ERR_INTENA | MASK_TXBRK | MASK_QUNK | + MASK_WCER | MASK_INER); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_CLEAR | + MASK_ADDRESS_CSI_ERR_HALT | MASK_TXBRK | MASK_QUNK); + + i2c_wr32(sd, CSI_CONFW, MASK_MODE_SET | + MASK_ADDRESS_CSI_INT_ENA | MASK_INTER); +} + +static void tc358743_set_hdmi_phy(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + + /* Default settings from REF_02, sheet "Source HDMI" + * and custom settings as platform data */ + i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, 0x0); + i2c_wr8(sd, PHY_CTL1, SET_PHY_AUTO_RST1_US(1600) | + SET_FREQ_RANGE_MODE_CYCLES(1)); + i2c_wr8_and_or(sd, PHY_CTL2, ~MASK_PHY_AUTO_RSTn, pdata->phy_auto_rst); + i2c_wr8(sd, PHY_BIAS, 0x40); + i2c_wr8(sd, PHY_CSQ, SET_CSQ_CNT_LEVEL(0x0a)); + i2c_wr8(sd, AVM_CTL, 45); + i2c_wr8_and_or(sd, HDMI_DET, ~MASK_HDMI_DET_V, + pdata->hdmi_det_v); + i2c_wr8_and_or(sd, HV_RST, ~(MASK_H_PI_RST | MASK_V_PI_RST), + (pdata->h_pi_rst ? MASK_H_PI_RST : 0) | + (pdata->v_pi_rst ? MASK_V_PI_RST : 0)); + i2c_wr8_and_or(sd, PHY_EN, ~MASK_ENABLE_PHY, MASK_ENABLE_PHY); +} + +static void tc358743_erase_bksv(struct v4l2_subdev *sd) +{ + int i; + + for (i = 0; i < 5; i++) + i2c_wr8(sd, BKSV + i, 0); +} + +static void tc358743_set_hdmi_audio(struct v4l2_subdev *sd) +{ + /* Default settings from REF_02, sheet "Source HDMI" */ + i2c_wr8(sd, FORCE_MUTE, 0x00); + i2c_wr8(sd, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 | + MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 | + MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0); + i2c_wr8(sd, AUTO_CMD1, MASK_AUTO_MUTE9); + i2c_wr8(sd, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2); + i2c_wr8(sd, BUFINIT_START, SET_BUFINIT_START_MS(500)); + i2c_wr8(sd, FS_MUTE, 0x00); + i2c_wr8(sd, FS_IMODE, MASK_NLPCM_SMODE | MASK_FS_SMODE); + i2c_wr8(sd, ACR_MODE, MASK_CTS_MODE); + i2c_wr8(sd, ACR_MDF0, MASK_ACR_L2MDF_1976_PPM | MASK_ACR_L1MDF_976_PPM); + i2c_wr8(sd, ACR_MDF1, MASK_ACR_L3MDF_3906_PPM); + i2c_wr8(sd, SDO_MODE1, MASK_SDO_FMT_I2S); + i2c_wr8(sd, DIV_MODE, SET_DIV_DLY_MS(100)); + i2c_wr16_and_or(sd, CONFCTL, 0xffff, MASK_AUDCHNUM_2 | + MASK_AUDOUTSEL_I2S | MASK_AUTOINDEX); +} + +static void tc358743_set_hdmi_info_frame_mode(struct v4l2_subdev *sd) +{ + /* Default settings from REF_02, sheet "Source HDMI" */ + i2c_wr8(sd, PK_INT_MODE, MASK_ISRC2_INT_MODE | MASK_ISRC_INT_MODE | + MASK_ACP_INT_MODE | MASK_VS_INT_MODE | + MASK_SPD_INT_MODE | MASK_MS_INT_MODE | + MASK_AUD_INT_MODE | MASK_AVI_INT_MODE); + i2c_wr8(sd, NO_PKT_LIMIT, 0x2c); + i2c_wr8(sd, NO_PKT_CLR, 0x53); + i2c_wr8(sd, ERR_PK_LIMIT, 0x01); + i2c_wr8(sd, NO_PKT_LIMIT2, 0x30); + i2c_wr8(sd, NO_GDB_LIMIT, 0x10); +} + +static void tc358743_initial_setup(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct tc358743_platform_data *pdata = &state->pdata; + + /* Keep CEC and IR in reset since driver support is missing */ + i2c_wr16_and_or(sd, SYSCTL, ~(MASK_CECRST | MASK_IRRST), + (MASK_CECRST | MASK_IRRST)); + + tc358743_reset(sd, MASK_CTXRST | MASK_HDMIRST); + tc358743_sleep_mode(sd, false); + + i2c_wr16(sd, FIFOCTL, pdata->fifo_level); + + tc358743_set_ref_clk(sd); + + i2c_wr8_and_or(sd, DDC_CTL, ~MASK_DDC5V_MODE, + pdata->ddc5v_delay & MASK_DDC5V_MODE); + i2c_wr8_and_or(sd, EDID_MODE, ~MASK_EDID_MODE, MASK_EDID_MODE_E_DDC); + + tc358743_set_hdmi_phy(sd); + tc358743_set_hdmi_hdcp(sd, pdata->enable_hdcp); + tc358743_set_hdmi_audio(sd); + tc358743_set_hdmi_info_frame_mode(sd); + + i2c_wr8_and_or(sd, VOUT_SET2, ~MASK_VOUTCOLORMODE, + MASK_VOUTCOLORMODE_AUTO); + i2c_wr8(sd, VOUT_SET3, MASK_VOUT_EXTCNT); +} + +/* --------------- IRQ --------------- */ + +static void tc358743_enable_interrupts(struct v4l2_subdev *sd) +{ + u16 i; + + /* clear interrupt status registers */ + for (i = SYS_INT; i <= KEY_INT; i++) + i2c_wr8(sd, i, 0xff); + + i2c_wr16(sd, INTSTATUS, 0xffff); + + /* enable interrupts */ + i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_HDMI_DET) & 0xff); + + i2c_wr8(sd, CLK_INTM, ~MASK_M_IN_DE_CHG); + + i2c_wr8(sd, MISC_INTM, ~MASK_M_SYNC_CHG); + + i2c_wr8(sd, CBIT_INTM, ~(MASK_M_CBIT_FS | MASK_M_AF_LOCK | + MASK_M_AF_UNLOCK) & 0xff); + + i2c_wr16(sd, INTMASK, ~(MASK_HDMI_MSK | MASK_CSI_MSK) & 0xffff); +} + +static void tc358743_csi_err_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + v4l2_err(sd, "%s: CSI_ERR = 0x%x\n", __func__, i2c_rd32(sd, CSI_ERR)); + + i2c_wr32(sd, CSI_INT_CLR, MASK_ICRER); +} + +static void tc358743_hdmi_misc_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 misc_int_mask = i2c_rd8(sd, MISC_INTM); + u8 misc_int = i2c_rd8(sd, MISC_INT) & ~misc_int_mask; + + i2c_wr8(sd, MISC_INT, misc_int); + + v4l2_dbg(3, debug, sd, "%s: MISC_INT = 0x%02x\n", __func__, misc_int); + + if (misc_int & MASK_I_SYNC_CHG) { + + v4l2_dbg(1, debug, sd, "%s: Format changed\n", __func__); + + if (no_sync(sd) || no_signal(sd)) { + tc358743_reset_phy(sd); + tc358743_erase_bksv(sd); + } + + v4l2_subdev_notify(sd, TC358743_FMT_CHANGE, NULL); + + misc_int &= ~MASK_I_SYNC_CHG; + if (handled) + *handled = true; + } + + if (misc_int) { + v4l2_err(sd, "%s: Unhandled MISC_INT interrupts: 0x%02x\n", + __func__, misc_int); + } +} + +static void tc358743_hdmi_cbit_int_handler(struct v4l2_subdev *sd, + bool *handled) +{ + u8 cbit_int_mask = i2c_rd8(sd, CBIT_INTM); + u8 cbit_int = i2c_rd8(sd, CBIT_INT) & ~cbit_int_mask; + + i2c_wr8(sd, CBIT_INT, cbit_int); + + v4l2_dbg(3, debug, sd, "%s: CBIT_INT = 0x%02x\n", __func__, cbit_int); + + if (cbit_int & MASK_I_CBIT_FS) { + + v4l2_dbg(1, debug, sd, "%s: Audio sample rate changed\n", + __func__); + tc358743_s_ctrl_audio_sampling_rate(sd); + + cbit_int &= ~MASK_I_CBIT_FS; + if (handled) + *handled = true; + } + + if (cbit_int & (MASK_I_AF_LOCK | MASK_I_AF_UNLOCK)) { + + v4l2_dbg(1, debug, sd, "%s: Audio present changed\n", + __func__); + tc358743_s_audio_present_ctrl(sd); + + cbit_int &= ~(MASK_I_AF_LOCK | MASK_I_AF_UNLOCK); + if (handled) + *handled = true; + } + + if (cbit_int) { + v4l2_err(sd, "%s: Unhandled CBIT_INT interrupts: 0x%02x\n", + __func__, cbit_int); + } +} + +static void tc358743_hdmi_clk_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + u8 clk_int_mask = i2c_rd8(sd, CLK_INTM); + u8 clk_int = i2c_rd8(sd, CLK_INT) & ~clk_int_mask; + + /* Bit 7 and bit 6 are set even when they are masked */ + i2c_wr8(sd, CLK_INT, clk_int | 0x80 | MASK_I_OUT_H_CHG); + + v4l2_dbg(3, debug, sd, "%s: CLK_INT = 0x%02x\n", __func__, clk_int); + + if (clk_int & (MASK_I_IN_DE_CHG)) { + + v4l2_dbg(1, debug, sd, "%s: DE size or position has changed\n", + __func__); + + /* If the source switch to a new resolution with the same pixel + * frequency as the existing (ie. 1080p25 -> 720p50), the + * I_SYNC_CHG interrupt is not always triggered, while the + * I_IN_DE_CHG interrupt seems to work fine. FMT_CHANGE + * notifications are only sent when the signal is stable to + * reduce the number of notifications. */ + if (!no_signal(sd) && !no_sync(sd)) + v4l2_subdev_notify(sd, TC358743_FMT_CHANGE, NULL); + + clk_int &= ~(MASK_I_IN_DE_CHG); + if (handled) + *handled = true; + } + + if (clk_int) { + v4l2_err(sd, "%s: Unhandled CLK_INT interrupts: 0x%02x\n", + __func__, clk_int); + } +} + +static void tc358743_hdmi_sys_int_handler(struct v4l2_subdev *sd, bool *handled) +{ + struct tc358743_state *state = to_state(sd); + u8 sys_int_mask = i2c_rd8(sd, SYS_INTM); + u8 sys_int = i2c_rd8(sd, SYS_INT) & ~sys_int_mask; + + i2c_wr8(sd, SYS_INT, sys_int); + + v4l2_dbg(3, debug, sd, "%s: SYS_INT = 0x%02x\n", __func__, sys_int); + + if (sys_int & MASK_I_DDC) { + bool tx_5v = tx_5v_power_present(sd); + + v4l2_dbg(1, debug, sd, "%s: Tx 5V power present: %s\n", + __func__, tx_5v ? "yes" : "no"); + + if (tx_5v) { + tc358743_enable_edid(sd); + } else { + tc358743_disable_edid(sd); + memset(&state->timings, 0, sizeof(state->timings)); + tc358743_erase_bksv(sd); + } + + tc358743_update_controls(sd); + + sys_int &= ~MASK_I_DDC; + if (handled) + *handled = true; + } + + if (sys_int & MASK_I_HDMI) { + v4l2_dbg(1, debug, sd, "%s: DVI->HDMI change detected\n", + __func__); + + /* Register is reset in DVI mode (REF_01, c. 6.6.41) */ + i2c_wr8(sd, ANA_CTL, MASK_APPL_PCSX_NORMAL | MASK_ANALOG_ON); + + sys_int &= ~MASK_I_HDMI; + if (handled) + *handled = true; + } + + if (sys_int) { + v4l2_err(sd, "%s: Unhandled SYS_INT interrupts: 0x%02x\n", + __func__, sys_int); + } +} + +/* --------------- CORE OPS --------------- */ + +static int tc358743_log_status(struct v4l2_subdev *sd) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_dv_timings timings; + uint8_t hdmi_sys_status = i2c_rd8(sd, SYS_STATUS); + uint16_t sysctl = i2c_rd16(sd, SYSCTL); + u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); + const int deep_color_mode[4] = { 8, 10, 12, 16 }; + static const char * const input_color_space[] = { + "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", + "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", + "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; + + v4l2_info(sd, "-----Chip status-----\n"); + v4l2_info(sd, "Chip ID: %d\n", i2c_rd16(sd, CHIPID) & + MASK_CHIPID); + v4l2_info(sd, "Chip revision: %d\n", + i2c_rd16(sd, CHIPID) & MASK_REVID); + v4l2_info(sd, "Reset: IR: %d, CEC: %d, CSI TX: %d, HDMI: %d\n", + !!(sysctl & MASK_IRRST), + !!(sysctl & MASK_CECRST), + !!(sysctl & MASK_CTXRST), + !!(sysctl & MASK_HDMIRST)); + v4l2_info(sd, "Sleep mode: %s\n", sysctl & MASK_SLEEP ? "on" : "off"); + v4l2_info(sd, "Cable detected (+5V power): %s\n", + hdmi_sys_status & MASK_S_DDC5V ? "yes" : "no"); + v4l2_info(sd, "DDC lines enabled: %s\n", + (i2c_rd8(sd, EDID_MODE) & MASK_EDID_MODE_E_DDC) ? + "yes" : "no"); + v4l2_info(sd, "Hotplug enabled: %s\n", + (i2c_rd8(sd, HPD_CTL) & MASK_HPD_OUT0) ? + "yes" : "no"); + v4l2_info(sd, "CEC enabled: %s\n", + (i2c_rd16(sd, CECEN) & MASK_CECEN) ? "yes" : "no"); + v4l2_info(sd, "-----Signal status-----\n"); + v4l2_info(sd, "TMDS signal detected: %s\n", + hdmi_sys_status & MASK_S_TMDS ? "yes" : "no"); + v4l2_info(sd, "Stable sync signal: %s\n", + hdmi_sys_status & MASK_S_SYNC ? "yes" : "no"); + v4l2_info(sd, "PHY PLL locked: %s\n", + hdmi_sys_status & MASK_S_PHY_PLL ? "yes" : "no"); + v4l2_info(sd, "PHY DE detected: %s\n", + hdmi_sys_status & MASK_S_PHY_SCDT ? "yes" : "no"); + + if (tc358743_get_detected_timings(sd, &timings)) { + v4l2_info(sd, "No video detected\n"); + } else { + v4l2_print_dv_timings(sd->name, "Detected format: ", &timings, + true); + } + v4l2_print_dv_timings(sd->name, "Configured format: ", &state->timings, + true); + + v4l2_info(sd, "-----CSI-TX status-----\n"); + v4l2_info(sd, "Lanes needed: %d\n", + tc358743_num_csi_lanes_needed(sd)); + v4l2_info(sd, "Lanes in use: %d\n", + tc358743_num_csi_lanes_in_use(sd)); + v4l2_info(sd, "Waiting for particular sync signal: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_WSYNC) ? + "yes" : "no"); + v4l2_info(sd, "Transmit mode: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_TXACT) ? + "yes" : "no"); + v4l2_info(sd, "Receive mode: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_RXACT) ? + "yes" : "no"); + v4l2_info(sd, "Stopped: %s\n", + (i2c_rd16(sd, CSI_STATUS) & MASK_S_HLT) ? + "yes" : "no"); + v4l2_info(sd, "Color space: %s\n", + state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16 ? + "YCbCr 422 16-bit" : + state->mbus_fmt_code == MEDIA_BUS_FMT_RGB888_1X24 ? + "RGB 888 24-bit" : "Unsupported"); + + v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); + v4l2_info(sd, "HDCP encrypted content: %s\n", + hdmi_sys_status & MASK_S_HDCP ? "yes" : "no"); + v4l2_info(sd, "Input color space: %s %s range\n", + input_color_space[(vi_status3 & MASK_S_V_COLOR) >> 1], + (vi_status3 & MASK_LIMITED) ? "limited" : "full"); + if (!is_hdmi(sd)) + return 0; + v4l2_info(sd, "AV Mute: %s\n", hdmi_sys_status & MASK_S_AVMUTE ? "on" : + "off"); + v4l2_info(sd, "Deep color mode: %d-bits per channel\n", + deep_color_mode[(i2c_rd8(sd, VI_STATUS1) & + MASK_S_DEEPCOLOR) >> 2]); + print_avi_infoframe(sd); + + return 0; +} + +static long tc358743_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + switch (cmd) { + case TC358743_CSI_RESET: + { + /* + * This function is called often with CSI overflow. It should + * run as fast as possible for quick recovery. I2C operations + * are slow. This function takes about 20 ms. Stop stream first, + * reset CSI and enable stream. Avoid calling enable_stream + * twice which add an extra I2C read. + */ + unsigned short confctl = i2c_rd16(sd, CONFCTL); + + v4l2_dbg(3, debug, sd, "%s: TC358743_CSI_RESET\n", __func__); + + i2c_wr16(sd, CONFCTL, confctl & + (~(MASK_VBUFEN | MASK_ABUFEN))); + tc358743_set_csi(sd); + i2c_wr16(sd, CONFCTL, confctl | + (MASK_VBUFEN | MASK_ABUFEN)); + return 0; + } + } + + v4l2_dbg(1, debug, sd, "%s: unknown ioctl %08x\n", __func__, cmd); + + return -ENOTTY; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void tc358743_print_register_map(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x0000–0x00FF: Global Control Register\n"); + v4l2_info(sd, "0x0100–0x01FF: CSI2-TX PHY Register\n"); + v4l2_info(sd, "0x0200–0x03FF: CSI2-TX PPI Register\n"); + v4l2_info(sd, "0x0400–0x05FF: Reserved\n"); + v4l2_info(sd, "0x0600–0x06FF: CEC Register\n"); + v4l2_info(sd, "0x0700–0x84FF: Reserved\n"); + v4l2_info(sd, "0x8500–0x85FF: HDMIRX System Control Register\n"); + v4l2_info(sd, "0x8600–0x86FF: HDMIRX Audio Control Register\n"); + v4l2_info(sd, "0x8700–0x87FF: HDMIRX InfoFrame packet data Register\n"); + v4l2_info(sd, "0x8800–0x88FF: HDMIRX HDCP Port Register\n"); + v4l2_info(sd, "0x8900–0x89FF: HDMIRX Video Output Port & 3D Register\n"); + v4l2_info(sd, "0x8A00–0x8BFF: Reserved\n"); + v4l2_info(sd, "0x8C00–0x8FFF: HDMIRX EDID-RAM (1024bytes)\n"); + v4l2_info(sd, "0x9000–0x90FF: HDMIRX GBD Extraction Control\n"); + v4l2_info(sd, "0x9100–0x92FF: HDMIRX GBD RAM read\n"); + v4l2_info(sd, "0x9300- : Reserved\n"); +} + +static int tc358743_get_reg_size(u16 address) +{ + /* REF_01 p. 66-72 */ + if (address <= 0x00ff) + return 2; + else if ((address >= 0x0100) && (address <= 0x06FF)) + return 4; + else if ((address >= 0x0700) && (address <= 0x84ff)) + return 2; + else + return 1; +} + +static int tc358743_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + if (reg->reg > 0xffff) { + tc358743_print_register_map(sd); + return -EINVAL; + } + + reg->size = tc358743_get_reg_size(reg->reg); + + i2c_rd(sd, reg->reg, (u8 *)®->val, reg->size); + + return 0; +} + +static int tc358743_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + if (reg->reg > 0xffff) { + tc358743_print_register_map(sd); + return -EINVAL; + } + + /* It should not be possible for the user to enable HDCP with a simple + * v4l2-dbg command. + * + * DO NOT REMOVE THIS unless all other issues with HDCP have been + * resolved. + */ + if (reg->reg == HDCP_MODE || + reg->reg == HDCP_REG1 || + reg->reg == HDCP_REG2 || + reg->reg == HDCP_REG3 || + reg->reg == BCAPS) + return 0; + + i2c_wr(sd, (u16)reg->reg, (u8 *)®->val, + tc358743_get_reg_size(reg->reg)); + + return 0; +} +#endif + +static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + u16 intstatus = i2c_rd16(sd, INTSTATUS); + + v4l2_dbg(1, debug, sd, "%s: IntStatus = 0x%04x\n", __func__, intstatus); + + if (intstatus & MASK_HDMI_INT) { + u8 hdmi_int0 = i2c_rd8(sd, HDMI_INT0); + u8 hdmi_int1 = i2c_rd8(sd, HDMI_INT1); + + if (hdmi_int0 & MASK_I_MISC) + tc358743_hdmi_misc_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_CBIT) + tc358743_hdmi_cbit_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_CLK) + tc358743_hdmi_clk_int_handler(sd, handled); + if (hdmi_int1 & MASK_I_SYS) + tc358743_hdmi_sys_int_handler(sd, handled); + + i2c_wr16(sd, INTSTATUS, MASK_HDMI_INT); + intstatus &= ~MASK_HDMI_INT; + } + + if (intstatus & MASK_CSI_INT) { + u32 csi_int = i2c_rd32(sd, CSI_INT); + + if (csi_int & MASK_INTER) + tc358743_csi_err_int_handler(sd, handled); + + i2c_wr16(sd, INTSTATUS, MASK_CSI_INT); + intstatus &= ~MASK_CSI_INT; + } + + intstatus = i2c_rd16(sd, INTSTATUS); + if (intstatus) { + v4l2_dbg(1, debug, sd, + "%s: Unhandled IntStatus interrupts: 0x%02x\n", + __func__, intstatus); + } + + return 0; +} + +/* --------------- VIDEO OPS --------------- */ + +static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + *status = 0; + *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; + *status |= no_sync(sd) ? V4L2_IN_ST_NO_SYNC : 0; + + v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); + + return 0; +} + +static int tc358743_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct tc358743_state *state = to_state(sd); + + if (format->pad != 0) + return -EINVAL; + + switch (format->format.code) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + state->mbus_fmt_code = format->format.code; + break; + default: + return -EINVAL; + } + + enable_stream(sd, false); + tc358743_set_pll(sd); + tc358743_set_csi(sd); + tc358743_set_csi_color_space(sd); + + return 0; +} + +static int tc358743_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct tc358743_state *state = to_state(sd); + u8 vi_rep = i2c_rd8(sd, VI_REP); + + if (format->pad != 0) + return -EINVAL; + + format->format.code = state->mbus_fmt_code; + format->format.width = state->timings.bt.width; + format->format.height = state->timings.bt.height; + format->format.field = V4L2_FIELD_NONE; + + switch (vi_rep & MASK_VOUT_COLOR_SEL) { + case MASK_VOUT_COLOR_RGB_FULL: + case MASK_VOUT_COLOR_RGB_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_SRGB; + break; + case MASK_VOUT_COLOR_601_YCBCR_LIMITED: + case MASK_VOUT_COLOR_601_YCBCR_FULL: + format->format.colorspace = V4L2_COLORSPACE_SMPTE170M; + break; + case MASK_VOUT_COLOR_709_YCBCR_FULL: + case MASK_VOUT_COLOR_709_YCBCR_LIMITED: + format->format.colorspace = V4L2_COLORSPACE_REC709; + break; + default: + format->format.colorspace = 0; + break; + } + + return 0; +} + +static int tc358743_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tc358743_state *state = to_state(sd); + struct v4l2_bt_timings *bt; + + if (!timings) + return -EINVAL; + + if (debug) + v4l2_print_dv_timings(sd->name, "tc358743_s_dv_timings: ", + timings, false); + + if (!memcmp(&state->timings, timings, sizeof(struct v4l2_dv_timings))) { + v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); + return 0; + } + + bt = &timings->bt; + + if (!v4l2_valid_dv_timings(timings, + &tc358743_timings_cap, NULL, NULL)) { + v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); + return -ERANGE; + } + + /* Fill the optional fields .standards and .flags in struct + * v4l2_dv_timings if the format is one of the CEA or DMT timings */ + v4l2_find_dv_timings_cap(timings, + &tc358743_timings_cap, 250000, NULL, NULL); + + state->timings = *timings; + + enable_stream(sd, false); + tc358743_set_pll(sd); + tc358743_set_csi(sd); + + return 0; +} + +static int tc358743_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct tc358743_state *state = to_state(sd); + + *timings = state->timings; + + return 0; +} + +static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + if (timings->pad != 0) + return -EINVAL; + + return v4l2_enum_dv_timings_cap(timings, + &tc358743_timings_cap, NULL, NULL); +} + +static int tc358743_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int ret; + + ret = tc358743_get_detected_timings(sd, timings); + if (ret) + return ret; + + if (debug) + v4l2_print_dv_timings(sd->name, "tc358743_query_dv_timings: ", + timings, false); + + if (!v4l2_valid_dv_timings(timings, + &tc358743_timings_cap, NULL, NULL)) { + v4l2_dbg(1, debug, sd, "%s: timings out of range\n", __func__); + return -ERANGE; + } + + return 0; +} + +static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + if (cap->pad != 0) + return -EINVAL; + + *cap = tc358743_timings_cap; + + return 0; +} + +static int tc358743_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + cfg->type = V4L2_MBUS_CSI2; + + cfg->flags = i2c_rd16(sd, TXOPTIONCNTRL) & MASK_CONTCLKMODE ? + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK : + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + + switch (tc358743_num_csi_lanes_in_use(sd)) { + case 1: + cfg->flags |= V4L2_MBUS_CSI2_1_LANE; + break; + case 2: + cfg->flags |= V4L2_MBUS_CSI2_2_LANE; + break; + case 3: + cfg->flags |= V4L2_MBUS_CSI2_3_LANE; + break; + case 4: + cfg->flags |= V4L2_MBUS_CSI2_4_LANE; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) +{ + enable_stream(sd, enable); + + return 0; +} + +/* --------------- PAD OPS --------------- */ + +static int tc358743_g_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + struct tc358743_state *state = to_state(sd); + + if (edid->pad != 0) + return -EINVAL; + + if (!state->edid_written) + return -ENODATA; + + if (edid->blocks == 0) + return -EINVAL; + + if (edid->start_block + edid->blocks > 8) { + edid->blocks = 8; + return -E2BIG; + } + + if (!edid->edid) + return -EINVAL; + + i2c_rd(sd, EDID_RAM + (edid->start_block * 128), edid->edid, + edid->blocks * 128); + + return 0; +} + +static int tc358743_s_edid(struct v4l2_subdev *sd, + struct v4l2_subdev_edid *edid) +{ + struct tc358743_state *state = to_state(sd); + u16 edid_len = edid->blocks * 128; + + v4l2_dbg(2, debug, sd, "%s\n", __func__); + + if (edid->pad != 0) + return -EINVAL; + + if (edid->start_block != 0) + return -EINVAL; + + if (edid->blocks > 8) { + edid->blocks = 8; + return -E2BIG; + } + + if (edid->blocks != 0 && !edid->edid) + return -EINVAL; + + tc358743_disable_edid(sd); + + i2c_wr8(sd, EDID_LEN1, edid_len & 0xff); + i2c_wr8(sd, EDID_LEN2, edid_len >> 8); + + if (edid->blocks == 0) { + state->edid_written = false; + return 0; + } + + i2c_wr(sd, EDID_RAM, edid->edid, edid_len); + + state->edid_written = true; + + if (tx_5v_power_present(sd)) + tc358743_enable_edid(sd); + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tc358743_core_ops = { + .log_status = tc358743_log_status, + .ioctl = tc358743_ioctl, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tc358743_g_register, + .s_register = tc358743_s_register, +#endif + .interrupt_service_routine = tc358743_isr, +}; + +static const struct v4l2_subdev_video_ops tc358743_video_ops = { + .g_input_status = tc358743_g_input_status, + .s_dv_timings = tc358743_s_dv_timings, + .g_dv_timings = tc358743_g_dv_timings, + .query_dv_timings = tc358743_query_dv_timings, + .g_mbus_config = tc358743_g_mbus_config, + .s_stream = tc358743_s_stream, +}; + +static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { + .set_fmt = tc358743_set_fmt, + .get_fmt = tc358743_get_fmt, + .get_edid = tc358743_g_edid, + .set_edid = tc358743_s_edid, + .enum_dv_timings = tc358743_enum_dv_timings, + .dv_timings_cap = tc358743_dv_timings_cap, +}; + +static const struct v4l2_subdev_ops tc358743_ops = { + .core = &tc358743_core_ops, + .video = &tc358743_video_ops, + .pad = &tc358743_pad_ops, +}; + +/* --------------- CUSTOM CTRLS --------------- */ + +static const struct v4l2_ctrl_config tc358743_ctrl_audio_sampling_rate = { + .id = TC358743_CID_AUDIO_SAMPLING_RATE, + .name = "Audio sampling rate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 768000, + .step = 1, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { + .id = TC358743_CID_AUDIO_PRESENT, + .name = "Audio present", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .def = 0, + .flags = V4L2_CTRL_FLAG_READ_ONLY, +}; + +/* --------------- PROBE / REMOVE --------------- */ + +static int tc358743_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + static struct v4l2_dv_timings default_timing = + V4L2_DV_BT_CEA_640X480P59_94; + struct tc358743_state *state; + struct tc358743_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + v4l_dbg(1, debug, client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = devm_kzalloc(&client->dev, sizeof(struct tc358743_state), + GFP_KERNEL); + if (!state) { + v4l_err(client, "Could not allocate tc358743_state memory!\n"); + return -ENOMEM; + } + + /* platform data */ + if (!pdata) { + v4l_err(client, "No platform data!\n"); + return -ENODEV; + } + state->pdata = *pdata; + + state->i2c_client = client; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tc358743_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + + /* i2c access */ + /* read the interrupt mask register, it should carry the + * default values, as it hasn't been touched at this point. + */ + if (i2c_rd16(sd, INTMASK) != 0x0400) { + v4l2_info(sd, "not a TC358743 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } + + /* control handlers */ + v4l2_ctrl_handler_init(&state->hdl, 3); + + /* private controls */ + state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&state->hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); + + /* custom controls */ + state->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&state->hdl, + &tc358743_ctrl_audio_sampling_rate, NULL); + + state->audio_present_ctrl = v4l2_ctrl_new_custom(&state->hdl, + &tc358743_ctrl_audio_present, NULL); + + sd->ctrl_handler = &state->hdl; + if (state->hdl.error) { + err = state->hdl.error; + goto err_hdl; + } + + if (tc358743_update_controls(sd)) { + err = -ENODEV; + goto err_hdl; + } + + /* work queues */ + state->work_queues = create_singlethread_workqueue(client->name); + if (!state->work_queues) { + v4l2_err(sd, "Could not create work queue\n"); + err = -ENOMEM; + goto err_hdl; + } + + INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, + tc358743_delayed_work_enable_hotplug); + + tc358743_initial_setup(sd); + + tc358743_s_dv_timings(sd, &default_timing); + + state->mbus_fmt_code = MEDIA_BUS_FMT_RGB888_1X24; + tc358743_set_csi_color_space(sd); + + tc358743_enable_interrupts(sd); + + v4l2_ctrl_handler_setup(sd->ctrl_handler); + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + return 0; + +err_hdl: + v4l2_ctrl_handler_free(&state->hdl); + return err; +} + +static int tc358743_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tc358743_state *state = to_state(sd); + + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&state->hdl); + + return 0; +} + +static struct i2c_device_id tc358743_id[] = { + {"tc358743", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tc358743_id); + +static struct i2c_driver tc358743_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tc358743", + }, + .probe = tc358743_probe, + .remove = tc358743_remove, + .id_table = tc358743_id, +}; + +module_i2c_driver(tc358743_driver); diff --git a/drivers/media/i2c/tc358743_regs.h b/drivers/media/i2c/tc358743_regs.h new file mode 100644 index 00000000000000..30a85377f94fdf --- /dev/null +++ b/drivers/media/i2c/tc358743_regs.h @@ -0,0 +1,670 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge - register names and bit masks + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may 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. + * + * 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. 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. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + */ + +/* Bit masks has prefix 'MASK_' and options after '_'. */ + +#ifndef __TC358743_REGS_H +#define __TC358743_REGS_H + +#define CHIPID 0x0000 +#define MASK_CHIPID 0xff00 +#define MASK_REVID 0x00ff + +#define SYSCTL 0x0002 +#define MASK_IRRST 0x0800 +#define MASK_CECRST 0x0400 +#define MASK_CTXRST 0x0200 +#define MASK_HDMIRST 0x0100 +#define MASK_SLEEP 0x0001 + +#define CONFCTL 0x0004 +#define MASK_PWRISO 0x8000 +#define MASK_ACLKOPT 0x1000 +#define MASK_AUDCHNUM 0x0c00 +#define MASK_AUDCHNUM_8 0x0000 +#define MASK_AUDCHNUM_6 0x0400 +#define MASK_AUDCHNUM_4 0x0800 +#define MASK_AUDCHNUM_2 0x0c00 +#define MASK_AUDCHSEL 0x0200 +#define MASK_I2SDLYOPT 0x0100 +#define MASK_YCBCRFMT 0x00c0 +#define MASK_YCBCRFMT_444 0x0000 +#define MASK_YCBCRFMT_422_12_BIT 0x0040 +#define MASK_YCBCRFMT_COLORBAR 0x0080 +#define MASK_YCBCRFMT_422_8_BIT 0x00c0 +#define MASK_INFRMEN 0x0020 +#define MASK_AUDOUTSEL 0x0018 +#define MASK_AUDOUTSEL_CSI 0x0000 +#define MASK_AUDOUTSEL_I2S 0x0010 +#define MASK_AUDOUTSEL_TDM 0x0018 +#define MASK_AUTOINDEX 0x0004 +#define MASK_ABUFEN 0x0002 +#define MASK_VBUFEN 0x0001 + +#define FIFOCTL 0x0006 + +#define INTSTATUS 0x0014 +#define MASK_AMUTE_INT 0x0400 +#define MASK_HDMI_INT 0x0200 +#define MASK_CSI_INT 0x0100 +#define MASK_SYS_INT 0x0020 +#define MASK_CEC_EINT 0x0010 +#define MASK_CEC_TINT 0x0008 +#define MASK_CEC_RINT 0x0004 +#define MASK_IR_EINT 0x0002 +#define MASK_IR_DINT 0x0001 + +#define INTMASK 0x0016 +#define MASK_AMUTE_MSK 0x0400 +#define MASK_HDMI_MSK 0x0200 +#define MASK_CSI_MSK 0x0100 +#define MASK_SYS_MSK 0x0020 +#define MASK_CEC_EMSK 0x0010 +#define MASK_CEC_TMSK 0x0008 +#define MASK_CEC_RMSK 0x0004 +#define MASK_IR_EMSK 0x0002 +#define MASK_IR_DMSK 0x0001 + +#define INTFLAG 0x0018 +#define INTSYSSTATUS 0x001A + +#define PLLCTL0 0x0020 +#define MASK_PLL_PRD 0xf000 +#define SET_PLL_PRD(prd) ((((prd) - 1) << 12) &\ + MASK_PLL_PRD) +#define MASK_PLL_FBD 0x01ff +#define SET_PLL_FBD(fbd) (((fbd) - 1) & MASK_PLL_FBD) + +#define PLLCTL1 0x0022 +#define MASK_PLL_FRS 0x0c00 +#define SET_PLL_FRS(frs) (((frs) << 10) & MASK_PLL_PRD) +#define MASK_PLL_LBWS 0x0300 +#define MASK_LFBREN 0x0040 +#define MASK_BYPCKEN 0x0020 +#define MASK_CKEN 0x0010 +#define MASK_RESETB 0x0002 +#define MASK_PLL_EN 0x0001 + +#define CLW_CNTRL 0x0140 +#define MASK_CLW_LANEDISABLE 0x0001 + +#define D0W_CNTRL 0x0144 +#define MASK_D0W_LANEDISABLE 0x0001 + +#define D1W_CNTRL 0x0148 +#define MASK_D1W_LANEDISABLE 0x0001 + +#define D2W_CNTRL 0x014C +#define MASK_D2W_LANEDISABLE 0x0001 + +#define D3W_CNTRL 0x0150 +#define MASK_D3W_LANEDISABLE 0x0001 + +#define STARTCNTRL 0x0204 +#define MASK_START 0x00000001 + +#define LINEINITCNT 0x0210 +#define LPTXTIMECNT 0x0214 +#define TCLK_HEADERCNT 0x0218 +#define TCLK_TRAILCNT 0x021C +#define THS_HEADERCNT 0x0220 +#define TWAKEUP 0x0224 +#define TCLK_POSTCNT 0x0228 +#define THS_TRAILCNT 0x022C +#define HSTXVREGCNT 0x0230 + +#define HSTXVREGEN 0x0234 +#define MASK_D3M_HSTXVREGEN 0x0010 +#define MASK_D2M_HSTXVREGEN 0x0008 +#define MASK_D1M_HSTXVREGEN 0x0004 +#define MASK_D0M_HSTXVREGEN 0x0002 +#define MASK_CLM_HSTXVREGEN 0x0001 + + +#define TXOPTIONCNTRL 0x0238 +#define MASK_CONTCLKMODE 0x00000001 + +#define CSI_CONTROL 0x040C +#define MASK_CSI_MODE 0x8000 +#define MASK_HTXTOEN 0x0400 +#define MASK_TXHSMD 0x0080 +#define MASK_HSCKMD 0x0020 +#define MASK_NOL 0x0006 +#define MASK_NOL_1 0x0000 +#define MASK_NOL_2 0x0002 +#define MASK_NOL_3 0x0004 +#define MASK_NOL_4 0x0006 +#define MASK_EOTDIS 0x0001 + +#define CSI_INT 0x0414 +#define MASK_INTHLT 0x00000008 +#define MASK_INTER 0x00000004 + +#define CSI_INT_ENA 0x0418 +#define MASK_IENHLT 0x00000008 +#define MASK_IENER 0x00000004 + +#define CSI_ERR 0x044C +#define MASK_INER 0x00000200 +#define MASK_WCER 0x00000100 +#define MASK_QUNK 0x00000010 +#define MASK_TXBRK 0x00000002 + +#define CSI_ERR_INTENA 0x0450 +#define CSI_ERR_HALT 0x0454 + +#define CSI_CONFW 0x0500 +#define MASK_MODE 0xe0000000 +#define MASK_MODE_SET 0xa0000000 +#define MASK_MODE_CLEAR 0xc0000000 +#define MASK_ADDRESS 0x1f000000 +#define MASK_ADDRESS_CSI_CONTROL 0x03000000 +#define MASK_ADDRESS_CSI_INT_ENA 0x06000000 +#define MASK_ADDRESS_CSI_ERR_INTENA 0x14000000 +#define MASK_ADDRESS_CSI_ERR_HALT 0x15000000 +#define MASK_DATA 0x0000ffff + +#define CSI_INT_CLR 0x050C +#define MASK_ICRER 0x00000004 + +#define CSI_START 0x0518 +#define MASK_STRT 0x00000001 + +#define CECEN 0x0600 +#define MASK_CECEN 0x0001 + +#define HDMI_INT0 0x8500 +#define MASK_I_KEY 0x80 +#define MASK_I_MISC 0x02 +#define MASK_I_PHYERR 0x01 + +#define HDMI_INT1 0x8501 +#define MASK_I_GBD 0x80 +#define MASK_I_HDCP 0x40 +#define MASK_I_ERR 0x20 +#define MASK_I_AUD 0x10 +#define MASK_I_CBIT 0x08 +#define MASK_I_PACKET 0x04 +#define MASK_I_CLK 0x02 +#define MASK_I_SYS 0x01 + +#define SYS_INT 0x8502 +#define MASK_I_ACR_CTS 0x80 +#define MASK_I_ACRN 0x40 +#define MASK_I_DVI 0x20 +#define MASK_I_HDMI 0x10 +#define MASK_I_NOPMBDET 0x08 +#define MASK_I_DPMBDET 0x04 +#define MASK_I_TMDS 0x02 +#define MASK_I_DDC 0x01 + +#define CLK_INT 0x8503 +#define MASK_I_OUT_H_CHG 0x40 +#define MASK_I_IN_DE_CHG 0x20 +#define MASK_I_IN_HV_CHG 0x10 +#define MASK_I_DC_CHG 0x08 +#define MASK_I_PXCLK_CHG 0x04 +#define MASK_I_PHYCLK_CHG 0x02 +#define MASK_I_TMDSCLK_CHG 0x01 + +#define CBIT_INT 0x8505 +#define MASK_I_AF_LOCK 0x80 +#define MASK_I_AF_UNLOCK 0x40 +#define MASK_I_CBIT_FS 0x02 + +#define ERR_INT 0x8507 +#define MASK_I_EESS_ERR 0x80 + +#define HDCP_INT 0x8508 +#define MASK_I_AVM_SET 0x80 +#define MASK_I_AVM_CLR 0x40 +#define MASK_I_LINKERR 0x20 +#define MASK_I_SHA_END 0x10 +#define MASK_I_R0_END 0x08 +#define MASK_I_KM_END 0x04 +#define MASK_I_AKSV_END 0x02 +#define MASK_I_AN_END 0x01 + +#define MISC_INT 0x850B +#define MASK_I_AS_LAYOUT 0x10 +#define MASK_I_NO_SPD 0x08 +#define MASK_I_NO_VS 0x03 +#define MASK_I_SYNC_CHG 0x02 +#define MASK_I_AUDIO_MUTE 0x01 + +#define KEY_INT 0x850F + +#define SYS_INTM 0x8512 +#define MASK_M_ACR_CTS 0x80 +#define MASK_M_ACR_N 0x40 +#define MASK_M_DVI_DET 0x20 +#define MASK_M_HDMI_DET 0x10 +#define MASK_M_NOPMBDET 0x08 +#define MASK_M_BPMBDET 0x04 +#define MASK_M_TMDS 0x02 +#define MASK_M_DDC 0x01 + +#define CLK_INTM 0x8513 +#define MASK_M_OUT_H_CHG 0x40 +#define MASK_M_IN_DE_CHG 0x20 +#define MASK_M_IN_HV_CHG 0x10 +#define MASK_M_DC_CHG 0x08 +#define MASK_M_PXCLK_CHG 0x04 +#define MASK_M_PHYCLK_CHG 0x02 +#define MASK_M_TMDS_CHG 0x01 + +#define PACKET_INTM 0x8514 + +#define CBIT_INTM 0x8515 +#define MASK_M_AF_LOCK 0x80 +#define MASK_M_AF_UNLOCK 0x40 +#define MASK_M_CBIT_FS 0x02 + +#define AUDIO_INTM 0x8516 + +#define ERR_INTM 0x8517 +#define MASK_M_EESS_ERR 0x80 + +#define HDCP_INTM 0x8518 +#define MASK_M_AVM_SET 0x80 +#define MASK_M_AVM_CLR 0x40 +#define MASK_M_LINKERR 0x20 +#define MASK_M_SHA_END 0x10 +#define MASK_M_R0_END 0x08 +#define MASK_M_KM_END 0x04 +#define MASK_M_AKSV_END 0x02 +#define MASK_M_AN_END 0x01 + +#define MISC_INTM 0x851B +#define MASK_M_AS_LAYOUT 0x10 +#define MASK_M_NO_SPD 0x08 +#define MASK_M_NO_VS 0x03 +#define MASK_M_SYNC_CHG 0x02 +#define MASK_M_AUDIO_MUTE 0x01 + +#define KEY_INTM 0x851F + +#define SYS_STATUS 0x8520 +#define MASK_S_SYNC 0x80 +#define MASK_S_AVMUTE 0x40 +#define MASK_S_HDCP 0x20 +#define MASK_S_HDMI 0x10 +#define MASK_S_PHY_SCDT 0x08 +#define MASK_S_PHY_PLL 0x04 +#define MASK_S_TMDS 0x02 +#define MASK_S_DDC5V 0x01 + +#define CSI_STATUS 0x0410 +#define MASK_S_WSYNC 0x0400 +#define MASK_S_TXACT 0x0200 +#define MASK_S_RXACT 0x0100 +#define MASK_S_HLT 0x0001 + +#define VI_STATUS1 0x8522 +#define MASK_S_V_GBD 0x08 +#define MASK_S_DEEPCOLOR 0x0c +#define MASK_S_V_422 0x02 +#define MASK_S_V_INTERLACE 0x01 + +#define AU_STATUS0 0x8523 +#define MASK_S_A_SAMPLE 0x01 + +#define VI_STATUS3 0x8528 +#define MASK_S_V_COLOR 0x1e +#define MASK_LIMITED 0x01 + +#define PHY_CTL0 0x8531 +#define MASK_PHY_SYSCLK_IND 0x02 +#define MASK_PHY_CTL 0x01 + + +#define PHY_CTL1 0x8532 /* Not in REF_01 */ +#define MASK_PHY_AUTO_RST1 0xf0 +#define MASK_PHY_AUTO_RST1_OFF 0x00 +#define SET_PHY_AUTO_RST1_US(us) ((((us) / 200) << 4) & \ + MASK_PHY_AUTO_RST1) +#define MASK_FREQ_RANGE_MODE 0x0f +#define SET_FREQ_RANGE_MODE_CYCLES(cycles) (((cycles) - 1) & \ + MASK_FREQ_RANGE_MODE) + +#define PHY_CTL2 0x8533 /* Not in REF_01 */ +#define MASK_PHY_AUTO_RST4 0x04 +#define MASK_PHY_AUTO_RST3 0x02 +#define MASK_PHY_AUTO_RST2 0x01 +#define MASK_PHY_AUTO_RSTn (MASK_PHY_AUTO_RST4 | \ + MASK_PHY_AUTO_RST3 | \ + MASK_PHY_AUTO_RST2) + +#define PHY_EN 0x8534 +#define MASK_ENABLE_PHY 0x01 + +#define PHY_RST 0x8535 +#define MASK_RESET_CTRL 0x01 /* Reset active low */ + +#define PHY_BIAS 0x8536 /* Not in REF_01 */ + +#define PHY_CSQ 0x853F /* Not in REF_01 */ +#define MASK_CSQ_CNT 0x0f +#define SET_CSQ_CNT_LEVEL(n) (n & MASK_CSQ_CNT) + +#define SYS_FREQ0 0x8540 +#define SYS_FREQ1 0x8541 + +#define SYS_CLK 0x8542 /* Not in REF_01 */ +#define MASK_CLK_DIFF 0x0C +#define MASK_CLK_DIV 0x03 + +#define DDC_CTL 0x8543 +#define MASK_DDC_ACK_POL 0x08 +#define MASK_DDC_ACTION 0x04 +#define MASK_DDC5V_MODE 0x03 +#define MASK_DDC5V_MODE_0MS 0x00 +#define MASK_DDC5V_MODE_50MS 0x01 +#define MASK_DDC5V_MODE_100MS 0x02 +#define MASK_DDC5V_MODE_200MS 0x03 + +#define HPD_CTL 0x8544 +#define MASK_HPD_CTL0 0x10 +#define MASK_HPD_OUT0 0x01 + +#define ANA_CTL 0x8545 +#define MASK_APPL_PCSX 0x30 +#define MASK_APPL_PCSX_HIZ 0x00 +#define MASK_APPL_PCSX_L_FIX 0x10 +#define MASK_APPL_PCSX_H_FIX 0x20 +#define MASK_APPL_PCSX_NORMAL 0x30 +#define MASK_ANALOG_ON 0x01 + +#define AVM_CTL 0x8546 + +#define INIT_END 0x854A +#define MASK_INIT_END 0x01 + +#define HDMI_DET 0x8552 +#define MASK_HDMI_DET_MOD1 0x80 +#define MASK_HDMI_DET_MOD0 0x40 +#define MASK_HDMI_DET_V 0x30 +#define MASK_HDMI_DET_V_SYNC 0x00 +#define MASK_HDMI_DET_V_ASYNC_25MS 0x10 +#define MASK_HDMI_DET_V_ASYNC_50MS 0x20 +#define MASK_HDMI_DET_V_ASYNC_100MS 0x30 +#define MASK_HDMI_DET_NUM 0x0f + +#define HDCP_MODE 0x8560 +#define MASK_MODE_RST_TN 0x20 +#define MASK_LINE_REKEY 0x10 +#define MASK_AUTO_CLR 0x04 + +#define HDCP_REG1 0x8563 /* Not in REF_01 */ +#define MASK_AUTH_UNAUTH_SEL 0x70 +#define MASK_AUTH_UNAUTH_SEL_12_FRAMES 0x70 +#define MASK_AUTH_UNAUTH_SEL_8_FRAMES 0x60 +#define MASK_AUTH_UNAUTH_SEL_4_FRAMES 0x50 +#define MASK_AUTH_UNAUTH_SEL_2_FRAMES 0x40 +#define MASK_AUTH_UNAUTH_SEL_64_FRAMES 0x30 +#define MASK_AUTH_UNAUTH_SEL_32_FRAMES 0x20 +#define MASK_AUTH_UNAUTH_SEL_16_FRAMES 0x10 +#define MASK_AUTH_UNAUTH_SEL_ONCE 0x00 +#define MASK_AUTH_UNAUTH 0x01 +#define MASK_AUTH_UNAUTH_AUTO 0x01 + +#define HDCP_REG2 0x8564 /* Not in REF_01 */ +#define MASK_AUTO_P3_RESET 0x0F +#define SET_AUTO_P3_RESET_FRAMES(n) (n & MASK_AUTO_P3_RESET) +#define MASK_AUTO_P3_RESET_OFF 0x00 + +#define VOUT_SET2 0x8573 +#define MASK_SEL422 0x80 +#define MASK_VOUT_422FIL_100 0x40 +#define MASK_VOUTCOLORMODE 0x03 +#define MASK_VOUTCOLORMODE_THROUGH 0x00 +#define MASK_VOUTCOLORMODE_AUTO 0x01 +#define MASK_VOUTCOLORMODE_MANUAL 0x03 + +#define VOUT_SET3 0x8574 +#define MASK_VOUT_EXTCNT 0x08 + +#define VI_REP 0x8576 +#define MASK_VOUT_COLOR_SEL 0xe0 +#define MASK_VOUT_COLOR_RGB_FULL 0x00 +#define MASK_VOUT_COLOR_RGB_LIMITED 0x20 +#define MASK_VOUT_COLOR_601_YCBCR_FULL 0x40 +#define MASK_VOUT_COLOR_601_YCBCR_LIMITED 0x60 +#define MASK_VOUT_COLOR_709_YCBCR_FULL 0x80 +#define MASK_VOUT_COLOR_709_YCBCR_LIMITED 0xa0 +#define MASK_VOUT_COLOR_FULL_TO_LIMITED 0xc0 +#define MASK_VOUT_COLOR_LIMITED_TO_FULL 0xe0 +#define MASK_IN_REP_HEN 0x10 +#define MASK_IN_REP 0x0f + +#define DE_WIDTH_H_LO 0x8582 /* Not in REF_01 */ +#define DE_WIDTH_H_HI 0x8583 /* Not in REF_01 */ +#define DE_WIDTH_V_LO 0x8588 /* Not in REF_01 */ +#define DE_WIDTH_V_HI 0x8589 /* Not in REF_01 */ +#define H_SIZE_LO 0x858A /* Not in REF_01 */ +#define H_SIZE_HI 0x858B /* Not in REF_01 */ +#define V_SIZE_LO 0x858C /* Not in REF_01 */ +#define V_SIZE_HI 0x858D /* Not in REF_01 */ +#define FV_CNT_LO 0x85A1 /* Not in REF_01 */ +#define FV_CNT_HI 0x85A2 /* Not in REF_01 */ + +#define FH_MIN0 0x85AA /* Not in REF_01 */ +#define FH_MIN1 0x85AB /* Not in REF_01 */ +#define FH_MAX0 0x85AC /* Not in REF_01 */ +#define FH_MAX1 0x85AD /* Not in REF_01 */ +#define HV_RST 0x85AF /* Not in REF_01 */ +#define MASK_H_PI_RST 0x20 +#define MASK_V_PI_RST 0x10 + +#define EDID_MODE 0x85C7 +#define MASK_EDID_SPEED 0x40 +#define MASK_EDID_MODE 0x03 +#define MASK_EDID_MODE_DISABLE 0x00 +#define MASK_EDID_MODE_DDC2B 0x01 +#define MASK_EDID_MODE_E_DDC 0x02 + +#define EDID_LEN1 0x85CA +#define EDID_LEN2 0x85CB + +#define HDCP_REG3 0x85D1 /* Not in REF_01 */ +#define KEY_RD_CMD 0x01 + +#define FORCE_MUTE 0x8600 +#define MASK_FORCE_AMUTE 0x10 +#define MASK_FORCE_DMUTE 0x01 + +#define CMD_AUD 0x8601 +#define MASK_CMD_BUFINIT 0x04 +#define MASK_CMD_LOCKDET 0x02 +#define MASK_CMD_MUTE 0x01 + +#define AUTO_CMD0 0x8602 +#define MASK_AUTO_MUTE7 0x80 +#define MASK_AUTO_MUTE6 0x40 +#define MASK_AUTO_MUTE5 0x20 +#define MASK_AUTO_MUTE4 0x10 +#define MASK_AUTO_MUTE3 0x08 +#define MASK_AUTO_MUTE2 0x04 +#define MASK_AUTO_MUTE1 0x02 +#define MASK_AUTO_MUTE0 0x01 + +#define AUTO_CMD1 0x8603 +#define MASK_AUTO_MUTE10 0x04 +#define MASK_AUTO_MUTE9 0x02 +#define MASK_AUTO_MUTE8 0x01 + +#define AUTO_CMD2 0x8604 +#define MASK_AUTO_PLAY3 0x08 +#define MASK_AUTO_PLAY2 0x04 + +#define BUFINIT_START 0x8606 +#define SET_BUFINIT_START_MS(milliseconds) ((milliseconds) / 100) + +#define FS_MUTE 0x8607 +#define MASK_FS_ELSE_MUTE 0x80 +#define MASK_FS22_MUTE 0x40 +#define MASK_FS24_MUTE 0x20 +#define MASK_FS88_MUTE 0x10 +#define MASK_FS96_MUTE 0x08 +#define MASK_FS176_MUTE 0x04 +#define MASK_FS192_MUTE 0x02 +#define MASK_FS_NO_MUTE 0x01 + +#define FS_IMODE 0x8620 +#define MASK_NLPCM_HMODE 0x40 +#define MASK_NLPCM_SMODE 0x20 +#define MASK_NLPCM_IMODE 0x10 +#define MASK_FS_HMODE 0x08 +#define MASK_FS_AMODE 0x04 +#define MASK_FS_SMODE 0x02 +#define MASK_FS_IMODE 0x01 + +#define FS_SET 0x8621 +#define MASK_FS 0x0f + +#define LOCKDET_REF0 0x8630 +#define LOCKDET_REF1 0x8631 +#define LOCKDET_REF2 0x8632 + +#define ACR_MODE 0x8640 +#define MASK_ACR_LOAD 0x10 +#define MASK_N_MODE 0x04 +#define MASK_CTS_MODE 0x01 + +#define ACR_MDF0 0x8641 +#define MASK_ACR_L2MDF 0x70 +#define MASK_ACR_L2MDF_0_PPM 0x00 +#define MASK_ACR_L2MDF_61_PPM 0x10 +#define MASK_ACR_L2MDF_122_PPM 0x20 +#define MASK_ACR_L2MDF_244_PPM 0x30 +#define MASK_ACR_L2MDF_488_PPM 0x40 +#define MASK_ACR_L2MDF_976_PPM 0x50 +#define MASK_ACR_L2MDF_1976_PPM 0x60 +#define MASK_ACR_L2MDF_3906_PPM 0x70 +#define MASK_ACR_L1MDF 0x07 +#define MASK_ACR_L1MDF_0_PPM 0x00 +#define MASK_ACR_L1MDF_61_PPM 0x01 +#define MASK_ACR_L1MDF_122_PPM 0x02 +#define MASK_ACR_L1MDF_244_PPM 0x03 +#define MASK_ACR_L1MDF_488_PPM 0x04 +#define MASK_ACR_L1MDF_976_PPM 0x05 +#define MASK_ACR_L1MDF_1976_PPM 0x06 +#define MASK_ACR_L1MDF_3906_PPM 0x07 + +#define ACR_MDF1 0x8642 +#define MASK_ACR_L3MDF 0x07 +#define MASK_ACR_L3MDF_0_PPM 0x00 +#define MASK_ACR_L3MDF_61_PPM 0x01 +#define MASK_ACR_L3MDF_122_PPM 0x02 +#define MASK_ACR_L3MDF_244_PPM 0x03 +#define MASK_ACR_L3MDF_488_PPM 0x04 +#define MASK_ACR_L3MDF_976_PPM 0x05 +#define MASK_ACR_L3MDF_1976_PPM 0x06 +#define MASK_ACR_L3MDF_3906_PPM 0x07 + +#define SDO_MODE1 0x8652 +#define MASK_SDO_BIT_LENG 0x70 +#define MASK_SDO_FMT 0x03 +#define MASK_SDO_FMT_RIGHT 0x00 +#define MASK_SDO_FMT_LEFT 0x01 +#define MASK_SDO_FMT_I2S 0x02 + +#define DIV_MODE 0x8665 /* Not in REF_01 */ +#define MASK_DIV_DLY 0xf0 +#define SET_DIV_DLY_MS(milliseconds) ((((milliseconds) / 100) << 4) & \ + MASK_DIV_DLY) +#define MASK_DIV_MODE 0x01 + +#define NCO_F0_MOD 0x8670 +#define MASK_NCO_F0_MOD 0x03 +#define MASK_NCO_F0_MOD_42MHZ 0x00 +#define MASK_NCO_F0_MOD_27MHZ 0x01 + +#define PK_INT_MODE 0x8709 +#define MASK_ISRC2_INT_MODE 0x80 +#define MASK_ISRC_INT_MODE 0x40 +#define MASK_ACP_INT_MODE 0x20 +#define MASK_VS_INT_MODE 0x10 +#define MASK_SPD_INT_MODE 0x08 +#define MASK_MS_INT_MODE 0x04 +#define MASK_AUD_INT_MODE 0x02 +#define MASK_AVI_INT_MODE 0x01 + +#define NO_PKT_LIMIT 0x870B +#define MASK_NO_ACP_LIMIT 0xf0 +#define SET_NO_ACP_LIMIT_MS(milliseconds) ((((milliseconds) / 80) << 4) & \ + MASK_NO_ACP_LIMIT) +#define MASK_NO_AVI_LIMIT 0x0f +#define SET_NO_AVI_LIMIT_MS(milliseconds) (((milliseconds) / 80) & \ + MASK_NO_AVI_LIMIT) + +#define NO_PKT_CLR 0x870C +#define MASK_NO_VS_CLR 0x40 +#define MASK_NO_SPD_CLR 0x20 +#define MASK_NO_ACP_CLR 0x10 +#define MASK_NO_AVI_CLR1 0x02 +#define MASK_NO_AVI_CLR0 0x01 + +#define ERR_PK_LIMIT 0x870D +#define NO_PKT_LIMIT2 0x870E +#define PK_AVI_0HEAD 0x8710 +#define PK_AVI_1HEAD 0x8711 +#define PK_AVI_2HEAD 0x8712 +#define PK_AVI_0BYTE 0x8713 +#define PK_AVI_1BYTE 0x8714 +#define PK_AVI_2BYTE 0x8715 +#define PK_AVI_3BYTE 0x8716 +#define PK_AVI_4BYTE 0x8717 +#define PK_AVI_5BYTE 0x8718 +#define PK_AVI_6BYTE 0x8719 +#define PK_AVI_7BYTE 0x871A +#define PK_AVI_8BYTE 0x871B +#define PK_AVI_9BYTE 0x871C +#define PK_AVI_10BYTE 0x871D +#define PK_AVI_11BYTE 0x871E +#define PK_AVI_12BYTE 0x871F +#define PK_AVI_13BYTE 0x8720 +#define PK_AVI_14BYTE 0x8721 +#define PK_AVI_15BYTE 0x8722 +#define PK_AVI_16BYTE 0x8723 + +#define BKSV 0x8800 + +#define BCAPS 0x8840 +#define MASK_HDMI_RSVD 0x80 +#define MASK_REPEATER 0x40 +#define MASK_READY 0x20 +#define MASK_FASTI2C 0x10 +#define MASK_1_1_FEA 0x02 +#define MASK_FAST_REAU 0x01 + +#define BSTATUS1 0x8842 +#define MASK_MAX_EXCED 0x08 + +#define EDID_RAM 0x8C00 +#define NO_GDB_LIMIT 0x9007 + +#endif diff --git a/include/media/tc358743.h b/include/media/tc358743.h new file mode 100644 index 00000000000000..c552a6506be405 --- /dev/null +++ b/include/media/tc358743.h @@ -0,0 +1,89 @@ +/* + * tc358743 - Toshiba HDMI to CSI-2 bridge + * + * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights + * reserved. + * + * This program is free software; you may 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. + * + * 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. 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. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Toshiba, TC358743XBG (H2C), Functional Specification, Rev 0.60 + * REF_02 - Toshiba, TC358743XBG_HDMI-CSI_Tv11p_nm.xls + */ + +#ifndef _TC358743_ +#define _TC358743_ + +enum tc358743_ddc5v_delays { + DDC5V_DELAY_0MS, + DDC5V_DELAY_50MS, + DDC5V_DELAY_100MS, + DDC5V_DELAY_200MS, +}; + +struct tc358743_platform_data { + u32 refclk_hz; /* System clock connected to REFCLK (pin H5) */ + enum tc358743_ddc5v_delays ddc5v_delay; /* DDC +5V debounce delay */ + bool enable_hdcp; + /* + * The FIFO size is 512x32, so Toshiba recommend to set the default FIFO + * level to somewhere in the middle (eg. 200), so it can cover speed + * mismatches in input and output ports. + */ + u16 fifo_level; + u16 pll_prd; /* PLLCTL0 */ + u16 pll_fbd; /*PLLCTL0 */ + + /* CSI + * Calculate CSI parameters with REF_02 for the highest resolution your + * CSI interface can handle. The driver will adjust the number of CSI + * lanes in use according to the pixel clock. + */ + u32 bps_pr_lane; + u32 lineinitcnt; + u32 lptxtimecnt; + u32 tclk_headercnt; + u32 tclk_trailcnt; + u32 ths_headercnt; + u32 twakeup; + u32 tclk_postcnt; + u32 ths_trailcnt; + u32 hstxvregcnt; + + /* HDMI PHY */ + u8 phy_auto_rst; /* PHY_CTL2, default = 0 */ + u8 hdmi_det_v; /* HDMI_DET, default = 0 */ + u8 h_pi_rst; /* HV_RST, default = 0 */ + u8 v_pi_rst; /* HV_RST, default = 0 */ +}; + +enum tc358743_cable_connection { + TC358743_CABLE_PLUGGED, + TC358743_CABLE_UNPLUGGED, +}; + +/* notify events */ +#define TC358743_FMT_CHANGE 1 + +/* ioctls */ +#define TC358743_CSI_RESET _IO('a', 1) + +/* custom controls */ +#define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0) +#define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1) + +#endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index d2b1e71760b22a..6b7b74b46f2901 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -168,6 +168,10 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_SAA7134_BASE (V4L2_CID_USER_BASE + 0x1060) +/* The base for the tc358743 driver controls. + * We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ From f171a983c439de6ac6e9d1f6bae798e3a6754100 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:46 +0200 Subject: [PATCH 0436/1983] tc358743: register v4l2 asynchronous subdevice Add support for registering the sensor subdevice using the v4l2-async API. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a86cbe0c36233e..dfc10f0d291bf9 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1711,6 +1711,16 @@ static int tc358743_probe(struct i2c_client *client, goto err_hdl; } + state->pad.flags = MEDIA_PAD_FL_SOURCE; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err < 0) + goto err_hdl; + + sd->dev = &client->dev; + err = v4l2_async_register_subdev(sd); + if (err < 0) + goto err_hdl; + INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, tc358743_delayed_work_enable_hotplug); @@ -1731,6 +1741,7 @@ static int tc358743_probe(struct i2c_client *client, return 0; err_hdl: + media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(&state->hdl); return err; } @@ -1742,6 +1753,7 @@ static int tc358743_remove(struct i2c_client *client) cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); + v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&state->hdl); From 737f108f7b8af7144fdbfdca61e20add72ec45e6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:47 +0200 Subject: [PATCH 0437/1983] tc358743: support probe from device tree Add support for probing the TC358743 subdevice from device tree. The reference clock must be supplied using the common clock bindings. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 133 +++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index dfc10f0d291bf9..85a0f7a99629b7 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -29,7 +29,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -84,6 +86,8 @@ struct tc358743_state { struct v4l2_dv_timings timings; u32 mbus_fmt_code; + + struct gpio_desc *reset_gpio; }; static inline struct tc358743_state *to_state(struct v4l2_subdev *sd) @@ -1628,12 +1632,126 @@ static const struct v4l2_ctrl_config tc358743_ctrl_audio_present = { .id = TC358743_CID_AUDIO_PRESENT, .name = "Audio present", .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, .def = 0, .flags = V4L2_CTRL_FLAG_READ_ONLY, }; /* --------------- PROBE / REMOVE --------------- */ +#if CONFIG_OF +static void tc358743_gpio_reset(struct tc358743_state *state) +{ + gpiod_set_value(state->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value(state->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value(state->reset_gpio, 0); + msleep(20); +} + +static int tc358743_probe_of(struct tc358743_state *state) +{ + struct device *dev = &state->i2c_client->dev; + struct device_node *np = dev->of_node; + struct clk *refclk; + u32 bps_pr_lane; + + refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(refclk)) { + if (PTR_ERR(refclk) != -EPROBE_DEFER) + dev_err(dev, "failed to get refclk: %ld\n", + PTR_ERR(refclk)); + return PTR_ERR(refclk); + } + + clk_prepare_enable(refclk); + + state->pdata.refclk_hz = clk_get_rate(refclk); + state->pdata.ddc5v_delay = DDC5V_DELAY_100MS; + state->pdata.enable_hdcp = false; + /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */ + state->pdata.fifo_level = 16; + /* + * The PLL input clock is obtained by dividing refclk by pll_prd. + * It must be between 6 MHz and 40 MHz, lower frequency is better. + */ + switch (state->pdata.refclk_hz) { + case 26000000: + case 27000000: + case 42000000: + state->pdata.pll_prd = state->pdata.refclk_hz / 6000000; + break; + default: + dev_err(dev, "unsupported refclk rate: %u Hz\n", + state->pdata.refclk_hz); + clk_disable_unprepare(refclk); + return -EINVAL; + } + + /* + * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. + * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. + */ + bps_pr_lane = 594000000U; + of_property_read_u32(np, "toshiba,bps-per-lane", &bps_pr_lane); + if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { + dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); + clk_disable_unprepare(refclk); + return -EINVAL; + } + state->pdata.bps_pr_lane = bps_pr_lane; + + /* The CSI speed per lane is refclk / pll_prd * pll_fbd */ + state->pdata.pll_fbd = state->pdata.bps_pr_lane / + state->pdata.refclk_hz * state->pdata.pll_prd; + bps_pr_lane = state->pdata.refclk_hz / state->pdata.pll_prd * + state->pdata.pll_fbd; + if (bps_pr_lane != state->pdata.bps_pr_lane) { + dev_warn(dev, "bps per lane corrected to %u bps\n", + bps_pr_lane); + state->pdata.bps_pr_lane = bps_pr_lane; + } + + /* FIXME: These timings are from REF_02 for 594 Mbps per lane */ + state->pdata.lineinitcnt = 0xe80; + state->pdata.lptxtimecnt = 0x003; + /* tclk-preparecnt: 3, tclk-zerocnt: 20 */ + state->pdata.tclk_headercnt = 0x1403; + state->pdata.tclk_trailcnt = 0x00; + /* ths-preparecnt: 3, ths-zerocnt: 1 */ + state->pdata.ths_headercnt = 0x0103; + state->pdata.twakeup = 0x4882; + state->pdata.tclk_postcnt = 0x008; + state->pdata.ths_trailcnt = 0x2; + state->pdata.hstxvregcnt = 0; + + /* HDMI PHY */ + state->pdata.phy_auto_rst = 0; + state->pdata.hdmi_det_v = 0; + state->pdata.h_pi_rst = 0; + state->pdata.v_pi_rst = 0; + + state->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(state->reset_gpio)) { + dev_err(dev, "failed to get reset gpio\n"); + clk_disable_unprepare(refclk); + return PTR_ERR(state->reset_gpio); + } + + tc358743_gpio_reset(state); + + return 0; +} +#else +static inline int tc358743_probe_of(struct tc358743_state *state) +{ + return -ENODEV; +} +#endif + static int tc358743_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1656,14 +1774,19 @@ static int tc358743_probe(struct i2c_client *client, return -ENOMEM; } + state->i2c_client = client; + /* platform data */ - if (!pdata) { - v4l_err(client, "No platform data!\n"); - return -ENODEV; + if (pdata) { + state->pdata = *pdata; + } else { + err = tc358743_probe_of(state); + if (err == -ENODEV) + v4l_err(client, "No platform data!\n"); + if (err) + return err; } - state->pdata = *pdata; - state->i2c_client = client; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &tc358743_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; From 9785befaa0df0c7113f5a8d2fb51e288b3af5b58 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:48 +0200 Subject: [PATCH 0438/1983] tc358743: fix set_pll to enable PLL with default frequency set_pll not only skips PLL changes but also doesn't enable it in the first place if the rate is the same as the default values. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 85a0f7a99629b7..dd2ea16ef8824e 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -606,6 +606,7 @@ static void tc358743_set_pll(struct v4l2_subdev *sd) struct tc358743_state *state = to_state(sd); struct tc358743_platform_data *pdata = &state->pdata; u16 pllctl0 = i2c_rd16(sd, PLLCTL0); + u16 pllctl1 = i2c_rd16(sd, PLLCTL1); u16 pllctl0_new = SET_PLL_PRD(pdata->pll_prd) | SET_PLL_FBD(pdata->pll_fbd); @@ -613,8 +614,8 @@ static void tc358743_set_pll(struct v4l2_subdev *sd) /* Only rewrite when needed, since rewriting triggers another format * change event. */ - if (pllctl0 != pllctl0_new) { - u32 hsck = (pdata->refclk_hz * pdata->pll_prd) / pdata->pll_fbd; + if ((pllctl0 != pllctl0_new) || ((pllctl1 & MASK_PLL_EN) == 0)) { + u32 hsck = (pdata->refclk_hz * pdata->pll_fbd) / pdata->pll_prd; u16 pll_frs; if (hsck > 500000000) From 6e7eeb085fc20eba2a9952e96ffa3bb65a27a212 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:49 +0200 Subject: [PATCH 0439/1983] tc358743: fix lane number calculation to include blanking Instead of only using the visible width and height, also add the horizontal and vertical blanking to calculate the bit rate. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index dd2ea16ef8824e..74e83c56a9f680 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -713,9 +713,11 @@ static unsigned tc358743_num_csi_lanes_needed(struct v4l2_subdev *sd) { struct tc358743_state *state = to_state(sd); struct v4l2_bt_timings *bt = &state->timings.bt; + u32 htotal = bt->width + bt->hfrontporch + bt->hsync + bt->hbackporch; + u32 vtotal = bt->height + bt->vfrontporch + bt->vsync + bt->vbackporch; u32 bits_pr_pixel = (state->mbus_fmt_code == MEDIA_BUS_FMT_UYVY8_1X16) ? 16 : 24; - u32 bps = bt->width * bt->height * fps(bt) * bits_pr_pixel; + u32 bps = htotal * vtotal * fps(bt) * bits_pr_pixel; return DIV_ROUND_UP(bps, state->pdata.bps_pr_lane); } From cae95ba6e1c8c62d0d5c6adab0d2b3cb5ebffd94 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:50 +0200 Subject: [PATCH 0440/1983] tc358743: split set_csi into set_csi and start_csi The CSI Tx can't be stopped except by pulling the CTx reset. Split the actual CSI start out of set_csi. This allows to call it later in s_stream, as the Synopsys Designware MIPI CSI-2 Host Controller needs to start with the lanes in stop state before it can sync its PLL to the clock lane. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 74e83c56a9f680..34acfed1cf7680 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -759,6 +759,14 @@ static void tc358743_set_csi(struct v4l2_subdev *sd) ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); +} + +static void tc358743_start_csi(struct v4l2_subdev *sd) +{ + unsigned lanes = tc358743_num_csi_lanes_needed(sd); + + v4l2_dbg(3, debug, sd, "%s:\n", __func__); + i2c_wr32(sd, STARTCNTRL, MASK_START); i2c_wr32(sd, CSI_START, MASK_STRT); @@ -1177,6 +1185,7 @@ static long tc358743_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) i2c_wr16(sd, CONFCTL, confctl & (~(MASK_VBUFEN | MASK_ABUFEN))); tc358743_set_csi(sd); + tc358743_start_csi(sd); i2c_wr16(sd, CONFCTL, confctl | (MASK_VBUFEN | MASK_ABUFEN)); return 0; @@ -1505,7 +1514,11 @@ static int tc358743_g_mbus_config(struct v4l2_subdev *sd, static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) { + if (enable) + tc358743_start_csi(sd); enable_stream(sd, enable); + if (!enable) + tc358743_set_csi(sd); return 0; } From 87db642a6b4a04237bfc58ce7bc05b5c1e2d4cf9 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:51 +0200 Subject: [PATCH 0441/1983] tc358743: also set TCLK_TRAILCNT and TCLK_POSTCNT Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 34acfed1cf7680..a510a147d923a4 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -749,6 +749,8 @@ static void tc358743_set_csi(struct v4l2_subdev *sd) i2c_wr32(sd, THS_HEADERCNT, pdata->ths_headercnt); i2c_wr32(sd, TWAKEUP, pdata->twakeup); i2c_wr32(sd, THS_TRAILCNT, pdata->ths_trailcnt); + i2c_wr32(sd, TCLK_TRAILCNT, pdata->tclk_trailcnt); + i2c_wr32(sd, TCLK_POSTCNT, pdata->tclk_postcnt); i2c_wr32(sd, HSTXVREGCNT, pdata->hstxvregcnt); i2c_wr32(sd, HSTXVREGEN, From 8e37914f258ab2ec4d75a3ca3e621d3315d7ccd9 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:52 +0200 Subject: [PATCH 0442/1983] tc358743: parse MIPI CSI-2 endpoint, support noncontinuous clock Parse the MIPI CSI-2 specific properties in the OF graph endpoint and add support for the 'clock-noncontinuous' property. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a510a147d923a4..e1059cc161a127 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "tc358743_regs.h" @@ -67,6 +68,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = { struct tc358743_state { struct tc358743_platform_data pdata; + struct v4l2_of_bus_mipi_csi2 bus; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; @@ -760,7 +762,8 @@ static void tc358743_set_csi(struct v4l2_subdev *sd) ((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) | ((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0)); - i2c_wr32(sd, TXOPTIONCNTRL, MASK_CONTCLKMODE); + i2c_wr32(sd, TXOPTIONCNTRL, (state->bus.flags & + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) ? MASK_CONTCLKMODE : 0); } static void tc358743_start_csi(struct v4l2_subdev *sd) @@ -1674,6 +1677,7 @@ static int tc358743_probe_of(struct tc358743_state *state) { struct device *dev = &state->i2c_client->dev; struct device_node *np = dev->of_node; + struct device_node *ep; struct clk *refclk; u32 bps_pr_lane; @@ -1685,6 +1689,23 @@ static int tc358743_probe_of(struct tc358743_state *state) return PTR_ERR(refclk); } + ep = of_graph_get_next_endpoint(dev->of_node, NULL); + if (ep) { + struct v4l2_of_endpoint endpoint; + + v4l2_of_parse_endpoint(ep, &endpoint); + if (endpoint.bus_type != V4L2_MBUS_CSI2 || + endpoint.bus.mipi_csi2.num_data_lanes == 0) { + dev_err(dev, "missing CSI-2 properties in endpoint\n"); + return -EINVAL; + } + + state->bus = endpoint.bus.mipi_csi2; + } else { + dev_err(dev, "missing endpoint node\n"); + return -EINVAL; + } + clk_prepare_enable(refclk); state->pdata.refclk_hz = clk_get_rate(refclk); From 2fa42fe03dfce89616ebb610ec88d53268b058b5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:53 +0200 Subject: [PATCH 0443/1983] tc358743: add direct interrupt handling When probed from device tree, the i2c client driver can handle the interrupt on its own. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index e1059cc161a127..2cf97d9adb592d 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -891,7 +892,7 @@ static void tc358743_initial_setup(struct v4l2_subdev *sd) /* --------------- IRQ --------------- */ -static void tc358743_enable_interrupts(struct v4l2_subdev *sd) +static void tc358743_clear_interrupt_status(struct v4l2_subdev *sd) { u16 i; @@ -900,6 +901,11 @@ static void tc358743_enable_interrupts(struct v4l2_subdev *sd) i2c_wr8(sd, i, 0xff); i2c_wr16(sd, INTSTATUS, 0xffff); +} + +static void tc358743_enable_interrupts(struct v4l2_subdev *sd) +{ + tc358743_clear_interrupt_status(sd); /* enable interrupts */ i2c_wr8(sd, SYS_INTM, ~(MASK_M_DDC | MASK_M_HDMI_DET) & 0xff); @@ -1322,6 +1328,16 @@ static int tc358743_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } +static irqreturn_t tc358743_irq_handler(int irq, void *dev_id) +{ + struct tc358743_state *state = dev_id; + bool handled; + + tc358743_isr(&state->sd, 0, &handled); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + /* --------------- VIDEO OPS --------------- */ static int tc358743_g_input_status(struct v4l2_subdev *sd, u32 *status) @@ -1838,6 +1854,17 @@ static int tc358743_probe(struct i2c_client *client, v4l2_info(sd, "not a TC358743 on address 0x%x\n", client->addr << 1); return -ENODEV; + + tc358743_clear_interrupt_status(sd); + + if (state->i2c_client->irq) { + err = devm_request_threaded_irq(&client->dev, + state->i2c_client->irq, + NULL, tc358743_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "tc358743", state); + if (err) + return err; } /* control handlers */ From bdf8d05bfa6e990027398a383fbd6d76354dc295 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:54 +0200 Subject: [PATCH 0444/1983] tc358743: detect chip by ChipID instead of IntMask When resetting the CPU instead of power cycling, IntMask is still set from last boot. Instead of depending on it to be set to its reset defaults, check the ChipID register and rewrite IntMask if needed. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 2cf97d9adb592d..02b131b88b2ff0 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1847,13 +1847,19 @@ static int tc358743_probe(struct i2c_client *client, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* i2c access */ + if (i2c_rd16(sd, CHIPID) != 0x0000) { + v4l2_info(sd, "not a TC358743 on address 0x%x\n", + client->addr << 1); + return -ENODEV; + } /* read the interrupt mask register, it should carry the * default values, as it hasn't been touched at this point. */ if (i2c_rd16(sd, INTMASK) != 0x0400) { - v4l2_info(sd, "not a TC358743 on address 0x%x\n", - client->addr << 1); - return -ENODEV; + v4l2_warn(sd, "initial interrupt mask: 0x%04x\n", + i2c_rd16(sd, INTMASK)); + i2c_wr16(sd, INTMASK, 0x0400); + } tc358743_clear_interrupt_status(sd); From 591f2796e1c8927cb4a0c28c2a03969193ef55ab Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:55 +0200 Subject: [PATCH 0445/1983] tc358743: don't return E2BIG from G_EDID E2BIG is meant to be returned by S_EDID if userspace provided more data than the hardware can handle. If userspace requested too much data with G_EDID, we should silently correct the number of EDID blocks downwards. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 02b131b88b2ff0..7d70acc402f750 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1560,10 +1560,11 @@ static int tc358743_g_edid(struct v4l2_subdev *sd, if (edid->blocks == 0) return -EINVAL; - if (edid->start_block + edid->blocks > 8) { - edid->blocks = 8; - return -E2BIG; - } + if (edid->start_block >= 8) + return -EINVAL; + + if (edid->start_block + edid->blocks > 8) + edid->blocks = 8 - edid->start_block; if (!edid->edid) return -EINVAL; From 86d92727f9650a72a545979059073d50c40fac28 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 30 Mar 2015 13:10:56 +0200 Subject: [PATCH 0446/1983] tc358743: allow event subscription This is useful to subscribe to HDMI hotplug events via the V4L2_CID_DV_RX_POWER_PRESENT control. Signed-off-by: Philipp Zabel --- drivers/media/i2c/tc358743.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 7d70acc402f750..aec44570e1bd4e 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -1627,6 +1628,8 @@ static const struct v4l2_subdev_core_ops tc358743_core_ops = { .s_register = tc358743_s_register, #endif .interrupt_service_routine = tc358743_isr, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops tc358743_video_ops = { From e223319cd5fa16052dcdd7882357d5da0d9e5d7b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 12 Jul 2015 17:47:16 +0200 Subject: [PATCH 0447/1983] [PATCH] media: tc358743: adapt driver to 3.14 kernel apis This patch was initially for a later kernel. Change it to use the structure available in 3.14. --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/tc358743.c | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 98988238b16757..8abd4cd93efa62 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -296,6 +296,7 @@ config VIDEO_SAA7191 config VIDEO_TC358743 tristate "Toshiba TC358743 decoder" depends on VIDEO_V4L2 && I2C + select MEDIA_CONTROLLER ---help--- Support for the Toshiba TC358743 HDMI to MIPI CSI-2 bridge diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index aec44570e1bd4e..5214fc8a955e42 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1465,9 +1465,6 @@ static int tc358743_g_dv_timings(struct v4l2_subdev *sd, static int tc358743_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - if (timings->pad != 0) - return -EINVAL; - return v4l2_enum_dv_timings_cap(timings, &tc358743_timings_cap, NULL, NULL); } @@ -1497,9 +1494,6 @@ static int tc358743_query_dv_timings(struct v4l2_subdev *sd, static int tc358743_dv_timings_cap(struct v4l2_subdev *sd, struct v4l2_dv_timings_cap *cap) { - if (cap->pad != 0) - return -EINVAL; - *cap = tc358743_timings_cap; return 0; @@ -1639,6 +1633,8 @@ static const struct v4l2_subdev_video_ops tc358743_video_ops = { .query_dv_timings = tc358743_query_dv_timings, .g_mbus_config = tc358743_g_mbus_config, .s_stream = tc358743_s_stream, + .enum_dv_timings = tc358743_enum_dv_timings, + .dv_timings_cap = tc358743_dv_timings_cap, }; static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { @@ -1646,8 +1642,6 @@ static const struct v4l2_subdev_pad_ops tc358743_pad_ops = { .get_fmt = tc358743_get_fmt, .get_edid = tc358743_g_edid, .set_edid = tc358743_s_edid, - .enum_dv_timings = tc358743_enum_dv_timings, - .dv_timings_cap = tc358743_dv_timings_cap, }; static const struct v4l2_subdev_ops tc358743_ops = { @@ -1709,7 +1703,7 @@ static int tc358743_probe_of(struct tc358743_state *state) return PTR_ERR(refclk); } - ep = of_graph_get_next_endpoint(dev->of_node, NULL); + ep = v4l2_of_get_next_endpoint(dev->of_node, NULL); if (ep) { struct v4l2_of_endpoint endpoint; From 156823d4ac5b6adf2f6a39563f398d5b202e0447 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 13 Jul 2015 19:14:07 +0200 Subject: [PATCH 0448/1983] tc358743: make reset_gpio optional There are some board designs that do not have the reset_gpio wired up. Only try to reset the gpios if the option has been set in device-tree. --- drivers/media/i2c/tc358743.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 5214fc8a955e42..ae596a2ccc2c27 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1788,13 +1788,12 @@ static int tc358743_probe_of(struct tc358743_state *state) state->pdata.v_pi_rst = 0; state->reset_gpio = devm_gpiod_get(dev, "reset"); - if (IS_ERR(state->reset_gpio)) { - dev_err(dev, "failed to get reset gpio\n"); - clk_disable_unprepare(refclk); - return PTR_ERR(state->reset_gpio); + if (!IS_ERR(state->reset_gpio)) { + tc358743_gpio_reset(state); + } else { + dev_warn(dev, "failed to get reset gpio\n"); } - tc358743_gpio_reset(state); return 0; } From 86b51359a02e7a2549358d776673870e74b42540 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 25 Nov 2014 16:15:32 +0000 Subject: [PATCH 0449/1983] irqchip: gic: Optimize locking in gic_raise_softirq Currently gic_raise_softirq() is locked using upon irq_controller_lock. This lock is primarily used to make register read-modify-write sequences atomic but gic_raise_softirq() uses it instead to ensure that the big.LITTLE migration logic can figure out when it is safe to migrate interrupts between physical cores. This is sub-optimal in closely related ways: 1. No locking at all is required on systems where the b.L switcher is not configured. 2. Finer grain locking can be used on systems where the b.L switcher is present. This patch resolves both of the above by introducing a separate finer grain lock and providing conditionally compiled inlines to lock/unlock it. Signed-off-by: Daniel Thompson Cc: Thomas Gleixner Cc: Jason Cooper Cc: Russell King Cc: Marc Zyngier Acked-by: Nicolas Pitre --- drivers/irqchip/irq-gic.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 12698ee9e06b8d..673d4a59083cef 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -73,6 +73,27 @@ struct gic_chip_data { static DEFINE_RAW_SPINLOCK(irq_controller_lock); +/* + * This lock is used by the big.LITTLE migration code to ensure no IPIs + * can be pended on the old core after the map has been updated. + */ +#ifdef CONFIG_BL_SWITCHER +static DEFINE_RAW_SPINLOCK(cpu_map_migration_lock); + +static inline void gic_migration_lock(unsigned long *flags) +{ + raw_spin_lock_irqsave(&cpu_map_migration_lock, *flags); +} + +static inline void gic_migration_unlock(unsigned long flags) +{ + raw_spin_unlock_irqrestore(&cpu_map_migration_lock, flags); +} +#else +static inline void gic_migration_lock(unsigned long *flags) {} +static inline void gic_migration_unlock(unsigned long flags) {} +#endif + /* * The GIC mapping of CPU interfaces does not necessarily match * the logical CPU numbering. Let's use a mapping as returned @@ -658,7 +679,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) int cpu; unsigned long flags, map = 0; - raw_spin_lock_irqsave(&irq_controller_lock, flags); + gic_migration_lock(&flags); /* Convert our logical CPU mask into a physical one. */ for_each_cpu(cpu, mask) @@ -673,7 +694,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) /* this always happens on GIC0 */ writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); - raw_spin_unlock_irqrestore(&irq_controller_lock, flags); + gic_migration_unlock(flags); } #endif @@ -744,8 +765,17 @@ void gic_migrate_target(unsigned int new_cpu_id) raw_spin_lock(&irq_controller_lock); - /* Update the target interface for this logical CPU */ + /* + * Update the target interface for this logical CPU + * + * From the point we release the cpu_map_migration_lock any new + * SGIs will be pended on the new cpu which makes the set of SGIs + * pending on the old cpu static. That means we can defer the + * migration until after we have released the irq_controller_lock. + */ + raw_spin_lock(&cpu_map_migration_lock); gic_cpu_map[cpu] = 1 << new_cpu_id; + raw_spin_unlock(&cpu_map_migration_lock); /* * Find all the peripheral interrupts targetting the current From e9f78603b53f52ba1b3e4fe61667e01893882f91 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 15 Jul 2015 06:50:53 +0200 Subject: [PATCH 0450/1983] media: mxc: Fix clashing symbols between _mipi devices. Both mipi sensors couldn't be run at the same time due to a common symbol. Ideally this should be moved out into common code, but for now just name the symbol local to the driver. --- drivers/media/platform/mxc/capture/ov5640_mipi.c | 4 ++-- drivers/media/platform/mxc/capture/ov5647_mipi.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/mxc/capture/ov5640_mipi.c b/drivers/media/platform/mxc/capture/ov5640_mipi.c index e2f3511fa4a77a..1617d3e5a33f30 100644 --- a/drivers/media/platform/mxc/capture/ov5640_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5640_mipi.c @@ -1103,7 +1103,7 @@ void OV5640_turn_on_AE_AG(int enable) ov5640_write_reg(0x3503, ae_ag_ctrl); } -bool binning_on(void) +bool ov5640_binning_on(void) { u8 temp; ov5640_read_reg(0x3821, &temp); @@ -1199,7 +1199,7 @@ static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate, /* read preview shutter */ prev_shutter = OV5640_get_shutter(); - if ((binning_on()) && (mode != ov5640_mode_720P_1280_720) + if ((ov5640_binning_on()) && (mode != ov5640_mode_720P_1280_720) && (mode != ov5640_mode_1080P_1920_1080)) prev_shutter *= 2; diff --git a/drivers/media/platform/mxc/capture/ov5647_mipi.c b/drivers/media/platform/mxc/capture/ov5647_mipi.c index 4b5d6f2ebe10a2..aca04083f84d1a 100644 --- a/drivers/media/platform/mxc/capture/ov5647_mipi.c +++ b/drivers/media/platform/mxc/capture/ov5647_mipi.c @@ -2596,7 +2596,7 @@ void OV5647_turn_on_AE_AG(int enable) ov5647_write_reg(0x3503, ae_ag_ctrl); } -bool binning_on(void) +bool ov5647_binning_on(void) { u8 temp; ov5647_read_reg(0x3821, &temp); @@ -2691,7 +2691,7 @@ static int ov5647_change_mode_exposure_calc(enum ov5647_frame_rate frame_rate, /* read preview shutter */ prev_shutter = OV5647_get_shutter(); - if ((binning_on()) && (mode != ov5647_mode_960P_1280_960) && + if ((ov5647_binning_on()) && (mode != ov5647_mode_960P_1280_960) && (mode != ov5647_mode_720P_1280_720) && (mode != ov5647_mode_1080P_1920_1080)) prev_shutter *= 2; From 13a2fb66a35253f2b716c99fd1c89360fd65bb60 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 15 Jul 2015 06:53:21 +0200 Subject: [PATCH 0451/1983] dts: hummingboard2: Add the device-tree support for the HB2 board This is the initial commit to support Solid Run's HummingBoard v2. --- arch/arm/boot/dts/Makefile | 2 + arch/arm/boot/dts/imx6dl-hummingboard2.dts | 52 ++ arch/arm/boot/dts/imx6q-hummingboard2.dts | 60 ++ arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi | 649 +++++++++++++++++++ 4 files changed, 763 insertions(+) create mode 100644 arch/arm/boot/dts/imx6dl-hummingboard2.dts create mode 100644 arch/arm/boot/dts/imx6q-hummingboard2.dts create mode 100644 arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 5047b94a89d41c..c0119d62785c58 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -155,6 +155,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx53-smd.dtb \ imx6dl-cubox-i.dtb \ imx6dl-hummingboard.dtb \ + imx6dl-hummingboard2.dtb \ imx6dl-sabreauto.dtb \ imx6dl-sabreauto-flexcan1.dtb \ imx6dl-sabreauto-ecspi.dtb \ @@ -168,6 +169,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6q-arm2.dtb \ imx6q-cubox-i.dtb \ imx6q-hummingboard.dtb \ + imx6q-hummingboard2.dtb \ imx6q-phytec-pbab01.dtb \ imx6q-sabreauto.dtb \ imx6q-sabreauto-flexcan1.dtb \ diff --git a/arch/arm/boot/dts/imx6dl-hummingboard2.dts b/arch/arm/boot/dts/imx6dl-hummingboard2.dts new file mode 100644 index 00000000000000..990b5050de5b7a --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-hummingboard2.dts @@ -0,0 +1,52 @@ +/* + * Device Tree file for SolidRun HummingBoard2 + * Copyright (C) 2015 Rabeeh Khoury + * Based on work by Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. + */ +/dts-v1/; + +#include "imx6dl.dtsi" +#include "imx6qdl-hummingboard2.dtsi" + +/ { + model = "SolidRun HummingBoard2 Solo/DualLite"; + compatible = "solidrun,hummingboard2/dl", "fsl,imx6dl"; +}; diff --git a/arch/arm/boot/dts/imx6q-hummingboard2.dts b/arch/arm/boot/dts/imx6q-hummingboard2.dts new file mode 100644 index 00000000000000..f5eec9163bb876 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-hummingboard2.dts @@ -0,0 +1,60 @@ +/* + * Device Tree file for SolidRun HummingBoard2 + * Copyright (C) 2015 Rabeeh Khoury + * Based on work by Russell King + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. + */ +/dts-v1/; + +#include "imx6q.dtsi" +#include "imx6qdl-hummingboard2.dtsi" + +/ { + model = "SolidRun HummingBoard2 Dual/Quad"; + compatible = "solidrun,hummingboard2/q", "fsl,imx6q"; +}; + +&sata { + status = "okay"; + fsl,transmit-level-mV = <1104>; + fsl,transmit-boost-mdB = <0>; + fsl,transmit-atten-16ths = <9>; + fsl,no-spread-spectrum; +}; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi new file mode 100644 index 00000000000000..650f5b2054cf37 --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi @@ -0,0 +1,649 @@ +/* + * Device Tree file for SolidRun HummingBoard2 + * Copyright (C) 2015 Rabeeh Khoury + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) 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. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. 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. + */ +#include "imx6qdl-microsom.dtsi" +#include "imx6qdl-microsom-ar8035.dtsi" + +/ { + aliases { + mmc0 = &usdhc2; + mmc1 = &usdhc1; + mxcfb0 = &mxcfb1; + }; + + chosen { + bootargs = "quiet console=ttymxc0,115200 root=/dev/mmcblk0p2 rw"; + stdout-path = &uart1; + }; + + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio7 9 1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_gpio7_9>; + linux,rc-map-name = "rc-rc6-mce"; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + + regulators { + compatible = "simple-bus"; + + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + reg_1p8v: 1p8v { + compatible = "regulator-fixed"; + regulator-name = "1P8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + reg_usbh1_vbus: usb-h1-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio1 0 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_usbh1_vbus>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_usbotg_vbus: usb-otg-vbus { + compatible = "regulator-fixed"; + enable-active-high; + gpio = <&gpio3 22 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_usbotg_vbus>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + + reg_usbh2_vbus: usb-h2-vbus { + compatible = "regulator-gpio"; + enable-active-high; + enable-gpio = <&gpio2 13 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_usbh2_vbus>; + regulator-name = "usb_h2_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + }; + + reg_usbh3_vbus: usb-h3-vbus { + compatible = "regulator-gpio"; + enable-active-high; + enable-gpio = <&gpio7 10 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_usbh3_vbus>; + regulator-name = "usb_h3_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + }; + + reg_usdhc2_vbus: usdhc-2-vbus { + compatible = "regulator-fixed"; + regulator-name = "USDHC2-VBUS"; + gpio = <&gpio4 30 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_usdhc2_pwr>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; + + sound-sgtl5000 { + audio-codec = <&sgtl5000>; + audio-routing = + "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + compatible = "fsl,imx-audio-sgtl5000"; + model = "On-board Codec"; + mux-ext-port = <5>; + mux-int-port = <1>; + cpu-dai = <&ssi1>; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + mipi_camera = <1>; + default_input = <0>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; +}; + +&audmux { + status = "okay"; +}; + +&ecspi2 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio2 26 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_ecspi2>; + status = "okay"; + + spidev0: spi@0 { + compatible = "spidev"; + reg = <0>; + spi-max-frequency = <20000000>; + }; +}; + +&gpc { + fsl,cpu_pupscr_sw2iso = <0xf>; + fsl,cpu_pupscr_sw = <0xf>; + fsl,cpu_pdnscr_iso2sw = <0x1>; + fsl,cpu_pdnscr_iso = <0x1>; +}; + +&hdmi_core { + ipu_id = <0>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_hdmi>; + status = "okay"; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_i2c1>; + status = "okay"; + + ov5640_mipi: ov5640_mipi@3c { + compatible = "ovti,ov5640_mipi"; + reg = <0x3c>; + clocks = <&clks IMX6QDL_CLK_CKO2>; + clock-names = "csi_mclk"; +/* + DOVDD-supply = <®_3p3v>; + AVDD-supply = <®_3p3v>; + DVDD-supply = <®_3p3v>; +*/ + pwn-gpios = <&gpio4 14 GPIO_ACTIVE_LOW>; + rst-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>; + ipu_id = <0>; + csi_id = <1>; + mclk = <24000000>; + mclk_source = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_mipi>; + extended-buffer; + }; + + rtc: pcf8523@68 { + compatible = "nxp,pcf8523"; + reg = <0x68>; + nxp,12p5_pf; + }; + + sgtl5000: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clocks = <&clks IMX6QDL_CLK_CKO>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_sgtl5000>; + VDDA-supply = <®_3p3v>; + VDDIO-supply = <®_3p3v>; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_i2c2>; + status = "okay"; + + ddc: imx6_hdmi_i2c@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_i2c3>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + hummingboard2 { + pinctrl_hog: hoggrp { + fsl,pins = < + /* + * 36 pin headers GPIO description. The pins + * numbering as following - + * + * 3.2v 5v 74 75 + * 73 72 71 70 + * 69 68 67 66 + * + * 77 78 79 76 + * 65 64 61 60 + * 53 52 51 50 + * 49 48 166 132 + * 95 94 90 91 + * GND 54 24 204 + * + * The GPIO numbers can be extracted using + * signal name from below. + * Example - + * MX6QDL_PAD_EIM_DA10__GPIO3_IO10 is + * GPIO(3,10) which is (3-1)*32+10 = gpio 74 + * + * i.e. The mapping of GPIO(X,Y) to Linux gpio + * number is : gpio number = (X-1) * 32 + Y + */ + /* DI1_PIN15 */ + MX6QDL_PAD_EIM_DA10__GPIO3_IO10 0x400130b1 + /* DI1_PIN02 */ + MX6QDL_PAD_EIM_DA11__GPIO3_IO11 0x400130b1 + /* DISP1_DATA00 */ + MX6QDL_PAD_EIM_DA9__GPIO3_IO09 0x400130b1 + /* DISP1_DATA01 */ + MX6QDL_PAD_EIM_DA8__GPIO3_IO08 0x400130b1 + /* DISP1_DATA02 */ + MX6QDL_PAD_EIM_DA7__GPIO3_IO07 0x400130b1 + /* DISP1_DATA03 */ + MX6QDL_PAD_EIM_DA6__GPIO3_IO06 0x400130b1 + /* DISP1_DATA04 */ + MX6QDL_PAD_EIM_DA5__GPIO3_IO05 0x400130b1 + /* DISP1_DATA05 */ + MX6QDL_PAD_EIM_DA4__GPIO3_IO04 0x400130b1 + /* DISP1_DATA06 */ + MX6QDL_PAD_EIM_DA3__GPIO3_IO03 0x400130b1 + /* DISP1_DATA07 */ + MX6QDL_PAD_EIM_DA2__GPIO3_IO02 0x400130b1 + /* DI1_D0_CS */ + MX6QDL_PAD_EIM_DA13__GPIO3_IO13 0x400130b1 + /* DI1_D1_CS */ + MX6QDL_PAD_EIM_DA14__GPIO3_IO14 0x400130b1 + /* DI1_PIN01 */ + MX6QDL_PAD_EIM_DA15__GPIO3_IO15 0x400130b1 + /* DI1_PIN03 */ + MX6QDL_PAD_EIM_DA12__GPIO3_IO12 0x400130b1 + /* DISP1_DATA08 */ + MX6QDL_PAD_EIM_DA1__GPIO3_IO01 0x400130b1 + /* DISP1_DATA09 */ + MX6QDL_PAD_EIM_DA0__GPIO3_IO00 0x400130b1 + /* DISP1_DATA10 */ + MX6QDL_PAD_EIM_EB1__GPIO2_IO29 0x400130b1 + /* DISP1_DATA11 */ + MX6QDL_PAD_EIM_EB0__GPIO2_IO28 0x400130b1 + /* DISP1_DATA12 */ + MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x400130b1 + /* DISP1_DATA13 */ + MX6QDL_PAD_EIM_A18__GPIO2_IO20 0x400130b1 + /* DISP1_DATA14 */ + MX6QDL_PAD_EIM_A19__GPIO2_IO19 0x400130b1 + /* DISP1_DATA15 */ + MX6QDL_PAD_EIM_A20__GPIO2_IO18 0x400130b1 + /* DISP1_DATA16 */ + MX6QDL_PAD_EIM_A21__GPIO2_IO17 0x400130b1 + /* DISP1_DATA17 */ + MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x400130b1 + /* DISP1_DATA18 */ + MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x400130b1 + /* DISP1_DATA19 */ + MX6QDL_PAD_EIM_A24__GPIO5_IO04 0x400130b1 + /* DISP1_DATA20 */ + MX6QDL_PAD_EIM_D31__GPIO3_IO31 0x400130b1 + /* DISP1_DATA21 */ + MX6QDL_PAD_EIM_D30__GPIO3_IO30 0x400130b1 + /* DISP1_DATA22 */ + MX6QDL_PAD_EIM_D26__GPIO3_IO26 0x400130b1 + /* DISP1_DATA23 */ + MX6QDL_PAD_EIM_D27__GPIO3_IO27 0x400130b1 + /* DI1_DISP_CLK */ + MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x400130b1 + /* SPDIF_IN */ + MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24 0x400130b1 + /* SPDIF_OUT */ + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x400130b1 + + /* MikroBUS GPIO pin number 10 */ + MX6QDL_PAD_EIM_LBA__GPIO2_IO27 0x400130b1 + >; + }; + + pinctrl_hummingboard2_gpio7_9: hummingboard2-gpio7_9 { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__GPIO7_IO09 0x80000000 + >; + }; + + pinctrl_hummingboard2_hdmi: hummingboard2-hdmi { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_hummingboard2_i2c1: hummingboard2-i2c1 { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + pinctrl_hummingboard2_i2c2: hummingboard2-i2c2 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_hummingboard2_i2c3: hummingboard2-i2c3 { + fsl,pins = < + MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_hummingboard2_mipi: hummingboard2_mipi { + fsl,pins = < + MX6QDL_PAD_SD4_DAT2__GPIO2_IO10 0x4001b8b1 + MX6QDL_PAD_KEY_COL4__GPIO4_IO14 0x4001b8b1 + MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0 + >; + }; + + pinctrl_hummingboard2_pcie_reset: hummingboard2-pcie-reset { + fsl,pins = < + MX6QDL_PAD_SD4_DAT3__GPIO2_IO11 0x1b0b1 + >; + }; + + pinctrl_hummingboard2_pwm1: pwm1grp { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT8__PWM1_OUT 0x1b0b1 + >; + }; + + pinctrl_hummingboard2_sgtl5000: hummingboard2-sgtl5000 { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT19__AUD5_RXD 0x130b0 + MX6QDL_PAD_KEY_COL0__AUD5_TXC 0x130b0 + MX6QDL_PAD_KEY_ROW0__AUD5_TXD 0x110b0 + MX6QDL_PAD_KEY_COL1__AUD5_TXFS 0x130b0 + MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x130b0 + >; + }; + + pinctrl_hummingboard2_usbh1_vbus: hummingboard2-usbh1-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard2_usbh2_vbus: hummingboard2-usbh2-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard2_usbh3_vbus: hummingboard2-usbh3-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard2_usbotg_id: hummingboard2-usbotg-id { + /* + * Similar to pinctrl_usbotg_2, but we want it + * pulled down for a fixed host connection. + */ + fsl,pins = ; + }; + + pinctrl_hummingboard2_usbotg_vbus: hummingboard2-usbotg-vbus { + fsl,pins = ; + }; + + pinctrl_hummingboard2_usdhc2_pwr: hummingboard2-usdhc2-pwr { + fsl,pins = ; + }; + + pinctrl_hummingboard2_usdhc2_aux: hummingboard2-usdhc2-aux { + fsl,pins = < + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1f071 + MX6QDL_PAD_KEY_ROW1__SD2_VSELECT 0x1b071 + >; + }; + + pinctrl_hummingboard2_usdhc2: hummingboard2-usdhc2 { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 + >; + }; + + pinctrl_hummingboard2_usdhc2_100mhz: hummingboard2-usdhc2-100mhz { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130b9 + >; + }; + + pinctrl_hummingboard2_usdhc2_200mhz: hummingboard2-usdhc2-200mhz { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170f9 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100f9 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130f9 + >; + }; + + pinctrl_hummingboard2_usdhc3: hummingboard2-usdhc3 { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 + MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 + MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 + MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 + MX6QDL_PAD_SD3_RST__SD3_RESET 0x17059 + >; + }; + pinctrl_hummingboard2_uart3: hummingboard2-uart3 { + fsl,pins = < + MX6QDL_PAD_EIM_D25__UART3_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D24__UART3_RX_DATA 0x40013000 + >; + }; + pinctrl_hummingboard2_ecspi2: hummingboard2-ecspi2grp { + fsl,pins = < + MX6QDL_PAD_EIM_OE__ECSPI2_MISO 0x100b1 + MX6QDL_PAD_EIM_CS1__ECSPI2_MOSI 0x100b1 + MX6QDL_PAD_EIM_CS0__ECSPI2_SCLK 0x100b1 + MX6QDL_PAD_EIM_RW__GPIO2_IO26 0x000b1 /* CS */ + >; + }; + }; +}; + +&mipi_csi { + ipu_id = <0>; + csi_id = <1>; + v_channel = <0>; + lanes = <2>; + mipi_dphy_clk = <0x14>; + status = "okay"; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = < + &pinctrl_hummingboard2_pcie_reset + >; + reset-gpio = <&gpio2 11 0>; + status = "okay"; + no-msi; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_pwm1>; + status = "okay"; +}; + +&pwm3 { + status = "disabled"; +}; + +&pwm4 { + status = "disabled"; +}; + +&ssi1 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&usbh1 { + disable-over-current; + vbus-supply = <®_usbh1_vbus>; + status = "okay"; +}; + +&usbotg { + disable-over-current; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_usbotg_id>; + vbus-supply = <®_usbotg_vbus>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = < + &pinctrl_hummingboard2_usdhc2_aux + &pinctrl_hummingboard2_usdhc2 + >; + pinctrl-1 = < + &pinctrl_hummingboard2_usdhc2_aux + &pinctrl_hummingboard2_usdhc2_100mhz + >; + pinctrl-2 = < + &pinctrl_hummingboard2_usdhc2_aux + &pinctrl_hummingboard2_usdhc2_200mhz + >; + + vmmc-supply = <®_usdhc2_vbus>; + cd-gpios = <&gpio1 4 0>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = < + &pinctrl_hummingboard2_usdhc3 + >; + vmmc-supply = <®_3p3v>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hummingboard2_uart3>; + status = "okay"; +}; From 56bed787bef435462162fbfecb310146e26d88d2 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 16 Jul 2015 08:15:57 +0200 Subject: [PATCH 0452/1983] media: mxc: capture: don't report error on unknown or all standards If we receive a request for either unknown or all standards we don't need to report an error, these are valid settings that we handle with autodetection. --- .../media/platform/mxc/capture/mxc_v4l2_capture.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index 772d661250030f..b3e4d780fcddb2 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1592,17 +1592,26 @@ static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) static int mxc_v4l2_s_std(cam_data *cam, v4l2_std_id e) { pr_debug("%s: %Lx\n", __func__, e); - if (e == V4L2_STD_PAL) { + switch (e) { + case V4L2_STD_PAL: pr_debug(" Setting standard to PAL %Lx\n", V4L2_STD_PAL); cam->standard.id = V4L2_STD_PAL; video_index = TV_PAL; - } else if (e == V4L2_STD_NTSC) { + break; + case V4L2_STD_NTSC: pr_debug(" Setting standard to NTSC %Lx\n", V4L2_STD_NTSC); /* Get rid of the white dot line in NTSC signal input */ cam->standard.id = V4L2_STD_NTSC; video_index = TV_NTSC; - } else { + break; + case V4L2_STD_UNKNOWN: + case V4L2_STD_ALL: + /* auto-detect don't report an error */ + cam->standard.id = V4L2_STD_ALL; + video_index = TV_NOT_LOCKED; + break; + default: cam->standard.id = V4L2_STD_ALL; video_index = TV_NOT_LOCKED; pr_err("ERROR: unrecognized std! %Lx (PAL=%Lx, NTSC=%Lx\n", From 71c0a26a24b956c541697e51267f2e9ff9e13d0d Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 16 Jul 2015 08:40:54 +0200 Subject: [PATCH 0453/1983] mxc: mipi: Fix initial dphy setup. Don't check the return of of_property_read_u8 to see if we should use the default value, instead check if we received a clock back and if not then set the default value of 0x14. --- drivers/mxc/mipi/mxc_mipi_csi2.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mxc/mipi/mxc_mipi_csi2.c b/drivers/mxc/mipi/mxc_mipi_csi2.c index fc4f8ff0f0350b..36dc81e90f3a89 100644 --- a/drivers/mxc/mipi/mxc_mipi_csi2.c +++ b/drivers/mxc/mipi/mxc_mipi_csi2.c @@ -334,7 +334,6 @@ static int mipi_csi2_probe(struct platform_device *pdev) struct resource *res; u32 mipi_csi2_dphy_ver; int ret; - u8 clk; gmipi_csi2 = kmalloc(sizeof(struct mipi_csi2_info), GFP_KERNEL); if (!gmipi_csi2) { @@ -385,10 +384,10 @@ static int mipi_csi2_probe(struct platform_device *pdev) goto err; } - dphy_clk = 0x14; - ret = of_property_read_u8(np, "mipi_dphy_clk", &clk); - if (!ret) - dphy_clk = clk; + of_property_read_u8(np, "mipi_dphy_clk", &dphy_clk); + if (!dphy_clk) { + dphy_clk = 0x14; + } /* mipi dphy clk enable for register access */ clk_prepare_enable(gmipi_csi2->dphy_clk); From 9ad02f2af2e1118a9f5c6f75f92dea27c3580e12 Mon Sep 17 00:00:00 2001 From: Mark Salter Date: Mon, 7 Apr 2014 15:39:48 -0700 Subject: [PATCH 0454/1983] mm: create generic early_ioremap() support Upstream-commit: 9e5c33d7aeeef62e5fa7e74f94432685bd03026b This patch creates a generic implementation of early_ioremap() support based on the existing x86 implementation. early_ioremp() is useful for early boot code which needs to temporarily map I/O or memory regions before normal mapping functions such as ioremap() are available. Some architectures have optional MMU. In the no-MMU case, the remap functions simply return the passed in physical address and the unmap functions do nothing. Signed-off-by: Mark Salter Acked-by: Catalin Marinas Acked-by: H. Peter Anvin Cc: Borislav Petkov Cc: Dave Young Cc: Will Deacon Cc: Ingo Molnar Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: mm/Kconfig --- include/asm-generic/early_ioremap.h | 42 +++++ mm/Kconfig | 3 + mm/Makefile | 1 + mm/early_ioremap.c | 245 ++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 include/asm-generic/early_ioremap.h create mode 100644 mm/early_ioremap.c diff --git a/include/asm-generic/early_ioremap.h b/include/asm-generic/early_ioremap.h new file mode 100644 index 00000000000000..a5de55c04fb2ee --- /dev/null +++ b/include/asm-generic/early_ioremap.h @@ -0,0 +1,42 @@ +#ifndef _ASM_EARLY_IOREMAP_H_ +#define _ASM_EARLY_IOREMAP_H_ + +#include + +/* + * early_ioremap() and early_iounmap() are for temporary early boot-time + * mappings, before the real ioremap() is functional. + */ +extern void __iomem *early_ioremap(resource_size_t phys_addr, + unsigned long size); +extern void *early_memremap(resource_size_t phys_addr, + unsigned long size); +extern void early_iounmap(void __iomem *addr, unsigned long size); +extern void early_memunmap(void *addr, unsigned long size); + +/* + * Weak function called by early_ioremap_reset(). It does nothing, but + * architectures may provide their own version to do any needed cleanups. + */ +extern void early_ioremap_shutdown(void); + +#if defined(CONFIG_GENERIC_EARLY_IOREMAP) && defined(CONFIG_MMU) +/* Arch-specific initialization */ +extern void early_ioremap_init(void); + +/* Generic initialization called by architecture code */ +extern void early_ioremap_setup(void); + +/* + * Called as last step in paging_init() so library can act + * accordingly for subsequent map/unmap requests. + */ +extern void early_ioremap_reset(void); + +#else +static inline void early_ioremap_init(void) { } +static inline void early_ioremap_setup(void) { } +static inline void early_ioremap_reset(void) { } +#endif + +#endif /* _ASM_EARLY_IOREMAP_H_ */ diff --git a/mm/Kconfig b/mm/Kconfig index 0862816bb45592..515cfadafb0605 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -581,6 +581,9 @@ config PGTABLE_MAPPING You can check speed with zsmalloc benchmark: https://github.com/spartacus06/zsmapbench +config GENERIC_EARLY_IOREMAP + bool + config MAX_STACK_SIZE_MB int "Maximum user stack size for 32-bit processes (MB)" default 80 diff --git a/mm/Makefile b/mm/Makefile index c561f1f6bca017..3c21f087037d71 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_CLEANCACHE) += cleancache.o obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o obj-$(CONFIG_ZBUD) += zbud.o obj-$(CONFIG_ZSMALLOC) += zsmalloc.o +obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o diff --git a/mm/early_ioremap.c b/mm/early_ioremap.c new file mode 100644 index 00000000000000..e10ccd299d6666 --- /dev/null +++ b/mm/early_ioremap.c @@ -0,0 +1,245 @@ +/* + * Provide common bits of early_ioremap() support for architectures needing + * temporary mappings during boot before ioremap() is available. + * + * This is mostly a direct copy of the x86 early_ioremap implementation. + * + * (C) Copyright 1995 1996, 2014 Linus Torvalds + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MMU +static int early_ioremap_debug __initdata; + +static int __init early_ioremap_debug_setup(char *str) +{ + early_ioremap_debug = 1; + + return 0; +} +early_param("early_ioremap_debug", early_ioremap_debug_setup); + +static int after_paging_init __initdata; + +void __init __weak early_ioremap_shutdown(void) +{ +} + +void __init early_ioremap_reset(void) +{ + early_ioremap_shutdown(); + after_paging_init = 1; +} + +/* + * Generally, ioremap() is available after paging_init() has been called. + * Architectures wanting to allow early_ioremap after paging_init() can + * define __late_set_fixmap and __late_clear_fixmap to do the right thing. + */ +#ifndef __late_set_fixmap +static inline void __init __late_set_fixmap(enum fixed_addresses idx, + phys_addr_t phys, pgprot_t prot) +{ + BUG(); +} +#endif + +#ifndef __late_clear_fixmap +static inline void __init __late_clear_fixmap(enum fixed_addresses idx) +{ + BUG(); +} +#endif + +static void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata; +static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata; +static unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata; + +void __init early_ioremap_setup(void) +{ + int i; + + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) + if (WARN_ON(prev_map[i])) + break; + + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) + slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i); +} + +static int __init check_early_ioremap_leak(void) +{ + int count = 0; + int i; + + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) + if (prev_map[i]) + count++; + + if (WARN(count, KERN_WARNING + "Debug warning: early ioremap leak of %d areas detected.\n" + "please boot with early_ioremap_debug and report the dmesg.\n", + count)) + return 1; + return 0; +} +late_initcall(check_early_ioremap_leak); + +static void __init __iomem * +__early_ioremap(resource_size_t phys_addr, unsigned long size, pgprot_t prot) +{ + unsigned long offset; + resource_size_t last_addr; + unsigned int nrpages; + enum fixed_addresses idx; + int i, slot; + + WARN_ON(system_state != SYSTEM_BOOTING); + + slot = -1; + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (!prev_map[i]) { + slot = i; + break; + } + } + + if (WARN(slot < 0, "%s(%08llx, %08lx) not found slot\n", + __func__, (u64)phys_addr, size)) + return NULL; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (WARN_ON(!size || last_addr < phys_addr)) + return NULL; + + prev_size[slot] = size; + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr + 1) - phys_addr; + + /* + * Mappings have to fit in the FIX_BTMAP area. + */ + nrpages = size >> PAGE_SHIFT; + if (WARN_ON(nrpages > NR_FIX_BTMAPS)) + return NULL; + + /* + * Ok, go for it.. + */ + idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; + while (nrpages > 0) { + if (after_paging_init) + __late_set_fixmap(idx, phys_addr, prot); + else + __early_set_fixmap(idx, phys_addr, prot); + phys_addr += PAGE_SIZE; + --idx; + --nrpages; + } + WARN(early_ioremap_debug, "%s(%08llx, %08lx) [%d] => %08lx + %08lx\n", + __func__, (u64)phys_addr, size, slot, offset, slot_virt[slot]); + + prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]); + return prev_map[slot]; +} + +void __init early_iounmap(void __iomem *addr, unsigned long size) +{ + unsigned long virt_addr; + unsigned long offset; + unsigned int nrpages; + enum fixed_addresses idx; + int i, slot; + + slot = -1; + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (prev_map[i] == addr) { + slot = i; + break; + } + } + + if (WARN(slot < 0, "early_iounmap(%p, %08lx) not found slot\n", + addr, size)) + return; + + if (WARN(prev_size[slot] != size, + "early_iounmap(%p, %08lx) [%d] size not consistent %08lx\n", + addr, size, slot, prev_size[slot])) + return; + + WARN(early_ioremap_debug, "early_iounmap(%p, %08lx) [%d]\n", + addr, size, slot); + + virt_addr = (unsigned long)addr; + if (WARN_ON(virt_addr < fix_to_virt(FIX_BTMAP_BEGIN))) + return; + + offset = virt_addr & ~PAGE_MASK; + nrpages = PAGE_ALIGN(offset + size) >> PAGE_SHIFT; + + idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; + while (nrpages > 0) { + if (after_paging_init) + __late_clear_fixmap(idx); + else + __early_set_fixmap(idx, 0, FIXMAP_PAGE_CLEAR); + --idx; + --nrpages; + } + prev_map[slot] = NULL; +} + +/* Remap an IO device */ +void __init __iomem * +early_ioremap(resource_size_t phys_addr, unsigned long size) +{ + return __early_ioremap(phys_addr, size, FIXMAP_PAGE_IO); +} + +/* Remap memory */ +void __init * +early_memremap(resource_size_t phys_addr, unsigned long size) +{ + return (__force void *)__early_ioremap(phys_addr, size, + FIXMAP_PAGE_NORMAL); +} +#else /* CONFIG_MMU */ + +void __init __iomem * +early_ioremap(resource_size_t phys_addr, unsigned long size) +{ + return (__force void __iomem *)phys_addr; +} + +/* Remap memory */ +void __init * +early_memremap(resource_size_t phys_addr, unsigned long size) +{ + return (void *)phys_addr; +} + +void __init early_iounmap(void __iomem *addr, unsigned long size) +{ +} + +#endif /* CONFIG_MMU */ + + +void __init early_memunmap(void *addr, unsigned long size) +{ + early_iounmap((__force void __iomem *)addr, size); +} From f4a26100c5a0db05f429afa3ebd6300bb9daa037 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 25 Feb 2014 11:01:19 -0800 Subject: [PATCH 0455/1983] cma: Remove potential deadlock situation Upstream-commit: 7ee793a62fa8c544f8b844e6e87b2d8e8836b219 CMA locking is currently very coarse. The cma_mutex protects both the bitmap and avoids concurrency with alloc_contig_range. There are several situations which may result in a deadlock on the CMA mutex currently, mostly involving AB/BA situations with alloc and free. Fix this issue by protecting the bitmap with a mutex per CMA region and use the existing mutex for protecting against concurrency with alloc_contig_range. Signed-off-by: Laura Abbott Signed-off-by: Marek Szyprowski --- drivers/base/dma-contiguous.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index d3bffa478eca20..c687b0b19938c2 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -37,6 +37,7 @@ struct cma { unsigned long base_pfn; unsigned long count; unsigned long *bitmap; + struct mutex lock; }; struct cma *dma_contiguous_default_area; @@ -167,6 +168,7 @@ static int __init cma_activate_area(struct cma *cma) init_cma_reserved_pageblock(pfn_to_page(base_pfn)); } while (--i); + mutex_init(&cma->lock); return 0; err: @@ -271,6 +273,13 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, return ret; } +static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count) +{ + mutex_lock(&cma->lock); + bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count); + mutex_unlock(&cma->lock); +} + /** * dma_alloc_from_contiguous() - allocate pages from contiguous area * @dev: Pointer to device for which the allocation is performed. @@ -304,30 +313,41 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count, mask = (1 << align) - 1; - mutex_lock(&cma_mutex); for (;;) { + mutex_lock(&cma->lock); pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count, start, count, mask); - if (pageno >= cma->count) + if (pageno >= cma->count) { + mutex_unlock(&cma_mutex); break; + } + bitmap_set(cma->bitmap, pageno, count); + /* + * It's safe to drop the lock here. We've marked this region for + * our exclusive use. If the migration fails we will take the + * lock again and unmark it. + */ + mutex_unlock(&cma->lock); pfn = cma->base_pfn + pageno; + mutex_lock(&cma_mutex); ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); + mutex_unlock(&cma_mutex); if (ret == 0) { - bitmap_set(cma->bitmap, pageno, count); page = pfn_to_page(pfn); break; } else if (ret != -EBUSY) { + clear_cma_bitmap(cma, pfn, count); break; } + clear_cma_bitmap(cma, pfn, count); pr_debug("%s(): memory range at %p is busy, retrying\n", __func__, pfn_to_page(pfn)); /* try again with a bit different memory target */ start = pageno + mask + 1; } - mutex_unlock(&cma_mutex); pr_debug("%s(): returned %p\n", __func__, page); return page; } @@ -360,10 +380,8 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); - mutex_lock(&cma_mutex); - bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count); free_contig_range(pfn, count); - mutex_unlock(&cma_mutex); + clear_cma_bitmap(cma, pfn, count); return true; } From 86a0e63a93c7b9f72f5e87226205941b746f7609 Mon Sep 17 00:00:00 2001 From: Gioh Kim Date: Thu, 22 May 2014 13:31:37 +0900 Subject: [PATCH 0456/1983] drivers/base/dma-contiguous.c: erratum of dev_get_cma_area Upstream-commit: bb56d0dc23aa3428c0b1901414bfcf698eb693fb fix erratum get_dev_cma_area into dev_get_cma_area Signed-off-by: Gioh Kim Signed-off-by: Marek Szyprowski --- drivers/base/dma-contiguous.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index c687b0b19938c2..1e9fafe63862f6 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -288,7 +288,7 @@ static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count) * * This function allocates memory buffer for specified device. It uses * device specific contiguous memory area if available or the default - * global one. Requires architecture specific get_dev_cma_area() helper + * global one. Requires architecture specific dev_get_cma_area() helper * function. */ struct page *dma_alloc_from_contiguous(struct device *dev, int count, From 7caf825ae0dcb97805f8fea1e61fa5f19d85aadb Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Thu, 29 May 2014 15:29:18 +0900 Subject: [PATCH 0457/1983] CMA: correct unlock target Upstream-commit: f70e3c4f8b6ab61f713e040822ec51f5de498146 'cma: Remove potential deadlock situation' introduces per cma area mutex for bitmap management. It is good, but there is one mistake. When we can't find appropriate area in bitmap, we release cma_mutex global lock rather than cma->lock and this is a bug. So fix it. Signed-off-by: Joonsoo Kim Signed-off-by: Marek Szyprowski --- drivers/base/dma-contiguous.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 1e9fafe63862f6..d1b1e357a3fc46 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -319,7 +319,7 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count, pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count, start, count, mask); if (pageno >= cma->count) { - mutex_unlock(&cma_mutex); + mutex_unlock(&cma->lock); break; } bitmap_set(cma->bitmap, pageno, count); From babb8b3583a5ac916fdb8f7bca9562c7f65fd0b9 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 4 Jun 2014 16:06:53 -0700 Subject: [PATCH 0458/1983] memblock: introduce memblock_alloc_range() Upstream-commit: 2bfc2862c4fe38379a2fb2cfba33fad32ccb4ff4 This introduces memblock_alloc_range() which allocates memblock from the specified range of physical address. I would like to use this function to specify the location of CMA. Signed-off-by: Akinobu Mita Cc: Marek Szyprowski Cc: Konrad Rzeszutek Wilk Cc: David Woodhouse Cc: Don Dutile Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Andi Kleen Cc: Yinghai Lu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memblock.h | 2 ++ mm/memblock.c | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 1ef66360f0b092..59f6be08315a4e 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -221,6 +221,8 @@ static inline bool memblock_bottom_up(void) { return false; } #define MEMBLOCK_ALLOC_ANYWHERE (~(phys_addr_t)0) #define MEMBLOCK_ALLOC_ACCESSIBLE 0 +phys_addr_t __init memblock_alloc_range(phys_addr_t size, phys_addr_t align, + phys_addr_t start, phys_addr_t end); phys_addr_t memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr); phys_addr_t __memblock_alloc_base(phys_addr_t size, phys_addr_t align, diff --git a/mm/memblock.c b/mm/memblock.c index 0739dc1b409547..dfee4aa0947e8c 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -974,22 +974,35 @@ int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size, } #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ -static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, - phys_addr_t align, phys_addr_t max_addr, - int nid) +static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size, + phys_addr_t align, phys_addr_t start, + phys_addr_t end, int nid) { phys_addr_t found; if (!align) align = SMP_CACHE_BYTES; - found = memblock_find_in_range_node(size, align, 0, max_addr, nid); + found = memblock_find_in_range_node(size, align, start, end, nid); if (found && !memblock_reserve(found, size)) return found; return 0; } +phys_addr_t __init memblock_alloc_range(phys_addr_t size, phys_addr_t align, + phys_addr_t start, phys_addr_t end) +{ + return memblock_alloc_range_nid(size, align, start, end, NUMA_NO_NODE); +} + +static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size, + phys_addr_t align, phys_addr_t max_addr, + int nid) +{ + return memblock_alloc_range_nid(size, align, 0, max_addr, nid); +} + phys_addr_t __init memblock_alloc_nid(phys_addr_t size, phys_addr_t align, int nid) { return memblock_alloc_base_nid(size, align, MEMBLOCK_ALLOC_ACCESSIBLE, nid); From 29166887f17c228015d37ef25d26a70660fa92b1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 4 Jun 2014 16:06:54 -0700 Subject: [PATCH 0459/1983] cma: add placement specifier for "cma=" kernel parameter Upstream-commit: 5ea3b1b2f8ad9162684431ce6188102ca4c64b7a Currently, "cma=" kernel parameter is used to specify the size of CMA, but we can't specify where it is located. We want to locate CMA below 4GB for devices only supporting 32-bit addressing on 64-bit systems without iommu. This enables to specify the placement of CMA by extending "cma=" kernel parameter. Examples: 1. locate 64MB CMA below 4GB by "cma=64M@0-4G" 2. locate 64MB CMA exact at 512MB by "cma=64M@512M" Note that the DMA contiguous memory allocator on x86 assumes that page_address() works for the pages to allocate. So this change requires to limit end address of contiguous memory area upto max_pfn_mapped to prevent from locating it on highmem area by the argument of dma_contiguous_reserve(). Signed-off-by: Akinobu Mita Cc: Marek Szyprowski Cc: Konrad Rzeszutek Wilk Cc: David Woodhouse Cc: Don Dutile Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Andi Kleen Cc: Yinghai Lu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kernel-parameters.txt | 7 +++-- arch/x86/kernel/setup.c | 2 +- drivers/base/dma-contiguous.c | 42 ++++++++++++++++++++++------- include/linux/dma-contiguous.h | 9 ++++--- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index afe3dac778c224..5f4c3e7791f72f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -603,8 +603,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Also note the kernel might malfunction if you disable some critical bits. - cma=nn[MG] [ARM,KNL] - Sets the size of kernel global memory area for contiguous + cma=nn[MG]@[start[MG][-end[MG]]] + [ARM,X86,KNL] + Sets the size of kernel global memory area for + contiguous memory allocations and optionally the + placement constraint by the physical address range of memory allocations. For more information, see include/linux/dma-contiguous.h diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index ce72964b2f469d..5df9657297495e 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1120,7 +1120,7 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); memblock_set_current_limit(get_max_mapped()); - dma_contiguous_reserve(0); + dma_contiguous_reserve(max_pfn_mapped << PAGE_SHIFT); /* * NOTE: On x86-32, only from this point on, fixmaps are ready for use. diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index d1b1e357a3fc46..6467c919c50993 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -60,11 +60,22 @@ struct cma *dma_contiguous_default_area; */ static const phys_addr_t size_bytes = CMA_SIZE_MBYTES * SZ_1M; static phys_addr_t size_cmdline = -1; +static phys_addr_t base_cmdline; +static phys_addr_t limit_cmdline; static int __init early_cma(char *p) { pr_debug("%s(%s)\n", __func__, p); size_cmdline = memparse(p, &p); + if (*p != '@') + return 0; + base_cmdline = memparse(p + 1, &p); + if (*p != '-') { + limit_cmdline = base_cmdline + size_cmdline; + return 0; + } + limit_cmdline = memparse(p + 1, &p); + return 0; } early_param("cma", early_cma); @@ -108,11 +119,18 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) void __init dma_contiguous_reserve(phys_addr_t limit) { phys_addr_t selected_size = 0; + phys_addr_t selected_base = 0; + phys_addr_t selected_limit = limit; + bool fixed = false; pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit); if (size_cmdline != -1) { selected_size = size_cmdline; + selected_base = base_cmdline; + selected_limit = min_not_zero(limit_cmdline, limit); + if (base_cmdline + size_cmdline == limit_cmdline) + fixed = true; } else { #ifdef CONFIG_CMA_SIZE_SEL_MBYTES selected_size = size_bytes; @@ -129,10 +147,12 @@ void __init dma_contiguous_reserve(phys_addr_t limit) pr_debug("%s: reserving %ld MiB for global area\n", __func__, (unsigned long)selected_size / SZ_1M); - dma_contiguous_reserve_area(selected_size, 0, limit, - &dma_contiguous_default_area); + dma_contiguous_reserve_area(selected_size, selected_base, + selected_limit, + &dma_contiguous_default_area, + fixed); } -}; +} static DEFINE_MUTEX(cma_mutex); @@ -199,15 +219,20 @@ core_initcall(cma_init_reserved_areas); * @base: Base address of the reserved area optional, use 0 for any * @limit: End address of the reserved memory (optional, 0 for any). * @res_cma: Pointer to store the created cma region. + * @fixed: hint about where to place the reserved area * * This function reserves memory from early allocator. It should be * called by arch specific code once the early allocator (memblock or bootmem) * has been activated and all other subsystems have already allocated/reserved * memory. This function allows to create custom reserved areas for specific * devices. + * + * If @fixed is true, reserve contiguous area at exactly @base. If false, + * reserve in range from @base to @limit. */ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, - phys_addr_t limit, struct cma **res_cma) + phys_addr_t limit, struct cma **res_cma, + bool fixed) { struct cma *cma = &cma_areas[cma_area_count]; phys_addr_t alignment; @@ -233,18 +258,15 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, limit &= ~(alignment - 1); /* Reserve memory */ - if (base) { + if (base && fixed) { if (memblock_is_region_reserved(base, size) || memblock_reserve(base, size) < 0) { ret = -EBUSY; goto err; } } else { - /* - * Use __memblock_alloc_base() since - * memblock_alloc_base() panic()s. - */ - phys_addr_t addr = __memblock_alloc_base(size, alignment, limit); + phys_addr_t addr = memblock_alloc_range(size, alignment, base, + limit); if (!addr) { ret = -ENOMEM; goto err; diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 3b28f937d959d4..772eab5d524abb 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -88,7 +88,8 @@ static inline void dma_contiguous_set_default(struct cma *cma) void dma_contiguous_reserve(phys_addr_t addr_limit); int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, - phys_addr_t limit, struct cma **res_cma); + phys_addr_t limit, struct cma **res_cma, + bool fixed); /** * dma_declare_contiguous() - reserve area for contiguous memory handling @@ -108,7 +109,7 @@ static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, { struct cma *cma; int ret; - ret = dma_contiguous_reserve_area(size, base, limit, &cma); + ret = dma_contiguous_reserve_area(size, base, limit, &cma, true); if (ret == 0) dev_set_cma_area(dev, cma); @@ -136,7 +137,9 @@ static inline void dma_contiguous_set_default(struct cma *cma) { } static inline void dma_contiguous_reserve(phys_addr_t limit) { } static inline int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, - phys_addr_t limit, struct cma **res_cma) { + phys_addr_t limit, struct cma **res_cma, + bool fixed) +{ return -ENOSYS; } From 748ef5be580fe670c8c21ceecc5b2dd2c91b52c5 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Wed, 6 Aug 2014 16:05:19 -0700 Subject: [PATCH 0460/1983] DMA, CMA: separate core CMA management codes from DMA APIs Upstream-commit: 3162bbd7e65b9cc57b660796dd3409807bfc9070 To prepare future generalization work on CMA area management code, we need to separate core CMA management codes from DMA APIs. We will extend these core functions to cover requirements of PPC KVM's CMA area management functionality in following patches. This separation helps us not to touch DMA APIs while extending core functions. Signed-off-by: Joonsoo Kim Acked-by: Michal Nazarewicz Reviewed-by: Aneesh Kumar K.V Cc: Alexander Graf Cc: Aneesh Kumar K.V Cc: Gleb Natapov Cc: Minchan Kim Cc: Paolo Bonzini Cc: Zhang Yanfei Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/dma-contiguous.c | 125 +++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 6467c919c50993..9021762227a7a5 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -213,26 +213,9 @@ static int __init cma_init_reserved_areas(void) } core_initcall(cma_init_reserved_areas); -/** - * dma_contiguous_reserve_area() - reserve custom contiguous area - * @size: Size of the reserved area (in bytes), - * @base: Base address of the reserved area optional, use 0 for any - * @limit: End address of the reserved memory (optional, 0 for any). - * @res_cma: Pointer to store the created cma region. - * @fixed: hint about where to place the reserved area - * - * This function reserves memory from early allocator. It should be - * called by arch specific code once the early allocator (memblock or bootmem) - * has been activated and all other subsystems have already allocated/reserved - * memory. This function allows to create custom reserved areas for specific - * devices. - * - * If @fixed is true, reserve contiguous area at exactly @base. If false, - * reserve in range from @base to @limit. - */ -int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, - phys_addr_t limit, struct cma **res_cma, - bool fixed) +static int __init __dma_contiguous_reserve_area(phys_addr_t size, + phys_addr_t base, phys_addr_t limit, + struct cma **res_cma, bool fixed) { struct cma *cma = &cma_areas[cma_area_count]; phys_addr_t alignment; @@ -286,15 +269,47 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, (unsigned long)base); - - /* Architecture specific contiguous memory fixup. */ - dma_contiguous_early_fixup(base, size); return 0; + err: pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); return ret; } +/** + * dma_contiguous_reserve_area() - reserve custom contiguous area + * @size: Size of the reserved area (in bytes), + * @base: Base address of the reserved area optional, use 0 for any + * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. + * @fixed: hint about where to place the reserved area + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. + * + * If @fixed is true, reserve contiguous area at exactly @base. If false, + * reserve in range from @base to @limit. + */ +int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma, + bool fixed) +{ + int ret; + + ret = __dma_contiguous_reserve_area(size, base, limit, res_cma, fixed); + if (ret) + return ret; + + /* Architecture specific contiguous memory fixup. */ + dma_contiguous_early_fixup(PFN_PHYS((*res_cma)->base_pfn), + (*res_cma)->count << PAGE_SHIFT); + + return 0; +} + static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count) { mutex_lock(&cma->lock); @@ -302,31 +317,16 @@ static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count) mutex_unlock(&cma->lock); } -/** - * dma_alloc_from_contiguous() - allocate pages from contiguous area - * @dev: Pointer to device for which the allocation is performed. - * @count: Requested number of pages. - * @align: Requested alignment of pages (in PAGE_SIZE order). - * - * This function allocates memory buffer for specified device. It uses - * device specific contiguous memory area if available or the default - * global one. Requires architecture specific dev_get_cma_area() helper - * function. - */ -struct page *dma_alloc_from_contiguous(struct device *dev, int count, +static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, unsigned int align) { unsigned long mask, pfn, pageno, start = 0; - struct cma *cma = dev_get_cma_area(dev); struct page *page = NULL; int ret; if (!cma || !cma->count) return NULL; - if (align > CONFIG_CMA_ALIGNMENT) - align = CONFIG_CMA_ALIGNMENT; - pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, count, align); @@ -375,19 +375,30 @@ struct page *dma_alloc_from_contiguous(struct device *dev, int count, } /** - * dma_release_from_contiguous() - release allocated pages - * @dev: Pointer to device for which the pages were allocated. - * @pages: Allocated pages. - * @count: Number of allocated pages. + * dma_alloc_from_contiguous() - allocate pages from contiguous area + * @dev: Pointer to device for which the allocation is performed. + * @count: Requested number of pages. + * @align: Requested alignment of pages (in PAGE_SIZE order). * - * This function releases memory allocated by dma_alloc_from_contiguous(). - * It returns false when provided pages do not belong to contiguous area and - * true otherwise. + * This function allocates memory buffer for specified device. It uses + * device specific contiguous memory area if available or the default + * global one. Requires architecture specific dev_get_cma_area() helper + * function. */ -bool dma_release_from_contiguous(struct device *dev, struct page *pages, - int count) +struct page *dma_alloc_from_contiguous(struct device *dev, int count, + unsigned int align) { struct cma *cma = dev_get_cma_area(dev); + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; + + return __dma_alloc_from_contiguous(cma, count, align); +} + +static bool __dma_release_from_contiguous(struct cma *cma, struct page *pages, + int count) +{ unsigned long pfn; if (!cma || !pages) @@ -407,3 +418,21 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, return true; } + +/** + * dma_release_from_contiguous() - release allocated pages + * @dev: Pointer to device for which the pages were allocated. + * @pages: Allocated pages. + * @count: Number of allocated pages. + * + * This function releases memory allocated by dma_alloc_from_contiguous(). + * It returns false when provided pages do not belong to contiguous area and + * true otherwise. + */ +bool dma_release_from_contiguous(struct device *dev, struct page *pages, + int count) +{ + struct cma *cma = dev_get_cma_area(dev); + + return __dma_release_from_contiguous(cma, pages, count); +} From 0fce10ab9cc17e60b34f9fbd749abe19abaedcda Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Wed, 6 Aug 2014 16:05:21 -0700 Subject: [PATCH 0461/1983] DMA, CMA: support alignment constraint on CMA region Upstream-commit: a15bc0b89e8812d0db297bc771a85812c4fa83c1 PPC KVM's CMA area management needs alignment constraint on CMA region. So support it to prepare generalization of CMA area management functionality. Additionally, add some comments which tell us why alignment constraint is needed on CMA region. Signed-off-by: Joonsoo Kim Acked-by: Michal Nazarewicz Reviewed-by: Aneesh Kumar K.V Cc: Alexander Graf Cc: Aneesh Kumar K.V Cc: Gleb Natapov Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Cc: Minchan Kim Cc: Paolo Bonzini Cc: Zhang Yanfei Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/dma-contiguous.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 9021762227a7a5..5f62c284072c8d 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -32,6 +32,7 @@ #include #include #include +#include struct cma { unsigned long base_pfn; @@ -215,17 +216,16 @@ core_initcall(cma_init_reserved_areas); static int __init __dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, phys_addr_t limit, + phys_addr_t alignment, struct cma **res_cma, bool fixed) { struct cma *cma = &cma_areas[cma_area_count]; - phys_addr_t alignment; int ret = 0; - pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__, - (unsigned long)size, (unsigned long)base, - (unsigned long)limit); + pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n", + __func__, (unsigned long)size, (unsigned long)base, + (unsigned long)limit, (unsigned long)alignment); - /* Sanity checks */ if (cma_area_count == ARRAY_SIZE(cma_areas)) { pr_err("Not enough slots for CMA reserved regions!\n"); return -ENOSPC; @@ -234,8 +234,17 @@ static int __init __dma_contiguous_reserve_area(phys_addr_t size, if (!size) return -EINVAL; - /* Sanitise input arguments */ - alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order); + if (alignment && !is_power_of_2(alignment)) + return -EINVAL; + + /* + * Sanitise input arguments. + * Pages both ends in CMA area could be merged into adjacent unmovable + * migratetype page by page allocator's buddy algorithm. In the case, + * you couldn't get a contiguous memory, which is not what we want. + */ + alignment = max(alignment, + (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order)); base = ALIGN(base, alignment); size = ALIGN(size, alignment); limit &= ~(alignment - 1); @@ -299,7 +308,8 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, { int ret; - ret = __dma_contiguous_reserve_area(size, base, limit, res_cma, fixed); + ret = __dma_contiguous_reserve_area(size, base, limit, 0, + res_cma, fixed); if (ret) return ret; From ce0a91f9e178bb10e0564aa949d30c0b0f4ad244 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Wed, 6 Aug 2014 16:05:23 -0700 Subject: [PATCH 0462/1983] DMA, CMA: support arbitrary bitmap granularity Upstream-commit: e0bdb37d95dd44086159607e571fd70f6b62dc2d PPC KVM's CMA area management requires arbitrary bitmap granularity, since they want to reserve very large memory and manage this region with bitmap that one bit for several pages to reduce management overheads. So support arbitrary bitmap granularity for following generalization. [akpm@linux-foundation.org: s/1/1UL/] Signed-off-by: Joonsoo Kim Acked-by: Michal Nazarewicz Acked-by: Zhang Yanfei Acked-by: Minchan Kim Reviewed-by: Aneesh Kumar K.V Cc: Alexander Graf Cc: Aneesh Kumar K.V Cc: Gleb Natapov Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Cc: Paolo Bonzini Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/dma-contiguous.c | 77 ++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 5f62c284072c8d..ad8a85bf852f91 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -38,6 +38,7 @@ struct cma { unsigned long base_pfn; unsigned long count; unsigned long *bitmap; + unsigned int order_per_bit; /* Order of pages represented by one bit */ struct mutex lock; }; @@ -157,9 +158,37 @@ void __init dma_contiguous_reserve(phys_addr_t limit) static DEFINE_MUTEX(cma_mutex); +static unsigned long cma_bitmap_aligned_mask(struct cma *cma, int align_order) +{ + return (1UL << (align_order >> cma->order_per_bit)) - 1; +} + +static unsigned long cma_bitmap_maxno(struct cma *cma) +{ + return cma->count >> cma->order_per_bit; +} + +static unsigned long cma_bitmap_pages_to_bits(struct cma *cma, + unsigned long pages) +{ + return ALIGN(pages, 1UL << cma->order_per_bit) >> cma->order_per_bit; +} + +static void cma_clear_bitmap(struct cma *cma, unsigned long pfn, int count) +{ + unsigned long bitmap_no, bitmap_count; + + bitmap_no = (pfn - cma->base_pfn) >> cma->order_per_bit; + bitmap_count = cma_bitmap_pages_to_bits(cma, count); + + mutex_lock(&cma->lock); + bitmap_clear(cma->bitmap, bitmap_no, bitmap_count); + mutex_unlock(&cma->lock); +} + static int __init cma_activate_area(struct cma *cma) { - int bitmap_size = BITS_TO_LONGS(cma->count) * sizeof(long); + int bitmap_size = BITS_TO_LONGS(cma_bitmap_maxno(cma)) * sizeof(long); unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; unsigned i = cma->count >> pageblock_order; struct zone *zone; @@ -215,9 +244,9 @@ static int __init cma_init_reserved_areas(void) core_initcall(cma_init_reserved_areas); static int __init __dma_contiguous_reserve_area(phys_addr_t size, - phys_addr_t base, phys_addr_t limit, - phys_addr_t alignment, - struct cma **res_cma, bool fixed) + phys_addr_t base, phys_addr_t limit, + phys_addr_t alignment, unsigned int order_per_bit, + struct cma **res_cma, bool fixed) { struct cma *cma = &cma_areas[cma_area_count]; int ret = 0; @@ -249,6 +278,10 @@ static int __init __dma_contiguous_reserve_area(phys_addr_t size, size = ALIGN(size, alignment); limit &= ~(alignment - 1); + /* size should be aligned with order_per_bit */ + if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit)) + return -EINVAL; + /* Reserve memory */ if (base && fixed) { if (memblock_is_region_reserved(base, size) || @@ -273,6 +306,7 @@ static int __init __dma_contiguous_reserve_area(phys_addr_t size, */ cma->base_pfn = PFN_DOWN(base); cma->count = size >> PAGE_SHIFT; + cma->order_per_bit = order_per_bit; *res_cma = cma; cma_area_count++; @@ -308,7 +342,7 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, { int ret; - ret = __dma_contiguous_reserve_area(size, base, limit, 0, + ret = __dma_contiguous_reserve_area(size, base, limit, 0, 0, res_cma, fixed); if (ret) return ret; @@ -320,17 +354,11 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, return 0; } -static void clear_cma_bitmap(struct cma *cma, unsigned long pfn, int count) -{ - mutex_lock(&cma->lock); - bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count); - mutex_unlock(&cma->lock); -} - static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, unsigned int align) { - unsigned long mask, pfn, pageno, start = 0; + unsigned long mask, pfn, start = 0; + unsigned long bitmap_maxno, bitmap_no, bitmap_count; struct page *page = NULL; int ret; @@ -343,18 +371,19 @@ static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, if (!count) return NULL; - mask = (1 << align) - 1; - + mask = cma_bitmap_aligned_mask(cma, align); + bitmap_maxno = cma_bitmap_maxno(cma); + bitmap_count = cma_bitmap_pages_to_bits(cma, count); for (;;) { mutex_lock(&cma->lock); - pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count, - start, count, mask); - if (pageno >= cma->count) { + bitmap_no = bitmap_find_next_zero_area(cma->bitmap, + bitmap_maxno, start, bitmap_count, mask); + if (bitmap_no >= bitmap_maxno) { mutex_unlock(&cma->lock); break; } - bitmap_set(cma->bitmap, pageno, count); + bitmap_set(cma->bitmap, bitmap_no, bitmap_count); /* * It's safe to drop the lock here. We've marked this region for * our exclusive use. If the migration fails we will take the @@ -362,7 +391,7 @@ static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, */ mutex_unlock(&cma->lock); - pfn = cma->base_pfn + pageno; + pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit); mutex_lock(&cma_mutex); ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); mutex_unlock(&cma_mutex); @@ -370,14 +399,14 @@ static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, page = pfn_to_page(pfn); break; } else if (ret != -EBUSY) { - clear_cma_bitmap(cma, pfn, count); + cma_clear_bitmap(cma, pfn, count); break; } - clear_cma_bitmap(cma, pfn, count); + cma_clear_bitmap(cma, pfn, count); pr_debug("%s(): memory range at %p is busy, retrying\n", __func__, pfn_to_page(pfn)); /* try again with a bit different memory target */ - start = pageno + mask + 1; + start = bitmap_no + mask + 1; } pr_debug("%s(): returned %p\n", __func__, page); @@ -424,7 +453,7 @@ static bool __dma_release_from_contiguous(struct cma *cma, struct page *pages, VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); free_contig_range(pfn, count); - clear_cma_bitmap(cma, pfn, count); + cma_clear_bitmap(cma, pfn, count); return true; } From c30b05c6ef9107fe3cdc21cbf2ec9726e892fd74 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Wed, 6 Aug 2014 16:05:25 -0700 Subject: [PATCH 0463/1983] CMA: generalize CMA reserved area management functionality Upstream-commit: a254129e8686bff7a340b58f35241b04927e81c0 Currently, there are two users on CMA functionality, one is the DMA subsystem and the other is the KVM on powerpc. They have their own code to manage CMA reserved area even if they looks really similar. From my guess, it is caused by some needs on bitmap management. KVM side wants to maintain bitmap not for 1 page, but for more size. Eventually it use bitmap where one bit represents 64 pages. When I implement CMA related patches, I should change those two places to apply my change and it seem to be painful to me. I want to change this situation and reduce future code management overhead through this patch. This change could also help developer who want to use CMA in their new feature development, since they can use CMA easily without copying & pasting this reserved area management code. In previous patches, we have prepared some features to generalize CMA reserved area management and now it's time to do it. This patch moves core functions to mm/cma.c and change DMA APIs to use these functions. There is no functional change in DMA APIs. Signed-off-by: Joonsoo Kim Acked-by: Michal Nazarewicz Acked-by: Zhang Yanfei Acked-by: Minchan Kim Reviewed-by: Aneesh Kumar K.V Cc: Alexander Graf Cc: Aneesh Kumar K.V Cc: Gleb Natapov Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Cc: Paolo Bonzini Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/mm/dma-mapping.c | 1 + drivers/base/Kconfig | 10 - drivers/base/dma-contiguous.c | 280 +-------------------------- include/linux/cma.h | 27 +++ include/linux/dma-contiguous.h | 11 +- mm/Kconfig | 11 ++ mm/Makefile | 1 + mm/cma.c | 333 +++++++++++++++++++++++++++++++++ 8 files changed, 383 insertions(+), 291 deletions(-) create mode 100644 include/linux/cma.h create mode 100644 mm/cma.c diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 42f2fb8c5a00fd..fae192b19782ab 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index ec36e7772e5766..ad78c56027dd54 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -266,16 +266,6 @@ config CMA_ALIGNMENT If unsure, leave the default value "8". -config CMA_AREAS - int "Maximum count of the CMA device-private areas" - default 7 - help - CMA allows to create CMA areas for particular devices. This parameter - sets the maximum number of such device private CMA areas in the - system. - - If unsure, leave the default value "7". - endif endmenu diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index ad8a85bf852f91..0411c1c57005cc 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -24,25 +24,9 @@ #include #include -#include -#include -#include #include -#include -#include -#include #include -#include - -struct cma { - unsigned long base_pfn; - unsigned long count; - unsigned long *bitmap; - unsigned int order_per_bit; /* Order of pages represented by one bit */ - struct mutex lock; -}; - -struct cma *dma_contiguous_default_area; +#include #ifdef CONFIG_CMA_SIZE_MBYTES #define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES @@ -50,6 +34,8 @@ struct cma *dma_contiguous_default_area; #define CMA_SIZE_MBYTES 0 #endif +struct cma *dma_contiguous_default_area; + /* * Default global CMA area size can be defined in kernel's .config. * This is useful mainly for distro maintainers to create a kernel @@ -156,169 +142,6 @@ void __init dma_contiguous_reserve(phys_addr_t limit) } } -static DEFINE_MUTEX(cma_mutex); - -static unsigned long cma_bitmap_aligned_mask(struct cma *cma, int align_order) -{ - return (1UL << (align_order >> cma->order_per_bit)) - 1; -} - -static unsigned long cma_bitmap_maxno(struct cma *cma) -{ - return cma->count >> cma->order_per_bit; -} - -static unsigned long cma_bitmap_pages_to_bits(struct cma *cma, - unsigned long pages) -{ - return ALIGN(pages, 1UL << cma->order_per_bit) >> cma->order_per_bit; -} - -static void cma_clear_bitmap(struct cma *cma, unsigned long pfn, int count) -{ - unsigned long bitmap_no, bitmap_count; - - bitmap_no = (pfn - cma->base_pfn) >> cma->order_per_bit; - bitmap_count = cma_bitmap_pages_to_bits(cma, count); - - mutex_lock(&cma->lock); - bitmap_clear(cma->bitmap, bitmap_no, bitmap_count); - mutex_unlock(&cma->lock); -} - -static int __init cma_activate_area(struct cma *cma) -{ - int bitmap_size = BITS_TO_LONGS(cma_bitmap_maxno(cma)) * sizeof(long); - unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; - unsigned i = cma->count >> pageblock_order; - struct zone *zone; - - cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - - if (!cma->bitmap) - return -ENOMEM; - - WARN_ON_ONCE(!pfn_valid(pfn)); - zone = page_zone(pfn_to_page(pfn)); - - do { - unsigned j; - base_pfn = pfn; - for (j = pageblock_nr_pages; j; --j, pfn++) { - WARN_ON_ONCE(!pfn_valid(pfn)); - /* - * alloc_contig_range requires the pfn range - * specified to be in the same zone. Make this - * simple by forcing the entire CMA resv range - * to be in the same zone. - */ - if (page_zone(pfn_to_page(pfn)) != zone) - goto err; - } - init_cma_reserved_pageblock(pfn_to_page(base_pfn)); - } while (--i); - - mutex_init(&cma->lock); - return 0; - -err: - kfree(cma->bitmap); - return -EINVAL; -} - -static struct cma cma_areas[MAX_CMA_AREAS]; -static unsigned cma_area_count; - -static int __init cma_init_reserved_areas(void) -{ - int i; - - for (i = 0; i < cma_area_count; i++) { - int ret = cma_activate_area(&cma_areas[i]); - if (ret) - return ret; - } - - return 0; -} -core_initcall(cma_init_reserved_areas); - -static int __init __dma_contiguous_reserve_area(phys_addr_t size, - phys_addr_t base, phys_addr_t limit, - phys_addr_t alignment, unsigned int order_per_bit, - struct cma **res_cma, bool fixed) -{ - struct cma *cma = &cma_areas[cma_area_count]; - int ret = 0; - - pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n", - __func__, (unsigned long)size, (unsigned long)base, - (unsigned long)limit, (unsigned long)alignment); - - if (cma_area_count == ARRAY_SIZE(cma_areas)) { - pr_err("Not enough slots for CMA reserved regions!\n"); - return -ENOSPC; - } - - if (!size) - return -EINVAL; - - if (alignment && !is_power_of_2(alignment)) - return -EINVAL; - - /* - * Sanitise input arguments. - * Pages both ends in CMA area could be merged into adjacent unmovable - * migratetype page by page allocator's buddy algorithm. In the case, - * you couldn't get a contiguous memory, which is not what we want. - */ - alignment = max(alignment, - (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order)); - base = ALIGN(base, alignment); - size = ALIGN(size, alignment); - limit &= ~(alignment - 1); - - /* size should be aligned with order_per_bit */ - if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit)) - return -EINVAL; - - /* Reserve memory */ - if (base && fixed) { - if (memblock_is_region_reserved(base, size) || - memblock_reserve(base, size) < 0) { - ret = -EBUSY; - goto err; - } - } else { - phys_addr_t addr = memblock_alloc_range(size, alignment, base, - limit); - if (!addr) { - ret = -ENOMEM; - goto err; - } else { - base = addr; - } - } - - /* - * Each reserved area must be initialised later, when more kernel - * subsystems (like slab allocator) are available. - */ - cma->base_pfn = PFN_DOWN(base); - cma->count = size >> PAGE_SHIFT; - cma->order_per_bit = order_per_bit; - *res_cma = cma; - cma_area_count++; - - pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, - (unsigned long)base); - return 0; - -err: - pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); - return ret; -} - /** * dma_contiguous_reserve_area() - reserve custom contiguous area * @size: Size of the reserved area (in bytes), @@ -342,77 +165,17 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, { int ret; - ret = __dma_contiguous_reserve_area(size, base, limit, 0, 0, - res_cma, fixed); + ret = cma_declare_contiguous(size, base, limit, 0, 0, res_cma, fixed); if (ret) return ret; /* Architecture specific contiguous memory fixup. */ - dma_contiguous_early_fixup(PFN_PHYS((*res_cma)->base_pfn), - (*res_cma)->count << PAGE_SHIFT); + dma_contiguous_early_fixup(cma_get_base(*res_cma), + cma_get_size(*res_cma)); return 0; } -static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, - unsigned int align) -{ - unsigned long mask, pfn, start = 0; - unsigned long bitmap_maxno, bitmap_no, bitmap_count; - struct page *page = NULL; - int ret; - - if (!cma || !cma->count) - return NULL; - - pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, - count, align); - - if (!count) - return NULL; - - mask = cma_bitmap_aligned_mask(cma, align); - bitmap_maxno = cma_bitmap_maxno(cma); - bitmap_count = cma_bitmap_pages_to_bits(cma, count); - - for (;;) { - mutex_lock(&cma->lock); - bitmap_no = bitmap_find_next_zero_area(cma->bitmap, - bitmap_maxno, start, bitmap_count, mask); - if (bitmap_no >= bitmap_maxno) { - mutex_unlock(&cma->lock); - break; - } - bitmap_set(cma->bitmap, bitmap_no, bitmap_count); - /* - * It's safe to drop the lock here. We've marked this region for - * our exclusive use. If the migration fails we will take the - * lock again and unmark it. - */ - mutex_unlock(&cma->lock); - - pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit); - mutex_lock(&cma_mutex); - ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); - mutex_unlock(&cma_mutex); - if (ret == 0) { - page = pfn_to_page(pfn); - break; - } else if (ret != -EBUSY) { - cma_clear_bitmap(cma, pfn, count); - break; - } - cma_clear_bitmap(cma, pfn, count); - pr_debug("%s(): memory range at %p is busy, retrying\n", - __func__, pfn_to_page(pfn)); - /* try again with a bit different memory target */ - start = bitmap_no + mask + 1; - } - - pr_debug("%s(): returned %p\n", __func__, page); - return page; -} - /** * dma_alloc_from_contiguous() - allocate pages from contiguous area * @dev: Pointer to device for which the allocation is performed. @@ -427,35 +190,10 @@ static struct page *__dma_alloc_from_contiguous(struct cma *cma, int count, struct page *dma_alloc_from_contiguous(struct device *dev, int count, unsigned int align) { - struct cma *cma = dev_get_cma_area(dev); - if (align > CONFIG_CMA_ALIGNMENT) align = CONFIG_CMA_ALIGNMENT; - return __dma_alloc_from_contiguous(cma, count, align); -} - -static bool __dma_release_from_contiguous(struct cma *cma, struct page *pages, - int count) -{ - unsigned long pfn; - - if (!cma || !pages) - return false; - - pr_debug("%s(page %p)\n", __func__, (void *)pages); - - pfn = page_to_pfn(pages); - - if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count) - return false; - - VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); - - free_contig_range(pfn, count); - cma_clear_bitmap(cma, pfn, count); - - return true; + return cma_alloc(dev_get_cma_area(dev), count, align); } /** @@ -471,7 +209,5 @@ static bool __dma_release_from_contiguous(struct cma *cma, struct page *pages, bool dma_release_from_contiguous(struct device *dev, struct page *pages, int count) { - struct cma *cma = dev_get_cma_area(dev); - - return __dma_release_from_contiguous(cma, pages, count); + return cma_release(dev_get_cma_area(dev), pages, count); } diff --git a/include/linux/cma.h b/include/linux/cma.h new file mode 100644 index 00000000000000..f6f7809acb98cf --- /dev/null +++ b/include/linux/cma.h @@ -0,0 +1,27 @@ +#ifndef __CMA_H__ +#define __CMA_H__ + +/* + * There is always at least global CMA area and a few optional + * areas configured in kernel .config. + */ +#ifdef CONFIG_CMA_AREAS +#define MAX_CMA_AREAS (1 + CONFIG_CMA_AREAS) + +#else +#define MAX_CMA_AREAS (0) + +#endif + +struct cma; + +extern phys_addr_t cma_get_base(struct cma *cma); +extern unsigned long cma_get_size(struct cma *cma); + +extern int __init cma_declare_contiguous(phys_addr_t size, + phys_addr_t base, phys_addr_t limit, + phys_addr_t alignment, unsigned int order_per_bit, + struct cma **res_cma, bool fixed); +extern struct page *cma_alloc(struct cma *cma, int count, unsigned int align); +extern bool cma_release(struct cma *cma, struct page *pages, int count); +#endif diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 772eab5d524abb..569bbd039896f3 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -53,18 +53,13 @@ #ifdef __KERNEL__ +#include + struct cma; struct page; -struct device; #ifdef CONFIG_DMA_CMA -/* - * There is always at least global CMA area and a few optional device - * private areas configured in kernel .config. - */ -#define MAX_CMA_AREAS (1 + CONFIG_CMA_AREAS) - extern struct cma *dma_contiguous_default_area; static inline struct cma *dev_get_cma_area(struct device *dev) @@ -123,8 +118,6 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, #else -#define MAX_CMA_AREAS (0) - static inline struct cma *dev_get_cma_area(struct device *dev) { return NULL; diff --git a/mm/Kconfig b/mm/Kconfig index 515cfadafb0605..570b49be19c921 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -514,6 +514,17 @@ config CMA_DEBUG processing calls such as dma_alloc_from_contiguous(). This option does not affect warning and error messages. +config CMA_AREAS + int "Maximum count of the CMA areas" + depends on CMA + default 7 + help + CMA allows to create CMA areas for particular purpose, mainly, + used as device private area. This parameter sets the maximum + number of CMA area in the system. + + If unsure, leave the default value "7". + config ZBUD tristate default n diff --git a/mm/Makefile b/mm/Makefile index 3c21f087037d71..bb6e41845100a4 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o obj-$(CONFIG_ZBUD) += zbud.o obj-$(CONFIG_ZSMALLOC) += zsmalloc.o obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o +obj-$(CONFIG_CMA) += cma.o diff --git a/mm/cma.c b/mm/cma.c new file mode 100644 index 00000000000000..6560042169530c --- /dev/null +++ b/mm/cma.c @@ -0,0 +1,333 @@ +/* + * Contiguous Memory Allocator + * + * Copyright (c) 2010-2011 by Samsung Electronics. + * Copyright IBM Corporation, 2013 + * Copyright LG Electronics Inc., 2014 + * Written by: + * Marek Szyprowski + * Michal Nazarewicz + * Aneesh Kumar K.V + * Joonsoo Kim + * + * 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 optional) any later version of the license. + */ + +#define pr_fmt(fmt) "cma: " fmt + +#ifdef CONFIG_CMA_DEBUG +#ifndef DEBUG +# define DEBUG +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +struct cma { + unsigned long base_pfn; + unsigned long count; + unsigned long *bitmap; + unsigned int order_per_bit; /* Order of pages represented by one bit */ + struct mutex lock; +}; + +static struct cma cma_areas[MAX_CMA_AREAS]; +static unsigned cma_area_count; +static DEFINE_MUTEX(cma_mutex); + +phys_addr_t cma_get_base(struct cma *cma) +{ + return PFN_PHYS(cma->base_pfn); +} + +unsigned long cma_get_size(struct cma *cma) +{ + return cma->count << PAGE_SHIFT; +} + +static unsigned long cma_bitmap_aligned_mask(struct cma *cma, int align_order) +{ + return (1UL << (align_order >> cma->order_per_bit)) - 1; +} + +static unsigned long cma_bitmap_maxno(struct cma *cma) +{ + return cma->count >> cma->order_per_bit; +} + +static unsigned long cma_bitmap_pages_to_bits(struct cma *cma, + unsigned long pages) +{ + return ALIGN(pages, 1UL << cma->order_per_bit) >> cma->order_per_bit; +} + +static void cma_clear_bitmap(struct cma *cma, unsigned long pfn, int count) +{ + unsigned long bitmap_no, bitmap_count; + + bitmap_no = (pfn - cma->base_pfn) >> cma->order_per_bit; + bitmap_count = cma_bitmap_pages_to_bits(cma, count); + + mutex_lock(&cma->lock); + bitmap_clear(cma->bitmap, bitmap_no, bitmap_count); + mutex_unlock(&cma->lock); +} + +static int __init cma_activate_area(struct cma *cma) +{ + int bitmap_size = BITS_TO_LONGS(cma_bitmap_maxno(cma)) * sizeof(long); + unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; + unsigned i = cma->count >> pageblock_order; + struct zone *zone; + + cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + + if (!cma->bitmap) + return -ENOMEM; + + WARN_ON_ONCE(!pfn_valid(pfn)); + zone = page_zone(pfn_to_page(pfn)); + + do { + unsigned j; + + base_pfn = pfn; + for (j = pageblock_nr_pages; j; --j, pfn++) { + WARN_ON_ONCE(!pfn_valid(pfn)); + /* + * alloc_contig_range requires the pfn range + * specified to be in the same zone. Make this + * simple by forcing the entire CMA resv range + * to be in the same zone. + */ + if (page_zone(pfn_to_page(pfn)) != zone) + goto err; + } + init_cma_reserved_pageblock(pfn_to_page(base_pfn)); + } while (--i); + + mutex_init(&cma->lock); + return 0; + +err: + kfree(cma->bitmap); + return -EINVAL; +} + +static int __init cma_init_reserved_areas(void) +{ + int i; + + for (i = 0; i < cma_area_count; i++) { + int ret = cma_activate_area(&cma_areas[i]); + + if (ret) + return ret; + } + + return 0; +} +core_initcall(cma_init_reserved_areas); + +/** + * cma_declare_contiguous() - reserve custom contiguous area + * @size: Size of the reserved area (in bytes), + * @base: Base address of the reserved area optional, use 0 for any + * @limit: End address of the reserved memory (optional, 0 for any). + * @alignment: Alignment for the CMA area, should be power of 2 or zero + * @order_per_bit: Order of pages represented by one bit on bitmap. + * @res_cma: Pointer to store the created cma region. + * @fixed: hint about where to place the reserved area + * + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas. + * + * If @fixed is true, reserve contiguous area at exactly @base. If false, + * reserve in range from @base to @limit. + */ +int __init cma_declare_contiguous(phys_addr_t size, + phys_addr_t base, phys_addr_t limit, + phys_addr_t alignment, unsigned int order_per_bit, + struct cma **res_cma, bool fixed) +{ + struct cma *cma = &cma_areas[cma_area_count]; + int ret = 0; + + pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n", + __func__, (unsigned long)size, (unsigned long)base, + (unsigned long)limit, (unsigned long)alignment); + + if (cma_area_count == ARRAY_SIZE(cma_areas)) { + pr_err("Not enough slots for CMA reserved regions!\n"); + return -ENOSPC; + } + + if (!size) + return -EINVAL; + + if (alignment && !is_power_of_2(alignment)) + return -EINVAL; + + /* + * Sanitise input arguments. + * Pages both ends in CMA area could be merged into adjacent unmovable + * migratetype page by page allocator's buddy algorithm. In the case, + * you couldn't get a contiguous memory, which is not what we want. + */ + alignment = max(alignment, + (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order)); + base = ALIGN(base, alignment); + size = ALIGN(size, alignment); + limit &= ~(alignment - 1); + + /* size should be aligned with order_per_bit */ + if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit)) + return -EINVAL; + + /* Reserve memory */ + if (base && fixed) { + if (memblock_is_region_reserved(base, size) || + memblock_reserve(base, size) < 0) { + ret = -EBUSY; + goto err; + } + } else { + phys_addr_t addr = memblock_alloc_range(size, alignment, base, + limit); + if (!addr) { + ret = -ENOMEM; + goto err; + } else { + base = addr; + } + } + + /* + * Each reserved area must be initialised later, when more kernel + * subsystems (like slab allocator) are available. + */ + cma->base_pfn = PFN_DOWN(base); + cma->count = size >> PAGE_SHIFT; + cma->order_per_bit = order_per_bit; + *res_cma = cma; + cma_area_count++; + + pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, + (unsigned long)base); + return 0; + +err: + pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); + return ret; +} + +/** + * cma_alloc() - allocate pages from contiguous area + * @cma: Contiguous memory region for which the allocation is performed. + * @count: Requested number of pages. + * @align: Requested alignment of pages (in PAGE_SIZE order). + * + * This function allocates part of contiguous memory on specific + * contiguous memory area. + */ +struct page *cma_alloc(struct cma *cma, int count, unsigned int align) +{ + unsigned long mask, pfn, start = 0; + unsigned long bitmap_maxno, bitmap_no, bitmap_count; + struct page *page = NULL; + int ret; + + if (!cma || !cma->count) + return NULL; + + pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma, + count, align); + + if (!count) + return NULL; + + mask = cma_bitmap_aligned_mask(cma, align); + bitmap_maxno = cma_bitmap_maxno(cma); + bitmap_count = cma_bitmap_pages_to_bits(cma, count); + + for (;;) { + mutex_lock(&cma->lock); + bitmap_no = bitmap_find_next_zero_area(cma->bitmap, + bitmap_maxno, start, bitmap_count, mask); + if (bitmap_no >= bitmap_maxno) { + mutex_unlock(&cma->lock); + break; + } + bitmap_set(cma->bitmap, bitmap_no, bitmap_count); + /* + * It's safe to drop the lock here. We've marked this region for + * our exclusive use. If the migration fails we will take the + * lock again and unmark it. + */ + mutex_unlock(&cma->lock); + + pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit); + mutex_lock(&cma_mutex); + ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA); + mutex_unlock(&cma_mutex); + if (ret == 0) { + page = pfn_to_page(pfn); + break; + } else if (ret != -EBUSY) { + cma_clear_bitmap(cma, pfn, count); + break; + } + cma_clear_bitmap(cma, pfn, count); + pr_debug("%s(): memory range at %p is busy, retrying\n", + __func__, pfn_to_page(pfn)); + /* try again with a bit different memory target */ + start = bitmap_no + mask + 1; + } + + pr_debug("%s(): returned %p\n", __func__, page); + return page; +} + +/** + * cma_release() - release allocated pages + * @cma: Contiguous memory region for which the allocation is performed. + * @pages: Allocated pages. + * @count: Number of allocated pages. + * + * This function releases memory allocated by alloc_cma(). + * It returns false when provided pages do not belong to contiguous area and + * true otherwise. + */ +bool cma_release(struct cma *cma, struct page *pages, int count) +{ + unsigned long pfn; + + if (!cma || !pages) + return false; + + pr_debug("%s(page %p)\n", __func__, (void *)pages); + + pfn = page_to_pfn(pages); + + if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count) + return false; + + VM_BUG_ON(pfn + count > cma->base_pfn + cma->count); + + free_contig_range(pfn, count); + cma_clear_bitmap(cma, pfn, count); + + return true; +} From 10474a114f4334d4510bfb48bd831b939164d8c1 Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Wed, 6 Aug 2014 16:05:30 -0700 Subject: [PATCH 0464/1983] mm, CMA: clean-up CMA allocation error path Upstream-commit: b7155e76a702d97553660828347b9f10858b4dd5 We can remove one call sites for clear_cma_bitmap() if we first call it before checking error number. Signed-off-by: Joonsoo Kim Acked-by: Minchan Kim Reviewed-by: Michal Nazarewicz Reviewed-by: Zhang Yanfei Reviewed-by: Aneesh Kumar K.V Cc: Alexander Graf Cc: Aneesh Kumar K.V Cc: Gleb Natapov Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Cc: Paolo Bonzini Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/cma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mm/cma.c b/mm/cma.c index 6560042169530c..103a6663b7c730 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -285,11 +285,12 @@ struct page *cma_alloc(struct cma *cma, int count, unsigned int align) if (ret == 0) { page = pfn_to_page(pfn); break; - } else if (ret != -EBUSY) { - cma_clear_bitmap(cma, pfn, count); - break; } + cma_clear_bitmap(cma, pfn, count); + if (ret != -EBUSY) + break; + pr_debug("%s(): memory range at %p is busy, retrying\n", __func__, pfn_to_page(pfn)); /* try again with a bit different memory target */ From 966fb91ddeee5db13a44c65914d8d4e2e340be4a Mon Sep 17 00:00:00 2001 From: Joonsoo Kim Date: Wed, 6 Aug 2014 16:05:32 -0700 Subject: [PATCH 0465/1983] mm, CMA: change cma_declare_contiguous() to obey coding convention Upstream-commit: c1f733aaaf30a0068a3126d5aa9d5b4c25ba4c0c * Note that we do not change the KVM cma because it is still using an internal allocator. * Conventionally, we put output param to the end of param list and put the 'base' ahead of 'size', but cma_declare_contiguous() doesn't look like that, so change it. Additionally, move down cma_areas reference code to the position where it is really needed. Signed-off-by: Joonsoo Kim Acked-by: Michal Nazarewicz Reviewed-by: Aneesh Kumar K.V Cc: Alexander Graf Cc: Aneesh Kumar K.V Cc: Gleb Natapov Acked-by: Marek Szyprowski Tested-by: Marek Szyprowski Cc: Minchan Kim Cc: Paolo Bonzini Cc: Zhang Yanfei Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: arch/powerpc/kvm/book3s_hv_builtin.c --- drivers/base/dma-contiguous.c | 2 +- include/linux/cma.h | 2 +- mm/cma.c | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 0411c1c57005cc..6606abdf880c81 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -165,7 +165,7 @@ int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, { int ret; - ret = cma_declare_contiguous(size, base, limit, 0, 0, res_cma, fixed); + ret = cma_declare_contiguous(base, size, limit, 0, 0, fixed, res_cma); if (ret) return ret; diff --git a/include/linux/cma.h b/include/linux/cma.h index f6f7809acb98cf..371b9304252052 100644 --- a/include/linux/cma.h +++ b/include/linux/cma.h @@ -21,7 +21,7 @@ extern unsigned long cma_get_size(struct cma *cma); extern int __init cma_declare_contiguous(phys_addr_t size, phys_addr_t base, phys_addr_t limit, phys_addr_t alignment, unsigned int order_per_bit, - struct cma **res_cma, bool fixed); + bool fixed, struct cma **res_cma); extern struct page *cma_alloc(struct cma *cma, int count, unsigned int align); extern bool cma_release(struct cma *cma, struct page *pages, int count); #endif diff --git a/mm/cma.c b/mm/cma.c index 103a6663b7c730..488e50810ed187 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -141,13 +141,13 @@ core_initcall(cma_init_reserved_areas); /** * cma_declare_contiguous() - reserve custom contiguous area - * @size: Size of the reserved area (in bytes), * @base: Base address of the reserved area optional, use 0 for any + * @size: Size of the reserved area (in bytes), * @limit: End address of the reserved memory (optional, 0 for any). * @alignment: Alignment for the CMA area, should be power of 2 or zero * @order_per_bit: Order of pages represented by one bit on bitmap. - * @res_cma: Pointer to store the created cma region. * @fixed: hint about where to place the reserved area + * @res_cma: Pointer to store the created cma region. * * This function reserves memory from early allocator. It should be * called by arch specific code once the early allocator (memblock or bootmem) @@ -157,12 +157,12 @@ core_initcall(cma_init_reserved_areas); * If @fixed is true, reserve contiguous area at exactly @base. If false, * reserve in range from @base to @limit. */ -int __init cma_declare_contiguous(phys_addr_t size, - phys_addr_t base, phys_addr_t limit, +int __init cma_declare_contiguous(phys_addr_t base, + phys_addr_t size, phys_addr_t limit, phys_addr_t alignment, unsigned int order_per_bit, - struct cma **res_cma, bool fixed) + bool fixed, struct cma **res_cma) { - struct cma *cma = &cma_areas[cma_area_count]; + struct cma *cma; int ret = 0; pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n", @@ -218,6 +218,7 @@ int __init cma_declare_contiguous(phys_addr_t size, * Each reserved area must be initialised later, when more kernel * subsystems (like slab allocator) are available. */ + cma = &cma_areas[cma_area_count]; cma->base_pfn = PFN_DOWN(base); cma->count = size >> PAGE_SHIFT; cma->order_per_bit = order_per_bit; From fd1de4ccac109c87b556800924842f2ba9fbc9b0 Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Wed, 6 Aug 2014 16:05:38 -0700 Subject: [PATCH 0466/1983] mm, thp: replace smp_mb after atomic_add by smp_mb__after_atomic Upstream-commit: 3a79d52aa3c63c939f5a1f86e80e634f84e987c4 In some architectures like x86, atomic_add() is a full memory barrier. In that case, an additional smp_mb() is just a waste of time. This patch replaces that smp_mb() by smp_mb__after_atomic() which will avoid the redundant memory barrier in some architectures. With a 3.16-rc1 based kernel, this patch reduced the execution time of breaking 1000 transparent huge pages from 38,245us to 30,964us. A reduction of 19% which is quite sizeable. It also reduces the %cpu time of the __split_huge_page_refcount function in the perf profile from 2.18% to 1.15%. Signed-off-by: Waiman Long Acked-by: Kirill A. Shutemov Cc: Andrea Arcangeli Cc: Mel Gorman Cc: Rik van Riel Cc: Scott J Norton Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/huge_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index adce656d2e9c43..743f16d30e69eb 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1725,7 +1725,7 @@ static void __split_huge_page_refcount(struct page *page, &page_tail->_count); /* after clearing PageTail the gup refcount can be released */ - smp_mb(); + smp_mb__after_atomic(); /* * retain hwpoison flag of the poisoned tail page: From 34bedcd87133fc803599189d30ea978424f586a4 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 9 Oct 2014 15:26:47 -0700 Subject: [PATCH 0467/1983] mm: cma: adjust address limit to avoid hitting low/high memory boundary Upstream-commit: f7426b983a6a353cf21e5733e84458219c4a817e Russell King recently noticed that limiting default CMA region only to low memory on ARM architecture causes serious memory management issues with machines having a lot of memory (which is mainly available as high memory). More information can be found the following thread: http://thread.gmane.org/gmane.linux.ports.arm.kernel/348441/ Those two patches removes this limit letting kernel to put default CMA region into high memory when this is possible (there is enough high memory available and architecture specific DMA limit fits). This should solve strange OOM issues on systems with lots of RAM (i.e. >1GiB) and large (>256M) CMA area. This patch (of 2): Automatically allocated regions should not cross low/high memory boundary, because such regions cannot be later correctly initialized due to spanning across two memory zones. This patch adds a check for this case and a simple code for moving region to low memory if automatically selected address might not fit completely into high memory. Signed-off-by: Marek Szyprowski Acked-by: Michal Nazarewicz Cc: Daniel Drake Cc: Minchan Kim Cc: Russell King Cc: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/cma.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mm/cma.c b/mm/cma.c index 488e50810ed187..669d592f6cf0db 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -32,6 +32,7 @@ #include #include #include +#include struct cma { unsigned long base_pfn; @@ -163,6 +164,8 @@ int __init cma_declare_contiguous(phys_addr_t base, bool fixed, struct cma **res_cma) { struct cma *cma; + phys_addr_t memblock_end = memblock_end_of_DRAM(); + phys_addr_t highmem_start = __pa(high_memory); int ret = 0; pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n", @@ -196,6 +199,24 @@ int __init cma_declare_contiguous(phys_addr_t base, if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit)) return -EINVAL; + /* + * adjust limit to avoid crossing low/high memory boundary for + * automatically allocated regions + */ + if (((limit == 0 || limit > memblock_end) && + (memblock_end - size < highmem_start && + memblock_end > highmem_start)) || + (!fixed && limit > highmem_start && limit - size < highmem_start)) { + limit = highmem_start; + } + + if (fixed && base < highmem_start && base+size > highmem_start) { + ret = -EINVAL; + pr_err("Region at %08lx defined on low/high memory boundary (%08lx)\n", + (unsigned long)base, (unsigned long)highmem_start); + goto err; + } + /* Reserve memory */ if (base && fixed) { if (memblock_is_region_reserved(base, size) || From 8453d1eb6149da0cb8417169aaf1b06b68a1e320 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 24 Oct 2014 13:18:41 +0300 Subject: [PATCH 0468/1983] mm: cma: Ensure that reservations never cross the low/high mem boundary Upstream-commit: 16195ddd4ebcc10c30b2f232f8e400df8d464380 Commit 95b0e655f914 ("ARM: mm: don't limit default CMA region only to low memory") extended CMA memory reservation to allow usage of high memory. It relied on commit f7426b983a6a ("mm: cma: adjust address limit to avoid hitting low/high memory boundary") to ensure that the reserved block never crossed the low/high memory boundary. While the implementation correctly lowered the limit, it failed to consider the case where the base..limit range crossed the low/high memory boundary with enough space on each side to reserve the requested size on either low or high memory. Rework the base and limit adjustment to fix the problem. The function now starts by rejecting the reservation altogether for fixed reservations that cross the boundary, tries to reserve from high memory first and then falls back to low memory. Signed-off-by: Laurent Pinchart Signed-off-by: Marek Szyprowski --- mm/cma.c | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/mm/cma.c b/mm/cma.c index 669d592f6cf0db..108fd7ba6b6c98 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -200,23 +200,24 @@ int __init cma_declare_contiguous(phys_addr_t base, return -EINVAL; /* - * adjust limit to avoid crossing low/high memory boundary for - * automatically allocated regions + * If allocating at a fixed base the request region must not cross the + * low/high memory boundary. */ - if (((limit == 0 || limit > memblock_end) && - (memblock_end - size < highmem_start && - memblock_end > highmem_start)) || - (!fixed && limit > highmem_start && limit - size < highmem_start)) { - limit = highmem_start; - } - - if (fixed && base < highmem_start && base+size > highmem_start) { + if (fixed && base < highmem_start && base + size > highmem_start) { ret = -EINVAL; pr_err("Region at %08lx defined on low/high memory boundary (%08lx)\n", (unsigned long)base, (unsigned long)highmem_start); goto err; } + /* + * If the limit is unspecified or above the memblock end, its effective + * value will be the memblock end. Set it explicitly to simplify further + * checks. + */ + if (limit == 0 || limit > memblock_end) + limit = memblock_end; + /* Reserve memory */ if (base && fixed) { if (memblock_is_region_reserved(base, size) || @@ -225,14 +226,30 @@ int __init cma_declare_contiguous(phys_addr_t base, goto err; } } else { - phys_addr_t addr = memblock_alloc_range(size, alignment, base, - limit); + phys_addr_t addr = 0; + + /* + * All pages in the reserved area must come from the same zone. + * If the requested region crosses the low/high memory boundary, + * try allocating from high memory first and fall back to low + * memory in case of failure. + */ + if (base < highmem_start && limit > highmem_start) { + addr = memblock_alloc_range(size, alignment, + highmem_start, limit); + limit = highmem_start; + } + if (!addr) { - ret = -ENOMEM; - goto err; - } else { - base = addr; + addr = memblock_alloc_range(size, alignment, base, + limit); + if (!addr) { + ret = -ENOMEM; + goto err; + } } + + base = addr; } /* From 472f30d8c9eaed18849f478206fd0131b35d2e6a Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 29 Aug 2014 12:44:13 +0200 Subject: [PATCH 0469/1983] mxc: gpu_viv: On broadcast events try harder to get mutex. Just trying once to get a mutex on global power events and then erroring doesn't make much sense. Especially when just looping a second time always seems to succeed. This may need to be bumped up a bit, but I will leave it here until then. I would like to use this as a tracker to help identify what the lowest possible value should be. --- drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c | 2 +- drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 97f1b9c88eb118..7e277fd58ae722 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4418,7 +4418,7 @@ gckHARDWARE_SetPowerManagementState( if (broadcast) { /* Try to acquire the power mutex. */ - status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0); + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 1); if (status == gcvSTATUS_TIMEOUT) { diff --git a/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c index 3cb47dd04b6fc6..590b60592f1c6f 100644 --- a/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c +++ b/drivers/gpu/galcore/archvg/gc_hal_kernel_hardware_vg.c @@ -1669,7 +1669,7 @@ gckVGHARDWARE_SetPowerManagementState( if (broadcast) { /* Try to acquire the power mutex. */ - status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0); + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 1); if (status == gcvSTATUS_TIMEOUT) { From 5b48df356ec5ebd215ced54547e2e47f45e303f8 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 14 Jul 2015 07:57:55 +0200 Subject: [PATCH 0470/1983] gpu: galcore: Optimize gckEVENT_Notify Don't bother trying to get the eventQueueMutex unless we have pending events and are going to do something with them. --- drivers/gpu/galcore/gc_hal_kernel_event.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index f4be42efbd8c06..654c0311b4af87 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -2095,12 +2095,6 @@ gckEVENT_Notify( { gcsEVENT_PTR record; - /* Grab the mutex queue. */ - gcmkONERROR(gckOS_AcquireMutex(Event->os, - Event->eventQueueMutex, - gcvINFINITE)); - acquired = gcvTRUE; - spin_lock_irqsave(&Event->kernel->irq_lock, flags); #if gcdSMP gckOS_AtomGet(Event->os, Event->pending, (gctINT32_PTR)&pending); @@ -2144,6 +2138,12 @@ gckEVENT_Notify( queue = gcvNULL; + /* Grab the mutex queue. */ + gcmkONERROR(gckOS_AcquireMutex(Event->os, + Event->eventQueueMutex, + gcvINFINITE)); + acquired = gcvTRUE; + gcmDEBUG_ONLY( if (IDs == 0) { From 8ec1840192c3c2f9ab1cbb0c0d623e971fde791c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 14 Jul 2015 15:36:02 +0200 Subject: [PATCH 0471/1983] gpu: galcore: Clear errors before checking for events Make sure we clear away any error status interrupts before checking if there are actual events to process. Before we would miss this optimization if we had an error status but no event interrupts. --- drivers/gpu/galcore/gc_hal_kernel_event.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index 654c0311b4af87..595fad9e16fbd4 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -2103,16 +2103,6 @@ gckEVENT_Notify( #endif spin_unlock_irqrestore(&Event->kernel->irq_lock, flags); - if (pending == 0) - { - /* Release the mutex queue. */ - gcmkONERROR(gckOS_ReleaseMutex(Event->os, Event->eventQueueMutex)); - acquired = gcvFALSE; - - /* No more pending interrupts - done. */ - break; - } - if (pending & 0x80000000) { gctUINT32 AQAxiStatus = 0; @@ -2129,6 +2119,12 @@ gckEVENT_Notify( pending &= 0xBFFFFFFF; } + if (pending == 0) + { + /* No more pending interrupts - done. */ + break; + } + gcmkTRACE_ZONE_N( gcvLEVEL_INFO, gcvZONE_EVENT, gcmSIZEOF(pending), From 4f05ef2a1595832fb0f46bdf183d89923e9cf2a8 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 14 Jul 2015 15:39:31 +0200 Subject: [PATCH 0472/1983] gpu: galcore: Be more diligent about our page validation The v5 driver had slightly better page validation but was actually erroring out if we received an invalid page rather than continuing the loop. This is an adaption of a patch by Russell King. --- drivers/gpu/galcore/gc_hal_kernel_os.c | 32 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index a77dfa06e3e946..7ac3efe9ab6c80 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -4723,15 +4723,20 @@ gckOS_MapUserMemory( if (Physical != ~0U) { + unsigned long pfn = Physical >> PAGE_SHIFT; for (i = 0; i < pageCount; i++) { - pages[i] = pfn_to_page((Physical >> PAGE_SHIFT) + i); - - if (pfn_valid(page_to_pfn(pages[i]))) + if (!pfn_valid(pfn + i)) { - get_page(pages[i]); + gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); } } + + for (i = 0; i < pageCount; i++) + { + pages[i] = pfn_to_page(pfn + i); + get_page(pages[i]); + } } else { @@ -4759,13 +4764,11 @@ gckOS_MapUserMemory( { for (i = 0; i < result; i++) { - if (pages[i] == gcvNULL) + if (pages[i] != gcvNULL) { - break; + page_cache_release(pages[i]); + pages[i] = gcvNULL; } - - page_cache_release(pages[i]); - pages[i] = gcvNULL; } result = 0; @@ -4783,6 +4786,7 @@ gckOS_MapUserMemory( { pgd_t * pgd = pgd_offset(current->mm, logical); pud_t * pud = pud_offset(pgd, logical); + unsigned long pfn; if (pud) { @@ -4798,9 +4802,17 @@ gckOS_MapUserMemory( gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); } - pages[i] = pte_page(*pte); + if (pte_present(*pte)) + pfn = pte_pfn(*pte); + else + pfn = ~0UL; pte_unmap_unlock(pte, ptl); + if (pfn == ~0UL || !pfn_valid(pfn)) + gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); + + pages[i] = pfn_to_page(pfn); + /* Advance to next. */ logical += PAGE_SIZE; } From d077aacd504b1cf393f211d0b54f7d3c74ad758a Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 14 Jul 2015 15:44:24 +0200 Subject: [PATCH 0473/1983] gpu: galcore: temp patch to get debugfs logging to work This was kicking off all sorts of sleeping while atomic patches depending on the log level you were using. --- drivers/gpu/galcore/gc_hal_kernel_debugfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/galcore/gc_hal_kernel_debugfs.c b/drivers/gpu/galcore/gc_hal_kernel_debugfs.c index de7b96ccb0910f..23e67977b858b8 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_debugfs.c +++ b/drivers/gpu/galcore/gc_hal_kernel_debugfs.c @@ -475,10 +475,12 @@ _DebugFSPrint ( return - ERESTARTSYS ; } +/* if(down_interruptible( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ) { return - ERESTARTSYS ; } +*/ len = vsnprintf ( buffer , sizeof (buffer ) , Message , *( va_list * ) Arguments ) ; buffer[len] = '\0' ; @@ -490,7 +492,9 @@ _DebugFSPrint ( } res = _AppendString ( gc_dbgfs.currentNode , buffer , len ) ; up ( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ; +#if 0 wake_up_interruptible ( gcmkNODE_READQ ( gc_dbgfs.currentNode ) ) ; /* blocked in read*/ +#endif return res; } From cc970a372db6ef1dbeea21bec16b8f15e9e11cf0 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 14 Jul 2015 15:45:43 +0200 Subject: [PATCH 0474/1983] gpu: galcore: re-enable the VG support. With all my previous fixes we can now re-enable the VG part of the hardware and know it will behave. --- drivers/gpu/galcore/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/galcore/config b/drivers/gpu/galcore/config index 51f3c891101461..a8a3bdb17ca5b2 100644 --- a/drivers/gpu/galcore/config +++ b/drivers/gpu/galcore/config @@ -23,7 +23,7 @@ ARCH_TYPE ?= arm SDK_DIR ?= $(AQROOT)/build/sdk VIVANTE_ENABLE_3D ?= 1 VIVANTE_ENABLE_2D ?= 1 -VIVANTE_ENABLE_VG ?= 0 +VIVANTE_ENABLE_VG ?= 1 FORCE_ALL_VIDEO_MEMORY_CACHED ?= 0 NONPAGED_MEMORY_CACHEABLE ?= 0 NONPAGED_MEMORY_BUFFERABLE ?= 1 From ad784a36dcd96dd03cf8df264783acdc2cef1e51 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 20 Jul 2015 15:49:51 +0200 Subject: [PATCH 0475/1983] gpu: galcore: Don't add more nops than will fit in totalSize If the first NOP falls on an odd address for some reason it would be possible that that we could overrun Context->totalSize. Adjust for this before we start the loop of adding NOP's. --- drivers/gpu/galcore/arch/gc_hal_kernel_context.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c index 0b7961c41263d6..4e9bf969cdcc97 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c @@ -2066,14 +2066,12 @@ gckCONTEXT_Update( /* Determine the location of the first NOP. */ nop = &buffer->logical[base + (elementCount | 1)]; + if ((nop + (nopCount * 2)) >= buffer->logical + Context->totalSize) + nopCount = (buffer->logical + Context->totalSize - nop) / 2; + /* Fill the unused space with NOPs. */ for (i = 0; i < nopCount; i += 1) { - if (nop >= buffer->logical + Context->totalSize) - { - break; - } - /* Generate a NOP command. */ *nop = ((((gctUINT32) (0)) & ~(((gctUINT32) (((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))) | (((gctUINT32) (0x03 & ((gctUINT32) ((((1 ? 31:27) - (0 ? 31:27) + 1) == 32) ? ~0 : (~(~0 << ((1 ? 31:27) - (0 ? 31:27) + 1))))))) << (0 ? 31:27))); From 45735d1b0a4f21b0210e98f639cebc8daa5d4651 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 20 Jul 2015 15:55:53 +0200 Subject: [PATCH 0476/1983] gpu: galcore: remove locking from _QueryProcessPageTable We are just looking up the physical address to a virtual address that belongs to us. I do not think that the locking is really necessary here. Since there are no other kernel drivers using this locking and removing it fixes a kernel warning about slow mutexes I will remove it. --- drivers/gpu/galcore/gc_hal_kernel_os.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index 7ac3efe9ab6c80..bfe388683a6744 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -331,7 +331,6 @@ _QueryProcessPageTable( OUT gctUINT32 * Address ) { - spinlock_t *lock; gctUINTPTR_T logical = (gctUINTPTR_T)Logical; pgd_t *pgd; pud_t *pud; @@ -361,7 +360,7 @@ _QueryProcessPageTable( return gcvSTATUS_NOT_FOUND; } - pte = pte_offset_map_lock(current->mm, pmd, logical, &lock); + pte = pte_offset_map(pmd, logical); if (!pte) { return gcvSTATUS_NOT_FOUND; @@ -369,12 +368,10 @@ _QueryProcessPageTable( if (!pte_present(*pte)) { - pte_unmap_unlock(pte, lock); return gcvSTATUS_NOT_FOUND; } *Address = (pte_pfn(*pte) << PAGE_SHIFT) | (logical & ~PAGE_MASK); - pte_unmap_unlock(pte, lock); return gcvSTATUS_OK; } From 627d229cb5d0ccabf2a2ace5971c41fea65c3495 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 20 Jul 2015 20:20:07 +0200 Subject: [PATCH 0477/1983] gpu: galcore: Remove legacy debug defines For some reason there are 3 different defines that are used to signify debugging. Slim this down to one. Also some of the debug levels are only valid if you recompile userspace. This means we can not use the default of all. --- drivers/gpu/galcore/Kbuild | 6 +++--- drivers/gpu/galcore/arch/gc_hal_kernel_context.c | 2 +- drivers/gpu/galcore/gc_hal_kernel_allocator.c | 2 +- drivers/gpu/galcore/gc_hal_kernel_debug.c | 2 +- drivers/gpu/galcore/inc/gc_hal_types.h | 4 ++-- drivers/gpu/galcore/inc/gc_hal_vg.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/galcore/Kbuild b/drivers/gpu/galcore/Kbuild index 540a78fca7da4e..7695af8aaf24a0 100644 --- a/drivers/gpu/galcore/Kbuild +++ b/drivers/gpu/galcore/Kbuild @@ -49,7 +49,7 @@ MODULE_NAME ?= galcore CUSTOMER_ALLOCATOR_OBJS ?= ALLOCATOR_ARRAY_H_LOCATION ?= allocator/default/ -EXTRA_CFLAGS += -Werror +#EXTRA_CFLAGS += -Werror OBJS := gc_hal_kernel_device.o \ gc_hal_kernel_linux.o \ @@ -125,9 +125,9 @@ else EXTRA_CFLAGS += -DLINUX -DDRIVER ifeq ($(DEBUG), 1) -EXTRA_CFLAGS += -DDBG=1 -DDEBUG -D_DEBUG +EXTRA_CFLAGS += -DDEBUG=1 else -EXTRA_CFLAGS += -DDBG=0 +EXTRA_CFLAGS += -DDEBUG=0 endif ifeq ($(NO_DMA_COHERENT), 1) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c index 4e9bf969cdcc97..0a51f416fe3651 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_context.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_context.c @@ -624,7 +624,7 @@ _InitializeContextBuffer( index += _SwitchPipe(Context, index, gcvPIPE_3D); /* Current context pointer. */ -#if gcdDEBUG +#if DEBUG index += _State(Context, index, 0x03850 >> 2, 0x00000000, 1, gcvFALSE, gcvFALSE); #endif diff --git a/drivers/gpu/galcore/gc_hal_kernel_allocator.c b/drivers/gpu/galcore/gc_hal_kernel_allocator.c index a7e62a4b5aa274..9e7be0a0f28805 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_allocator.c +++ b/drivers/gpu/galcore/gc_hal_kernel_allocator.c @@ -848,7 +848,7 @@ gckOS_ImportAllocators( } } -#if gcdDEBUG +#if DEBUG list_for_each_entry(allocator, &Os->allocatorList, head) { gcmkTRACE_ZONE( diff --git a/drivers/gpu/galcore/gc_hal_kernel_debug.c b/drivers/gpu/galcore/gc_hal_kernel_debug.c index 5eac72fdc5a71d..d3652caba72254 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_debug.c +++ b/drivers/gpu/galcore/gc_hal_kernel_debug.c @@ -27,7 +27,7 @@ \******************************************************************************/ static gceSTATUS _lastError = gcvSTATUS_OK; -static gctUINT32 _debugLevel = gcvLEVEL_INFO; +static gctUINT32 _debugLevel = gcvLEVEL_ERROR; /* _debugZones config value Please Reference define in gc_hal_base.h diff --git a/drivers/gpu/galcore/inc/gc_hal_types.h b/drivers/gpu/galcore/inc/gc_hal_types.h index 7134165df8a9d0..893a3252ca373b 100644 --- a/drivers/gpu/galcore/inc/gc_hal_types.h +++ b/drivers/gpu/galcore/inc/gc_hal_types.h @@ -88,8 +88,8 @@ extern "C" { #define gcmIS_DEBUG(flag) ( gcdDEBUG & (flag | gcdDEBUG_ALL) ) #ifndef gcdDEBUG -#if (defined(DBG) && DBG) || defined(DEBUG) || defined(_DEBUG) -# define gcdDEBUG gcdDEBUG_ALL +#if defined(DEBUG) +# define gcdDEBUG ( gcdDEBUG_FATAL | gcdDEBUG_TRACE | gcdDEBUG_BREAK | gcdDEBUG_ASSERT ) # else # define gcdDEBUG gcdDEBUG_NONE # endif diff --git a/drivers/gpu/galcore/inc/gc_hal_vg.h b/drivers/gpu/galcore/inc/gc_hal_vg.h index 781cb5ab7c3c1f..d5d0b628032276 100644 --- a/drivers/gpu/galcore/inc/gc_hal_vg.h +++ b/drivers/gpu/galcore/inc/gc_hal_vg.h @@ -52,7 +52,7 @@ typedef gctTHREADFUNCRESULT (gctTHREADFUNCTYPE * gctTHREADFUNC) ( #define gcdFORCE_MESSAGES 0 -#if DBG || defined(DEBUG) || defined(_DEBUG) || gcdFORCE_DEBUG +#if defined(DEBUG) # define gcvDEBUG 1 #else # define gcvDEBUG 0 From 04cdacb89357e2957162ef6550bd40c0ce458152 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 21 Jul 2015 08:42:09 +0200 Subject: [PATCH 0478/1983] mxc: vpu: pass device to dma_* operations Add a bit of security by limiting the vpu driver to only free memory it has allocated. --- drivers/mxc/vpu/mxc_vpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c index 0e4497f833d794..d46ddee54ad69c 100644 --- a/drivers/mxc/vpu/mxc_vpu.c +++ b/drivers/mxc/vpu/mxc_vpu.c @@ -259,7 +259,7 @@ static int cpu_is_mx51(void) static int vpu_alloc_dma_buffer(struct vpu_mem_desc *mem) { mem->cpu_addr = (unsigned long) - dma_alloc_coherent(NULL, PAGE_ALIGN(mem->size), + dma_alloc_coherent(vpu_dev, PAGE_ALIGN(mem->size), (dma_addr_t *) (&mem->phy_addr), GFP_DMA | GFP_KERNEL); dev_dbg(vpu_dev, "[ALLOC] mem alloc cpu_addr = 0x%x\n", mem->cpu_addr); @@ -276,7 +276,7 @@ static int vpu_alloc_dma_buffer(struct vpu_mem_desc *mem) static void vpu_free_dma_buffer(struct vpu_mem_desc *mem) { if (mem->cpu_addr != 0) { - dma_free_coherent(0, PAGE_ALIGN(mem->size), + dma_free_coherent(vpu_dev, PAGE_ALIGN(mem->size), (void *)mem->cpu_addr, mem->phy_addr); } } From 6329ea90d351a47b2ea90e8d97f722baf8df2e88 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Wed, 11 Feb 2015 15:28:18 -0800 Subject: [PATCH 0479/1983] mm: always steal split buddies in fallback allocations Upstream-commit: 047ad27045280ac4bc7443 When allocation falls back to another migratetype, it will steal a page with highest available order, and (depending on this order and desired migratetype), it might also steal the rest of free pages from the same pageblock. Given the preference of highest available order, it is likely that it will be higher than the desired order, and result in the stolen buddy page being split. The remaining pages after split are currently stolen only when the rest of the free pages are stolen. This can however lead to situations where for MOVABLE allocations we split e.g. order-4 fallback UNMOVABLE page, but steal only order-0 page. Then on the next MOVABLE allocation (which may be batched to fill the pcplists) we split another order-3 or higher page, etc. By stealing all pages that we have split, we can avoid further stealing. This patch therefore adjusts the page stealing so that buddy pages created by split are always stolen. This has effect only on MOVABLE allocations, as RECLAIMABLE and UNMOVABLE allocations already always do that in addition to stealing the rest of free pages from the pageblock. The change also allows to simplify try_to_steal_freepages() and factor out CMA handling. According to Mel, it has been intended since the beginning that buddy pages after split would be stolen always, but it doesn't seem like it was ever the case until commit 47118af076f6 ("mm: mmzone: MIGRATE_CMA migration type added"). The commit has unintentionally introduced this behavior, but was reverted by commit 0cbef29a7821 ("mm: __rmqueue_fallback() should respect pageblock type"). Neither included evaluation. My evaluation with stress-highalloc from mmtests shows about 2.5x reduction of page stealing events for MOVABLE allocations, without affecting the page stealing events for other allocation migratetypes. Signed-off-by: Vlastimil Babka Acked-by: Mel Gorman Cc: Zhang Yanfei Acked-by: Minchan Kim Cc: David Rientjes Cc: Rik van Riel Cc: "Aneesh Kumar K.V" Cc: "Kirill A. Shutemov" Cc: Johannes Weiner Cc: Joonsoo Kim Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 62 +++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0479732f6b02bf..3c50fc48d0a4d3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1075,33 +1075,18 @@ static void change_pageblock_range(struct page *pageblock_page, /* * If breaking a large block of pages, move all free pages to the preferred * allocation list. If falling back for a reclaimable kernel allocation, be - * more aggressive about taking ownership of free pages. - * - * On the other hand, never change migration type of MIGRATE_CMA pageblocks - * nor move CMA pages to different free lists. We don't want unmovable pages - * to be allocated from MIGRATE_CMA areas. - * - * Returns the allocation migratetype if free pages were stolen, or the - * fallback migratetype if it was decided not to steal. + * more aggressive about taking ownership of free pages. If we claim more than + * half of the pageblock, change pageblock's migratetype as well. */ -static int try_to_steal_freepages(struct zone *zone, struct page *page, +static void try_to_steal_freepages(struct zone *zone, struct page *page, int start_type, int fallback_type) { int current_order = page_order(page); - /* - * When borrowing from MIGRATE_CMA, we need to release the excess - * buddy pages to CMA itself. We also ensure the freepage_migratetype - * is set to CMA so it is returned to the correct freelist in case - * the page ends up being not actually allocated from the pcp lists. - */ - if (is_migrate_cma(fallback_type)) - return fallback_type; - /* Take ownership for orders >= pageblock_order */ if (current_order >= pageblock_order) { change_pageblock_range(page, current_order, start_type); - return start_type; + return; } if (current_order >= pageblock_order / 2 || @@ -1115,11 +1100,7 @@ static int try_to_steal_freepages(struct zone *zone, struct page *page, if (pages >= (1 << (pageblock_order-1)) || page_group_by_mobility_disabled) set_pageblock_migratetype(page, start_type); - - return start_type; } - - return fallback_type; } /* Remove an element from the buddy allocator from the fallback list */ @@ -1129,14 +1110,15 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype) struct free_area *area; unsigned int current_order; struct page *page; - int migratetype, new_type, i; /* Find the largest possible block of pages in the other list */ for (current_order = MAX_ORDER-1; current_order >= order && current_order <= MAX_ORDER-1; --current_order) { + int i; for (i = 0;; i++) { - migratetype = fallbacks[start_migratetype][i]; + int migratetype = fallbacks[start_migratetype][i]; + int buddy_type = start_migratetype; /* MIGRATE_RESERVE handled later if necessary */ if (migratetype == MIGRATE_RESERVE) @@ -1150,22 +1132,36 @@ __rmqueue_fallback(struct zone *zone, unsigned int order, int start_migratetype) struct page, lru); area->nr_free--; - new_type = try_to_steal_freepages(zone, page, - start_migratetype, - migratetype); + if (!is_migrate_cma(migratetype)) { + try_to_steal_freepages(zone, page, + start_migratetype, + migratetype); + } else { + /* + * When borrowing from MIGRATE_CMA, we need to + * release the excess buddy pages to CMA + * itself, and we do not try to steal extra + * free pages. + */ + buddy_type = migratetype; + } /* Remove the page from the freelists */ list_del(&page->lru); rmv_page_order(page); expand(zone, page, order, current_order, area, - new_type); - /* The freepage_migratetype may differ from pageblock's + buddy_type); + + /* + * The freepage_migratetype may differ from pageblock's * migratetype depending on the decisions in - * try_to_steal_freepages. This is OK as long as it does - * not differ for MIGRATE_CMA type. + * try_to_steal_freepages(). This is OK as long as it + * does not differ for MIGRATE_CMA pageblocks. For CMA + * we need to make sure unallocated pages flushed from + * pcp lists are returned to the correct freelist. */ - set_freepage_migratetype(page, new_type); + set_freepage_migratetype(page, buddy_type); trace_mm_page_alloc_extfrag(page, order, current_order, start_migratetype, migratetype); From e00da1ca18bf7a9d951f8569bcd4a0e4e2a6d857 Mon Sep 17 00:00:00 2001 From: Vlastimil Babka Date: Wed, 11 Feb 2015 15:28:21 -0800 Subject: [PATCH 0480/1983] mm: more aggressive page stealing for UNMOVABLE allocations Upstream-commit: 9c0415eb8cbf0c8fd043b6c0f0354308ab099df5 When allocation falls back to stealing free pages of another migratetype, it can decide to steal extra pages, or even the whole pageblock in order to reduce fragmentation, which could happen if further allocation fallbacks pick a different pageblock. In try_to_steal_freepages(), one of the situations where extra pages are stolen happens when we are trying to allocate a MIGRATE_RECLAIMABLE page. However, MIGRATE_UNMOVABLE allocations are not treated the same way, although spreading such allocation over multiple fallback pageblocks is arguably even worse than it is for RECLAIMABLE allocations. To minimize fragmentation, we should minimize the number of such fallbacks, and thus steal as much as is possible from each fallback pageblock. Note that in theory this might put more pressure on movable pageblocks and cause movable allocations to steal back from unmovable pageblocks. However, movable allocations are not as aggressive with stealing, and do not cause permanent fragmentation, so the tradeoff is reasonable, and evaluation seems to support the change. This patch thus adds a check for MIGRATE_UNMOVABLE to the decision to steal extra free pages. When evaluating with stress-highalloc from mmtests, this has reduced the number of MIGRATE_UNMOVABLE fallbacks to roughly 1/6. The number of these fallbacks stealing from MIGRATE_MOVABLE block is reduced to 1/3. There was no observation of growing number of unmovable pageblocks over time, and also not of increased movable allocation fallbacks. Signed-off-by: Vlastimil Babka Acked-by: Mel Gorman Cc: Zhang Yanfei Cc: Minchan Kim Cc: David Rientjes Cc: Rik van Riel Cc: "Aneesh Kumar K.V" Cc: "Kirill A. Shutemov" Cc: Johannes Weiner Cc: Joonsoo Kim Cc: Michal Hocko Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 3c50fc48d0a4d3..0aee7f32006edf 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1073,10 +1073,19 @@ static void change_pageblock_range(struct page *pageblock_page, } /* - * If breaking a large block of pages, move all free pages to the preferred - * allocation list. If falling back for a reclaimable kernel allocation, be - * more aggressive about taking ownership of free pages. If we claim more than - * half of the pageblock, change pageblock's migratetype as well. + * When we are falling back to another migratetype during allocation, try to + * steal extra free pages from the same pageblocks to satisfy further + * allocations, instead of polluting multiple pageblocks. + * + * If we are stealing a relatively large buddy page, it is likely there will + * be more free pages in the pageblock, so try to steal them all. For + * reclaimable and unmovable allocations, we steal regardless of page size, + * as fragmentation caused by those allocations polluting movable pageblocks + * is worse than movable allocations stealing from unmovable and reclaimable + * pageblocks. + * + * If we claim more than half of the pageblock, change pageblock's migratetype + * as well. */ static void try_to_steal_freepages(struct zone *zone, struct page *page, int start_type, int fallback_type) @@ -1091,6 +1100,7 @@ static void try_to_steal_freepages(struct zone *zone, struct page *page, if (current_order >= pageblock_order / 2 || start_type == MIGRATE_RECLAIMABLE || + start_type == MIGRATE_UNMOVABLE || page_group_by_mobility_disabled) { int pages; From 47a1df45a6fe47567f329cf31c283af54da26ceb Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 21 Jul 2015 09:41:46 +0200 Subject: [PATCH 0481/1983] arm: configs: Add defconfigs for SolidRun hbi/cb Move these over from the old repository. These are a base and will need to be tweaked for new drivers etc. --- arch/arm/configs/imx_v7_cbi_hb_base_defconfig | 367 ++ arch/arm/configs/imx_v7_cbi_hb_defconfig | 5138 +++++++++++++++++ 2 files changed, 5505 insertions(+) create mode 100644 arch/arm/configs/imx_v7_cbi_hb_base_defconfig create mode 100644 arch/arm/configs/imx_v7_cbi_hb_defconfig diff --git a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig new file mode 100644 index 00000000000000..3f134686bbf277 --- /dev/null +++ b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig @@ -0,0 +1,367 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_FHANDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_ZSWAP=y +CONFIG_ZSMALLOC=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y +CONFIG_MXC_DEBUG_BOARD=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +# CONFIG_SWP_EMULATE is not set +CONFIG_PCI=y +CONFIG_PCIE_DW=y +CONFIG_PCI_IMX6=y +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_HIGHMEM=y +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_BFQ=y +CONFIG_DEFAULT_IOSCHED="bfq" +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_VLAN_8021Q=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_FARADAY +# CONFIG_NET_VENDOR_INTEL +# CONFIG_NET_VENDOR_I825XX +# CONFIG_NET_VENDOR_MARVELL +# CONFIG_NET_VENDOR_MICROCHIP +# CONFIG_NET_VENDOR_MICROCHIP=y +# CONFIG_ENC28J60 is not set +# CONFIG_NET_VENDOR_NATSEMI=y +# CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +# CONFIG_SH_ETH is not set +# CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_NET_VENDOR_SMSC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +# CONFIG_NET_VENDOR_VIA=y +# CONFIG_VIA_VELOCITY is not set +# CONFIG_NET_VENDOR_WIZNET=y +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_FEC=y +CONFIG_PHYLIB=y +CONFIG_AT803X_PHY=y +CONFIG_WLAN=y +CONFIG_BRCMUTIL=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_SDIO=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_DMA_CMA=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_CONNECTOR=y +# CONFIG_MTD is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_NETDEVICES=y +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_IMX=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_MOUSE_PS2 is not set +CONFIG_INPUT_MISC=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_FSL_OTP=y +CONFIG_GPIO_MXC=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_SI476X_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_RADIO_SUPPORT is not set +CONFIG_VIDEO_V4L2_INT_DEVICE=y +# CONFIG_MEDIA_USB_SUPPORT isnot set +# CONFIG_USB_VIDEO_CLASS is not set +# CONFIG_RADIO_ADAPTERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_CAPTURE=m +CONFIG_VIDEO_MXC_CSI_CAMERA=m +CONFIG_MXC_CAMERA_OV5640=m +CONFIG_MXC_CAMERA_OV5642=m +CONFIG_MXC_CAMERA_OV5640_MIPI=m +CONFIG_MXC_TVIN_ADV7180=m +CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m +CONFIG_VIDEO_MXC_IPU_OUTPUT=y +CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_SOC_CAMERA=y +CONFIG_SOC_CAMERA_OV2640=y +CONFIG_DRM=y +CONFIG_DRM_VIVANTE=y +CONFIG_FB=y +# CONFIG_FB_MX3 is not set +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_LDB=y +CONFIG_FB_MXC_HDMI=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_IMX_SGTL5000=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_HDMI=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y +CONFIG_MXC_HDMI_CEC=y +CONFIG_MXC_MIPI_CSI2=y +CONFIG_MXC_MLB150=m +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_RTC_DRV_PCF8523=y +CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_SRAM=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_SYSFS=y +CONFIG_PWM_IMX=y +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +# CONFIG_IPACK_BUS is not set +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_GPIO=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD128=y +CONFIG_CRYPTO_RMD160=y +CONFIG_CRYPTO_RMD256=y +CONFIG_CRYPTO_RMD320=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TGR192=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_CAMELLIA=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRYPTO_AES_ARM_BS=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +# CONFIG_MXC_MMA8451 is not set +CONFIG_RC_CORE=m +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_RC_LOOPBACK=m +CONFIG_RC_MAP=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_RC5_SZ_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_IMON=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_ITE_CIR=m +CONFIG_IR_NUVOTON=m +CONFIG_IR_FINTEK=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_ENE=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_WINBOND_CIR=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_IR_GPIO_CIR=m diff --git a/arch/arm/configs/imx_v7_cbi_hb_defconfig b/arch/arm/configs/imx_v7_cbi_hb_defconfig new file mode 100644 index 00000000000000..164c758bb2990c --- /dev/null +++ b/arch/arm/configs/imx_v7_cbi_hb_defconfig @@ -0,0 +1,5138 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_MMU=y +CONFIG_HOTPLUG_CPU=y +# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set +# CONFIG_DEBUG_HOTPLUG_CPU0 is not set +CONFIG_LOCALVERSION="" +CONFIG_CROSS_COMPILE="" +CONFIG_DEFAULT_HOSTNAME="(none)" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_HOTPLUG=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_PREVENT_FIRMWARE_BUILD=y + +CONFIG_BUILD_DOCSRC=y + +# +# General setup +# +CONFIG_KERNEL_LZO=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_SWAP=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +# CONFIG_COMPILE_TEST is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_SYSCTL=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_BFQ=y +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_PID_NS=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_NET_NS=y +CONFIG_USER_NS=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +CONFIG_SYSVIPC=y +CONFIG_FHANDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y + +CONFIG_POSIX_MQUEUE=y +CONFIG_PREEMPT_VOLUNTARY=y + +CONFIG_SLUB=y +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SLUB_STATS is not set +# CONFIG_SLUB_DEBUG_ON is not set + +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_IWMC3200TOP is not set +# CONFIG_BLK_DEV_BSG is not set + +# MX6 specific kernel configuration +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y +CONFIG_MXC_DEBUG_BOARD=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +# CONFIG_SWP_EMULATE is not set +CONFIG_PCI=y +CONFIG_PCIE_DW=y +CONFIG_PCI_IMX6=y +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_HIGHMEM=y +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +CONFIG_KERNEL_MODE_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_VLAN_8021Q=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +CONFIG_ETHERNET=y +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_FARADAY +# CONFIG_NET_VENDOR_INTEL +# CONFIG_NET_VENDOR_I825XX +# CONFIG_NET_VENDOR_MARVELL +# CONFIG_NET_VENDOR_MICROCHIP +# CONFIG_NET_VENDOR_MICROCHIP=y +# CONFIG_ENC28J60 is not set +# CONFIG_NET_VENDOR_NATSEMI=y +# CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +# CONFIG_SH_ETH is not set +# CONFIG_NET_VENDOR_SEEQ=y +# CONFIG_NET_VENDOR_SMSC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +# CONFIG_NET_VENDOR_VIA=y +# CONFIG_VIA_VELOCITY is not set +# CONFIG_NET_VENDOR_WIZNET=y +CONFIG_NET_VENDOR_FREESCALE=y +CONFIG_FEC=y +CONFIG_PHYLIB=y +CONFIG_AT803X_PHY=y +CONFIG_WLAN=y +CONFIG_BRCMUTIL=m +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_SDIO=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_DMA_CMA=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_CONNECTOR=y +# CONFIG_MTD is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_NETDEVICES=y +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_IMX=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_MOUSE_PS2 is not set +CONFIG_INPUT_MISC=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_FSL_OTP=y +CONFIG_GPIO_MXC=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_SI476X_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_RADIO_SUPPORT is not set +CONFIG_VIDEO_V4L2_INT_DEVICE=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +# CONFIG_RADIO_ADAPTERS is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_CAPTURE=m +CONFIG_VIDEO_MXC_CSI_CAMERA=m +CONFIG_MXC_CAMERA_OV5640=m +CONFIG_MXC_CAMERA_OV5642=m +CONFIG_MXC_CAMERA_OV5640_MIPI=m +CONFIG_MXC_TVIN_ADV7180=m +CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m +CONFIG_VIDEO_MXC_IPU_OUTPUT=y +CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_SOC_CAMERA=y +CONFIG_SOC_CAMERA_OV2640=y +CONFIG_DRM=y +CONFIG_DRM_VIVANTE=y +CONFIG_FB=y +# CONFIG_FB_MX3 is not set +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_LDB=y +CONFIG_FB_MXC_HDMI=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_IMX_SGTL5000=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_HDMI=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y +CONFIG_MXC_HDMI_CEC=y +CONFIG_MXC_MIPI_CSI2=y +CONFIG_MXC_MLB150=m +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_RTC_DRV_PCF8523=y +CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_SRAM=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_SYSFS=y +CONFIG_PWM_IMX=y +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_GPIO=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD128=y +CONFIG_CRYPTO_RMD160=y +CONFIG_CRYPTO_RMD256=y +CONFIG_CRYPTO_RMD320=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TGR192=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_CAMELLIA=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRYPTO_AES_ARM_BS=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m +# CONFIG_MXC_MMA8451 is not set + +# +# Loadable module support +# +# CONFIG_MODULE_FORCE_LOAD is not set +# -- MODULE_FORCE_UNLOAD is controlled by config-debug/nodebug + +# CONFIG_PCI_DEBUG is not set +CONFIG_PCI_STUB=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PRI=y +CONFIG_PCI_PASID=y +CONFIG_HT_IRQ=y +CONFIG_PCI_MSI=y +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCIEAER=y +CONFIG_PCIEASPM=y +# CONFIG_PCIEASPM_DEBUG is not set +CONFIG_PCIE_ECRC=y +CONFIG_PCIEAER_INJECT=m +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_HOTPLUG_PCI_FAKE=m + +# CONFIG_SGI_IOC4 is not set + +# CONFIG_ISA is not set +# CONFIG_SCx200 is not set + +# +# PCMCIA/CardBus support +# FIXME: Deprecate Cardbus ? +# +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_YENTA=m +CONFIG_CARDBUS=y +CONFIG_I82092=m +CONFIG_PD6729=m + +CONFIG_PCCARD=y +CONFIG_SDIO_UART=m +# CONFIG_MMC_TEST is not set +# CONFIG_MMC_DEBUG is not set +# https://lists.fedoraproject.org/pipermail/kernel/2014-February/004889.html +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_MMC_BLOCK_BOUNCE=y +CONFIG_MMC_SDHCI_PCI=m +CONFIG_MMC_SDHCI_ACPI=m +CONFIG_MMC_SDRICOH_CS=m +CONFIG_MMC_TIFM_SD=m +CONFIG_MMC_WBSD=m +CONFIG_MMC_VIA_SDMMC=m +CONFIG_MMC_CB710=m +CONFIG_MMC_RICOH_MMC=y +CONFIG_MMC_USHC=m +CONFIG_MMC_REALTEK_PCI=m +CONFIG_MMC_VUB300=m +# CONFIG_MMC_SDHCI_PXAV2 is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_OF_ARASAN is not set + + +CONFIG_CB710_CORE=m +# CONFIG_CB710_DEBUG is not set + +CONFIG_INFINIBAND=m +CONFIG_INFINIBAND_MTHCA=m +# CONFIG_INFINIBAND_MTHCA_DEBUG is not set +CONFIG_INFINIBAND_IPOIB=m +CONFIG_INFINIBAND_IPOIB_DEBUG=y +CONFIG_INFINIBAND_IPOIB_DEBUG_DATA=y +CONFIG_INFINIBAND_IPOIB_CM=y +CONFIG_INFINIBAND_SRP=m +CONFIG_INFINIBAND_SRPT=m +CONFIG_INFINIBAND_USER_MAD=m +CONFIG_INFINIBAND_USER_ACCESS=m +# CONFIG_INFINIBAND_EXPERIMENTAL_UVERBS_FLOW_STEERING is not set #staging +CONFIG_INFINIBAND_IPATH=m +CONFIG_INFINIBAND_ISER=m +CONFIG_INFINIBAND_ISERT=m +CONFIG_INFINIBAND_AMSO1100=m +# CONFIG_INFINIBAND_AMSO1100_DEBUG is not set +CONFIG_INFINIBAND_CXGB3=m +CONFIG_INFINIBAND_CXGB4=m +CONFIG_SCSI_CXGB3_ISCSI=m +CONFIG_SCSI_CXGB4_ISCSI=m +# CONFIG_INFINIBAND_CXGB3_DEBUG is not set +CONFIG_MLX4_INFINIBAND=m +CONFIG_MLX5_INFINIBAND=m +CONFIG_INFINIBAND_NES=m +# CONFIG_INFINIBAND_NES_DEBUG is not set +CONFIG_INFINIBAND_QIB=m +CONFIG_INFINIBAND_QIB_DCA=y +# CONFIG_INFINIBAND_OCRDMA is not set +# CONFIG_INFINIBAND_USNIC is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_SCRIPT=y + +# +# Device Drivers +# + +# CONFIG_COMMON_CLK_SI5351 is not set + +# +# Generic Driver Options +# +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" + +# Give this a try in rawhide for now +# CONFIG_FW_LOADER_USER_HELPER is not set + + + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +# CONFIG_MTD_CHAR is not set +# CONFIG_MTD_BLKDEVS is not set +# CONFIG_MTD_BLOCK is not set +# CONFIG_MTD_BLOCK_RO is not set +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set +# CONFIG_MTD_SWAP is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_TS5500 is not set +# CONFIG_MTD_INTEL_VR_NOR is not set +# CONFIG_MTD_PLATRAM is not set + +# Self-contained MTD device drivers +# CONFIG_MTD_PMC551 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOCG3 is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_BCH is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_LPDDR is not set +CONFIG_MTD_UBI=m +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_LIMIT=20 +# CONFIG_MTD_UBI_FASTMAP is not set +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# Parallel port support +# +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_SERIAL=m +# CONFIG_PARPORT_PC_FIFO is not set +# CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_PC_PCMCIA=m +CONFIG_PARPORT_1284=y +# CONFIG_PARPORT_AX88796 is not set + +CONFIG_ACPI_PCI_SLOT=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_HOTPLUG_PCI_ACPI_IBM=m + +# +# Block devices +# +CONFIG_BLK_DEV=y +CONFIG_BLK_DEV_NULL_BLK=m +CONFIG_BLK_DEV_FD=m +# CONFIG_PARIDE is not set +CONFIG_ZRAM=m +# CONFIG_ZRAM_DEBUG is not set +CONFIG_ENHANCEIO=m + +CONFIG_BLK_CPQ_DA=m +CONFIG_BLK_CPQ_CISS_DA=m +CONFIG_CISS_SCSI_TAPE=y +CONFIG_BLK_DEV_DAC960=m +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set +CONFIG_BLK_DEV_DRBD=m +CONFIG_BLK_DEV_UMEM=m +CONFIG_BLK_DEV_LOOP_MIN_COUNT=0 +# Fedora 18 util-linux is the last release that supports cryptoloop devices +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_NVME=m +CONFIG_BLK_DEV_SKD=m # 64-bit only but easier to put here +CONFIG_BLK_DEV_OSD=m +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_IO_TRACE=y + +CONFIG_BLK_DEV_BSGLIB=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_THROTTLING=y +# CONFIG_BLK_CMDLINE_PARSER is not set + + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_RSXX is not set + +CONFIG_SCSI_VIRTIO=m +CONFIG_VIRTIO_BLK=m +CONFIG_VIRTIO_PCI=m +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_MMIO=m +# CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES is not set +CONFIG_VIRTIO_NET=m +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_VIRTIO_CONSOLE=m +CONFIG_VHOST_NET=m +CONFIG_TCM_VHOST=m +CONFIG_VHOST_SCSI=m + +# +# SCSI device support +# +CONFIG_SCSI=y + +CONFIG_SCSI_ENCLOSURE=m +CONFIG_SCSI_SRP=m +CONFIG_SCSI_SRP_ATTRS=m +CONFIG_SCSI_TGT=m +CONFIG_SCSI_ISCI=m +CONFIG_SCSI_CHELSIO_FCOE=m + +CONFIG_SCSI_DH=y +CONFIG_SCSI_DH_RDAC=m +CONFIG_SCSI_DH_HP_SW=m +CONFIG_SCSI_DH_EMC=m +CONFIG_SCSI_DH_ALUA=m + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_FC_TGT_ATTRS=y +CONFIG_SCSI_ISCSI_ATTRS=m +CONFIG_SCSI_SAS_ATTRS=m +CONFIG_SCSI_SRP_TGT_ATTRS=y +CONFIG_SCSI_SAS_LIBSAS=m +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_SAS_HOST_SMP=y +CONFIG_RAID_ATTRS=m + +CONFIG_ISCSI_TCP=m +CONFIG_ISCSI_BOOT_SYSFS=m + +# +# SCSI low-level drivers +# +CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_SCSI_3W_9XXX=m +CONFIG_SCSI_ACARD=m +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_AIC7XXX=m +# http://lists.fedoraproject.org/pipermail/kernel/2013-February/004102.html +# CONFIG_SCSI_AIC7XXX_OLD is not set +CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 +CONFIG_AIC7XXX_RESET_DELAY_MS=15000 +# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set +# CONFIG_AIC7XXX_DEBUG_ENABLE is not set +CONFIG_AIC7XXX_DEBUG_MASK=0 +# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC79XX=m +CONFIG_AIC79XX_CMDS_PER_DEVICE=4 +CONFIG_AIC79XX_RESET_DELAY_MS=15000 +# CONFIG_AIC79XX_BUILD_FIRMWARE is not set +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set +CONFIG_SCSI_AIC94XX=m +# CONFIG_AIC94XX_DEBUG is not set +# CONFIG_SCSI_ADVANSYS is not set +CONFIG_SCSI_BFA_FC=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=m +CONFIG_MEGARAID_MAILBOX=m +CONFIG_MEGARAID_LEGACY=m +CONFIG_MEGARAID_SAS=m +CONFIG_SCSI_ESAS2R=m +CONFIG_SCSI_MVSAS=m +# CONFIG_SCSI_MVSAS_DEBUG is not set +CONFIG_SCSI_MVSAS_TASKLET=y +CONFIG_SCSI_MPT2SAS=m +CONFIG_SCSI_MPT2SAS_MAX_SGE=128 +CONFIG_SCSI_MPT2SAS_LOGGING=y +CONFIG_SCSI_MPT3SAS=m +CONFIG_SCSI_MPT3SAS_MAX_SGE=128 +CONFIG_SCSI_MPT3SAS_LOGGING=y + +CONFIG_SCSI_UFSHCD=m +CONFIG_SCSI_UFSHCD_PCI=m +# CONFIG_SCSI_UFSHCD_PLATFORM is not set + +CONFIG_SCSI_MVUMI=m + +CONFIG_SCSI_OSD_INITIATOR=m +CONFIG_SCSI_OSD_ULD=m +CONFIG_SCSI_OSD_DPRINT_SENSE=1 +# CONFIG_SCSI_OSD_DEBUG is not set + +CONFIG_SCSI_BNX2_ISCSI=m +CONFIG_SCSI_BNX2X_FCOE=m +CONFIG_BE2ISCSI=m +CONFIG_SCSI_PMCRAID=m + +CONFIG_SCSI_HPSA=m +CONFIG_SCSI_3W_SAS=m +CONFIG_SCSI_PM8001=m +CONFIG_VMWARE_PVSCSI=m +CONFIG_VMWARE_BALLOON=m + +CONFIG_SCSI_ARCMSR=m +CONFIG_SCSI_BUSLOGIC=m +CONFIG_SCSI_INITIO=m +CONFIG_SCSI_FLASHPOINT=y +CONFIG_SCSI_DMX3191D=m +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +CONFIG_SCSI_GDTH=m +CONFIG_SCSI_HPTIOP=m +CONFIG_SCSI_IPS=m +CONFIG_SCSI_INIA100=m +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_IMM is not set +# CONFIG_SCSI_IZIP_EPP16 is not set +# CONFIG_SCSI_IZIP_SLOW_CTR is not set +CONFIG_SCSI_STEX=m +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 +CONFIG_SCSI_SYM53C8XX_MMIO=y +CONFIG_SCSI_QLOGIC_1280=m +CONFIG_SCSI_DC395x=m +# CONFIG_SCSI_NSP32 is not set +CONFIG_SCSI_DEBUG=m +CONFIG_SCSI_DC390T=m +CONFIG_SCSI_QLA_FC=m +CONFIG_TCM_QLA2XXX=m +CONFIG_SCSI_QLA_ISCSI=m +CONFIG_SCSI_IPR=m +CONFIG_SCSI_IPR_TRACE=y +CONFIG_SCSI_IPR_DUMP=y +# CONFIG_SCSI_DPT_I2O is not set +CONFIG_SCSI_LPFC=m +# CONFIG_SCSI_LPFC_DEBUG_FS is not set + +# PCMCIA SCSI adapter support +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set + +CONFIG_ATA_BMDMA=y +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_ATA_SFF=y +CONFIG_ATA_PIIX=y +# CONFIG_SATA_HIGHBANK is not set +CONFIG_ATA_ACPI=y +CONFIG_BLK_DEV_SX8=m +CONFIG_PDC_ADMA=m +CONFIG_SATA_AHCI=y +CONFIG_SATA_INIC162X=m +CONFIG_SATA_MV=m +CONFIG_SATA_NV=m +CONFIG_SATA_PMP=y +CONFIG_SATA_PROMISE=m +CONFIG_SATA_QSTOR=m +CONFIG_SATA_RCAR=m +CONFIG_SATA_SIL=m +CONFIG_SATA_SIL24=m +CONFIG_SATA_SIS=m +CONFIG_SATA_SVW=m +CONFIG_SATA_SX4=m +CONFIG_SATA_ULI=m +CONFIG_SATA_VIA=m +CONFIG_SATA_VITESSE=m +# CONFIG_SATA_ZPODD is not set +CONFIG_SATA_ACARD_AHCI=m + +# CONFIG_PATA_LEGACY is not set +CONFIG_PATA_ACPI=m +CONFIG_PATA_ALI=m +CONFIG_PATA_AMD=m +CONFIG_PATA_ARASAN_CF=m +CONFIG_PATA_ARTOP=m +CONFIG_PATA_ATIIXP=m +CONFIG_PATA_CMD640_PCI=m +CONFIG_PATA_CMD64X=m +CONFIG_PATA_CS5520=m +CONFIG_PATA_CS5530=m +CONFIG_PATA_CS5535=m +CONFIG_PATA_CS5536=m +CONFIG_PATA_CYPRESS=m +CONFIG_PATA_EFAR=m +CONFIG_ATA_GENERIC=m +CONFIG_PATA_HPT366=m +CONFIG_PATA_HPT37X=m +CONFIG_PATA_HPT3X2N=m +CONFIG_PATA_HPT3X3=m +# CONFIG_PATA_HPT3X3_DMA is not set +CONFIG_PATA_IT821X=m +CONFIG_PATA_IT8213=m +CONFIG_PATA_JMICRON=m +CONFIG_PATA_NINJA32=m +CONFIG_PATA_MARVELL=m +CONFIG_PATA_MPIIX=m +CONFIG_PATA_NETCELL=m +CONFIG_PATA_NS87410=m +CONFIG_PATA_NS87415=m +CONFIG_PATA_OLDPIIX=m +CONFIG_PATA_OPTI=m +CONFIG_PATA_OPTIDMA=m +CONFIG_PATA_PCMCIA=m +CONFIG_PATA_PDC_OLD=m +# CONFIG_PATA_RADISYS is not set +CONFIG_PATA_RDC=m +# CONFIG_PATA_RZ1000 is not set +# CONFIG_PATA_SC1200 is not set +CONFIG_PATA_SERVERWORKS=m +CONFIG_PATA_PDC2027X=m +CONFIG_PATA_SCH=m +CONFIG_PATA_SIL680=m +CONFIG_PATA_SIS=m +CONFIG_PATA_TOSHIBA=m +CONFIG_PATA_TRIFLEX=m +CONFIG_PATA_VIA=m +CONFIG_PATA_WINBOND=m +CONFIG_PATA_ATP867X=m + + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_AUTODETECT=y +CONFIG_MD_FAULTY=m +CONFIG_MD_LINEAR=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m + +CONFIG_BCACHE=m +# CONFIG_BCACHE_DEBUG is not set +# CONFIG_BCACHE_EDEBUG is not set +# CONFIG_BCACHE_CLOSURES_DEBUG is not set + +# CONFIG_MULTICORE_RAID456 is not set +CONFIG_ASYNC_RAID6_TEST=m +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=m +CONFIG_DM_DEBUG=y +CONFIG_DM_DELAY=m +CONFIG_DM_MIRROR=y +CONFIG_DM_MULTIPATH=m +CONFIG_DM_SNAPSHOT=y +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_DM_CACHE=m +CONFIG_DM_CACHE_MQ=m +CONFIG_DM_CACHE_CLEANER=m +# CONFIG_DM_DEBUG_BLOCK_STACK_TRACING is not set +# CONFIG_DM_DEBUG_SPACE_MAPS is not set +CONFIG_DM_UEVENT=y +CONFIG_DM_ZERO=y +CONFIG_DM_LOG_USERSPACE=m +CONFIG_DM_MULTIPATH_QL=m +CONFIG_DM_MULTIPATH_ST=m +CONFIG_DM_RAID=m +CONFIG_DM_FLAKEY=m +CONFIG_DM_VERITY=m +CONFIG_DM_SWITCH=m + +# +# Fusion MPT device support +# +CONFIG_FUSION=y +CONFIG_FUSION_SPI=m +CONFIG_FUSION_FC=m +CONFIG_FUSION_MAX_SGE=40 +CONFIG_FUSION_CTL=m +CONFIG_FUSION_LAN=m +CONFIG_FUSION_SAS=m +CONFIG_FUSION_LOGGING=y + +# +# IEEE 1394 (FireWire) support (JUJU alternative stack) +# +CONFIG_FIREWIRE=m +CONFIG_FIREWIRE_OHCI=m +CONFIG_FIREWIRE_SBP2=m +CONFIG_FIREWIRE_NET=m +CONFIG_FIREWIRE_OHCI_DEBUG=y +CONFIG_FIREWIRE_NOSY=m +# CONFIG_FIREWIRE_SERIAL is not set +# CONFIG_FIREWIRE_OHCI_REMOTE_DMA is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# +# CONFIG_I2O is not set +# CONFIG_I2O_LCT_NOTIFY_ON_CHANGES is not set + +# +# Virtualization support drivers +# +# CONFIG_VIRT_DRIVERS is not set + +# Networking support +# + +CONFIG_NET_DMA=y + +CONFIG_NETLINK_MMAP=y +CONFIG_NETLINK_DIAG=m + +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HSTCP=m +CONFIG_TCP_CONG_HYBLA=m +CONFIG_TCP_CONG_ILLINOIS=m +CONFIG_TCP_CONG_LP=m +CONFIG_TCP_CONG_SCALABLE=m +CONFIG_TCP_CONG_VEGAS=m +CONFIG_TCP_CONG_VENO=m +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_YEAH=m + +CONFIG_TCP_MD5SIG=y + +# +# Networking options +# +CONFIG_PACKET_DIAG=m +CONFIG_UNIX_DIAG=m +CONFIG_NET_KEY=m +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET_TUNNEL=m +CONFIG_INET_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_NF_SECURITY=m +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=m +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y +CONFIG_IP_VS_IPV6=y +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS SH scheduler +# +CONFIG_IP_VS_SH_TAB_BITS=8 + +CONFIG_IP_VS_FTP=m +CONFIG_IP_VS_PE_SIP=m + +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=m +CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_TUNNEL=m +# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y + +CONFIG_RDS=m +# CONFIG_RDS_DEBUG is not set +CONFIG_RDS_RDMA=m +CONFIG_RDS_TCP=m + +CONFIG_NET_9P=m +CONFIG_NET_9P_VIRTIO=m +# CONFIG_NET_9P_DEBUG is not set +CONFIG_NET_9P_RDMA=m + +# CONFIG_DECNET is not set +CONFIG_BRIDGE=m +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_BRIDGE_VLAN_FILTERING=y + +# PHY timestamping adds overhead +CONFIG_NETWORK_PHY_TIMESTAMPING=y + +CONFIG_NETFILTER_ADVANCED=y +CONFIG_NF_CONNTRACK=m +CONFIG_NETFILTER_NETLINK=m +CONFIG_NETFILTER_NETLINK_ACCT=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_NETLINK_QUEUE_CT=y +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NETFILTER_TPROXY=m +CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XT_SET=m +CONFIG_NETFILTER_XT_MARK=m +CONFIG_NETFILTER_XT_CONNMARK=m + +CONFIG_NETFILTER_XT_TARGET_AUDIT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m + +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ECN=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_HL=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_L2TP=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m + +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# + +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_PROCFS=y # check if contrack(8) in f17 supports netlink +# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_NAT=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_CT_PROTO_DCCP=m +CONFIG_NF_CT_PROTO_SCTP=m +CONFIG_NF_CT_NETLINK=m +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +CONFIG_NF_CT_NETLINK_HELPER=m +CONFIG_NF_CT_PROTO_UDPLITE=m + +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_RAW=m + +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_FILTER=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_QUEUE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_SECURITY=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_NF_NAT_IPV6=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +# CONFIG_IP6_NF_TARGET_NPT is not set + +# nf_tables support +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=m +CONFIG_NFT_EXTHDR=m +CONFIG_NFT_META=m +CONFIG_NFT_CT=m +CONFIG_NFT_RBTREE=m +CONFIG_NFT_HASH=m +CONFIG_NFT_COUNTER=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_NAT=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_COMPAT=m + +CONFIG_NF_TABLES_IPV4=m +CONFIG_NFT_REJECT_IPV4=m +CONFIG_NFT_CHAIN_ROUTE_IPV4=m +CONFIG_NFT_CHAIN_NAT_IPV4=m +CONFIG_NF_TABLES_ARP=m + +CONFIG_NF_TABLES_IPV6=m +CONFIG_NFT_CHAIN_ROUTE_IPV6=m +CONFIG_NFT_CHAIN_NAT_IPV6=m + +CONFIG_NF_TABLES_BRIDGE=m +# +# Bridge: Netfilter Configuration +# +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_ULOG=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_XFRM=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_STATISTICS=y +CONFIG_XFRM_USER=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +CONFIG_INET6_XFRM_MODE_BEET=m + +CONFIG_IP_SET=m +CONFIG_IP_SET_MAX=256 +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_NETPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETNET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +CONFIG_NET_SCTPPROBE=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1=y +# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5 is not set +# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set +CONFIG_SCTP_COOKIE_HMAC_MD5=y +CONFIG_SCTP_COOKIE_HMAC_SHA1=y +CONFIG_ATM=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_LLC=m +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m +CONFIG_IP_DCCP=m +CONFIG_IP_DCCP_CCID2=m +# CONFIG_IP_DCCP_CCID2_DEBUG is not set +CONFIG_IP_DCCP_CCID3=y +# CONFIG_IP_DCCP_CCID3_DEBUG is not set +# CONFIG_IP_DCCP_DEBUG is not set +# CONFIG_NET_DCCPPROBE is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +CONFIG_TIPC=m +CONFIG_TIPC_PORTS=8192 +# CONFIG_TIPC_MEDIA_IB is not set +# CONFIG_TIPC_ADVANCED is not set +# CONFIG_TIPC_DEBUG is not set + +CONFIG_NETLABEL=y + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_HHF=m +CONFIG_NET_SCH_PIE=m +CONFIG_NET_SCH_PLUG=m +CONFIG_NET_CLS=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_CGROUP=y +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_MARK=y +CONFIG_CLS_U32_PERF=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_IPSET=m +CONFIG_NET_EMATCH_U32=m + +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m + +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=m +CONFIG_BATMAN_ADV=m +CONFIG_BATMAN_ADV_BLA=y +CONFIG_BATMAN_ADV_DAT=y +CONFIG_BATMAN_ADV_NC=y + +# CONFIG_BATMAN_ADV_DEBUG is not set +CONFIG_OPENVSWITCH=m +CONFIG_OPENVSWITCH_GRE=y +CONFIG_OPENVSWITCH_VXLAN=y +CONFIG_VSOCKETS=m + + +# +# Network testing +# +CONFIG_NET_PKTGEN=m +# CONFIG_NET_TCPPROBE is not set +CONFIG_NET_DROP_MONITOR=y + +# disable later --kyle + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_IFB=m +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_DUMMY=m +CONFIG_BONDING=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_VXLAN=m +CONFIG_EQUALIZER=m +CONFIG_TUN=m +CONFIG_VETH=m +CONFIG_NLMON=m + +# +# ATM +# +CONFIG_ATM_DRIVERS=y +# CONFIG_ATM_DUMMY is not set +CONFIG_ATM_CLIP=m +CONFIG_ATM_LANE=m +CONFIG_ATM_BR2684=m +CONFIG_NET_SCH_ATM=m +CONFIG_ATM_TCP=m +# CONFIG_ATM_LANAI is not set +CONFIG_ATM_ENI=m +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +# CONFIG_ATM_IDT77252 is not set +# CONFIG_ATM_AMBASSADOR is not set +# CONFIG_ATM_HORIZON is not set +# CONFIG_ATM_FORE200E is not set +# CONFIG_ATM_FORE200E_USE_TASKLET is not set +CONFIG_ATM_FORE200E_TX_RETRY=16 +CONFIG_ATM_FORE200E_DEBUG=0 + +CONFIG_ATM_HE=m +CONFIG_PPTP=m +CONFIG_PPPOATM=m +CONFIG_PPPOL2TP=m +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_IA is not set +# CONFIG_ATM_CLIP_NO_ICMP is not set +# CONFIG_ATM_MPOA is not set +# CONFIG_ATM_BR2684_IPFILTER is not set +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +# CONFIG_ATM_ZATM_DEBUG is not set +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_HE_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +# CONFIG_ATM_IA_DEBUG is not set +CONFIG_ATM_SOLOS=m + +CONFIG_L2TP=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m + +# CONFIG_CAIF is not set + +CONFIG_RFKILL=m +CONFIG_RFKILL_GPIO=m +CONFIG_RFKILL_INPUT=y + + +# +# Ethernet (10 or 100Mbit) +# + +CONFIG_NET_VENDOR_ADAPTEC=y +CONFIG_ADAPTEC_STARFIRE=m + +CONFIG_NET_VENDOR_ALTEON=y +CONFIG_ACENIC=m +# CONFIG_ACENIC_OMIT_TIGON_I is not set + +CONFIG_NET_VENDOR_AMD=y +CONFIG_PCNET32=m +CONFIG_AMD8111_ETH=m +CONFIG_PCMCIA_NMCLAN=m + +CONFIG_NET_VENDOR_ARC=y +CONFIG_ARC_EMAC=m + +CONFIG_NET_VENDOR_ATHEROS=y +CONFIG_ALX=m +CONFIG_ATL2=m +CONFIG_ATL1=m +CONFIG_ATL1C=m +CONFIG_ATL1E=m +CONFIG_NET_CADENCE=y +CONFIG_ARM_AT91_ETHER=m +CONFIG_MACB=m + +CONFIG_NET_VENDOR_BROCADE=y +CONFIG_BNA=m +CONFIG_NET_CALXEDA_XGMAC=m + +CONFIG_NET_VENDOR_CHELSIO=y +CONFIG_CHELSIO_T1=m +CONFIG_CHELSIO_T1_1G=y +CONFIG_CHELSIO_T3=m +CONFIG_CHELSIO_T4=m +CONFIG_CHELSIO_T4VF=m + +CONFIG_NET_VENDOR_CISCO=y +CONFIG_ENIC=m + +CONFIG_NET_VENDOR_DEC=y +# +# Tulip family network device support +# +CONFIG_NET_TULIP=y +CONFIG_DE2104X=m +CONFIG_DE2104X_DSL=0 +CONFIG_TULIP=m +# CONFIG_TULIP_NAPI is not set +# CONFIG_TULIP_MWI is not set +CONFIG_TULIP_MMIO=y +# CONFIG_NI5010 is not set +CONFIG_DE4X5=m +CONFIG_WINBOND_840=m +CONFIG_DM9102=m +CONFIG_PCMCIA_XIRCOM=m +CONFIG_ULI526X=m + +CONFIG_NET_VENDOR_DLINK=y +CONFIG_DE600=m +CONFIG_DE620=m +CONFIG_DL2K=m +CONFIG_SUNDANCE=m +# CONFIG_SUNDANCE_MMIO is not set + +CONFIG_NET_VENDOR_EMULEX=y +CONFIG_BE2NET=m + +CONFIG_NET_VENDOR_EXAR=y +CONFIG_S2IO=m +CONFIG_VXGE=m +# CONFIG_VXGE_DEBUG_TRACE_ALL is not set + +# CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_FUJITSU is not set +# CONFIG_NET_VENDOR_HP is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_E100=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IGB=m +CONFIG_IGB_HWMON=y +CONFIG_IGB_DCA=y +CONFIG_IGB_PTP=y +CONFIG_IGBVF=m +CONFIG_IXGB=m +CONFIG_IXGBEVF=m +CONFIG_IXGBE=m +CONFIG_IXGBE_DCA=y +CONFIG_IXGBE_DCB=y +CONFIG_IXGBE_HWMON=y +CONFIG_IXGBE_PTP=y +CONFIG_I40E=m +# CONFIG_I40E_VXLAN is not set +# CONFIG_I40E_DCB is not set +# CONFIG_I40EVF is not set + + +# CONFIG_NET_VENDOR_I825XX is not set +CONFIG_NET_VENDOR_MARVELL=y +CONFIG_MVMDIO=m +CONFIG_SKGE=m +# CONFIG_SKGE_DEBUG is not set +CONFIG_SKGE_GENESIS=y +CONFIG_SKY2=m +# CONFIG_SKY2_DEBUG is not set + +CONFIG_NET_VENDOR_MICREL=y +CONFIG_KSZ884X_PCI=m +# CONFIG_KS8842 is not set +# CONFIG_KS8851_MLL is not set + +CONFIG_NET_VENDOR_MYRI=y +CONFIG_MYRI10GE=m +CONFIG_MYRI10GE_DCA=y + +CONFIG_NATSEMI=m +CONFIG_NS83820=m + +CONFIG_PCMCIA_AXNET=m +CONFIG_NE2K_PCI=m +CONFIG_NE3210=m +CONFIG_PCMCIA_PCNET=m + +CONFIG_NET_VENDOR_NVIDIA=y +CONFIG_FORCEDETH=m + +CONFIG_NET_VENDOR_OKI=y +# CONFIG_PCH_GBE is not set +# CONFIG_PCH_PTP is not set + +CONFIG_NET_PACKET_ENGINE=y +CONFIG_HAMACHI=m +CONFIG_YELLOWFIN=m + +CONFIG_NET_VENDOR_QLOGIC=y +CONFIG_QLA3XXX=m +CONFIG_QLCNIC=m +CONFIG_QLCNIC_SRIOV=y +CONFIG_QLCNIC_DCB=y +CONFIG_QLGE=m +CONFIG_NETXEN_NIC=m + +CONFIG_NET_VENDOR_REALTEK=y +CONFIG_ATP=m +CONFIG_8139CP=m +CONFIG_8139TOO=m +# CONFIG_8139TOO_PIO is not set +# CONFIG_8139TOO_TUNE_TWISTER is not set +CONFIG_8139TOO_8129=y +# CONFIG_8139_OLD_RX_RESET is not set +CONFIG_R8169=m + + +CONFIG_NET_VENDOR_RDC=y +CONFIG_R6040=m + + +CONFIG_NET_VENDOR_SILAN=y +CONFIG_SC92031=m + +CONFIG_NET_VENDOR_SIS=y +CONFIG_SIS900=m +CONFIG_SIS190=m + +CONFIG_PCMCIA_SMC91C92=m +CONFIG_EPIC100=m +CONFIG_SMSC9420=m + +# CONFIG_STMMAC_PLATFORM is not set +# CONFIG_STMMAC_PCI is not set +# CONFIG_STMMAC_DA is not set +# CONFIG_STMMAC_DUAL_MAC is not set +# CONFIG_STMMAC_TIMER is not set +# CONFIG_STMMAC_DEBUG_FS is not set + +CONFIG_NET_VENDOR_SUN=y +CONFIG_HAPPYMEAL=m +CONFIG_SUNGEM=m +CONFIG_CASSINI=m +CONFIG_NIU=m + +CONFIG_NET_VENDOR_TEHUTI=y +CONFIG_TEHUTI=m + +CONFIG_NET_VENDOR_TI=y +CONFIG_TLAN=m + +CONFIG_VIA_RHINE=m +CONFIG_VIA_RHINE_MMIO=y + +CONFIG_WIZNET_W5100=m +CONFIG_WIZNET_W5300=m +CONFIG_NET_VENDOR_XIRCOM=y +CONFIG_PCMCIA_XIRC2PS=m + +CONFIG_AMD_PHY=m +CONFIG_BROADCOM_PHY=m +CONFIG_BCM87XX_PHY=m +CONFIG_CICADA_PHY=m +CONFIG_DAVICOM_PHY=m +CONFIG_DP83640_PHY=m +CONFIG_FIXED_PHY=y +CONFIG_MDIO_BITBANG=m +CONFIG_NATIONAL_PHY=m +CONFIG_ICPLUS_PHY=m +CONFIG_BCM63XX_PHY=m +CONFIG_LSI_ET1011C_PHY=m +CONFIG_LXT_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_QSEMI_PHY=m +CONFIG_REALTEK_PHY=m +CONFIG_SMSC_PHY=m +CONFIG_STE10XP=m +CONFIG_VITESSE_PHY=m +CONFIG_MICREL_PHY=m + +CONFIG_MII=m +CONFIG_NET_CORE=y +CONFIG_NET_VENDOR_3COM=y +CONFIG_VORTEX=m +CONFIG_TYPHOON=m +CONFIG_DNET=m + + +CONFIG_LNE390=m +CONFIG_ES3210=m +CONFIG_NET_PCI=y +CONFIG_B44=m +CONFIG_B44_PCI=y +CONFIG_BNX2=m +CONFIG_BNX2X=m +CONFIG_BNX2X_SRIOV=y +CONFIG_CNIC=m +CONFIG_FEALNX=m +CONFIG_NET_POCKET=y + +# +# Ethernet (1000 Mbit) +# +CONFIG_TIGON3=m +CONFIG_JME=m + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IP1000 is not set +# CONFIG_MLX4_EN is not set +# CONFIG_SFC is not set + +# CONFIG_FDDI is not set +# CONFIG_DEFXX is not set +# CONFIG_SKFP is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_IPPP_FILTER=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPPOE=m +CONFIG_PPP_MPPE=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set + +# +# Wireless LAN +# +# +# CONFIG_STRIP is not set +# CONFIG_PCMCIA_RAYCS is not set + +CONFIG_CFG80211_WEXT=y +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +CONFIG_CFG80211_DEFAULT_PS=y +CONFIG_NL80211=y +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_WIRELESS_EXT_SYSFS is not set +CONFIG_LIB80211=m +CONFIG_LIB80211_CRYPT_WEP=m +CONFIG_LIB80211_CRYPT_CCMP=m +CONFIG_LIB80211_CRYPT_TKIP=m +# CONFIG_LIB80211_DEBUG is not set + +CONFIG_MAC80211=m +CONFIG_MAC80211_RC_MINSTREL=y +# CONFIG_MAC80211_RC_DEFAULT_PID is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel" +CONFIG_MAC80211_MESH=y +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUG_MENU is not set + +# CONFIG_WIMAX is not set + +# CONFIG_ADM8211 is not set +CONFIG_ATH_COMMON=m +CONFIG_ATH_CARDS=m +CONFIG_ATH5K=m +CONFIG_ATH5K_DEBUG=y +# CONFIG_ATH5K_TRACER is not set +CONFIG_ATH6KL=m +CONFIG_ATH6KL_DEBUG=y +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +# CONFIG_ATH6KL_TRACING is not set +CONFIG_AR5523=m +CONFIG_ATH9K=m +CONFIG_ATH9K_PCI=y +CONFIG_ATH9K_AHB=y +# CONFIG_ATH9K_DEBUG is not set +# CONFIG_ATH9K_MAC_DEBUG is not set +CONFIG_ATH9K_HTC=m +CONFIG_ATH9K_BTCOEX_SUPPORT=y +# CONFIG_ATH9K_LEGACY_RATE_CONTROL is not set +# CONFIG_ATH9K_WOW is not set +# +CONFIG_ATH10K=m +CONFIG_ATH10K_PCI=m +# CONFIG_ATH10K_DEBUG is not set +# CONFIG_ATH10K_TRACING is not set +CONFIG_ATH10K_DEBUGFS=y +CONFIG_WCN36XX=m +# CONFIG_WCN36XX_DEBUGFS is not set +CONFIG_WIL6210=m +CONFIG_WIL6210_ISR_COR=y +# CONFIG_WIL6210_TRACING is not set +CONFIG_CARL9170=m +CONFIG_CARL9170_LEDS=y +# CONFIG_CARL9170_HWRNG is not set +CONFIG_AT76C50X_USB=m +# CONFIG_AIRO is not set +# CONFIG_AIRO_CS is not set +# CONFIG_ATMEL is not set +CONFIG_B43=m +CONFIG_B43_PCMCIA=y +CONFIG_B43_SDIO=y +CONFIG_B43_BCMA=y +# CONFIG_B43_BCMA_EXTRA is not set +CONFIG_B43_BCMA_PIO=y +# CONFIG_B43_DEBUG is not set +CONFIG_B43_PHY_LP=y +CONFIG_B43_PHY_N=y +CONFIG_B43_PHY_HT=y +# CONFIG_B43_FORCE_PIO is not set +CONFIG_B43LEGACY=m +# CONFIG_B43LEGACY_DEBUG is not set +CONFIG_B43LEGACY_DMA=y +CONFIG_B43LEGACY_PIO=y +CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y +# CONFIG_B43LEGACY_DMA_MODE is not set +# CONFIG_B43LEGACY_PIO_MODE is not set +CONFIG_BRCMSMAC=m +# CONFIG_BRCMFMAC_SDIO_OOB is not set +CONFIG_BRCMFMAC_USB=y +# CONFIG_BRCM_TRACING is not set +# CONFIG_BRCMISCAN is not set +# CONFIG_BRCMDBG is not set +CONFIG_HERMES=m +CONFIG_HERMES_CACHE_FW_ON_INIT=y +# CONFIG_HERMES_PRISM is not set +CONFIG_NORTEL_HERMES=m +CONFIG_PCI_HERMES=m +CONFIG_PLX_HERMES=m +CONFIG_PCMCIA_HERMES=m +CONFIG_ORINOCO_USB=m +# CONFIG_TMD_HERMES is not set +# CONFIG_PCMCIA_SPECTRUM is not set +CONFIG_CW1200=m +CONFIG_CW1200_WLAN_SDIO=m +CONFIG_CW1200_WLAN_SPI=m +# CONFIG_HOSTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2200 is not set +# CONFIG_IPW2100_DEBUG is not set +# CONFIG_IPW2200_DEBUG is not set +# CONFIG_LIBIPW_DEBUG is not set +CONFIG_LIBERTAS=m +CONFIG_LIBERTAS_USB=m +CONFIG_LIBERTAS_CS=m +CONFIG_LIBERTAS_SDIO=m +# CONFIG_LIBERTAS_DEBUG is not set +# CONFIG_LIBERTAS_THINFIRM is not set +CONFIG_LIBERTAS_MESH=y +CONFIG_IWLWIFI=m +CONFIG_IWLDVM=m +CONFIG_IWLMVM=m +CONFIG_IWLWIFI_DEBUG=y +CONFIG_IWLWIFI_DEVICE_SVTOOL=y +# CONFIG_IWLWIFI_EXPERIMENTAL_MFP is not set +CONFIG_IWLWIFI_UCODE16=y +# CONFIG_IWLWIFI_P2P is not set +CONFIG_IWLEGACY=m +CONFIG_IWLEGACY_DEBUG=y +# CONFIG_IWLWIFI_LEGACY_DEVICE_TRACING is not set +CONFIG_IWL4965=y +CONFIG_IWL3945=m +# CONFIG_IWM is not set +# CONFIG_IWLWIFI_DEBUG_EXPERIMENTAL_UCODE is not set +CONFIG_MAC80211_HWSIM=m +CONFIG_P54_COMMON=m +CONFIG_P54_USB=m +CONFIG_P54_PCI=m +CONFIG_MWL8K=m +# CONFIG_PRISM54 is not set +# CONFIG_PCMCIA_WL3501 is not set +CONFIG_RT2X00=m +# CONFIG_RT2X00_DEBUG is not set +CONFIG_RT2400PCI=m +CONFIG_RT2500PCI=m +CONFIG_RT61PCI=m +CONFIG_RT2500USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RT2800PCI=m +CONFIG_RT2800PCI_RT3290=y +CONFIG_RT2800PCI_RT33XX=y +CONFIG_RT2800PCI_RT35XX=y +CONFIG_RT2800PCI_RT53XX=y +CONFIG_RT73USB=m +CONFIG_RTL8180=m +CONFIG_RTL8187=m +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_SR9800 is not set +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_NET_SMSC75XX=m +# CONFIG_WL_TI is not set +CONFIG_ZD1211RW=m +# CONFIG_ZD1211RW_DEBUG is not set + +CONFIG_WL12XX=m +CONFIG_WL12XX_SPI=m +CONFIG_WL12XX_SDIO=m + +CONFIG_WL1251=m +CONFIG_WL1251_SPI=m +CONFIG_WL1251_SDIO=m + +CONFIG_RTL_CARDS=m +CONFIG_RTLWIFI=m +CONFIG_RTL8192CE=m +CONFIG_RTL8192SE=m +CONFIG_RTL8192CU=m +CONFIG_RTL8192DE=m +CONFIG_RTL8723AE=m +CONFIG_RTL8188EE=m + +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_MWIFIEX_PCIE=m +CONFIG_MWIFIEX_USB=m + +# +# Token Ring devices +# +# CONFIG_TR is not set + +CONFIG_NET_FC=y + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m + +# +# Amateur Radio support +# +CONFIG_HAMRADIO=y +CONFIG_AX25=m +CONFIG_AX25_DAMA_SLAVE=y + +# CONFIG_CAN is not set + +CONFIG_NETROM=m +CONFIG_ROSE=m +CONFIG_MKISS=m +CONFIG_6PACK=m +CONFIG_BPQETHER=m +CONFIG_BAYCOM_SER_FDX=m +CONFIG_BAYCOM_SER_HDX=m +CONFIG_BAYCOM_PAR=m +CONFIG_BAYCOM_EPP=m +CONFIG_YAM=m + +CONFIG_NFC=m +CONFIG_NFC_DIGITAL=m +CONFIG_NFC_NCI=m +CONFIG_NFC_HCI=m +CONFIG_NFC_SHDLC=y +CONFIG_NFC_LLCP=y +CONFIG_NFC_SIM=m +CONFIG_NFC_MRVL=m +CONFIG_NFC_MRVL_USB=m + +# +# Near Field Communication (NFC) devices +# +CONFIG_NFC_PORT100=m +CONFIG_NFC_PN544=m +CONFIG_NFC_PN544_I2C=m +CONFIG_NFC_PN533=m +CONFIG_NFC_MICROREAD=m +CONFIG_NFC_MICROREAD_I2C=m + +# +# IrDA (infrared) support +# +CONFIG_IRDA=m +# CONFIG_IRDA_DEBUG is not set +CONFIG_IRLAN=m +CONFIG_IRNET=m +CONFIG_IRCOMM=m +# CONFIG_IRDA_ULTRA is not set +CONFIG_IRDA_CACHE_LAST_LSAP=y +CONFIG_IRDA_FAST_RR=y +CONFIG_IRTTY_SIR=m +CONFIG_DONGLE=y +CONFIG_ACTISYS_DONGLE=m +CONFIG_ACT200L_DONGLE=m +CONFIG_ESI_DONGLE=m +CONFIG_GIRBIL_DONGLE=m +CONFIG_KINGSUN_DONGLE=m +CONFIG_KSDAZZLE_DONGLE=m +CONFIG_KS959_DONGLE=m +CONFIG_LITELINK_DONGLE=m +CONFIG_MA600_DONGLE=m +CONFIG_MCP2120_DONGLE=m +CONFIG_OLD_BELKIN_DONGLE=m +CONFIG_TEKRAM_DONGLE=m +CONFIG_TOIM3232_DONGLE=m + +CONFIG_ALI_FIR=m +CONFIG_MCS_FIR=m +CONFIG_NSC_FIR=m +CONFIG_SIGMATEL_FIR=m +CONFIG_SMC_IRCC_FIR=m +# CONFIG_TOSHIBA_FIR is not set +CONFIG_USB_IRDA=m +CONFIG_VLSI_FIR=m +CONFIG_VIA_FIR=m +CONFIG_WINBOND_FIR=m + +# +# Bluetooth support +# +CONFIG_BT=m +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_CMTP=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=m +# Disable the BT_HCIUSB driver. +# It sucks more power than BT_HCIBTUSB which has the same functionality. +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m +CONFIG_BT_WILINK=m + +# +# ISDN subsystem +# +CONFIG_ISDN=y +CONFIG_MISDN=m +CONFIG_MISDN_DSP=m +CONFIG_MISDN_L1OIP=m +CONFIG_MISDN_AVMFRITZ=m +CONFIG_MISDN_SPEEDFAX=m +CONFIG_MISDN_INFINEON=m +CONFIG_MISDN_W6692=m +CONFIG_MISDN_NETJET=m + +# +# mISDN hardware drivers +# +CONFIG_MISDN_HFCPCI=m +CONFIG_MISDN_HFCMULTI=m +CONFIG_ISDN_I4L=m +CONFIG_ISDN_DRV_AVMB1_B1PCI=m +CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m +CONFIG_ISDN_DRV_AVMB1_T1PCI=m +CONFIG_ISDN_DRV_AVMB1_C4=m + +CONFIG_MISDN_HFCUSB=m + +CONFIG_ISDN_PPP=y +CONFIG_ISDN_PPP_VJ=y +CONFIG_ISDN_MPP=y +# CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_TTY_FAX=y +CONFIG_DE_AOC=y + +CONFIG_ISDN_AUDIO=y + +CONFIG_ISDN_DRV_HISAX=m +CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y +CONFIG_ISDN_DRV_AVMB1_AVM_CS=m + +CONFIG_ISDN_CAPI_CAPIDRV=m +CONFIG_ISDN_DIVERSION=m + +CONFIG_HISAX_EURO=y +CONFIG_HISAX_1TR6=y +CONFIG_HISAX_NI1=y +CONFIG_HISAX_MAX_CARDS=8 +CONFIG_HISAX_16_3=y +CONFIG_HISAX_TELESPCI=y +CONFIG_HISAX_S0BOX=y +CONFIG_HISAX_FRITZPCI=y +CONFIG_HISAX_AVM_A1_PCMCIA=y +CONFIG_HISAX_ELSA=y +CONFIG_HISAX_DIEHLDIVA=y +CONFIG_HISAX_SEDLBAUER=y +CONFIG_HISAX_NETJET=y +CONFIG_HISAX_NETJET_U=y +CONFIG_HISAX_NICCY=y +CONFIG_HISAX_BKM_A4T=y +CONFIG_HISAX_SCT_QUADRO=y +CONFIG_HISAX_GAZEL=y +CONFIG_HISAX_HFC_PCI=y +CONFIG_HISAX_W6692=y +CONFIG_HISAX_HFC_SX=y +CONFIG_HISAX_ENTERNOW_PCI=y +# CONFIG_HISAX_DEBUG is not set +CONFIG_HISAX_AVM_A1_CS=m +CONFIG_HISAX_ST5481=m +# CONFIG_HISAX_HFCUSB is not set +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y +CONFIG_HISAX_SEDLBAUER_CS=m +CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_TELES_CS=m +CONFIG_HISAX_HFC4S8S=m + +CONFIG_ISDN_DRV_LOOP=m +CONFIG_HYSDN=m +CONFIG_HYSDN_CAPI=y + + +# +# CAPI subsystem +# +CONFIG_ISDN_CAPI=m +# CONFIG_CAPI_TRACE is not set +CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y +CONFIG_ISDN_CAPI_MIDDLEWARE=y +CONFIG_ISDN_CAPI_CAPI20=m + +# +# CAPI hardware drivers +# + +# +# Active AVM cards +# +CONFIG_CAPI_AVM=y + +# +# Active Eicon DIVA Server cards +# +# CONFIG_CAPI_EICON is not set +CONFIG_ISDN_DIVAS=m +CONFIG_ISDN_DIVAS_BRIPCI=y +CONFIG_ISDN_DIVAS_PRIPCI=y +CONFIG_ISDN_DIVAS_DIVACAPI=m +CONFIG_ISDN_DIVAS_USERIDI=m +CONFIG_ISDN_DIVAS_MAINT=m + +CONFIG_ISDN_DRV_GIGASET=m +CONFIG_GIGASET_CAPI=y +CONFIG_GIGASET_BASE=m +CONFIG_GIGASET_M101=m +CONFIG_GIGASET_M105=m +# CONFIG_GIGASET_DEBUG is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=m + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_JOYDEV=m +# CONFIG_INPUT_MATRIXKMAP is not set + +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_GTCO=m +CONFIG_TABLET_USB_HANWANG=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_USB_WACOM=m + +CONFIG_INPUT_POWERMATE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_POLLDEV=m +CONFIG_INPUT_SPARSEKMAP=m +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_IMS_PCU is not set +CONFIG_INPUT_CMA3000=m +CONFIG_INPUT_CMA3000_I2C=m +CONFIG_INPUT_IDEAPAD_SLIDEBAR=m + +# +# Input I/O drivers +# +CONFIG_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m +CONFIG_GAMEPORT_EMU10K1=m +CONFIG_GAMEPORT_FM801=m +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_RAW=m +CONFIG_SERIO_ALTERA_PS2=m +# CONFIG_SERIO_PS2MULT is not set +CONFIG_SERIO_ARC_PS2=m +# CONFIG_SERIO_APBPS2 is not set + +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_OLPC_APSP is not set +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_LIBPS2 is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_SH_KEYSC is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_OMAP4 is not set +CONFIG_INPUT_MOUSE=y +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_MOUSE_PS2_SENTELIC=y +CONFIG_MOUSE_SERIAL=m +CONFIG_MOUSE_VSXXXAA=m +CONFIG_MOUSE_APPLETOUCH=m +CONFIG_MOUSE_BCM5974=m +CONFIG_MOUSE_SYNAPTICS_I2C=m +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_MOUSE_CYAPA=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_WALKERA0701=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_JOYSTICK_ZHENHUA=m +# CONFIG_JOYSTICK_AS5011 is not set + +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879 is not set +CONFIG_TOUCHSCREEN_AD7879_I2C=m +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_EETI=m +CONFIG_TOUCHSCREEN_EGALAX=m +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_GUNZE=m +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_ILI210X=m +CONFIG_TOUCHSCREEN_MMS114=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_MCS5000=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +# CONFIG_TOUCHSCREEN_SUR40 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_TOUCHSCREEN_TSC_SERIO=m +CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_TOUCHSCREEN_TOUCHIT213=m +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_UCB1400=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_WACOM_I2C=m +CONFIG_TOUCHSCREEN_USB_E2I=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +# CONFIG_TOUCHSCREEN_WM97XX is not set +CONFIG_TOUCHSCREEN_W90X900=m +# CONFIG_TOUCHSCREEN_BU21013 is not set +CONFIG_TOUCHSCREEN_ST1232=m +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +# CONFIG_TOUCHSCREEN_MAX11801 is not set +CONFIG_TOUCHSCREEN_AUO_PIXCIR=m +CONFIG_TOUCHSCREEN_TI_AM335X_TSC=m +CONFIG_TOUCHSCREEN_ZFORCE=m + +CONFIG_INPUT_PCSPKR=m +CONFIG_INPUT_RETU_PWRBUTTON=m +CONFIG_INPUT_UINPUT=m +CONFIG_INPUT_WISTRON_BTNS=m +CONFIG_INPUT_ATLAS_BTNS=m + +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m + +CONFIG_MAC_EMUMOUSEBTN=y + +CONFIG_INPUT_WM831X_ON=m + + +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_PCF8574 is not set +CONFIG_INPUT_MMA8450=m +CONFIG_INPUT_MPU3050=m +CONFIG_INPUT_KXTJ9=m +# CONFIG_INPUT_KXTJ9_POLLED_MODE is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_ROCKETPORT=m +CONFIG_SYNCLINK=m +CONFIG_SYNCLINKMP=m +CONFIG_SYNCLINK_GT=m +CONFIG_N_HDLC=m +CONFIG_N_GSM=m +# CONFIG_TRACE_SINK is not set +# CONFIG_STALDRV is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_IBM_ASM is not set +CONFIG_TIFM_CORE=m +CONFIG_TIFM_7XX1=m +CONFIG_TCG_TPM=m +CONFIG_TCG_TIS=m +# CONFIG_TCG_TIS_I2C_INFINEON is not set +# CONFIG_TCG_TIS_I2C_ATMEL is not set +# CONFIG_TCG_TIS_I2C_NUVOTON is not set +CONFIG_TCG_NSC=m +CONFIG_TCG_ATMEL=m +# CONFIG_TCG_INFINEON is not set +# CONFIG_TCG_ST33_I2C is not set +# CONFIG_TCG_XEN is not set +CONFIG_TELCLOCK=m + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_CS=m +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +CONFIG_SERIAL_8250_RSA=y +# CONFIG_SERIAL_8250_DW is not set +CONFIG_CYCLADES=m +# CONFIG_CYZ_INTR is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_ISI is not set +# CONFIG_RIO is not set +CONFIG_SERIAL_JSM=m +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_MFD_HSU is not set + +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_TIMBERDALE is not set +CONFIG_SERIAL_ARC=m +CONFIG_SERIAL_ARC_NR_PORTS=1 +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_SERIAL_PCH_UART is not set + +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +CONFIG_PRINTER=m +CONFIG_LP_CONSOLE=y +CONFIG_PPDEV=m + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_MUX is not set +# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set +# CONFIG_I2C_MUX_PCA954x is not set +# CONFIG_I2C_MUX_GPIO is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PINCTRL is not set +# + +# +# I2C Algorithms +# +# CONFIG_I2C_DEBUG_ALGO is not set +CONFIG_I2C_ALGOBIT=m + +# +# I2C Hardware Bus support +# + +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD756_S4882 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_NFORCE2_S4985 is not set +# CONFIG_I2C_INTEL_MID is not set +# CONFIG_I2C_EG20T is not set +# CONFIG_I2C_CBUS_GPIO is not set +CONFIG_I2C_VIPERBOARD=m + +CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_LEGACY=m +CONFIG_EEPROM_93CX6=m +CONFIG_EEPROM_MAX6875=m + +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PARPORT=m +CONFIG_I2C_PARPORT_LIGHT=m +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +CONFIG_I2C_PASEMI=m +CONFIG_I2C_PCA_PLATFORM=m +# CONFIG_I2C_PIIX4 is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +CONFIG_I2C_SIMTEC=m +CONFIG_I2C_STUB=m +CONFIG_I2C_TINY_USB=m +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_XILINX is not set + +CONFIG_I2C_DIOLAN_U2C=m + +# +# I2C Hardware Sensors Chip support +# +CONFIG_SENSORS_ATK0110=m +CONFIG_SENSORS_ABITUGURU=m +CONFIG_SENSORS_ABITUGURU3=m +CONFIG_SENSORS_AD7414=m +CONFIG_SENSORS_AD7418=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1026=m +CONFIG_SENSORS_ADM1029=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ADM9240=m +CONFIG_SENSORS_ADT7310=m +CONFIG_SENSORS_ADT7410=m +CONFIG_SENSORS_ADS7828=m +CONFIG_SENSORS_ADT7462=m +CONFIG_SENSORS_ADT7470=m +CONFIG_SENSORS_ADT7475=m +CONFIG_SENSORS_APPLESMC=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_ATXP1=m +CONFIG_SENSORS_CORETEMP=m +CONFIG_SENSORS_DME1737=m +CONFIG_SENSORS_DS1621=m +# CONFIG_DS1682 is not set +CONFIG_SENSORS_F71805F=m +CONFIG_SENSORS_F71882FG=m +CONFIG_SENSORS_F75375S=m +CONFIG_SENSORS_FSCHMD=m +CONFIG_SENSORS_G760A=m +CONFIG_SENSORS_G762=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_GL520SM=m +CONFIG_SENSORS_HDAPS=m +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_HTU21 is not set +# CONFIG_SENSORS_I5K_AMB is not set +# FIXME: IBMAEM x86 only? +CONFIG_SENSORS_IBMAEM=m +CONFIG_SENSORS_IBMPEX=m +# CONFIG_SENSORS_IIO_HWMON is not set +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_K8TEMP=m +CONFIG_SENSORS_K10TEMP=m +CONFIG_SENSORS_LIS3LV02D=m +CONFIG_SENSORS_LIS3_SPI=m +CONFIG_SENSORS_LIS3_I2C=m +CONFIG_SENSORS_LM63=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM87=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_LM92=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_LM95234=m +CONFIG_SENSORS_LTC4245=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_MAX6650=m +CONFIG_SENSORS_MAX6697=m +CONFIG_SENSORS_MCP3021=m +CONFIG_SENSORS_NCT6775=m +CONFIG_SENSORS_NTC_THERMISTOR=m +CONFIG_SENSORS_PC87360=m +CONFIG_SENSORS_PC87427=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_SHT15=m +CONFIG_SENSORS_SIS5595=m +CONFIG_CHARGER_SMB347=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_SMSC47M192=m +CONFIG_SENSORS_SMSC47B397=m +CONFIG_SENSORS_THMC50=m +CONFIG_SENSORS_TMP401=m +CONFIG_APDS9802ALS=m +CONFIG_ISL29020=m +CONFIG_ISL29003=m +CONFIG_SENSORS_BH1770=m +CONFIG_SENSORS_APDS990X=m +CONFIG_SENSORS_TSL2550=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_VIA_CPUTEMP=m +CONFIG_SENSORS_VT1211=m +CONFIG_SENSORS_VT8231=m +CONFIG_SENSORS_W83627HF=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83L786NG=m +CONFIG_SENSORS_W83627EHF=m +CONFIG_SENSORS_W83791D=m +CONFIG_SENSORS_W83792D=m +CONFIG_SENSORS_W83793=m +CONFIG_SENSORS_LTC4215=m +CONFIG_SENSORS_LM95241=m +CONFIG_SENSORS_LM95245=m +CONFIG_SENSORS_TMP421=m +CONFIG_SENSORS_WM8350=m +CONFIG_SENSORS_WM831X=m +CONFIG_SENSORS_LM73=m +CONFIG_SENSORS_AMC6821=m +CONFIG_SENSORS_INA2XX=m +CONFIG_SENSORS_INA209=m +CONFIG_SENSORS_ADT7411=m +CONFIG_SENSORS_ASC7621=m +CONFIG_SENSORS_EMC1403=m +CONFIG_SENSORS_TMP102=m +CONFIG_SENSORS_LTC4261=m +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_GPIO_FAN is not set +CONFIG_SENSORS_W83795=m +# CONFIG_SENSORS_W83795_FANCTRL is not set +CONFIG_SENSORS_DS620=m +CONFIG_SENSORS_SHT21=m +CONFIG_SENSORS_LINEAGE=m +CONFIG_SENSORS_LTC4151=m +CONFIG_SENSORS_MAX6639=m +CONFIG_SENSORS_SCH5627=m +CONFIG_SENSORS_SCH5636=m +CONFIG_SENSORS_ADS1015=m +CONFIG_SENSORS_MAX16065=m +CONFIG_SENSORS_MAX6642=m +CONFIG_SENSORS_ADM1275=m +CONFIG_SENSORS_UCD9000=m +CONFIG_SENSORS_UCD9200=m +CONFIG_SENSORS_ZL6100=m +CONFIG_SENSORS_EMC6W201=m + +CONFIG_PMBUS=m +CONFIG_SENSORS_PMBUS=m +CONFIG_SENSORS_MAX16064=m +CONFIG_SENSORS_LM25066=m +CONFIG_SENSORS_LTC2978=m +CONFIG_SENSORS_MAX34440=m +CONFIG_SENSORS_MAX8688=m +CONFIG_SENSORS_MAX1668=m +CONFIG_SENSORS_MAX197=m + +# Industrial I/O subsystem configuration +CONFIG_IIO=m +CONFIG_IIO_BUFFER=y +CONFIG_IIO_BUFFER_CB=y +# CONFIG_IIO_KFIFO_BUF is not set +CONFIG_IIO_TRIGGERED_BUFFER=m +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 +CONFIG_IIO_INTERRUPT_TRIGGER=m +CONFIG_HID_SENSOR_IIO_COMMON=m +CONFIG_HID_SENSOR_IIO_TRIGGER=m +CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS=y +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5064 is not set +# CONFIG_BMA180 is not set +# CONFIG_MAX1363 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set +# CONFIG_ITG3200 is not set +# CONFIG_APDS9300 is not set +# CONFIG_CM32181 is not set +# CONFIG_CM36651 is not set +# CONFIG_GP2AP020A00F is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set +# CONFIG_TCS3472 is not set +# CONFIG_TSL4531 is not set +# CONFIG_NAU7802 is not set +# CONFIG_TI_ADC081C is not set +# CONFIG_EXYNOS_ADC is not set +# CONFIG_VIPERBOARD_ADC is not set +# CONFIG_INV_MPU6050_IIO is not set +CONFIG_IIO_ST_GYRO_3AXIS=m +CONFIG_IIO_ST_MAGN_3AXIS=m +CONFIG_IIO_ST_ACCEL_3AXIS=m +CONFIG_HID_SENSOR_INCLINOMETER_3D=m +# CONFIG_ADJD_S311 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_VCNL4000 is not set +# CONFIG_AK8975 is not set +# CONFIG_MAG3110 is not set +# CONFIG_TMP006 is not set +# CONFIG_IIO_ST_PRESS is not set +# CONFIG_KXSD9 is not set +# CONFIG_AD7266 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7887 is not set +# CONFIG_AD7923 is not set +# CONFIG_MCP320X is not set +# CONFIG_MCP3422 is not set +# CONFIG_AD8366 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5686 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD7303 is not set +# CONFIG_AD9523 is not set +# CONFIG_ADF4350 is not set +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADIS16260 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_DHT11 is not set +# CONFIG_MPL3115 is not set + +# staging IIO drivers +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +# CONFIG_ADT7316 is not set +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set +# CONFIG_AD5933 is not set +# CONFIG_ADE7854 is not set +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_SENSORS_HMC5843 is not set +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_SCA3000 is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set +# CONFIG_ADIS16060 is not set +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + + + +# CONFIG_HMC6352 is not set +# CONFIG_BMP085 is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_USB_SWITCH_FSA9480 is not set + +CONFIG_W1=m +CONFIG_W1_CON=y +# CONFIG_W1_MASTER_MATROX is not set +CONFIG_W1_MASTER_DS2490=m +CONFIG_W1_MASTER_DS2482=m +CONFIG_W1_MASTER_DS1WM=m +CONFIG_W1_MASTER_GPIO=m +# CONFIG_HDQ_MASTER_OMAP is not set +CONFIG_W1_SLAVE_THERM=m +CONFIG_W1_SLAVE_SMEM=m +CONFIG_W1_SLAVE_DS2408=m +# CONFIG_W1_SLAVE_DS2408_READBACK is not set +CONFIG_W1_SLAVE_DS2413=m +CONFIG_W1_SLAVE_DS2423=m +CONFIG_W1_SLAVE_DS2431=m +CONFIG_W1_SLAVE_DS2433=m +CONFIG_W1_SLAVE_DS2433_CRC=y +CONFIG_W1_SLAVE_DS2760=m +CONFIG_W1_SLAVE_DS2780=m +CONFIG_W1_SLAVE_DS2781=m +CONFIG_W1_SLAVE_DS28E04=m +CONFIG_W1_SLAVE_BQ27000=m + +# +# Mice +# + +# +# IPMI +# +CONFIG_IPMI_HANDLER=m +# CONFIG_IPMI_PANIC_EVENT is not set +CONFIG_IPMI_DEVICE_INTERFACE=m +CONFIG_IPMI_WATCHDOG=m +CONFIG_IPMI_SI=m +CONFIG_IPMI_POWEROFF=m + +# +# Watchdog Cards +# +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_SOFT_WATCHDOG=m +CONFIG_WDTPCI=m +# CONFIG_ACQUIRE_WDT is not set +# CONFIG_ADVANTECH_WDT is not set +# CONFIG_EUROTECH_WDT is not set +CONFIG_IB700_WDT=m +# CONFIG_SCx200_WDT is not set +# CONFIG_60XX_WDT is not set +CONFIG_W83877F_WDT=m +CONFIG_W83627HF_WDT=m +CONFIG_MACHZ_WDT=m +# CONFIG_SC520_WDT is not set +CONFIG_ALIM7101_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_IT87_WDT=m +CONFIG_ITCO_WDT=m +CONFIG_ITCO_VENDOR_SUPPORT=y +# CONFIG_SC1200_WDT is not set +# CONFIG_PC87413_WDT is not set +# CONFIG_WAFER_WDT is not set +# CONFIG_CPU5_WDT is not set +CONFIG_I6300ESB_WDT=m +CONFIG_IT8712F_WDT=m +# CONFIG_SBC8360_WDT is not set +# CONFIG_SBC7240_WDT is not set +CONFIG_SMSC_SCH311X_WDT=m +CONFIG_W83977F_WDT=m +CONFIG_PCIPCWATCHDOG=m +CONFIG_USBPCWATCHDOG=m +# CONFIG_SBC_EPX_C3_WATCHDOG is not set +CONFIG_WM8350_WATCHDOG=m +CONFIG_WM831X_WATCHDOG=m +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +CONFIG_W83697UG_WDT=m +# CONFIG_MEN_A21_WDT is not set +# CONFIG_GPIO_WATCHDOG is not set + +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_TIMERIOMEM=m +CONFIG_HW_RANDOM_TPM=m +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_RTC_DEBUG is not set +# CONFIG_GEN_RTC is not set +CONFIG_RTC_HCTOSYS=y +# CONFIG_RTC_SYSTOHC is not set +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_DRV_CMOS=y +CONFIG_RTC_DRV_DS1307=m +CONFIG_RTC_DRV_DS1511=m +CONFIG_RTC_DRV_DS1553=m +CONFIG_RTC_DRV_DS1672=m +CONFIG_RTC_DRV_DS1742=m +CONFIG_RTC_DRV_DS1374=m +# CONFIG_RTC_DRV_EP93XX is not set +CONFIG_RTC_DRV_FM3130=m +CONFIG_RTC_DRV_ISL1208=m +CONFIG_RTC_DRV_M41T80=m +CONFIG_RTC_DRV_M41T80_WDT=y +CONFIG_RTC_DRV_M48T59=m +CONFIG_RTC_DRV_MAX6900=m +# CONFIG_RTC_DRV_M48T86 is not set +CONFIG_RTC_DRV_PCF2127=m +CONFIG_RTC_DRV_PCF8563=m +CONFIG_RTC_DRV_PCF8583=m +CONFIG_RTC_DRV_RS5C372=m +# CONFIG_RTC_DRV_SA1100 is not set +# CONFIG_RTC_DRV_TEST is not set +CONFIG_RTC_DRV_X1205=m +CONFIG_RTC_DRV_V3020=m +CONFIG_RTC_DRV_DS2404=m +CONFIG_RTC_DRV_STK17TA8=m +# CONFIG_RTC_DRV_S35390A is not set +CONFIG_RTC_DRV_RX8581=m +CONFIG_RTC_DRV_RX8025=m +CONFIG_RTC_DRV_DS1286=m +CONFIG_RTC_DRV_M48T35=m +CONFIG_RTC_DRV_BQ4802=m +CONFIG_RTC_DRV_WM8350=m +# CONFIG_RTC_DRV_AB3100 is not set +CONFIG_RTC_DRV_WM831X=m +CONFIG_RTC_DRV_BQ32K=m +CONFIG_RTC_DRV_MSM6242=m +CONFIG_RTC_DRV_RP5C01=m +CONFIG_RTC_DRV_EM3027=m +CONFIG_RTC_DRV_RV3029C2=m +CONFIG_RTC_DRV_PCF50633=m +CONFIG_RTC_DRV_DS3232=m +CONFIG_RTC_DRV_ISL12022=m +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_RTC_DRV_MOXART is not set +# CONFIG_RTC_DRV_ISL12057 is not set + +CONFIG_R3964=m +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +CONFIG_AGP=y +CONFIG_AGP_ALI=y +CONFIG_AGP_ATI=y +CONFIG_AGP_AMD=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_AGP_NVIDIA=y +CONFIG_AGP_SIS=y +CONFIG_AGP_SWORKS=y +CONFIG_AGP_VIA=y +CONFIG_AGP_EFFICEON=y + +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 + +# CONFIG_STUB_POULSBO is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set + +CONFIG_CARDMAN_4000=m +CONFIG_CARDMAN_4040=m + +CONFIG_MWAVE=m +CONFIG_RAW_DRIVER=y +CONFIG_MAX_RAW_DEVS=8192 +CONFIG_HANGCHECK_TIMER=m + +CONFIG_MEDIA_PCI_SUPPORT=y +# +# Multimedia devices +# +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RC_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=m +# CONFIG_VIDEO_ADV_DEBUG is not set +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_VIDEO_VIVI is not set +# CONFIG_USB_SI4713 is not set +# CONFIG_PLATFORM_SI4713 is not set +# CONFIG_I2C_SI4713 is not set +# CONFIG_USB_RAREMONO is not set + +# +# Video For Linux +# + +# +# Video Adapters +# +CONFIG_V4L_USB_DRIVERS=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +CONFIG_V4L_PCI_DRIVERS=y +CONFIG_VIDEO_AU0828=m +CONFIG_VIDEO_AU0828_V4L2=y +CONFIG_VIDEO_BT848=m +CONFIG_VIDEO_BT848_DVB=y +CONFIG_VIDEO_BWQCAM=m +CONFIG_VIDEO_SR030PC30=m +CONFIG_VIDEO_NOON010PC30=m +CONFIG_VIDEO_CAFE_CCIC=m +# CONFIG_VIDEO_CPIA is not set +CONFIG_VIDEO_CPIA2=m +CONFIG_VIDEO_CQCAM=m +CONFIG_VIDEO_CX23885=m +CONFIG_MEDIA_ALTERA_CI=m +CONFIG_VIDEO_CX18=m +CONFIG_VIDEO_CX18_ALSA=m +CONFIG_VIDEO_CX88=m +CONFIG_VIDEO_CX88_DVB=m +CONFIG_VIDEO_CX88_ALSA=m +CONFIG_VIDEO_CX88_BLACKBIRD=m +CONFIG_VIDEO_CX88_ENABLE_VP3054=y +CONFIG_VIDEO_CX88_VP3054=m +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_V4L2=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_VIDEO_EM28XX_RC=y +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_VIDEO_CX231XX_RC=y +CONFIG_VIDEO_HEXIUM_ORION=m +CONFIG_VIDEO_HEXIUM_GEMINI=m +CONFIG_VIDEO_IVTV=m +# CONFIG_VIDEO_IVTV_ALSA is not set +CONFIG_VIDEO_MEYE=m +CONFIG_VIDEO_MXB=m +CONFIG_VIDEO_PVRUSB2_DVB=y +# CONFIG_VIDEO_PMS is not set +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_SAA6588=m +CONFIG_VIDEO_SAA7134=m +CONFIG_VIDEO_SAA7134_ALSA=m +CONFIG_VIDEO_SAA7134_DVB=m +CONFIG_VIDEO_SAA7134_RC=y +CONFIG_VIDEO_USBVISION=m +CONFIG_VIDEO_STK1160_COMMON=m +CONFIG_VIDEO_STK1160=m +CONFIG_VIDEO_STK1160_AC97=y +CONFIG_VIDEO_W9966=m +CONFIG_VIDEO_ZORAN=m +CONFIG_VIDEO_ZORAN_AVS6EYES=m +CONFIG_VIDEO_ZORAN_BUZ=m +CONFIG_VIDEO_ZORAN_DC10=m +CONFIG_VIDEO_ZORAN_DC30=m +CONFIG_VIDEO_ZORAN_LML33=m +CONFIG_VIDEO_ZORAN_LML33R10=m +CONFIG_VIDEO_ZORAN_ZR36060=m +# CONFIG_V4L_ISA_PARPORT_DRIVERS is not set +CONFIG_VIDEO_FB_IVTV=m +CONFIG_VIDEO_SAA7164=m +CONFIG_VIDEO_TM6000=m +CONFIG_VIDEO_TM6000_ALSA=m +CONFIG_VIDEO_TM6000_DVB=m +CONFIG_VIDEO_TLG2300=m +CONFIG_VIDEO_USBTV=m + +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y + +# +# Radio Adapters +# +CONFIG_RADIO_MAXIRADIO=m +CONFIG_RADIO_SHARK=m +CONFIG_RADIO_SHARK2=m +CONFIG_RADIO_WL1273=m + +CONFIG_MEDIA_ATTACH=y + +# +# V4L/DVB tuners +# Selected automatically by not setting CONFIG_MEDIA_TUNER_CUSTOMISE +# +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set + +# +# Digital Video Broadcasting Devices +# +CONFIG_DVB_CAPTURE_DRIVERS=y +CONFIG_DVB_CORE=m +CONFIG_DVB_NET=y +CONFIG_DVB_MAX_ADAPTERS=8 +CONFIG_DVB_DYNAMIC_MINORS=y + +# +# DVB frontends +# Selected automatically by not setting CONFIG_DVB_FE_CUSTOMISE +# +# CONFIG_DVB_FE_CUSTOMISE is not set + +# +# Supported DVB bridge Modules +# +CONFIG_DVB_BT8XX=m +CONFIG_DVB_BUDGET_CORE=m +CONFIG_DVB_PLUTO2=m +CONFIG_SMS_SIANO_MDTV=m +CONFIG_SMS_SIANO_RC=y +# CONFIG_SMS_SIANO_DEBUGFS is not set +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_SMS_USB_DRV=m +CONFIG_SMS_SDIO_DRV=m +CONFIG_DVB_TTUSB_DEC=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_FRIIO=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_PCTV452E=m +CONFIG_DVB_USB_IT913X=m +CONFIG_DVB_USB_MXL111SF=m +CONFIG_DVB_DM1105=m +CONFIG_DVB_FIREDTV=m +CONFIG_DVB_NGENE=m +CONFIG_DVB_DDBRIDGE=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_USB_V2=m + +CONFIG_DVB_AV7110=m +CONFIG_DVB_AV7110_OSD=y +CONFIG_DVB_BUDGET=m +CONFIG_DVB_BUDGET_CI=m +CONFIG_DVB_BUDGET_AV=m +CONFIG_DVB_BUDGET_PATCH=m + +CONFIG_DVB_TTUSB_BUDGET=m + +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_B2C2_FLEXCOP=m +# CONFIG_DVB_B2C2_FLEXCOP_USB_DEBUG is not set + +CONFIG_DVB_B2C2_FLEXCOP_PCI=m +# CONFIG_DVB_B2C2_FLEXCOP_PCI_DEBUG is not set +CONFIG_DVB_B2C2_FLEXCOP_USB=m +# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set +CONFIG_DVB_USB=m +# CONFIG_DVB_USB_DEBUG is not set +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_DIBUSB_MB=m +# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_USB_AZ6007=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_USB_RTL28XXU=m +CONFIG_DVB_USB_AF9035=m + +CONFIG_DVB_PT1=m + +CONFIG_MANTIS_CORE=m +CONFIG_DVB_MANTIS=m +CONFIG_DVB_HOPPER=m + +CONFIG_VIDEO_SAA7146=m +CONFIG_VIDEO_SAA7146_VV=m +CONFIG_VIDEO_TVP5150=m +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEO_BTCX=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_SYSFS=y +# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set + +CONFIG_RC_CORE=m +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_RC_LOOPBACK=m +CONFIG_RC_MAP=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_RC5_SZ_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_IMON=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_ITE_CIR=m +CONFIG_IR_NUVOTON=m +CONFIG_IR_FINTEK=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_ENE=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_WINBOND_CIR=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_IR_GPIO_CIR=m + +CONFIG_V4L_MEM2MEM_DRIVERS=y +# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set +# CONFIG_VIDEO_SH_VEU is not set +# CONFIG_VIDEO_RENESAS_VSP1 is not set +# CONFIG_V4L_TEST_DRIVERS is not set + +# CONFIG_VIDEO_MEM2MEM_TESTDEV is not set + +# +# Broadcom Crystal HD video decoder driver +# +CONFIG_CRYSTALHD=m + +# +# Graphics support +# + +CONFIG_DISPLAY_SUPPORT=m +CONFIG_VIDEO_OUTPUT_CONTROL=m + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_VGACON_SOFT_SCROLLBACK=y +CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y + +# +# Logo configuration +# +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y + +# +# Sound +# + +# +# Advanced Linux Sound Architecture +# +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +# CONFIG_SND_DEBUG_VERBOSE is not set +CONFIG_SND_VERBOSE_PROCFS=y +CONFIG_SND_SEQUENCER=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_SEQ_HRTIMER_DEFAULT=y +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_SEQ_RTCTIMER_DEFAULT=y +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_RTCTIMER=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_MAX_CARDS=32 +# CONFIG_SND_SUPPORT_OLD_API is not set + +# +# Generic devices +# +CONFIG_SND_DUMMY=m +CONFIG_SND_ALOOP=m +CONFIG_SND_VIRMIDI=m +CONFIG_SND_MTPAV=m +CONFIG_SND_MTS64=m +CONFIG_SND_SERIAL_U16550=m +CONFIG_SND_MPU401=m +CONFIG_SND_PORTMAN2X4=m +CONFIG_SND_AC97_POWER_SAVE=y +CONFIG_SND_AC97_POWER_SAVE_DEFAULT=0 + +CONFIG_SND_DRIVERS=y + +# +# ISA devices +# +CONFIG_SND_AD1889=m + +# +# PCI devices +# +CONFIG_SND_PCI=y +CONFIG_SND_ALI5451=m +CONFIG_SND_ALS300=m +CONFIG_SND_ALS4000=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m +# CONFIG_SND_AW2 is not set +CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m +# CONFIG_SND_BT87X_OVERCLOCK is not set +CONFIG_SND_CA0106=m +CONFIG_SND_CMIPCI=m +CONFIG_SND_CS46XX=m +CONFIG_SND_CS46XX_NEW_DSP=y +CONFIG_SND_CS4281=m +CONFIG_SND_CS5530=m +CONFIG_SND_CS5535AUDIO=m +CONFIG_SND_EMU10K1=m +CONFIG_SND_EMU10K1X=m +CONFIG_SND_ENS1370=m +CONFIG_SND_ENS1371=m +CONFIG_SND_ES1938=m +CONFIG_SND_ES1968=m +CONFIG_SND_ES1968_INPUT=y +CONFIG_SND_ES1968_RADIO=y +CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X_BOOL=y +CONFIG_SND_CTXFI=m +CONFIG_SND_LX6464ES=m +CONFIG_SND_HDA_INTEL=y +CONFIG_SND_HDA_INPUT_BEEP=y +CONFIG_SND_HDA_INPUT_BEEP_MODE=0 +CONFIG_SND_HDA_INPUT_JACK=y +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_HWDEP=y +CONFIG_SND_HDA_CODEC_REALTEK=y +CONFIG_SND_HDA_ENABLE_REALTEK_QUIRKS=y +CONFIG_SND_HDA_CODEC_CA0110=y +CONFIG_SND_HDA_CODEC_ANALOG=y +CONFIG_SND_HDA_CODEC_SIGMATEL=y +CONFIG_SND_HDA_CODEC_VIA=y +CONFIG_SND_HDA_CODEC_CIRRUS=y +CONFIG_SND_HDA_CODEC_CONEXANT=y +CONFIG_SND_HDA_CODEC_CMEDIA=y +CONFIG_SND_HDA_CODEC_SI3054=y +CONFIG_SND_HDA_CODEC_HDMI=y +CONFIG_SND_HDA_I915=y +CONFIG_SND_HDA_CODEC_CA0132=y +CONFIG_SND_HDA_CODEC_CA0132_DSP=y +CONFIG_SND_HDA_GENERIC=y +CONFIG_SND_HDA_POWER_SAVE=y +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0 +CONFIG_SND_HDA_RECONFIG=y +CONFIG_SND_HDA_PREALLOC_SIZE=4096 +CONFIG_SND_HDSPM=m +CONFIG_SND_ICE1712=m +CONFIG_SND_ICE1724=m +CONFIG_SND_INTEL8X0=y +CONFIG_SND_INTEL8X0M=m +CONFIG_SND_KORG1212=m +CONFIG_SND_MAESTRO3=m +CONFIG_SND_MAESTRO3_INPUT=y +CONFIG_SND_MIXART=m +CONFIG_SND_NM256=m +CONFIG_SND_OXYGEN=m +CONFIG_SND_RME32=m +CONFIG_SND_PCSP=m +CONFIG_SND_PCXHR=m +CONFIG_SND_RIPTIDE=m +CONFIG_SND_RME96=m +CONFIG_SND_RME9652=m +CONFIG_SND_SIS7019=m +CONFIG_SND_SONICVIBES=m +CONFIG_SND_HDSP=m +CONFIG_SND_TRIDENT=m +CONFIG_SND_VIA82XX=m +CONFIG_SND_VIA82XX_MODEM=m +CONFIG_SND_VIRTUOSO=m +CONFIG_SND_VX222=m +CONFIG_SND_YMFPCI=m +CONFIG_SND_ASIHPI=m +CONFIG_SND_LOLA=m + +# +# ALSA USB devices +# +CONFIG_SND_USB=y +CONFIG_SND_USB_CAIAQ=m +CONFIG_SND_USB_CAIAQ_INPUT=y +CONFIG_SND_USB_USX2Y=m +CONFIG_SND_USB_US122L=m +CONFIG_SND_USB_UA101=m +CONFIG_SND_USB_6FIRE=m +CONFIG_SND_USB_HIFACE=m + +# +# PCMCIA devices +# +# CONFIG_SND_PCMCIA is not set + +CONFIG_SND_FIREWIRE=y +CONFIG_SND_FIREWIRE_SPEAKERS=m +CONFIG_SND_ISIGHT=m +CONFIG_SND_SCS1X=m +CONFIG_SND_DICE=m + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB_SUPPORT=y +# CONFIG_USB_DEBUG is not set + +# DEPRECATED: See bug 362221. Fix udev. +# CONFIG_USB_DEVICE_CLASS is not set + + +# +# Miscellaneous USB options +# + +# Deprecated. +# CONFIG_USB_DEVICEFS is not set + +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y + +# +# USB Host Controller Drivers +# +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +# CONFIG_USB_EHCI_MV is not set +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +CONFIG_USB_ISP1362_HCD=m +CONFIG_USB_FUSBH200_HCD=m +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_GR_UDC is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PCI=y +# CONFIG_USB_OHCI_HCD_SSB is not set +# CONFIG_USB_HCD_TEST_MODE is not set +# CONFIG_USB_OHCI_HCD_PLATFORM is not set +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_SL811_HCD=m +CONFIG_USB_SL811_HCD_ISO=y +# CONFIG_USB_SL811_CS is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_XHCI_HCD=y +# CONFIG_USB_XHCI_HCD_DEBUGGING is not set + +# +# USB Device Class drivers +# + +# +# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_WDM=m +CONFIG_USB_TMC=m +# CONFIG_BLK_DEV_UB is not set +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_CYPRESS_ATACB=m +CONFIG_USB_STORAGE_DATAFAB=m +CONFIG_USB_STORAGE_FREECOM=m +CONFIG_USB_STORAGE_ISD200=m +CONFIG_USB_STORAGE_SDDR09=m +CONFIG_USB_STORAGE_SDDR55=m +CONFIG_USB_STORAGE_JUMPSHOT=m +CONFIG_USB_STORAGE_USBAT=y +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_REALTEK=m +CONFIG_REALTEK_AUTOPM=y +CONFIG_USB_STORAGE_ENE_UB6250=m +# CONFIG_USB_LIBUSUAL is not set +# CONFIG_USB_UAS is not set + + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y + +CONFIG_HID_SUPPORT=y + +CONFIG_HID=y +CONFIG_I2C_HID=m +CONFIG_HID_BATTERY_STRENGTH=y +# debugging default is y upstream now +CONFIG_HIDRAW=y +CONFIG_UHID=m +CONFIG_HID_PID=y +CONFIG_LOGITECH_FF=y +CONFIG_HID_LOGITECH_DJ=m +CONFIG_LOGIWII_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_PANTHERLORD_FF=y +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_WACOM=m +CONFIG_HID_WACOM_POWER_SUPPLY=y +CONFIG_ZEROPLUS_FF=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_IDMOUSE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_GREENASIA_FF=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTRIG=y +CONFIG_HID_QUANTA=y +CONFIG_HID_PRIMAX=m +CONFIG_HID_PS3REMOTE=m +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_TWINHAN=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_RMI=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_ROCCAT_KONE=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_SONY_FF=y +CONFIG_HID_SUNPLUS=m +CONFIG_HID_STEELSERIES=m +CONFIG_HID_GREENASIA=m +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_HID_ZYDACRON=m +CONFIG_HID_SENSOR_HUB=m +CONFIG_HID_SENSOR_GYRO_3D=m +CONFIG_HID_SENSOR_MAGNETOMETER_3D=m +CONFIG_HID_SENSOR_ALS=m +CONFIG_HID_SENSOR_ACCEL_3D=m +CONFIG_HID_EMS_FF=m +CONFIG_HID_ELECOM=m +CONFIG_HID_ELO=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_ROCCAT_PYRA=m +CONFIG_HID_ROCCAT_KONEPLUS=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LENOVO_TPKBD=m +CONFIG_HID_ROCCAT_ARVO=m +CONFIG_HID_ROCCAT_ISKU=m +CONFIG_HID_ROCCAT_KOVAPLUS=m +CONFIG_HID_HOLTEK=m +CONFIG_HOLTEK_FF=y +CONFIG_HID_HUION=m +CONFIG_HID_SPEEDLINK=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_WIIMOTE_EXT=y +CONFIG_HID_KYE=m +CONFIG_HID_SAITEK=m +CONFIG_HID_TIVO=m +CONFIG_HID_GENERIC=y +CONFIG_HID_AUREAL=m +CONFIG_HID_APPLEIR=m + + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m + +# +# USB Multimedia devices +# + +CONFIG_USB_DSBR=m +# CONFIG_USB_ET61X251 is not set +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GSPCA=m +CONFIG_USB_GSPCA_MR97310A=m +CONFIG_USB_GSPCA_BENQ=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SN9C2028=m +CONFIG_USB_GSPCA_SN9C20X=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STK1135=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TOPRO=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_USB_GSPCA_SQ905=m +CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_PAC7302=m +CONFIG_USB_GSPCA_STV0680=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_JEILINJ=m +CONFIG_USB_GSPCA_JL2005BCD=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m +CONFIG_USB_GSPCA_SPCA1528=m +CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_NW80X=m +CONFIG_USB_GSPCA_VICAM=m +CONFIG_USB_GSPCA_KINECT=m +CONFIG_USB_GSPCA_SE401=m + +CONFIG_USB_S2255=m +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_USB_SN9C102 is not set +CONFIG_USB_ZR364XX=m + +# +# USB Network adaptors +# +CONFIG_USB_CATC=m +CONFIG_USB_HSO=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_USBNET=m +CONFIG_USB_SPEEDTOUCH=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9700=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_CDC_PHONET=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m + +# +# USB Host-to-Host Cables +# +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y + +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB port drivers +# +CONFIG_USB_USS720=m + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_SIMPLE=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_EMPEG=m +# CONFIG_USB_SERIAL_F81232 is not set +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +# CONFIG_USB_SERIAL_METRO is not set +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7715_PARPORT=y +# CONFIG_USB_SERIAL_ZIO is not set +# CONFIG_USB_SERIAL_WISHBONE is not set +# CONFIG_USB_SERIAL_ZTE is not set +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MOTOROLA=m +# CONFIG_USB_SERIAL_MXUPORT is not set +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_PL2303=m +# CONFIG_USB_SERIAL_QUATECH2 is not set +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SIEMENS_MPI=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_DEBUG=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +CONFIG_USB_SERIAL_FLASHLOADER=m +CONFIG_USB_SERIAL_SUUNTO=m +CONFIG_USB_SERIAL_CONSOLE=y + +CONFIG_USB_EZUSB=y +CONFIG_USB_EMI62=m +CONFIG_USB_LED=m +# CONFIG_USB_CYPRESS_CY7C63 is not set + +# +# USB Miscellaneous drivers +# + +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_APPLEDISPLAY=m + +# Physical Layer USB driver +# CONFIG_USB_OTG_FSM is not set + +# CONFIG_GENERIC_PHY is not set +# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set +# CONFIG_PHY_EXYNOS_DP_VIDEO is not set +# CONFIG_OMAP_USB2 is not set +# CONFIG_OMAP_USB3 is not set +# CONFIG_OMAP_CONTROL_USB is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_SAMSUNG_USBPHY is not set +# CONFIG_SAMSUNG_USB2PHY is not set +# CONFIG_SAMSUNG_USB3PHY is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +CONFIG_USB_RCAR_PHY=m +CONFIG_USB_ATM=m +CONFIG_USB_CXACRU=m +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_CYTHERM is not set +CONFIG_USB_EMI26=m +CONFIG_USB_FTDI_ELAN=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_OXU210HP_HCD is not set +CONFIG_USB_IOWARRIOR=m +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_YUREX=m +CONFIG_USB_EZUSB_FX2=m +CONFIG_USB_HSIC_USB3503=m +CONFIG_USB_LCD=m +CONFIG_USB_LD=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_MON=y +CONFIG_USB_PWC=m +CONFIG_USB_PWC_INPUT_EVDEV=y +# CONFIG_USB_PWC_DEBUG is not set +# CONFIG_USB_RIO500 is not set +CONFIG_USB_SISUSBVGA=m +CONFIG_USB_SISUSBVGA_CON=y +CONFIG_RADIO_SI470X=y +CONFIG_USB_KEENE=m +CONFIG_USB_MA901=m +CONFIG_USB_SI470X=m +CONFIG_I2C_SI470X=m +CONFIG_RADIO_SI4713=m +# CONFIG_RADIO_TEF6862 is not set +CONFIG_USB_MR800=m +CONFIG_USB_STKWEBCAM=m +# CONFIG_USB_TEST is not set +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_U132_HCD=m +CONFIG_USB_UEAGLEATM=m +CONFIG_USB_XUSBATM=m + +# CONFIG_USB_DWC2 is not set + +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# CONFIG_USB_ISP1301 is not set + +# CONFIG_USB_OTG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB=m +CONFIG_SSB_PCIHOST=y +CONFIG_SSB_SDIOHOST=y +CONFIG_SSB_PCMCIAHOST=y +# CONFIG_SSB_SILENT is not set +# CONFIG_SSB_DEBUG is not set +CONFIG_SSB_DRIVER_PCICORE=y +CONFIG_SSB_DRIVER_GPIO=y + +# Multifunction USB devices +# CONFIG_MFD_PCF50633 is not set +CONFIG_PCF50633_ADC=m +CONFIG_PCF50633_GPIO=m +# CONFIG_AB3100_CORE is not set +CONFIG_INPUT_PCF50633_PMU=m +CONFIG_INPUT_GPIO_ROTARY_ENCODER=m + +CONFIG_MFD_SUPPORT=y +CONFIG_MFD_VX855=m +CONFIG_MFD_SM501=m +CONFIG_MFD_SM501_GPIO=y +CONFIG_MFD_RTSX_PCI=m +# CONFIG_MFD_TI_AM335X_TSCADC is not set +CONFIG_MFD_VIPERBOARD=m +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8350 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_AB3100_OTP is not set +# CONFIG_MFD_TIMBERDALE is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_LPC_SCH is not set +# CONFIG_LPC_ICH is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_TPS6507X is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_ARIZONA is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_LP3943 is not set + +# +# File systems +# +CONFIG_MISC_FILESYSTEMS=y + +# ext4 is used for ext2 and ext3 filesystems +CONFIG_JBD2=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +CONFIG_XFS_FS=m +# CONFIG_XFS_DEBUG is not set +# CONFIG_XFS_RT is not set +CONFIG_XFS_QUOTA=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_DNOTIFY=y +# Autofsv3 is obsolete. +# systemd is dependant upon AUTOFS, so build it in. +# CONFIG_EXOFS_FS is not set +# CONFIG_EXOFS_DEBUG is not set +CONFIG_NILFS2_FS=m +# CONFIG_LOGFS is not set +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_BLK_DEV_RBD=m +CONFIG_CEPH_LIB=m +CONFIG_CEPH_FS_POSIX_ACL=y +# CONFIG_CEPH_LIB_USE_DNS_RESOLVER is not set + +CONFIG_FSCACHE=m +CONFIG_FSCACHE_STATS=y +# CONFIG_FSCACHE_HISTOGRAM is not set +# CONFIG_FSCACHE_DEBUG is not set +CONFIG_FSCACHE_OBJECT_LIST=y + +CONFIG_CACHEFILES=m +# CONFIG_CACHEFILES_DEBUG is not set +# CONFIG_CACHEFILES_HISTOGRAM is not set + +# +# CD-ROM/DVD Filesystems +# + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_VMCORE=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +CONFIG_HUGETLBFS=y +CONFIG_HUGETLB_PAGE=y +# CONFIG_DEBUG_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +CONFIG_AFFS_FS=m +CONFIG_ECRYPT_FS=m +# CONFIG_ECRYPT_FS_MESSAGING is not set +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +# CONFIG_HFSPLUS_FS_POSIX_ACL is not set +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set + +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=m +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set +# CONFIG_UFS_DEBUG is not set +CONFIG_9P_FS=m +CONFIG_9P_FSCACHE=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_9P_FS_SECURITY=y +# CONFIG_OMFS_FS is not set +CONFIG_CUSE=m +# CONFIG_F2FS_FS is not set + +# +# Network File Systems +# +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_V2 is not set +CONFIG_NFS_V3=y +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" +# CONFIG_NFS_V4_1_MIGRATION is not set +CONFIG_NFS_V4_2=y +CONFIG_NFSD=m +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_V4_SECURITY_LABEL=y +CONFIG_NFS_FSCACHE=y +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_PNFS_OBJLAYOUT=m +CONFIG_PNFS_BLOCK=m +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_SUNRPC_XPRT_RDMA=m +CONFIG_SUNRPC_DEBUG=y +CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +# CONFIG_CIFS_STATS2 is not set +CONFIG_CIFS_SMB2=y +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_FSCACHE=y +CONFIG_CIFS_ACL=y +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_DEBUG=y +# CONFIG_CIFS_DEBUG2 is not set +CONFIG_CIFS_DFS_UPCALL=y +CONFIG_CIFS_NFSD_EXPORT=y +CONFIG_NCP_FS=m +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y +CONFIG_CODA_FS=m +# CONFIG_AFS_FS is not set +# CONFIG_AF_RXRPC is not set + +CONFIG_OCFS2_FS=m +# CONFIG_OCFS2_DEBUG_FS is not set +# CONFIG_OCFS2_DEBUG_MASKLOG is not set +CONFIG_OCFS2_FS_O2CB=m +CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m +# CONFIG_OCFS2_FS_STATS is not set + +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +# Maybe see if we want this on for debug kernels? +# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_ASSERT is not set + +CONFIG_CONFIGFS_FS=y + +CONFIG_DLM=m +CONFIG_DLM_DEBUG=y +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=y + + +CONFIG_UBIFS_FS_XATTR=y +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +# CONFIG_UBIFS_FS_DEBUG is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_AIX_PARTITION=y +CONFIG_AMIGA_PARTITION=y +# CONFIG_ATARI_PARTITION is not set +CONFIG_BSD_DISKLABEL=y +CONFIG_EFI_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_LDM_PARTITION=y +# CONFIG_LDM_DEBUG is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_OSF_PARTITION=y +CONFIG_SGI_PARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_SUN_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_CMDLINE_PARTITION is not set + +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_OPROFILE_EVENT_MULTIPLEX=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0 +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +# CONFIG_DEBUG_DRIVER is not set +CONFIG_HEADERS_CHECK=y +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_READABLE_ASM is not set + +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_LOCKDEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set + +# DEBUG options that don't get enabled/disabled with 'make debug/release' + +# This generates a huge amount of dmesg spew +# CONFIG_DEBUG_KOBJECT is not set +# +# This breaks booting until the module patches are in-tree +# CONFIG_DEBUG_KOBJECT_RELEASE is not set +# +# +# These debug options are deliberatly left on (even in 'make release' kernels). +# They aren't that much of a performance impact, and the value +# from getting useful bug-reports makes it worth leaving them on. +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_BOOT_PRINTK_DELAY=y +CONFIG_DEBUG_DEVRES=y +CONFIG_DEBUG_RODATA_TEST=y +CONFIG_DEBUG_NX_TEST=m +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_DEBUG_BOOT_PARAMS=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +CONFIG_LOCKUP_DETECTOR=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_TIMEOUT=0 +CONFIG_ATOMIC64_SELFTEST=y +CONFIG_MEMORY_FAILURE=y +CONFIG_HWPOISON_INJECT=m +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +CONFIG_EARLY_PRINTK_DBGP=y +# CONFIG_PAGE_POISONING is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_CRASH is not set +# CONFIG_GCOV_KERNEL is not set +# CONFIG_RAMOOPS is not set + + +# +# Security options +# +CONFIG_SECURITY=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_NETWORK_XFRM=y +# CONFIG_SECURITY_PATH is not set +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_DISABLE=y +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +CONFIG_SECURITY_SELINUX_AVC_STATS=y +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_YAMA is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +# http://lists.fedoraproject.org/pipermail/kernel/2013-February/004125.html +CONFIG_AUDIT_LOGINUID_IMMUTABLE=y + +CONFIG_SECCOMP=y + +# CONFIG_SSBI is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_FIPS=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_USER_API_SKCIPHER=y +CONFIG_CRYPTO_MANAGER=y +# Note, CONFIG_CRYPTO_MANAGER_DISABLE_TESTS needs to be unset, or FIPS will be disabled. +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_BLKCIPHER=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_CRC32=m +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_GF128MUL=m +CONFIG_CRYPTO_CMAC=m +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_LZO=m +CONFIG_CRYPTO_LZ4=m +CONFIG_CRYPTO_LZ4HC=m +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_SALSA20=m +CONFIG_CRYPTO_SALSA20_586=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SEQIV=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_XCBC=m +CONFIG_CRYPTO_VMAC=m +CONFIG_CRYPTO_CRC32C_INTEL=m +CONFIG_CRYPTO_GHASH=m +CONFIG_CRYPTO_DEV_HIFN_795X=m +CONFIG_CRYPTO_DEV_HIFN_795X_RNG=y +CONFIG_CRYPTO_PCRYPT=m + + + +# Random number generation + +# +# Library routines +# +CONFIG_CRC16=y +CONFIG_CRC32=m +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC8=m +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_CORDIC=m +# CONFIG_DDR is not set + +CONFIG_CRYPTO_ZLIB=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m + +CONFIG_INITRAMFS_SOURCE="" +CONFIG_KEYS=y +CONFIG_PERSISTENT_KEYRINGS=y +CONFIG_BIG_KEYS=y +CONFIG_TRUSTED_KEYS=m +CONFIG_ENCRYPTED_KEYS=m +CONFIG_KEYS_DEBUG_PROC_KEYS=y +CONFIG_CDROM_PKTCDVD=m +CONFIG_CDROM_PKTCDVD_BUFFERS=8 +# CONFIG_CDROM_PKTCDVD_WCACHE is not set + +CONFIG_ATA_OVER_ETH=m +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_BACKLIGHT_CLASS_DEVICE=m +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_PROGEAR=m + +CONFIG_LCD_CLASS_DEVICE=m +CONFIG_LCD_PLATFORM=m + +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_SCHED_OMIT_FRAME_POINTER=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_AUTOGROUP=y + +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y + +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_SCHED=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_SWAP_ENABLED=y +CONFIG_MEMCG_KMEM=y +# CONFIG_CGROUP_HUGETLB is not set +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_NET_PRIO=m +# CONFIG_CGROUP_NET_CLASSID is not set +CONFIG_BLK_CGROUP=y + +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set + +CONFIG_PRINTK_TIME=y + +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set + +CONFIG_KEXEC=y + +CONFIG_HWMON=y +# CONFIG_HWMON_DEBUG_CHIP is not set +CONFIG_THERMAL_HWMON=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +CONFIG_THERMAL_GOV_FAIR_SHARE=y +# CONFIG_THERMAL_GOV_USER_SPACE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_EMULATION is not set + +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y + +# +# Bus devices +# +# CONFIG_OMAP_OCP2SCP is not set +CONFIG_PROC_EVENTS=y + +CONFIG_IBMASR=m + +CONFIG_PM=y +CONFIG_PM_STD_PARTITION="" +# CONFIG_DPM_WATCHDOG is not set # revisit this in debug +CONFIG_PM_TRACE=y +CONFIG_PM_TRACE_RTC=y +# CONFIG_PM_OPP is not set +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +CONFIG_HIBERNATION=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_SUSPEND=y + +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=m +CONFIG_CPU_FREQ_STAT_DETAILS=y + + +CONFIG_NET_VENDOR_SMC=y +# CONFIG_IBMTR is not set +# CONFIG_SKISA is not set +# CONFIG_PROTEON is not set +# CONFIG_SMCTR is not set + +# CONFIG_MOUSE_ATIXL is not set + +# CONFIG_MEDIA_PARPORT_SUPPORT is not set + +CONFIG_RADIO_TEA5764=m +CONFIG_RADIO_SAA7706H=m +CONFIG_RADIO_CADET=m +CONFIG_RADIO_RTRACK=m +CONFIG_RADIO_RTRACK2=m +CONFIG_RADIO_AZTECH=m +CONFIG_RADIO_GEMTEK=m +CONFIG_RADIO_SF16FMI=m +CONFIG_RADIO_SF16FMR2=m +CONFIG_RADIO_TERRATEC=m +CONFIG_RADIO_TRUST=m +CONFIG_RADIO_TYPHOON=m +CONFIG_RADIO_ZOLTRIX=m + +CONFIG_SND_DARLA20=m +CONFIG_SND_GINA20=m +CONFIG_SND_LAYLA20=m +CONFIG_SND_DARLA24=m +CONFIG_SND_GINA24=m +CONFIG_SND_LAYLA24=m +CONFIG_SND_MONA=m +CONFIG_SND_MIA=m +CONFIG_SND_ECHO3G=m +CONFIG_SND_INDIGO=m +CONFIG_SND_INDIGOIO=m +CONFIG_SND_INDIGODJ=m +CONFIG_SND_INDIGOIOX=m +CONFIG_SND_INDIGODJX=m + +CONFIG_BALLOON_COMPACTION=y +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +CONFIG_BOUNCE=y +# CONFIG_LEDS_AMS_DELTA is not set +# CONFIG_LEDS_LOCOMO is not set +# CONFIG_LEDS_NET48XX is not set +# CONFIG_LEDS_NET5501 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_S3C24XX is not set +# CONFIG_LEDS_PCA9633 is not set +CONFIG_LEDS_DELL_NETBOOKS=m +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_PCA9685 is not set +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LEDS_TRIGGER_ONESHOT=m +CONFIG_LEDS_TRIGGER_IDE_DISK=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=m +CONFIG_LEDS_TRIGGER_BACKLIGHT=m +# CONFIG_LEDS_TRIGGER_CPU is not set +CONFIG_LEDS_TRIGGER_DEFAULT_ON=m +CONFIG_LEDS_TRIGGER_TRANSIENT=m +CONFIG_LEDS_TRIGGER_CAMERA=m +CONFIG_LEDS_ALIX2=m +CONFIG_LEDS_CLEVO_MAIL=m +CONFIG_LEDS_INTEL_SS4200=m +CONFIG_LEDS_LM3530=m +# CONFIG_LEDS_LM3642 is not set +CONFIG_LEDS_LM3556=m +CONFIG_LEDS_BLINKM=m +CONFIG_LEDS_LP3944=m +CONFIG_LEDS_LP5521=m +CONFIG_LEDS_LP5523=m +CONFIG_LEDS_LP5562=m +CONFIG_LEDS_LT3593=m +CONFIG_LEDS_REGULATOR=m +CONFIG_LEDS_WM8350=m +CONFIG_LEDS_WM831X_STATUS=m + +CONFIG_DMA_ENGINE=y +CONFIG_DW_DMAC_CORE=m +CONFIG_DW_DMAC=m +CONFIG_DW_DMAC_PCI=m +# CONFIG_DW_DMAC_BIG_ENDIAN_IO is not set +# CONFIG_TIMB_DMA is not set +# CONFIG_DMATEST is not set +CONFIG_ASYNC_TX_DMA=y + +CONFIG_UNUSED_SYMBOLS=y + +CONFIG_UPROBE_EVENT=y + +CONFIG_DYNAMIC_FTRACE=y +# CONFIG_IRQSOFF_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACER_SNAPSHOT=y +# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set +CONFIG_FTRACE_SYSCALLS=y +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +CONFIG_FUNCTION_PROFILER=y +CONFIG_RING_BUFFER_BENCHMARK=m +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +CONFIG_FUNCTION_TRACER=y +CONFIG_STACK_TRACER=y +# CONFIG_FUNCTION_GRAPH_TRACER is not set + +CONFIG_KPROBES=y +CONFIG_KPROBE_EVENT=y +# CONFIG_KPROBES_SANITY_TEST is not set +# CONFIG_JUMP_LABEL is not set +CONFIG_OPTPROBES=y + +CONFIG_HZ_1000=y + +CONFIG_TIMER_STATS=y +CONFIG_PERF_COUNTERS=y + +# Auxillary displays +CONFIG_KS0108=m +CONFIG_KS0108_PORT=0x378 +CONFIG_KS0108_DELAY=2 +CONFIG_CFAG12864B=y +CONFIG_CFAG12864B_RATE=20 + +# CONFIG_PHANTOM is not set + +# CONFIG_POWER_SUPPLY_DEBUG is not set + +# CONFIG_TEST_POWER is not set +CONFIG_APM_POWER=m +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_WM831X_POWER is not set + +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_BATTERY_GOLDFISH is not set + +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_PCF50633 is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24735 is not set +CONFIG_POWER_RESET=y + +# CONFIG_PDA_POWER is not set + +CONFIG_AUXDISPLAY=y + +CONFIG_UIO=m +CONFIG_UIO_CIF=m +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set +# CONFIG_UIO_DMEM_GENIRQ is not set +CONFIG_UIO_AEC=m +CONFIG_UIO_SERCOS3=m +CONFIG_UIO_PCI_GENERIC=m +# CONFIG_UIO_NETX is not set +# CONFIG_UIO_MF624 is not set + +CONFIG_VFIO=m +CONFIG_VFIO_IOMMU_TYPE1=m +CONFIG_VFIO_PCI=m + + +# LIRC +CONFIG_LIRC_STAGING=y +CONFIG_LIRC_BT829=m +CONFIG_LIRC_IGORPLUGUSB=m +CONFIG_LIRC_IMON=m +CONFIG_LIRC_ZILOG=m +CONFIG_LIRC_PARALLEL=m +CONFIG_LIRC_SERIAL=m +CONFIG_LIRC_SERIAL_TRANSMITTER=y +CONFIG_LIRC_SASEM=m +CONFIG_LIRC_SIR=m +CONFIG_LIRC_TTUSBIR=m + +# CONFIG_SAMPLES is not set + + +CONFIG_NOZOMI=m +# CONFIG_TPS65010 is not set + +CONFIG_INPUT_APANEL=m +CONFIG_INPUT_GP2A=m +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_GPIO_BEEPER is not set + +# CONFIG_INTEL_MENLOW is not set +CONFIG_ENCLOSURE_SERVICES=m +CONFIG_IPWIRELESS=m + +# CONFIG_BLK_DEV_XIP is not set +CONFIG_MEMSTICK=m +# CONFIG_MEMSTICK_DEBUG is not set +# CONFIG_MEMSTICK_UNSAFE_RESUME is not set +CONFIG_MSPRO_BLOCK=m +# CONFIG_MS_BLOCK is not set +CONFIG_MEMSTICK_TIFM_MS=m +CONFIG_MEMSTICK_JMICRON_38X=m +CONFIG_MEMSTICK_R592=m +CONFIG_MEMSTICK_REALTEK_PCI=m + +CONFIG_ACCESSIBILITY=y +CONFIG_A11Y_BRAILLE_CONSOLE=y + +# CONFIG_HTC_PASIC3 is not set + +# MT9V022_PCA9536_SWITCH is not set + +CONFIG_OPTIMIZE_INLINING=y + +# FIXME: This should be x86/ia64 only +# CONFIG_HP_ILO is not set + +CONFIG_GPIOLIB=y +# CONFIG_PINCTRL is not set +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINMUX is not set +# CONFIG_PINCONF is not set + +CONFIG_NET_DSA=m +CONFIG_NET_DSA_MV88E6060=m +CONFIG_NET_DSA_MV88E6131=m +CONFIG_NET_DSA_MV88E6123_61_65=m + +# Used by Maemo, we don't care. +# CONFIG_PHONET is not set + +# CONFIG_ICS932S401 is not set +# CONFIG_ATMEL_SSC is not set + +# CONFIG_C2PORT is not set + +# CONFIG_REGULATOR_DEBUG is not set + +CONFIG_WM8350_POWER=m + +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set + +CONFIG_USB_WUSB=m +CONFIG_USB_WUSB_CBAF=m +# CONFIG_USB_WUSB_CBAF_DEBUG is not set +CONFIG_USB_WHCI_HCD=m +CONFIG_USB_HWA_HCD=m +# CONFIG_USB_HCD_BCMA is not set +# CONFIG_USB_HCD_SSB is not set + +CONFIG_UWB=m +CONFIG_UWB_HWA=m +CONFIG_UWB_WHCI=m +CONFIG_UWB_I1480U=m + +# CONFIG_ANDROID is not set +CONFIG_STAGING_MEDIA=y +# CONFIG_DVB_AS102 is not set +# CONFIG_ET131X is not set +# CONFIG_SLICOSS is not set +# CONFIG_WLAGS49_H2 is not set +# CONFIG_WLAGS49_H25 is not set +# CONFIG_VIDEO_DT3155 is not set +# CONFIG_TI_ST is not set +# CONFIG_FB_XGI is not set +# CONFIG_VIDEO_GO7007 is not set +# CONFIG_I2C_BCM2048 is not set +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_OMAP4 is not set +# CONFIG_USB_MSI3101 is not set +# CONFIG_DT3155 is not set +# CONFIG_W35UND is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +CONFIG_USB_ATMEL=m +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_PANEL is not set +# CONFIG_TRANZPORT is not set +# CONFIG_POHMELFS is not set +# CONFIG_IDE_PHISON is not set +# CONFIG_LINE6_USB is not set +# CONFIG_VME_BUS is not set +# CONFIG_RAR_REGISTER is not set +# CONFIG_VT6656 is not set +# CONFIG_USB_SERIAL_QUATECH_USB2 is not set +# Larry Finger maintains these (rhbz 913753) +CONFIG_RTLLIB=m +CONFIG_RTLLIB_CRYPTO_CCMP=m +CONFIG_RTLLIB_CRYPTO_TKIP=m +CONFIG_RTLLIB_CRYPTO_WEP=m +CONFIG_RTL8192E=m +# CONFIG_INPUT_GPIO is not set +# CONFIG_VIDEO_CX25821 is not set +# CONFIG_R8187SE is not set +# CONFIG_R8188EU is not set +# CONFIG_R8821AE is not set +# CONFIG_RTL8192U is not set +# CONFIG_FB_SM7XX is not set +# CONFIG_SPECTRA is not set +# CONFIG_EASYCAP is not set +# CONFIG_SOLO6X10 is not set +# CONFIG_ACPI_QUICKSTART is not set +# CONFIG_LTE_GDM724X is not set +CONFIG_R8712U=m # Larry Finger maintains this (rhbz 699618) +# CONFIG_R8712_AP is not set +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_USB_BTMTK is not set +# CONFIG_FT1000 is not set +# CONFIG_SPEAKUP is not set +# CONFIG_DX_SEP is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_RTS_PSTOR is not set +CONFIG_ALTERA_STAPL=m +# CONFIG_DVB_CXD2099 is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_INTEL_MEI is not set +# CONFIG_ZCACHE is not set +# CONFIG_RTS5139 is not set +# CONFIG_NVEC_LEDS is not set +# CONFIG_VT6655 is not set +# CONFIG_RAMSTER is not set +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_IPACK_BUS is not set +# CONFIG_CSR_WIFI is not set +# CONFIG_ZCACHE2 is not set +# CONFIG_NET_VENDOR_SILICOM is not set +# CONFIG_SBYPASS is not set +# CONFIG_BPCTL is not set +# CONFIG_CED1401 is not set +# CONFIG_DGRP is not set +# CONFIG_SB105X is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_XILLYBUS is not set +# CONFIG_DGAP is not set +# CONFIG_DGNC is not set +# CONFIG_RTS5208 is not set +# END OF STAGING + +# +# Remoteproc drivers (EXPERIMENTAL) +# +# CONFIG_STE_MODEM_RPROC is not set + +CONFIG_LIBFC=m +CONFIG_LIBFCOE=m +CONFIG_FCOE=m +CONFIG_FCOE_FNIC=m + + +# CONFIG_IMA is not set +CONFIG_IMA_MEASURE_PCR_IDX=10 +CONFIG_IMA_AUDIT=y +CONFIG_IMA_LSM_RULES=y + +# CONFIG_EVM is not set +# CONFIG_PWM_PCA9685 is not set + +CONFIG_LSM_MMAP_MIN_ADDR=65536 + +CONFIG_STRIP_ASM_SYMS=y + +# CONFIG_RCU_FANOUT_EXACT is not set +# FIXME: Revisit FAST_NO_HZ after it's fixed +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_RCU_NOCB_CPU is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_USER_QS is not set + +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 + +CONFIG_FSNOTIFY=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y + +CONFIG_IEEE802154=m +CONFIG_IEEE802154_6LOWPAN=m +CONFIG_IEEE802154_DRIVERS=m +CONFIG_IEEE802154_FAKEHARD=m +CONFIG_IEEE802154_FAKELB=m + +CONFIG_MAC802154=m +CONFIG_NET_MPLS_GSO=m + +# CONFIG_HSR is not set + +# CONFIG_EXTCON is not set +# CONFIG_EXTCON_ADC_JACK is not set +# CONFIG_MEMORY is not set + +CONFIG_PPS=m +# CONFIG_PPS_CLIENT_KTIMER is not set +CONFIG_PPS_CLIENT_LDISC=m +# CONFIG_PPS_DEBUG is not set +CONFIG_PPS_CLIENT_PARPORT=m +CONFIG_PPS_GENERATOR_PARPORT=m +CONFIG_PPS_CLIENT_GPIO=m +CONFIG_NTP_PPS=y + +CONFIG_PTP_1588_CLOCK=m +CONFIG_PTP_1588_CLOCK_PCH=m + +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_ZSWAP=y +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set + +# CONFIG_MDIO_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_DESIGNWARE_PCI is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_CS5535 is not set +# CONFIG_GPIO_IT8761E is not set +# CONFIG SB105x is not set +# CONFIG_GPIO_TS5500 is not set +CONFIG_GPIO_VIPERBOARD=m +# CONFIG_UCB1400_CORE is not set +# CONFIG_TPS6105X is not set +# CONFIG_RADIO_MIROPCM20 is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_GPIO_SCH is not set +# CONFIG_GPIO_LANGWELL is not set +# CONFIG_GPIO_RDC321X is not set +# CONFIG_GPIO_VX855 is not set +# CONFIG_GPIO_PCH is not set +# CONFIG_GPIO_ML_IOH is not set +# CONFIG_GPIO_AMD8111 is not set +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_PL061 is not set +# CONFIG_GPIO_BCM_KONA is not set +# CONFIG_GPIO_SCH311X is not set +CONFIG_GPIO_MAX730X=m +CONFIG_GPIO_MAX7300=m +CONFIG_GPIO_MAX732X=m +CONFIG_GPIO_PCF857X=m +CONFIG_GPIO_SX150X=y +CONFIG_GPIO_ADP5588=m +CONFIG_GPIO_ADNP=m +CONFIG_GPIO_MAX7301=m +CONFIG_GPIO_MCP23S08=m +CONFIG_GPIO_MC33880=m +CONFIG_GPIO_74X164=m + +# FIXME: Why? +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y + +CONFIG_TEST_KSTRTOX=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +# CONFIG_XZ_DEC_ARMTHUMB is not set +# CONFIG_XZ_DEC_SPARC is not set +# CONFIG_XZ_DEC_TEST is not set + +# CONFIG_POWER_AVS is not set + +CONFIG_TARGET_CORE=m +CONFIG_ISCSI_TARGET=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_SBP_TARGET=m +CONFIG_TCM_IBLOCK=m +CONFIG_TCM_FILEIO=m +CONFIG_TCM_PSCSI=m +CONFIG_TCM_FC=m + +CONFIG_HWSPINLOCK=m + +CONFIG_PSTORE=y +CONFIG_PSTORE_RAM=m +# CONFIG_PSTORE_CONSOLE is not set +# CONFIG_PSTORE_FTRACE is not set + +# CONFIG_TEST_MODULE is not set +# CONFIG_TEST_USER_COPY is not set + +# CONFIG_AVERAGE is not set +# CONFIG_VMXNET3 is not set + +# CONFIG_SIGMA is not set + +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 + +CONFIG_BCMA=m +CONFIG_BCMA_BLOCKIO=y +CONFIG_BCMA_HOST_PCI_POSSIBLE=y +CONFIG_BCMA_HOST_PCI=y +# CONFIG_BCMA_HOST_SOC is not set +CONFIG_BCMA_DRIVER_GMAC_CMN=y +CONFIG_BCMA_DRIVER_GPIO=y +# CONFIG_BCMA_DEBUG is not set + +# CONFIG_GOOGLE_FIRMWARE is not set +# CONFIG_INTEL_MID_PTI is not set + +# CONFIG_MAILBOX is not set + +CONFIG_FMC=m +CONFIG_FMC_FAKEDEV=m +CONFIG_FMC_TRIVIAL=m +CONFIG_FMC_WRITE_EEPROM=m +CONFIG_FMC_CHARDEV=m + +# CONFIG_GENWQE is not set + +# CONFIG_POWERCAP is not set + +# CONFIG_HSI is not set + + +# CONFIG_ARM_ARCH_TIMER_EVTSTREAM is not set + +# CONFIG_PM_DEVFREQ is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +# CONFIG_MODULE_VERIFY_ELF is not set +# CONFIG_CRYPTO_KEY_TYPE is not set +# CONFIG_PGP_LIBRARY is not set +# CONFIG_PGP_PRELOAD is not set +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_PROC_DEVICETREE=y + From f622f7db57f29edd8c4b775b73806d2ad9cc375e Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 22 Jul 2015 10:10:01 +0200 Subject: [PATCH 0482/1983] mach: mx6: select cpufreq driver With the current setup you will get unresolved errors if you don't build with the cpufrequency driver enabled. I should fix this in the individual drivers, but for now just select it by default as this should be enabled in most configuration anyways. --- arch/arm/mach-imx/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 8636d6836941c1..f72fbcfbff771c 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -811,6 +811,7 @@ config SOC_IMX6 select HAVE_SMP select MFD_SYSCON select PL310_ERRATA_769419 if CACHE_PL310 + select ARM_IMX6Q_CPUFREQ config SOC_IMX6Q bool "i.MX6 Quad/DualLite support" From 92ea6be582a26ee5725aa5d0119811d0f82f6ac5 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 22 Jul 2015 10:11:28 +0200 Subject: [PATCH 0483/1983] media: mxc: subdev: Add IMX6SX dependency Only build this if you have enabled the IXM6SX support. --- drivers/media/platform/mxc/subdev/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/subdev/Kconfig b/drivers/media/platform/mxc/subdev/Kconfig index d3fc164e9f3319..08a06e3cdd67e6 100644 --- a/drivers/media/platform/mxc/subdev/Kconfig +++ b/drivers/media/platform/mxc/subdev/Kconfig @@ -1,4 +1,4 @@ -if VIDEO_MXC_CAPTURE +if VIDEO_MXC_CAPTURE && SOC_IMX6SX config VIDEO_MXC_CSI_CAMERA tristate "CSI camera support" From a05ada5b113aeaa94f90a0ded4a1aa7321528671 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 16 May 2014 16:14:04 +0200 Subject: [PATCH 0484/1983] net: phy: extend fixed driver with fixed_phy_register() Upstream-commit: a75951217472c522c324adb0a4de3ba69d656ef5 The existing fixed_phy_add() function has several drawbacks that prevents it from being used as is for OF-based declaration of fixed PHYs: * The address of the PHY on the fake bus needs to be passed, while a dynamic allocation is desired. * Since the phy_device instantiation is post-poned until the next mdiobus scan, there is no way to associate the fixed PHY with its OF node, which later prevents of_phy_connect() from finding this fixed PHY from a given OF node. To solve this, this commit introduces fixed_phy_register(), which will allocate an available PHY address, add the PHY using fixed_phy_add() and instantiate the phy_device structure associated with the provided OF node. Signed-off-by: Thomas Petazzoni Acked-by: Florian Fainelli Acked-by: Grant Likely Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/fixed.c | 61 +++++++++++++++++++++++++++++++++++++++ include/linux/phy_fixed.h | 11 +++++++ 2 files changed, 72 insertions(+) diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index ba55adfc7aaef0..7243a2b775e539 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -21,6 +21,7 @@ #include #include #include +#include #define MII_REGS_NUM 29 @@ -203,6 +204,66 @@ int fixed_phy_add(unsigned int irq, int phy_id, } EXPORT_SYMBOL_GPL(fixed_phy_add); +void fixed_phy_del(int phy_addr) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct fixed_phy *fp, *tmp; + + list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { + if (fp->addr == phy_addr) { + list_del(&fp->node); + kfree(fp); + return; + } + } +} +EXPORT_SYMBOL_GPL(fixed_phy_del); + +static int phy_fixed_addr; +static DEFINE_SPINLOCK(phy_fixed_addr_lock); + +int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np) +{ + struct fixed_mdio_bus *fmb = &platform_fmb; + struct phy_device *phy; + int phy_addr; + int ret; + + /* Get the next available PHY address, up to PHY_MAX_ADDR */ + spin_lock(&phy_fixed_addr_lock); + if (phy_fixed_addr == PHY_MAX_ADDR) { + spin_unlock(&phy_fixed_addr_lock); + return -ENOSPC; + } + phy_addr = phy_fixed_addr++; + spin_unlock(&phy_fixed_addr_lock); + + ret = fixed_phy_add(PHY_POLL, phy_addr, status); + if (ret < 0) + return ret; + + phy = get_phy_device(fmb->mii_bus, phy_addr, false); + if (!phy || IS_ERR(phy)) { + fixed_phy_del(phy_addr); + return -EINVAL; + } + + of_node_get(np); + phy->dev.of_node = np; + + ret = phy_device_register(phy); + if (ret) { + phy_device_free(phy); + of_node_put(np); + fixed_phy_del(phy_addr); + return ret; + } + + return 0; +} + static int __init fixed_mdio_bus_init(void) { struct fixed_mdio_bus *fmb = &platform_fmb; diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 509d8f5f984e39..4f2478b4713651 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -9,15 +9,26 @@ struct fixed_phy_status { int asym_pause; }; +struct device_node; + #ifdef CONFIG_FIXED_PHY extern int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status); +extern int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np); #else static inline int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status) { return -ENODEV; } +static inline int fixed_phy_register(unsigned int irq, + struct fixed_phy_status *status, + struct device_node *np) +{ + return -ENODEV; +} #endif /* CONFIG_FIXED_PHY */ /* From 76cdd92bf1277ce186ec316973fd4a0fa950ac3e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 16 May 2014 16:14:03 +0200 Subject: [PATCH 0485/1983] net: phy: decouple PHY id and PHY address in fixed PHY driver Upstream-commit: 9b744942290c168287013f0a19ff7392f65c8107 Until now, the fixed_phy_add() function was taking as argument 'phy_id', which was used both as the PHY address on the fake fixed MDIO bus, and as the PHY id, as available in the MII_PHYSID1 and MII_PHYSID2 registers. However, those two informations are completely unrelated. This patch decouples them. The PHY id of fixed PHYs is hardcoded to be 0x0. Ideally, a really reserved value would be nicer, but there doesn't seem to be an easy of making sure a dummy value can be assigned to the Linux kernel for such usage. The PHY address remains passed by the caller of phy_fixed_add(). Signed-off-by: Thomas Petazzoni Reviewed-by: Florian Fainelli Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/fixed.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 7243a2b775e539..d60d875cb4450a 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -32,7 +32,7 @@ struct fixed_mdio_bus { }; struct fixed_phy { - int id; + int addr; u16 regs[MII_REGS_NUM]; struct phy_device *phydev; struct fixed_phy_status status; @@ -105,8 +105,8 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) if (fp->status.asym_pause) lpa |= LPA_PAUSE_ASYM; - fp->regs[MII_PHYSID1] = fp->id >> 16; - fp->regs[MII_PHYSID2] = fp->id; + fp->regs[MII_PHYSID1] = 0; + fp->regs[MII_PHYSID2] = 0; fp->regs[MII_BMSR] = bmsr; fp->regs[MII_BMCR] = bmcr; @@ -116,7 +116,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) return 0; } -static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) +static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) { struct fixed_mdio_bus *fmb = bus->priv; struct fixed_phy *fp; @@ -125,7 +125,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) return -1; list_for_each_entry(fp, &fmb->phys, node) { - if (fp->id == phy_id) { + if (fp->addr == phy_addr) { /* Issue callback if user registered it. */ if (fp->link_update) { fp->link_update(fp->phydev->attached_dev, @@ -139,7 +139,7 @@ static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num) return 0xFFFF; } -static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num, +static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, u16 val) { return 0; @@ -161,7 +161,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev, return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { - if (fp->id == phydev->phy_id) { + if (fp->addr == phydev->addr) { fp->link_update = link_update; fp->phydev = phydev; return 0; @@ -172,7 +172,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); -int fixed_phy_add(unsigned int irq, int phy_id, +int fixed_phy_add(unsigned int irq, int phy_addr, struct fixed_phy_status *status) { int ret; @@ -185,9 +185,9 @@ int fixed_phy_add(unsigned int irq, int phy_id, memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); - fmb->irqs[phy_id] = irq; + fmb->irqs[phy_addr] = irq; - fp->id = phy_id; + fp->addr = phy_addr; fp->status = *status; ret = fixed_phy_update_regs(fp); From 0306681717ca515f3baa4daeea272d16757fe588 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 16 Dec 2014 14:30:09 -0500 Subject: [PATCH 0486/1983] net: Allow FIXED_PHY to be modular. Upstream-commit: 6539c44d08ac2eea693b6163135169b9c8c18bb1 Otherwise we get things like: warning: (NET_DSA_BCM_SF2 && BCMGENET && SYSTEMPORT) selects FIXED_PHY which has unmet direct dependencies (NETDEVICES && PHYLIB=y) In order to make this work we have to rename fixed.c to fixed_phy.c because the regulator drivers already have a module named "fixed.o". Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 4 ++-- drivers/net/phy/Makefile | 2 +- drivers/net/phy/{fixed.c => fixed_phy.c} | 0 include/linux/phy_fixed.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename drivers/net/phy/{fixed.c => fixed_phy.c} (100%) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 9b5d46c03eed34..83a5d56438f5c7 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -107,8 +107,8 @@ config MICREL_PHY Supports the KSZ9021, VSC8201, KS8001 PHYs. config FIXED_PHY - bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" - depends on PHYLIB=y + tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" + depends on PHYLIB ---help--- Adds the platform "fixed" MDIO Bus to cover the boards that use PHYs that are not connected to the real MDIO bus. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 9013dfa12aa39a..9ac26066fe43e8 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o -obj-$(CONFIG_FIXED_PHY) += fixed.o +obj-$(CONFIG_FIXED_PHY) += fixed_phy.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_NATIONAL_PHY) += national.o diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed_phy.c similarity index 100% rename from drivers/net/phy/fixed.c rename to drivers/net/phy/fixed_phy.c diff --git a/include/linux/phy_fixed.h b/include/linux/phy_fixed.h index 4f2478b4713651..3cfded40d07942 100644 --- a/include/linux/phy_fixed.h +++ b/include/linux/phy_fixed.h @@ -11,7 +11,7 @@ struct fixed_phy_status { struct device_node; -#ifdef CONFIG_FIXED_PHY +#if IS_ENABLED(CONFIG_FIXED_PHY) extern int fixed_phy_add(unsigned int irq, int phy_id, struct fixed_phy_status *status); extern int fixed_phy_register(unsigned int irq, From 8b0f90641b4f0131f7f5ec0f56d968f716f59c77 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 22 Jul 2015 15:51:17 +0200 Subject: [PATCH 0487/1983] Revert "[media] cx88: set dev_parent to the correct parent PCI bus" This reverts commit e5715cfb2802cb5988f856f84454645772f4e2f5. --- drivers/media/pci/cx88/cx88-core.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index ad59dc9235aefd..c8f3dcc579d451 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1034,14 +1034,7 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, if (NULL == vfd) return NULL; *vfd = *template_; - /* - * The dev pointer of v4l2_device is NULL, instead we set the - * video_device dev_parent pointer to the correct PCI bus device. - * This driver is a rare example where there is one v4l2_device, - * but the video nodes have different parent (PCI) devices. - */ vfd->v4l2_dev = &core->v4l2_dev; - vfd->dev_parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); From cbf85b285a0e7fd620e3258fb93812b8dd1e233b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 23 Jul 2015 07:51:04 +0200 Subject: [PATCH 0488/1983] arm: configs: update cbi/hb configs to use patched galcore driver To make it easier to use galcore upstream and also accept and rebase on top of newer freescale kernels I have stopped patching the fsl provided galcore driver and started working with a copy of it that lives in the drivers/gpu directory. By default build cbi/hb kernels with our version of the driver. --- arch/arm/configs/imx_v7_cbi_hb_base_defconfig | 3 ++- arch/arm/configs/imx_v7_cbi_hb_defconfig | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig index 3f134686bbf277..5e1ebb64509fb2 100644 --- a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig +++ b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig @@ -195,6 +195,7 @@ CONFIG_VIDEO_MXC_IPU_OUTPUT=y CONFIG_VIDEO_MXC_PXP_V4L2=y CONFIG_SOC_CAMERA=y CONFIG_SOC_CAMERA_OV2640=y +CONFIG_VIVANTE_GALCORE=y CONFIG_DRM=y CONFIG_DRM_VIVANTE=y CONFIG_FB=y @@ -236,7 +237,7 @@ CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_ESDHC_IMX=y CONFIG_MXC_IPU=y -CONFIG_MXC_GPU_VIV=y +# CONFIG_MXC_GPU_VIV is not set CONFIG_MXC_ASRC=y CONFIG_MXC_HDMI_CEC=y CONFIG_MXC_MIPI_CSI2=y diff --git a/arch/arm/configs/imx_v7_cbi_hb_defconfig b/arch/arm/configs/imx_v7_cbi_hb_defconfig index 164c758bb2990c..d3dbb90f65ba47 100644 --- a/arch/arm/configs/imx_v7_cbi_hb_defconfig +++ b/arch/arm/configs/imx_v7_cbi_hb_defconfig @@ -256,6 +256,7 @@ CONFIG_VIDEO_MXC_IPU_OUTPUT=y CONFIG_VIDEO_MXC_PXP_V4L2=y CONFIG_SOC_CAMERA=y CONFIG_SOC_CAMERA_OV2640=y +CONFIG_VIVANTE_GALCORE=y CONFIG_DRM=y CONFIG_DRM_VIVANTE=y CONFIG_FB=y @@ -297,7 +298,7 @@ CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_ESDHC_IMX=y CONFIG_MXC_IPU=y -CONFIG_MXC_GPU_VIV=y +# CONFIG_MXC_GPU_VIV is not set CONFIG_MXC_ASRC=y CONFIG_MXC_HDMI_CEC=y CONFIG_MXC_MIPI_CSI2=y From 305f0bd5d3871332e9b29118bf9028ebbc0a1c15 Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Tue, 21 Jul 2015 03:04:09 +0200 Subject: [PATCH 0489/1983] [DCIC] Change printk logging to pr_* (to work with dynamic debug) (cherry picked from commit ef4df15780edfb528d7c15e79db3f8eb4c3d07e9) Signed-off-by: Matus Kral --- drivers/video/mxc/mxc_dcic.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/video/mxc/mxc_dcic.c b/drivers/video/mxc/mxc_dcic.c index 8cf909d5563db4..a2c9fcdae48543 100644 --- a/drivers/video/mxc/mxc_dcic.c +++ b/drivers/video/mxc/mxc_dcic.c @@ -238,13 +238,13 @@ static bool roi_configure(struct dcic_data *dcic, struct roi_params *roi_param) u32 val; if (roi_param->roi_n < 0 || roi_param->roi_n >= 16) { - printk(KERN_ERR "Error, Wrong ROI number %d\n", roi_param->roi_n); + pr_err("Error, Wrong ROI number %d\n", roi_param->roi_n); return false; } if (roi_param->end_x <= roi_param->start_x || roi_param->end_y <= roi_param->start_y) { - printk(KERN_ERR "Error, Wrong ROI\n"); + pr_err("Error, Wrong ROI\n"); return false; } @@ -304,7 +304,7 @@ static irqreturn_t dcic_irq_handler(int irq, void *data) writel(dcics, &dcic->regs->dcics); for (i = 0; i < 16; i++) { - printk(KERN_INFO "ROI=%d,crcRS=0x%x, crcCS=0x%x\n", i, + pr_debug("ROI=%d,crcRS=0x%x, crcCS=0x%x\n", i, readl(&dcic->regs->ROI[i].dcicrrs), readl(&dcic->regs->ROI[i].dcicrcs)); } @@ -371,7 +371,7 @@ static int dcic_init(struct device_node *np, struct dcic_data *dcic) val = of_get_dcic_val(np, dcic); if (val < 0) { - printk(KERN_ERR "Error incorrect\n"); + pr_err("Error incorrect\n"); return -1; } @@ -428,7 +428,7 @@ static long dcic_ioctl(struct file *file, dcic_disable(dcic); break; default: - printk(KERN_ERR "%s, Unsupport cmd %d\n", __func__, cmd); + pr_err("%s, Unsupport cmd %d\n", __func__, cmd); break; } return ret; @@ -510,7 +510,7 @@ static int dcic_probe(struct platform_device *pdev) mutex_init(&dcic->lock); ret = dcic_init(np, dcic); if (ret < 0) { - printk(KERN_ERR "Failed init dcic\n"); + pr_err("Failed init dcic\n"); goto ealloc; } @@ -518,7 +518,7 @@ static int dcic_probe(struct platform_device *pdev) name = dcic->buses[dcic->bus_n].name; dcic->major = register_chrdev(0, name, &mxc_dcic_fops); if (dcic->major < 0) { - printk(KERN_ERR "DCIC: unable to get a major for dcic\n"); + pr_err("DCIC: unable to get a major for dcic\n"); ret = -EBUSY; goto ealloc; } From cf401e15a9860465be91f59d5a26a82fdd11d631 Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Tue, 21 Jul 2015 03:08:10 +0200 Subject: [PATCH 0490/1983] [DCIC] Implement 'freerun' mode (without disabling dcic interrupts in irq handler) to allow track of VSYNCs. (cherry picked from commit 36b5b542fc4c04425dc96a0cc8b7d930fad25f12) Signed-off-by: Matus Kral --- drivers/video/mxc/mxc_dcic.c | 68 +++++++++++++++++++++++++++++++++-- include/uapi/linux/mxc_dcic.h | 2 ++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/drivers/video/mxc/mxc_dcic.c b/drivers/video/mxc/mxc_dcic.c index a2c9fcdae48543..0ab32e64b7126e 100644 --- a/drivers/video/mxc/mxc_dcic.c +++ b/drivers/video/mxc/mxc_dcic.c @@ -59,6 +59,11 @@ #define FB_SYNC_OE_LOW_ACT 0x80000000 #define FB_SYNC_CLK_LAT_FALL 0x40000000 +static wait_queue_head_t mxc_dcic_wait; +static int mxc_dcic_vsync; +static int mxc_dcic_irq; +static unsigned long mxc_dcic_counter; + static const struct dcic_mux imx6q_dcic0_mux[] = { { .dcic = DCIC_IPU1_DI0, @@ -297,12 +302,22 @@ static irqreturn_t dcic_irq_handler(int irq, void *data) dcic->result = dcics & 0xffff; - dcic_int_disable(dcic); + if (!mxc_dcic_vsync) + dcic_int_disable(dcic); + else { + mxc_dcic_irq = 1; + mxc_dcic_counter++; + } /* clean dcic interrupt state */ writel(DCICS_FI_STAT_PENDING, &dcic->regs->dcics); writel(dcics, &dcic->regs->dcics); + if (mxc_dcic_vsync) { + wake_up(&mxc_dcic_wait); + return IRQ_HANDLED; + } + for (i = 0; i < 16; i++) { pr_debug("ROI=%d,crcRS=0x%x, crcCS=0x%x\n", i, readl(&dcic->regs->ROI[i].dcicrrs), @@ -310,7 +325,7 @@ static irqreturn_t dcic_irq_handler(int irq, void *data) } complete(&dcic->roi_crc_comp); - return 0; + return IRQ_HANDLED; } static int dcic_configure(struct dcic_data *dcic, unsigned int sync) @@ -427,6 +442,29 @@ static long dcic_ioctl(struct file *file, } dcic_disable(dcic); break; + case DCIC_IOC_START_VSYNC: + mxc_dcic_vsync = 1; + mxc_dcic_irq = 0; + mxc_dcic_counter = 0; + + // configure minimum roi block + roi_param.roi_n = 0; + roi_param.end_x = 1; + roi_param.start_x = 0; + roi_param.end_y = 1; + roi_param.start_y = 0; + roi_configure(dcic, &roi_param); + + dcic_enable(dcic); + dcic_int_enable(dcic); + break; + case DCIC_IOC_STOP_VSYNC: + mxc_dcic_vsync = 0; + mxc_dcic_irq = 0; + init_completion(&dcic->roi_crc_comp); + wait_for_completion_interruptible_timeout(&dcic->roi_crc_comp, 1 * HZ); + dcic_disable(dcic); + break; default: pr_err("%s, Unsupport cmd %d\n", __func__, cmd); break; @@ -434,12 +472,34 @@ static long dcic_ioctl(struct file *file, return ret; } +static ssize_t dcic_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + int ret = 0; + + do { + if (mxc_dcic_irq) { + count = min(sizeof(unsigned long), count); + ret = copy_to_user(buf, &mxc_dcic_counter, count) ? -EFAULT : count; + mxc_dcic_irq = 0; + break; + } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + } + else if (wait_event_interruptible(mxc_dcic_wait, mxc_dcic_irq)) + ret = -ERESTARTSYS; + } while(!ret); + + return ret; +} static const struct file_operations mxc_dcic_fops = { .owner = THIS_MODULE, .open = dcic_open, .release = dcic_release, .unlocked_ioctl = dcic_ioctl, + .read = dcic_read, }; static int dcic_probe(struct platform_device *pdev) @@ -553,6 +613,10 @@ static int dcic_probe(struct platform_device *pdev) goto err_out_cdev; } + init_waitqueue_head(&mxc_dcic_wait); + mxc_dcic_vsync = 0; + mxc_dcic_irq = 0; + return 0; err_out_cdev: diff --git a/include/uapi/linux/mxc_dcic.h b/include/uapi/linux/mxc_dcic.h index cbcacaa64b3cb5..3f28c7de12f480 100644 --- a/include/uapi/linux/mxc_dcic.h +++ b/include/uapi/linux/mxc_dcic.h @@ -33,6 +33,8 @@ #define DCIC_IOC_CONFIG_DCIC _IO('D', 12) #define DCIC_IOC_CONFIG_ROI _IO('D', 13) #define DCIC_IOC_GET_RESULT _IO('D', 14) +#define DCIC_IOC_START_VSYNC _IO('D', 15) +#define DCIC_IOC_STOP_VSYNC _IO('D', 16) struct roi_params { unsigned int roi_n; From 8f65b699d6d197046a06f4345e37864d7f25e151 Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Tue, 21 Jul 2015 03:10:52 +0200 Subject: [PATCH 0491/1983] [DCIC] Bundle mxc_dcic.h into linux-libc-dev pkg. (cherry picked from commit 364a5dcf6e70751eba368f002279c38ef8afa904) Signed-off-by: Matus Kral Conflicts: include/uapi/linux/Kbuild --- include/uapi/linux/Kbuild | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 1ee3e867adeb3a..b44ae9536bf0b0 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -257,6 +257,8 @@ header-y += msg.h header-y += mtio.h header-y += mxcfb.h header-y += mxc_v4l2.h +header-y += mxc_dcic.h +header-y += ipu.h header-y += n_r3964.h header-y += nbd.h header-y += ncp.h From 6c58f2dd6ec658ea79a6fccaab316f0296a6c64d Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Thu, 23 Jul 2015 17:45:33 +0200 Subject: [PATCH 0492/1983] misplaced clk_regenerator call. --- drivers/video/mxc/mxc_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 809143b5cd8604..cd08d1a92c1863 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -2054,6 +2054,7 @@ static int mxc_hdmi_power_on(struct mxc_dispdrv_handle *disp, { struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); mxc_hdmi_phy_init(hdmi); + hdmi_clk_regenerator_update_pixel_clock(fbi->var.pixclock); return 0; } @@ -2330,7 +2331,6 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) dev_dbg(&hdmi->pdev->dev, "%s CEA mode\n", __func__); /* HDMI Initialization Step E - Configure audio */ - hdmi_clk_regenerator_update_pixel_clock(hdmi->fbi->var.pixclock); hdmi_enable_audio_clk(hdmi); /* HDMI Initialization Step F - Configure AVI InfoFrame */ From 30fc4878e92bdcf1e137d6daa18ed99fb8be2db8 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 24 Jul 2015 10:12:13 +0200 Subject: [PATCH 0493/1983] mxc: gpu_viv: Only make selectable if VIVANTE_GALCORE is not This makes it so we can't have both Freescale's version and the community GPU version selected at the same time. Since I am patching the community version we will make that the preferred. --- drivers/mxc/gpu-viv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mxc/gpu-viv/Kconfig b/drivers/mxc/gpu-viv/Kconfig index e9cc4b27614ccd..95c42768255b9d 100644 --- a/drivers/mxc/gpu-viv/Kconfig +++ b/drivers/mxc/gpu-viv/Kconfig @@ -1,5 +1,5 @@ menu "MXC Vivante GPU support" - depends on SOC_IMX6Q + depends on SOC_IMX6Q && !VIVANTE_GALCORE config MXC_GPU_VIV tristate "MXC Vivante GPU support" From 9ad0c7f87663347e136f502b57f564eafe5dfd6f Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 24 Jul 2015 10:13:39 +0200 Subject: [PATCH 0494/1983] gpu: galcore: Don't use restricted API's for cache flushing Freescale's default config sets options that tell galcore to use the restricted dmac_ functions. Change this so we use the proper dma_* functions for managing the cache. --- drivers/gpu/galcore/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/galcore/config b/drivers/gpu/galcore/config index a8a3bdb17ca5b2..ce586d7c776c0b 100644 --- a/drivers/gpu/galcore/config +++ b/drivers/gpu/galcore/config @@ -27,8 +27,8 @@ VIVANTE_ENABLE_VG ?= 1 FORCE_ALL_VIDEO_MEMORY_CACHED ?= 0 NONPAGED_MEMORY_CACHEABLE ?= 0 NONPAGED_MEMORY_BUFFERABLE ?= 1 -CACHE_FUNCTION_UNIMPLEMENTED ?= 0 -ENABLE_OUTER_CACHE_PATCH ?= 1 +CACHE_FUNCTION_UNIMPLEMENTED ?= 1 +ENABLE_OUTER_CACHE_PATCH ?= 0 USE_BANK_ALIGNMENT ?= 1 BANK_BIT_START ?= 13 BANK_BIT_END ?= 15 From cba68ec664356727361a486b84da01768675a147 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 27 Jul 2015 16:21:35 +0200 Subject: [PATCH 0495/1983] Revert "ASoC: hdmi: Mark the maximum significant bits to HDMI codec" This reverts commit 084b0d98043e79ce373d8eaecca7e2296f2ec09f. --- sound/soc/codecs/hdmi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index 124f32edf482c6..e39647737d766a 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -46,7 +46,6 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, - .sig_bits = 24, }, .capture = { .stream_name = "Capture", From fcdc5e62599e65182aa7cc0a0714787ac2c515c0 Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Fri, 28 Mar 2014 15:39:41 +0800 Subject: [PATCH 0496/1983] phy/at8031: enable at8031 to work on interrupt mode Upstream-commit: 77a9939426f7a3f35f460afc9b11f1fe45955409 The at8031 can work on polling mode and interrupt mode. Add ack_interrupt and config intr funcs to enable interrupt mode for it. Signed-off-by: Zhao Qiang Signed-off-by: David S. Miller --- drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index bc71947b1ec329..643464d5a727b3 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -27,6 +27,9 @@ #define AT803X_MMD_ACCESS_CONTROL 0x0D #define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E #define AT803X_FUNC_DATA 0x4003 +#define AT803X_INER 0x0012 +#define AT803X_INER_INIT 0xec00 +#define AT803X_INSR 0x0013 #define AT803X_DEBUG_ADDR 0x1D #define AT803X_DEBUG_DATA 0x1E #define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 @@ -191,6 +194,31 @@ static int at803x_config_init(struct phy_device *phydev) return 0; } +static int at803x_ack_interrupt(struct phy_device *phydev) +{ + int err; + + err = phy_read(phydev, AT803X_INSR); + + return (err < 0) ? err : 0; +} + +static int at803x_config_intr(struct phy_device *phydev) +{ + int err; + int value; + + value = phy_read(phydev, AT803X_INER); + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + err = phy_write(phydev, AT803X_INER, + value | AT803X_INER_INIT); + else + err = phy_write(phydev, AT803X_INER, 0); + + return err; +} + static struct phy_driver at803x_driver[] = { { /* ATHEROS 8035 */ @@ -240,6 +268,8 @@ static struct phy_driver at803x_driver[] = { .flags = PHY_HAS_INTERRUPT, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, + .ack_interrupt = &at803x_ack_interrupt, + .config_intr = &at803x_config_intr, .driver = { .owner = THIS_MODULE, }, From b24b7201ac635903b87d5fb55edae3749189f47b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 29 Jul 2015 07:08:37 +0200 Subject: [PATCH 0497/1983] net: fec: simplifiy RX error accounting Rather than have this split out into multiple checks, add a single error catch all do the accounting and then drop the frame. --- drivers/net/ethernet/freescale/fec.h | 1 + drivers/net/ethernet/freescale/fec_main.c | 40 +++++++++++------------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 550a5d43948da9..b46d3109c96d7f 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -245,6 +245,7 @@ struct bufdesc_ex { #define BD_ENET_RX_OV ((ushort)0x0002) #define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ +#define BD_ENET_RX_ERROR ((ushort)0x003f) /* Enhanced buffer descriptor control/status used by Ethernet receive */ #define BD_ENET_RX_VLAN 0x00000004 diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 85ebdbe1edda84..aa610df47f9126 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1396,30 +1396,28 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) if ((status & BD_ENET_RX_LAST) == 0) netdev_err(ndev, "rcv is not +last\n"); - /* Check for errors. */ - if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | - BD_ENET_RX_CR | BD_ENET_RX_OV)) { + if (status & BD_ENET_RX_ERROR) { ndev->stats.rx_errors++; - if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { - /* Frame too long or too short. */ - ndev->stats.rx_length_errors++; - } - if (status & BD_ENET_RX_NO) /* Frame alignment */ - ndev->stats.rx_frame_errors++; - if (status & BD_ENET_RX_CR) /* CRC Error */ - ndev->stats.rx_crc_errors++; - if (status & BD_ENET_RX_OV) /* FIFO overrun */ - ndev->stats.rx_fifo_errors++; - } - /* Report late collisions as a frame error. - * On this error, the BD is closed, but we don't know what we - * have in the buffer. So, just drop this frame on the floor. - */ - if (status & BD_ENET_RX_CL) { - ndev->stats.rx_errors++; - ndev->stats.rx_frame_errors++; + /* Report late collisions as a frame error. + * On this error, the BD is closed, but we don't know what we + * have in the buffer. So, just drop this frame on the floor. + */ + if (status & BD_ENET_RX_CL) { + ndev->stats.rx_frame_errors++; + } else { + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) { + /* Frame too long or too short. */ + ndev->stats.rx_length_errors++; + } + if (status & BD_ENET_RX_NO) /* Frame alignment */ + ndev->stats.rx_frame_errors++; + if (status & BD_ENET_RX_CR) /* CRC Error */ + ndev->stats.rx_crc_errors++; + if (status & BD_ENET_RX_OV) /* FIFO overrun */ + ndev->stats.rx_fifo_errors++; + } goto rx_processing_done; } From 57ea31a616a788dff3567372395572b08a9bd565 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Tue, 14 Apr 2015 15:44:57 -0700 Subject: [PATCH 0498/1983] mm: cma: debugfs interface Upstream-commit: 28b24c1fc8c22cabe5b8a16ffe6a61dfce51a1f2 I've noticed that there is no interfaces exposed by CMA which would let me fuzz what's going on in there. This small patchset exposes some information out to userspace, plus adds the ability to trigger allocation and freeing from userspace. This patch (of 3): Implement a simple debugfs interface to expose information about CMA areas in the system. Useful for testing/sanity checks for CMA since it was impossible to previously retrieve this information in userspace. Signed-off-by: Sasha Levin Acked-by: Joonsoo Kim Cc: Marek Szyprowski Cc: Laura Abbott Cc: Konrad Rzeszutek Wilk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: mm/Makefile --- mm/Kconfig | 6 +++++ mm/Makefile | 1 + mm/cma.c | 19 ++++------------ mm/cma.h | 20 +++++++++++++++++ mm/cma_debug.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 mm/cma.h create mode 100644 mm/cma_debug.c diff --git a/mm/Kconfig b/mm/Kconfig index 570b49be19c921..793064bc0292ea 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -514,6 +514,12 @@ config CMA_DEBUG processing calls such as dma_alloc_from_contiguous(). This option does not affect warning and error messages. +config CMA_DEBUGFS + bool "CMA debugfs interface" + depends on CMA && DEBUG_FS + help + Turns on the DebugFS interface for CMA. + config CMA_AREAS int "Maximum count of the CMA areas" depends on CMA diff --git a/mm/Makefile b/mm/Makefile index bb6e41845100a4..f479ecf51ab329 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_ZBUD) += zbud.o obj-$(CONFIG_ZSMALLOC) += zsmalloc.o obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o obj-$(CONFIG_CMA) += cma.o +obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o diff --git a/mm/cma.c b/mm/cma.c index 108fd7ba6b6c98..3e9c786214d7c3 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -34,16 +34,10 @@ #include #include -struct cma { - unsigned long base_pfn; - unsigned long count; - unsigned long *bitmap; - unsigned int order_per_bit; /* Order of pages represented by one bit */ - struct mutex lock; -}; - -static struct cma cma_areas[MAX_CMA_AREAS]; -static unsigned cma_area_count; +#include "cma.h" + +struct cma cma_areas[MAX_CMA_AREAS]; +unsigned cma_area_count; static DEFINE_MUTEX(cma_mutex); phys_addr_t cma_get_base(struct cma *cma) @@ -61,11 +55,6 @@ static unsigned long cma_bitmap_aligned_mask(struct cma *cma, int align_order) return (1UL << (align_order >> cma->order_per_bit)) - 1; } -static unsigned long cma_bitmap_maxno(struct cma *cma) -{ - return cma->count >> cma->order_per_bit; -} - static unsigned long cma_bitmap_pages_to_bits(struct cma *cma, unsigned long pages) { diff --git a/mm/cma.h b/mm/cma.h new file mode 100644 index 00000000000000..4141887bbfb0bf --- /dev/null +++ b/mm/cma.h @@ -0,0 +1,20 @@ +#ifndef __MM_CMA_H__ +#define __MM_CMA_H__ + +struct cma { + unsigned long base_pfn; + unsigned long count; + unsigned long *bitmap; + unsigned int order_per_bit; /* Order of pages represented by one bit */ + struct mutex lock; +}; + +extern struct cma cma_areas[MAX_CMA_AREAS]; +extern unsigned cma_area_count; + +static unsigned long cma_bitmap_maxno(struct cma *cma) +{ + return cma->count >> cma->order_per_bit; +} + +#endif diff --git a/mm/cma_debug.c b/mm/cma_debug.c new file mode 100644 index 00000000000000..3af2de6d4e5f58 --- /dev/null +++ b/mm/cma_debug.c @@ -0,0 +1,60 @@ +/* + * CMA DebugFS Interface + * + * Copyright (c) 2015 Sasha Levin + */ + + +#include +#include + +#include "cma.h" + +static struct dentry *cma_debugfs_root; + +static int cma_debugfs_get(void *data, u64 *val) +{ + unsigned long *p = data; + + *val = *p; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(cma_debugfs_fops, cma_debugfs_get, NULL, "%llu\n"); + +static void cma_debugfs_add_one(struct cma *cma, int idx) +{ + struct dentry *tmp; + char name[16]; + int u32s; + + sprintf(name, "cma-%d", idx); + + tmp = debugfs_create_dir(name, cma_debugfs_root); + + debugfs_create_file("base_pfn", S_IRUGO, tmp, + &cma->base_pfn, &cma_debugfs_fops); + debugfs_create_file("count", S_IRUGO, tmp, + &cma->count, &cma_debugfs_fops); + debugfs_create_file("order_per_bit", S_IRUGO, tmp, + &cma->order_per_bit, &cma_debugfs_fops); + + u32s = DIV_ROUND_UP(cma_bitmap_maxno(cma), BITS_PER_BYTE * sizeof(u32)); + debugfs_create_u32_array("bitmap", S_IRUGO, tmp, (u32*)cma->bitmap, u32s); +} + +static int __init cma_debugfs_init(void) +{ + int i; + + cma_debugfs_root = debugfs_create_dir("cma", NULL); + if (!cma_debugfs_root) + return -ENOMEM; + + for (i = 0; i < cma_area_count; i++) + cma_debugfs_add_one(&cma_areas[i], i); + + return 0; +} +late_initcall(cma_debugfs_init); From 73ff733a6cc3d0b7a15db77a0f2f31d577e694a6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:05 +0200 Subject: [PATCH 0499/1983] ARM: l2c: remove outer_inv_all() method No one ever calls this function anywhere in the kernel, so let's completely remove it from the outer cache API and turn it into an internal-only thing. Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 8 -------- arch/arm/mm/cache-feroceon-l2.c | 1 - arch/arm/mm/cache-l2x0.c | 5 ----- 3 files changed, 14 deletions(-) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index f94784f0e3a6ce..0e4420858990c4 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -28,7 +28,6 @@ struct outer_cache_fns { void (*clean_range)(unsigned long, unsigned long); void (*flush_range)(unsigned long, unsigned long); void (*flush_all)(void); - void (*inv_all)(void); void (*disable)(void); #ifdef CONFIG_OUTER_CACHE_SYNC void (*sync)(void); @@ -63,12 +62,6 @@ static inline void outer_flush_all(void) outer_cache.flush_all(); } -static inline void outer_inv_all(void) -{ - if (outer_cache.inv_all) - outer_cache.inv_all(); -} - static inline void outer_disable(void) { if (outer_cache.disable) @@ -90,7 +83,6 @@ static inline void outer_clean_range(phys_addr_t start, phys_addr_t end) static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) { } static inline void outer_flush_all(void) { } -static inline void outer_inv_all(void) { } static inline void outer_disable(void) { } static inline void outer_resume(void) { } diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c index 48bc3c0a87ce32..dd3d59122cc374 100644 --- a/arch/arm/mm/cache-feroceon-l2.c +++ b/arch/arm/mm/cache-feroceon-l2.c @@ -343,7 +343,6 @@ void __init feroceon_l2_init(int __l2_wt_override) outer_cache.inv_range = feroceon_l2_inv_range; outer_cache.clean_range = feroceon_l2_clean_range; outer_cache.flush_range = feroceon_l2_flush_range; - outer_cache.inv_all = l2_inv_all; enable_l2(); diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 7abde2ce897336..f9985e5a208ced 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -414,7 +414,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) outer_cache.flush_range = l2x0_flush_range; outer_cache.sync = l2x0_cache_sync; outer_cache.flush_all = l2x0_flush_all; - outer_cache.inv_all = l2x0_inv_all; outer_cache.disable = l2x0_disable; } @@ -884,7 +883,6 @@ static const struct l2x0_of_data pl310_data = { .flush_range = l2x0_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; @@ -899,7 +897,6 @@ static const struct l2x0_of_data l2x0_data = { .flush_range = l2x0_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; @@ -914,7 +911,6 @@ static const struct l2x0_of_data aurora_with_outer_data = { .flush_range = aurora_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; @@ -946,7 +942,6 @@ static const struct l2x0_of_data bcm_l2x0_data = { .flush_range = bcm_flush_range, .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, - .inv_all = l2x0_inv_all, .disable = l2x0_disable, }, }; From 176d4ea41afa318e04898e670b91334819f21734 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:05 +0200 Subject: [PATCH 0500/1983] ARM: l2c: remove unnecessary call to outer_flush_all() outer_disable() is defined to safely turn the L2 cache off without data loss: this means that outer_flush_all() should never be called unless you need to implement some special L2 cache disabling, and even then only from your replacement L2 cache disable function. Acked-by: Shawn Guo Signed-off-by: Russell King --- arch/arm/mach-prima2/pm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-prima2/pm.c b/arch/arm/mach-prima2/pm.c index c4525a88e5da87..96e9bc10211701 100644 --- a/arch/arm/mach-prima2/pm.c +++ b/arch/arm/mach-prima2/pm.c @@ -71,7 +71,6 @@ static int sirfsoc_pm_enter(suspend_state_t state) case PM_SUSPEND_MEM: sirfsoc_pre_suspend_power_off(); - outer_flush_all(); outer_disable(); /* go zzz */ cpu_suspend(0, sirfsoc_finish_suspend); From b76f729bb03b7435132d95e7f73c11daedc4528e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:05 +0200 Subject: [PATCH 0501/1983] ARM: l2c: avoid calling outer_flush_all() unnecessarily (Spear) Spear calls outer_flush_all() from it's SMP bringup function. This is potentially dangerous as the L2C set/way operations which implement this don't take kindly to concurrent operations. Besides, there's better solutions to this, as implemented on other platforms. Signed-off-by: Russell King --- arch/arm/mach-spear/platsmp.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-spear/platsmp.c b/arch/arm/mach-spear/platsmp.c index 5c4a19887b2bb1..eb9f2ef1e97477 100644 --- a/arch/arm/mach-spear/platsmp.c +++ b/arch/arm/mach-spear/platsmp.c @@ -20,6 +20,18 @@ #include #include "generic.h" +/* + * Write pen_release in a way that is guaranteed to be visible to all + * observers, irrespective of whether they're taking part in coherency + * or not. This is necessary for the hotplug code to work reliably. + */ +static void write_pen_release(int val) +{ + pen_release = val; + smp_wmb(); + sync_cache_w(&pen_release); +} + static DEFINE_SPINLOCK(boot_lock); static void __iomem *scu_base = IOMEM(VA_SCU_BASE); @@ -30,8 +42,7 @@ static void spear13xx_secondary_init(unsigned int cpu) * let the primary processor know we're out of the * pen, then head off into the C entry point */ - pen_release = -1; - smp_wmb(); + write_pen_release(-1); /* * Synchronise with the boot thread. @@ -58,9 +69,7 @@ static int spear13xx_boot_secondary(unsigned int cpu, struct task_struct *idle) * Note that "pen_release" is the hardware CPU ID, whereas * "cpu" is Linux's internal ID. */ - pen_release = cpu; - flush_cache_all(); - outer_flush_all(); + write_pen_release(cpu); timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { From 9cec3e835308d1d1d613c6f87414245abbbf1cdc Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:05 +0200 Subject: [PATCH 0502/1983] ARM: l2c: omap2: remove ES1.0 support Santosh says: > But we should kill all of that since we long back decided to remove > ES1.0 related code. The mach-omap code alreasy has removed the ES1.0 > compatibility so feel free to remove any specific ES1.0 > related stuff. That silicon is long dead. Acked-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap2/omap4-common.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 6cd3f3772ecf4e..9f8d506f511dd0 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -181,7 +181,7 @@ static void omap4_l2x0_set_debug(unsigned long val) static int __init omap_l2_cache_init(void) { - u32 aux_ctrl = 0; + u32 aux_ctrl; /* * To avoid code running on other OMAPs in @@ -195,27 +195,18 @@ static int __init omap_l2_cache_init(void) if (WARN_ON(!l2cache_base)) return -ENOMEM; - /* - * 16-way associativity, parity disabled - * Way size - 32KB (es1.0) - * Way size - 64KB (es2.0 +) - */ - aux_ctrl = ((1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) | + /* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */ + aux_ctrl = (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) | (0x1 << 25) | (0x1 << L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT) | - (0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT)); - - if (omap_rev() == OMAP4430_REV_ES1_0) { - aux_ctrl |= 0x2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT; - } else { - aux_ctrl |= ((0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | + (0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT)) | + (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | (1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | (1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) | (1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | - (1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT)); - } - if (omap_rev() != OMAP4430_REV_ES1_0) - omap_smc1(0x109, aux_ctrl); + (1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT); + + omap_smc1(0x109, aux_ctrl); /* Enable PL310 L2 Cache controller */ omap_smc1(0x102, 0x1); From 8a42e444516da93b74836b6be04c3752ad528fee Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:21 +0200 Subject: [PATCH 0503/1983] ARM: l2c: remove unnecessary UL-suffix to mask values They're u32, they're not unsigned long. The UL suffix is not required here. Signed-off-by: Russell King --- arch/arm/mach-highbank/highbank.c | 2 +- arch/arm/mach-imx/mach-vf610.c | 2 +- arch/arm/mach-imx/system.c | 2 +- arch/arm/mach-rockchip/rockchip.c | 2 +- arch/arm/mach-socfpga/socfpga.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index c7de89b263dd3b..38e1dc3b4c6e91 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -69,7 +69,7 @@ static void __init highbank_init_irq(void) if (IS_ENABLED(CONFIG_CACHE_L2X0) && of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) { highbank_smc1(0x102, 0x1); - l2x0_of_init(0, ~0UL); + l2x0_of_init(0, ~0); outer_cache.disable = highbank_l2x0_disable; } } diff --git a/arch/arm/mach-imx/mach-vf610.c b/arch/arm/mach-imx/mach-vf610.c index 2d8aef5a6efab8..6288a9690e7868 100644 --- a/arch/arm/mach-imx/mach-vf610.c +++ b/arch/arm/mach-imx/mach-vf610.c @@ -22,7 +22,7 @@ static void __init vf610_init_machine(void) static void __init vf610_init_irq(void) { - l2x0_of_init(0, ~0UL); + l2x0_of_init(0, ~0); irqchip_init(); } diff --git a/arch/arm/mach-imx/system.c b/arch/arm/mach-imx/system.c index 2e3d6a8adac8d3..eb9fc8c08a59ae 100644 --- a/arch/arm/mach-imx/system.c +++ b/arch/arm/mach-imx/system.c @@ -174,6 +174,6 @@ void __init imx_init_l2cache(void) of_node_put(np); out: - l2x0_of_init(0, ~0UL); + l2x0_of_init(0, ~0); } #endif diff --git a/arch/arm/mach-rockchip/rockchip.c b/arch/arm/mach-rockchip/rockchip.c index 82c0b070971246..de9dc5f87d556d 100644 --- a/arch/arm/mach-rockchip/rockchip.c +++ b/arch/arm/mach-rockchip/rockchip.c @@ -25,7 +25,7 @@ static void __init rockchip_dt_init(void) { - l2x0_of_init(0, ~0UL); + l2x0_of_init(0, ~0); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index dd0d49cdbe097c..395fa085b43efe 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c @@ -104,7 +104,7 @@ static void socfpga_cyclone5_restart(enum reboot_mode mode, const char *cmd) static void __init socfpga_cyclone5_init(void) { - l2x0_of_init(0, ~0UL); + l2x0_of_init(0, ~0); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); socfpga_init_clocks(); } From 1ed46f853c90e6332df4791bfae0154d73c0b59f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:21 +0200 Subject: [PATCH 0504/1983] ARM: outer cache: add documentation of outer cache functions Add some documentation to cover the outer cache functions so that their requirements can be better understood. Of particular note are the flush_all() and disable() methods which must not be called except in very specific circumstances. Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 48 ++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index 0e4420858990c4..2615b3d9e807d4 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -39,35 +39,75 @@ struct outer_cache_fns { extern struct outer_cache_fns outer_cache; #ifdef CONFIG_OUTER_CACHE - +/** + * outer_inv_range - invalidate range of outer cache lines + * @start: starting physical address, inclusive + * @end: end physical address, exclusive + */ static inline void outer_inv_range(phys_addr_t start, phys_addr_t end) { if (outer_cache.inv_range) outer_cache.inv_range(start, end); } + +/** + * outer_clean_range - clean dirty outer cache lines + * @start: starting physical address, inclusive + * @end: end physical address, exclusive + */ static inline void outer_clean_range(phys_addr_t start, phys_addr_t end) { if (outer_cache.clean_range) outer_cache.clean_range(start, end); } + +/** + * outer_flush_range - clean and invalidate outer cache lines + * @start: starting physical address, inclusive + * @end: end physical address, exclusive + */ static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) { if (outer_cache.flush_range) outer_cache.flush_range(start, end); } +/** + * outer_flush_all - clean and invalidate all cache lines in the outer cache + * + * Note: depending on implementation, this may not be atomic - it must + * only be called with interrupts disabled and no other active outer + * cache masters. + * + * It is intended that this function is only used by implementations + * needing to override the outer_cache.disable() method due to security. + * (Some implementations perform this as a clean followed by an invalidate.) + */ static inline void outer_flush_all(void) { if (outer_cache.flush_all) outer_cache.flush_all(); } +/** + * outer_disable - clean, invalidate and disable the outer cache + * + * Disable the outer cache, ensuring that any data contained in the outer + * cache is pushed out to lower levels of system memory. The note and + * conditions above concerning outer_flush_all() applies here. + */ static inline void outer_disable(void) { if (outer_cache.disable) outer_cache.disable(); } +/** + * outer_resume - restore the cache configuration and re-enable outer cache + * + * Restore any configuration that the cache had when previously enabled, + * and re-enable the outer cache. + */ static inline void outer_resume(void) { if (outer_cache.resume) @@ -89,6 +129,12 @@ static inline void outer_resume(void) { } #endif #ifdef CONFIG_OUTER_CACHE_SYNC +/** + * outer_sync - perform a sync point for outer cache + * + * Ensure that all outer cache operations are complete and any store + * buffers are drained. + */ static inline void outer_sync(void) { if (outer_cache.sync) From 940504db8f545d52be3c8fbeb978b376fec038b2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:21 +0200 Subject: [PATCH 0505/1983] ARM: outer cache: add WARN_ON() to outer_disable() Add WARN_ON() conditions to outer_disable() to ensure that its requirements aren't violated. Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 7 ++----- arch/arm/mm/Makefile | 1 + arch/arm/mm/l2c-common.c | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 arch/arm/mm/l2c-common.c diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index 2615b3d9e807d4..e9a0797fe18890 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -21,6 +21,7 @@ #ifndef __ASM_OUTERCACHE_H #define __ASM_OUTERCACHE_H +#include #include struct outer_cache_fns { @@ -96,11 +97,7 @@ static inline void outer_flush_all(void) * cache is pushed out to lower levels of system memory. The note and * conditions above concerning outer_flush_all() applies here. */ -static inline void outer_disable(void) -{ - if (outer_cache.disable) - outer_cache.disable(); -} +extern void outer_disable(void); /** * outer_resume - restore the cache configuration and re-enable outer cache diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 7f39ce2f841fb1..de5a6a27081bf9 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_CPU_V7M) += proc-v7m.o AFLAGS_proc-v6.o :=-Wa,-march=armv6 AFLAGS_proc-v7.o :=-Wa,-march=armv7-a +obj-$(CONFIG_OUTER_CACHE) += l2c-common.o obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o diff --git a/arch/arm/mm/l2c-common.c b/arch/arm/mm/l2c-common.c new file mode 100644 index 00000000000000..10a3cf28c36239 --- /dev/null +++ b/arch/arm/mm/l2c-common.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2010 ARM Ltd. + * Written by Catalin Marinas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +void outer_disable(void) +{ + WARN_ON(!irqs_disabled()); + WARN_ON(num_online_cpus() > 1); + + if (outer_cache.disable) + outer_cache.disable(); +} From 60c3ec59a5d2934dd51209d058779a1ef7ba4a84 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0506/1983] ARM: l2c: add helper for L2 cache controller DT IDs Make it easier to declare L2 cache controller DT IDs by using a macro. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index f9985e5a208ced..ac410b21edfbbe 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -946,20 +946,17 @@ static const struct l2x0_of_data bcm_l2x0_data = { }, }; +#define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } static const struct of_device_id l2x0_ids[] __initconst = { - { .compatible = "arm,l210-cache", .data = (void *)&l2x0_data }, - { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data }, - { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data }, - { .compatible = "bcm,bcm11351-a2-pl310-cache", /* deprecated name */ - .data = (void *)&bcm_l2x0_data}, - { .compatible = "brcm,bcm11351-a2-pl310-cache", - .data = (void *)&bcm_l2x0_data}, - { .compatible = "marvell,aurora-outer-cache", - .data = (void *)&aurora_with_outer_data}, - { .compatible = "marvell,aurora-system-cache", - .data = (void *)&aurora_no_outer_data}, - { .compatible = "marvell,tauros3-cache", - .data = (void *)&tauros3_data }, + L2C_ID("arm,l210-cache", l2x0_data), + L2C_ID("arm,l220-cache", l2x0_data), + L2C_ID("arm,pl310-cache", pl310_data), + L2C_ID("brcm,bcm11351-a2-pl310-cache", bcm_l2x0_data), + L2C_ID("marvell,aurora-outer-cache", aurora_with_outer_data), + L2C_ID("marvell,aurora-system-cache", aurora_no_outer_data), + L2C_ID("marvell,tauros3-cache", tauros3_data), + /* Deprecated IDs */ + L2C_ID("bcm,bcm11351-a2-pl310-cache", bcm_l2x0_data), {} }; From debe1d788b73cc039eed453502e22e9171915faa Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0507/1983] ARM: l2c: tidy up l2x0_of_data declarations Remove NULL initialisers, make these all __initconst structures, and order their members in the same order as the structure declaration. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index ac410b21edfbbe..063e1787e8c3aa 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -873,49 +873,48 @@ static void __init aurora_of_setup(const struct device_node *np, *aux_mask &= ~mask; } -static const struct l2x0_of_data pl310_data = { +static const struct l2x0_of_data pl310_data __initconst = { .setup = pl310_of_setup, .save = pl310_save, .outer_cache = { - .resume = pl310_resume, .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, .flush_range = l2x0_flush_range, - .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = pl310_resume, }, }; -static const struct l2x0_of_data l2x0_data = { +static const struct l2x0_of_data l2x0_data __initconst = { .setup = l2x0_of_setup, - .save = NULL, .outer_cache = { - .resume = l2x0_resume, .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, .flush_range = l2x0_flush_range, - .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = l2x0_resume, }, }; -static const struct l2x0_of_data aurora_with_outer_data = { +static const struct l2x0_of_data aurora_with_outer_data __initconst = { .setup = aurora_of_setup, .save = aurora_save, .outer_cache = { - .resume = aurora_resume, .inv_range = aurora_inv_range, .clean_range = aurora_clean_range, .flush_range = aurora_flush_range, - .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = aurora_resume, }, }; -static const struct l2x0_of_data aurora_no_outer_data = { +static const struct l2x0_of_data aurora_no_outer_data __initconst = { .setup = aurora_of_setup, .save = aurora_save, .outer_cache = { @@ -923,8 +922,7 @@ static const struct l2x0_of_data aurora_no_outer_data = { }, }; -static const struct l2x0_of_data tauros3_data = { - .setup = NULL, +static const struct l2x0_of_data tauros3_data __initconst = { .save = tauros3_save, /* Tauros3 broadcasts L1 cache operations to L2 */ .outer_cache = { @@ -932,17 +930,17 @@ static const struct l2x0_of_data tauros3_data = { }, }; -static const struct l2x0_of_data bcm_l2x0_data = { +static const struct l2x0_of_data bcm_l2x0_data __initconst = { .setup = pl310_of_setup, .save = pl310_save, .outer_cache = { - .resume = pl310_resume, .inv_range = bcm_inv_range, .clean_range = bcm_clean_range, .flush_range = bcm_flush_range, - .sync = l2x0_cache_sync, .flush_all = l2x0_flush_all, .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = pl310_resume, }, }; From 37047c3904d601777fe3c3e021d4dee72c37776c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0508/1983] ARM: l2c: rename OF specific things, making l2x0_of_data available to all Rename a few things to help distinguish their function(s): l2x0_of_data -> l2c_init_data setup -> of_parse add of_ prefix to OF specific data Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 64 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 063e1787e8c3aa..d659c4ca46bb59 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -28,6 +28,12 @@ #include "cache-tauros3.h" #include "cache-aurora-l2.h" +struct l2c_init_data { + void (*of_parse)(const struct device_node *, u32 *, u32 *); + void (*save)(void); + struct outer_cache_fns outer_cache; +}; + #define CACHE_LINE_SIZE 32 static void __iomem *l2x0_base; @@ -42,12 +48,6 @@ static u32 cache_id_part_number_from_dt; struct l2x0_regs l2x0_saved_regs; -struct l2x0_of_data { - void (*setup)(const struct device_node *, u32 *, u32 *); - void (*save)(void); - struct outer_cache_fns outer_cache; -}; - static bool of_init = false; static inline void cache_wait_way(void __iomem *reg, unsigned long mask) @@ -664,7 +664,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end) new_end); } -static void __init l2x0_of_setup(const struct device_node *np, +static void __init l2x0_of_parse(const struct device_node *np, u32 *aux_val, u32 *aux_mask) { u32 data[2] = { 0, 0 }; @@ -698,7 +698,7 @@ static void __init l2x0_of_setup(const struct device_node *np, *aux_mask &= ~mask; } -static void __init pl310_of_setup(const struct device_node *np, +static void __init pl310_of_parse(const struct device_node *np, u32 *aux_val, u32 *aux_mask) { u32 data[3] = { 0, 0, 0 }; @@ -851,7 +851,7 @@ static void __init aurora_broadcast_l2_commands(void) isb(); } -static void __init aurora_of_setup(const struct device_node *np, +static void __init aurora_of_parse(const struct device_node *np, u32 *aux_val, u32 *aux_mask) { u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU; @@ -873,8 +873,8 @@ static void __init aurora_of_setup(const struct device_node *np, *aux_mask &= ~mask; } -static const struct l2x0_of_data pl310_data __initconst = { - .setup = pl310_of_setup, +static const struct l2c_init_data of_pl310_data __initconst = { + .of_parse = pl310_of_parse, .save = pl310_save, .outer_cache = { .inv_range = l2x0_inv_range, @@ -887,8 +887,8 @@ static const struct l2x0_of_data pl310_data __initconst = { }, }; -static const struct l2x0_of_data l2x0_data __initconst = { - .setup = l2x0_of_setup, +static const struct l2c_init_data of_l2x0_data __initconst = { + .of_parse = l2x0_of_parse, .outer_cache = { .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, @@ -900,8 +900,8 @@ static const struct l2x0_of_data l2x0_data __initconst = { }, }; -static const struct l2x0_of_data aurora_with_outer_data __initconst = { - .setup = aurora_of_setup, +static const struct l2c_init_data of_aurora_with_outer_data __initconst = { + .of_parse = aurora_of_parse, .save = aurora_save, .outer_cache = { .inv_range = aurora_inv_range, @@ -914,15 +914,15 @@ static const struct l2x0_of_data aurora_with_outer_data __initconst = { }, }; -static const struct l2x0_of_data aurora_no_outer_data __initconst = { - .setup = aurora_of_setup, +static const struct l2c_init_data of_aurora_no_outer_data __initconst = { + .of_parse = aurora_of_parse, .save = aurora_save, .outer_cache = { .resume = aurora_resume, }, }; -static const struct l2x0_of_data tauros3_data __initconst = { +static const struct l2c_init_data of_tauros3_data __initconst = { .save = tauros3_save, /* Tauros3 broadcasts L1 cache operations to L2 */ .outer_cache = { @@ -930,8 +930,8 @@ static const struct l2x0_of_data tauros3_data __initconst = { }, }; -static const struct l2x0_of_data bcm_l2x0_data __initconst = { - .setup = pl310_of_setup, +static const struct l2c_init_data of_bcm_l2x0_data __initconst = { + .of_parse = pl310_of_parse, .save = pl310_save, .outer_cache = { .inv_range = bcm_inv_range, @@ -946,22 +946,22 @@ static const struct l2x0_of_data bcm_l2x0_data __initconst = { #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } static const struct of_device_id l2x0_ids[] __initconst = { - L2C_ID("arm,l210-cache", l2x0_data), - L2C_ID("arm,l220-cache", l2x0_data), - L2C_ID("arm,pl310-cache", pl310_data), - L2C_ID("brcm,bcm11351-a2-pl310-cache", bcm_l2x0_data), - L2C_ID("marvell,aurora-outer-cache", aurora_with_outer_data), - L2C_ID("marvell,aurora-system-cache", aurora_no_outer_data), - L2C_ID("marvell,tauros3-cache", tauros3_data), + L2C_ID("arm,l210-cache", of_l2x0_data), + L2C_ID("arm,l220-cache", of_l2x0_data), + L2C_ID("arm,pl310-cache", of_pl310_data), + L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), + L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data), + L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data), + L2C_ID("marvell,tauros3-cache", of_tauros3_data), /* Deprecated IDs */ - L2C_ID("bcm,bcm11351-a2-pl310-cache", bcm_l2x0_data), + L2C_ID("bcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), {} }; int __init l2x0_of_init(u32 aux_val, u32 aux_mask) { + const struct l2c_init_data *data; struct device_node *np; - const struct l2x0_of_data *data; struct resource res; np = of_find_matching_node(NULL, l2x0_ids); @@ -981,12 +981,12 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) /* L2 configuration can only be changed if the cache is disabled */ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - if (data->setup) - data->setup(np, &aux_val, &aux_mask); + if (data->of_parse) + data->of_parse(np, &aux_val, &aux_mask); /* For aurora cache in no outer mode select the * correct mode using the coprocessor*/ - if (data == &aurora_no_outer_data) + if (data == &of_aurora_no_outer_data) aurora_broadcast_l2_commands(); } From 37dd8bee901c53c0c8d5558f98c86e1604bea92c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0509/1983] ARM: l2c: provide generic function for calling set_debug method Provide a generic function which always calls the set_debug method. This will be used later in the series as some work-arounds require that the debug register be written. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index d659c4ca46bb59..595c50519e410f 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -57,6 +57,16 @@ static inline void cache_wait_way(void __iomem *reg, unsigned long mask) cpu_relax(); } +/* + * This should only be called when we have a requirement that the + * register be written due to a work-around, as platforms running + * in non-secure mode may not be able to access this register. + */ +static inline void l2c_set_debug(void __iomem *base, unsigned long val) +{ + outer_cache.set_debug(val); +} + #ifdef CONFIG_CACHE_PL310 static inline void cache_wait(void __iomem *reg, unsigned long mask) { @@ -92,7 +102,7 @@ static inline void l2x0_inv_line(unsigned long addr) static inline void debug_writel(unsigned long val) { if (outer_cache.set_debug) - outer_cache.set_debug(val); + l2c_set_debug(l2x0_base, val); } static void pl310_set_debug(unsigned long val) From 120278bc35b967a0a2385897c634066b798a0ad4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0510/1983] ARM: l2c: split out cache unlock code Split the cache unlock code out of l2x0_unlock(). We want to be able to re-use this functionality later. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 595c50519e410f..a1313d20f2058a 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -50,6 +50,9 @@ struct l2x0_regs l2x0_saved_regs; static bool of_init = false; +/* + * Common code for all cache controllers. + */ static inline void cache_wait_way(void __iomem *reg, unsigned long mask) { /* wait for cache operation by line or way to complete */ @@ -67,6 +70,18 @@ static inline void l2c_set_debug(void __iomem *base, unsigned long val) outer_cache.set_debug(val); } +static inline void l2c_unlock(void __iomem *base, unsigned num) +{ + unsigned i; + + for (i = 0; i < num; i++) { + writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_D_BASE + + i * L2X0_LOCKDOWN_STRIDE); + writel_relaxed(0, base + L2X0_LOCKDOWN_WAY_I_BASE + + i * L2X0_LOCKDOWN_STRIDE); + } +} + #ifdef CONFIG_CACHE_PL310 static inline void cache_wait(void __iomem *reg, unsigned long mask) { @@ -308,7 +323,6 @@ static void l2x0_disable(void) static void l2x0_unlock(u32 cache_id) { int lockregs; - int i; switch (cache_id & L2X0_CACHE_ID_PART_MASK) { case L2X0_CACHE_ID_PART_L310: @@ -323,12 +337,7 @@ static void l2x0_unlock(u32 cache_id) break; } - for (i = 0; i < lockregs; i++) { - writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_D_BASE + - i * L2X0_LOCKDOWN_STRIDE); - writel_relaxed(0x0, l2x0_base + L2X0_LOCKDOWN_WAY_I_BASE + - i * L2X0_LOCKDOWN_STRIDE); - } + l2c_unlock(l2x0_base, lockregs); } void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) From c3fdb01946c6864d322f10d01a601b429675f81a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0511/1983] ARM: l2c: provide generic helper for way-based operations Provide a generic helper function for way based operations. These are always background operations, and thus have to be waited for before a new operation is commenced. This helper extracts that requirement from several locations in the code. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index a1313d20f2058a..1c3a23318f53fa 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -70,6 +70,12 @@ static inline void l2c_set_debug(void __iomem *base, unsigned long val) outer_cache.set_debug(val); } +static void __l2c_op_way(void __iomem *reg) +{ + writel_relaxed(l2x0_way_mask, reg); + cache_wait_way(reg, l2x0_way_mask); +} + static inline void l2c_unlock(void __iomem *base, unsigned num) { unsigned i; @@ -166,8 +172,7 @@ static void l2x0_cache_sync(void) static void __l2x0_flush_all(void) { debug_writel(0x03); - writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY); - cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask); + __l2c_op_way(l2x0_base + L2X0_CLEAN_INV_WAY); cache_sync(); debug_writel(0x00); } @@ -188,8 +193,7 @@ static void l2x0_clean_all(void) /* clean all ways */ raw_spin_lock_irqsave(&l2x0_lock, flags); - writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY); - cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask); + __l2c_op_way(l2x0_base + L2X0_CLEAN_WAY); cache_sync(); raw_spin_unlock_irqrestore(&l2x0_lock, flags); } @@ -202,8 +206,7 @@ static void l2x0_inv_all(void) raw_spin_lock_irqsave(&l2x0_lock, flags); /* Invalidating when L2 is enabled is a nono */ BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN); - writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY); - cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask); + __l2c_op_way(l2x0_base + L2X0_INV_WAY); cache_sync(); raw_spin_unlock_irqrestore(&l2x0_lock, flags); } From e949a56cd9cd574fc2d4d61a9e9c5597490a0a5a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0512/1983] ARM: l2c: rename cache_wait_way() cache_wait_way() is actually used to wait for a particular mask to report clear; it's not really got much to do with cache ways at all. Indeed, it gets used to wait for the C bit to clear on older caches. Rename this with a more generic function name which better reflects its purpose: l2c_wait_mask(). Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 1c3a23318f53fa..29ee7f6928012c 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -53,7 +53,7 @@ static bool of_init = false; /* * Common code for all cache controllers. */ -static inline void cache_wait_way(void __iomem *reg, unsigned long mask) +static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask) { /* wait for cache operation by line or way to complete */ while (readl_relaxed(reg) & mask) @@ -73,7 +73,7 @@ static inline void l2c_set_debug(void __iomem *base, unsigned long val) static void __l2c_op_way(void __iomem *reg) { writel_relaxed(l2x0_way_mask, reg); - cache_wait_way(reg, l2x0_way_mask); + l2c_wait_mask(reg, l2x0_way_mask); } static inline void l2c_unlock(void __iomem *base, unsigned num) @@ -94,7 +94,7 @@ static inline void cache_wait(void __iomem *reg, unsigned long mask) /* cache operations by line are atomic on PL310 */ } #else -#define cache_wait cache_wait_way +#define cache_wait l2c_wait_mask #endif static inline void cache_sync(void) From 08de13a6c2e00ff903f6d8a6ef4e00f240a2e434 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0513/1983] ARM: l2c: add and use L2C revision constants The revision namespace is specific to the L2 cache part, so don't name these with generic identifiers, use a part specific identifier. Signed-off-by: Russell King --- arch/arm/include/asm/hardware/cache-l2x0.h | 22 ++++++++++++++++------ arch/arm/mm/cache-l2x0.c | 10 +++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 6795ff743b3dbc..3af45734b51420 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -68,14 +68,24 @@ /* Registers shifts and masks */ #define L2X0_CACHE_ID_PART_MASK (0xf << 6) #define L2X0_CACHE_ID_PART_L210 (1 << 6) +#define L2X0_CACHE_ID_PART_L220 (2 << 6) #define L2X0_CACHE_ID_PART_L310 (3 << 6) #define L2X0_CACHE_ID_RTL_MASK 0x3f -#define L2X0_CACHE_ID_RTL_R0P0 0x0 -#define L2X0_CACHE_ID_RTL_R1P0 0x2 -#define L2X0_CACHE_ID_RTL_R2P0 0x4 -#define L2X0_CACHE_ID_RTL_R3P0 0x5 -#define L2X0_CACHE_ID_RTL_R3P1 0x6 -#define L2X0_CACHE_ID_RTL_R3P2 0x8 +#define L210_CACHE_ID_RTL_R0P2_02 0x00 +#define L210_CACHE_ID_RTL_R0P1 0x01 +#define L210_CACHE_ID_RTL_R0P2_01 0x02 +#define L210_CACHE_ID_RTL_R0P3 0x03 +#define L210_CACHE_ID_RTL_R0P4 0x0b +#define L210_CACHE_ID_RTL_R0P5 0x0f +#define L220_CACHE_ID_RTL_R1P7_01REL0 0x06 +#define L310_CACHE_ID_RTL_R0P0 0x00 +#define L310_CACHE_ID_RTL_R1P0 0x02 +#define L310_CACHE_ID_RTL_R2P0 0x04 +#define L310_CACHE_ID_RTL_R3P0 0x05 +#define L310_CACHE_ID_RTL_R3P1 0x06 +#define L310_CACHE_ID_RTL_R3P1_50REL0 0x07 +#define L310_CACHE_ID_RTL_R3P2 0x08 +#define L310_CACHE_ID_RTL_R3P3 0x09 #define L2X0_AUX_CTRL_MASK 0xc0000fff #define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT 0 diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 29ee7f6928012c..c39602ef2cddab 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -374,7 +374,7 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) /* Unmapped register. */ sync_reg_offset = L2X0_DUMMY_REG; #endif - if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L2X0_CACHE_ID_RTL_R3P0) + if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0) outer_cache.set_debug = pl310_set_debug; break; case L2X0_CACHE_ID_PART_L210: @@ -768,7 +768,7 @@ static void __init pl310_save(void) l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base + L2X0_ADDR_FILTER_START); - if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) { + if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { /* * From r2p0, there is Prefetch offset/control register */ @@ -777,7 +777,7 @@ static void __init pl310_save(void) /* * From r3p0, there is Power control register */ - if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0) + if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base + L2X0_POWER_CTRL); } @@ -830,10 +830,10 @@ static void pl310_resume(void) l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK; - if (l2x0_revision >= L2X0_CACHE_ID_RTL_R2P0) { + if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { writel_relaxed(l2x0_saved_regs.prefetch_ctrl, l2x0_base + L2X0_PREFETCH_CTRL); - if (l2x0_revision >= L2X0_CACHE_ID_RTL_R3P0) + if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) writel_relaxed(l2x0_saved_regs.pwr_ctrl, l2x0_base + L2X0_POWER_CTRL); } From 61a13b936ed42a7786507b63920b3334dcefaac4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0514/1983] ARM: l2c: clean up OF initialisation a bit Rather than having a boolean and other tricks to disable some bits of l2x0_init(), split this function into two parts: a common part shared between OF and non-OF, and the non-OF part. The common part can take a block of function pointers, and the cache ID (to cope with Aurora's DT specified ID.) Eliminate the redundant setting of l2x0_base in the OF case, moving it to the non-OF init function. This allows us to localise the OF-specific initialisation handling from the non-OF handling. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 66 ++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index c39602ef2cddab..0d83b24b7971eb 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -42,14 +42,8 @@ static u32 l2x0_way_mask; /* Bitmask of active ways */ static u32 l2x0_size; static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; -/* Aurora don't have the cache ID register available, so we have to - * pass it though the device tree */ -static u32 cache_id_part_number_from_dt; - struct l2x0_regs l2x0_saved_regs; -static bool of_init = false; - /* * Common code for all cache controllers. */ @@ -343,20 +337,26 @@ static void l2x0_unlock(u32 cache_id) l2c_unlock(l2x0_base, lockregs); } -void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) +static const struct l2c_init_data l2x0_init_fns __initconst = { + .outer_cache = { + .inv_range = l2x0_inv_range, + .clean_range = l2x0_clean_range, + .flush_range = l2x0_flush_range, + .flush_all = l2x0_flush_all, + .disable = l2x0_disable, + .sync = l2x0_cache_sync, + }, +}; + +static void __init __l2c_init(const struct l2c_init_data *data, + u32 aux_val, u32 aux_mask, u32 cache_id) { u32 aux; - u32 cache_id; u32 way_size = 0; int ways; int way_size_shift = L2X0_WAY_SIZE_SHIFT; const char *type; - l2x0_base = base; - if (cache_id_part_number_from_dt) - cache_id = cache_id_part_number_from_dt; - else - cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; @@ -374,8 +374,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) /* Unmapped register. */ sync_reg_offset = L2X0_DUMMY_REG; #endif - if ((cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0) - outer_cache.set_debug = pl310_set_debug; break; case L2X0_CACHE_ID_PART_L210: ways = (aux >> 13) & 0xf; @@ -430,23 +428,35 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) /* Save the value for resuming. */ l2x0_saved_regs.aux_ctrl = aux; - if (!of_init) { - outer_cache.inv_range = l2x0_inv_range; - outer_cache.clean_range = l2x0_clean_range; - outer_cache.flush_range = l2x0_flush_range; - outer_cache.sync = l2x0_cache_sync; - outer_cache.flush_all = l2x0_flush_all; - outer_cache.disable = l2x0_disable; - } + outer_cache = data->outer_cache; + + if ((cache_id & L2X0_CACHE_ID_PART_MASK) == L2X0_CACHE_ID_PART_L310 && + (cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0) + outer_cache.set_debug = pl310_set_debug; pr_info("%s cache controller enabled\n", type); pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n", ways, cache_id, aux, l2x0_size >> 10); } +void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) +{ + u32 cache_id; + + l2x0_base = base; + + cache_id = readl_relaxed(base + L2X0_CACHE_ID); + + __l2c_init(&l2x0_init_fns, aux_val, aux_mask, cache_id); +} + #ifdef CONFIG_OF static int l2_wt_override; +/* Aurora don't have the cache ID register available, so we have to + * pass it though the device tree */ +static u32 cache_id_part_number_from_dt; + /* * Note that the end addresses passed to Linux primitives are * noninclusive, while the hardware cache range operations use @@ -985,6 +995,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) const struct l2c_init_data *data; struct device_node *np; struct resource res; + u32 cache_id; np = of_find_matching_node(NULL, l2x0_ids); if (!np) @@ -1015,9 +1026,12 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) if (data->save) data->save(); - of_init = true; - memcpy(&outer_cache, &data->outer_cache, sizeof(outer_cache)); - l2x0_init(l2x0_base, aux_val, aux_mask); + if (cache_id_part_number_from_dt) + cache_id = cache_id_part_number_from_dt; + else + cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + + __l2c_init(data, aux_val, aux_mask, cache_id); return 0; } From 824e486e48fafc26b15db561c746a1de4eac6040 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0515/1983] ARM: l2c: pass iomem address into data->save function Pass the iomem address into this function so we don't have to keep accessing it from a global. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 0d83b24b7971eb..08f9cade028ad3 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -30,7 +30,7 @@ struct l2c_init_data { void (*of_parse)(const struct device_node *, u32 *, u32 *); - void (*save)(void); + void (*save)(void __iomem *); struct outer_cache_fns outer_cache; }; @@ -764,47 +764,47 @@ static void __init pl310_of_parse(const struct device_node *np, } } -static void __init pl310_save(void) +static void __init pl310_save(void __iomem *base) { - u32 l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & + u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK; - l2x0_saved_regs.tag_latency = readl_relaxed(l2x0_base + + l2x0_saved_regs.tag_latency = readl_relaxed(base + L2X0_TAG_LATENCY_CTRL); - l2x0_saved_regs.data_latency = readl_relaxed(l2x0_base + + l2x0_saved_regs.data_latency = readl_relaxed(base + L2X0_DATA_LATENCY_CTRL); - l2x0_saved_regs.filter_end = readl_relaxed(l2x0_base + + l2x0_saved_regs.filter_end = readl_relaxed(base + L2X0_ADDR_FILTER_END); - l2x0_saved_regs.filter_start = readl_relaxed(l2x0_base + + l2x0_saved_regs.filter_start = readl_relaxed(base + L2X0_ADDR_FILTER_START); if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { /* * From r2p0, there is Prefetch offset/control register */ - l2x0_saved_regs.prefetch_ctrl = readl_relaxed(l2x0_base + + l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + L2X0_PREFETCH_CTRL); /* * From r3p0, there is Power control register */ if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - l2x0_saved_regs.pwr_ctrl = readl_relaxed(l2x0_base + + l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + L2X0_POWER_CTRL); } } -static void aurora_save(void) +static void aurora_save(void __iomem *base) { - l2x0_saved_regs.ctrl = readl_relaxed(l2x0_base + L2X0_CTRL); - l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); + l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL); + l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL); } -static void __init tauros3_save(void) +static void __init tauros3_save(void __iomem *base) { l2x0_saved_regs.aux2_ctrl = - readl_relaxed(l2x0_base + TAUROS3_AUX2_CTRL); + readl_relaxed(base + TAUROS3_AUX2_CTRL); l2x0_saved_regs.prefetch_ctrl = - readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); + readl_relaxed(base + L2X0_PREFETCH_CTRL); } static void l2x0_resume(void) @@ -1024,7 +1024,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) } if (data->save) - data->save(); + data->save(l2x0_base); if (cache_id_part_number_from_dt) cache_id = cache_id_part_number_from_dt; From 8be47c1e6a2cbf983a21a61d431deb451963f947 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0516/1983] ARM: l2c: move l2c save function to __l2c_init() There's no reason this functionality should be specific to DT, so move it into the common initialisation function. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 08f9cade028ad3..3b621383805456 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -357,6 +357,13 @@ static void __init __l2c_init(const struct l2c_init_data *data, int way_size_shift = L2X0_WAY_SIZE_SHIFT; const char *type; + /* + * It is strange to save the register state before initialisation, + * but hey, this is what the DT implementations decided to do. + */ + if (data->save) + data->save(l2x0_base); + aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; @@ -1023,9 +1030,6 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) aurora_broadcast_l2_commands(); } - if (data->save) - data->save(l2x0_base); - if (cache_id_part_number_from_dt) cache_id = cache_id_part_number_from_dt; else From ed104cbcc168540de3413551061d0f4828548aeb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:22 +0200 Subject: [PATCH 0517/1983] ARM: l2c: group implementation specific code together Back in the mists of time, someone decided that it would be a good idea to group like functions together - so all the save functions in one place, all the resume functions in another, all the OF parsing functions some place else. This makes it difficult to get an overview on what a particular implementation is doing - grouping an implementations specific functions together makes more sense, because you can see what it's doing without the clutter of other implementations. Organise it according to implementation. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 502 +++++++++++++++++++-------------------- 1 file changed, 251 insertions(+), 251 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 3b621383805456..09fe0f5eada536 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -464,6 +464,175 @@ static int l2_wt_override; * pass it though the device tree */ static u32 cache_id_part_number_from_dt; +static void __init l2x0_of_parse(const struct device_node *np, + u32 *aux_val, u32 *aux_mask) +{ + u32 data[2] = { 0, 0 }; + u32 tag = 0; + u32 dirty = 0; + u32 val = 0, mask = 0; + + of_property_read_u32(np, "arm,tag-latency", &tag); + if (tag) { + mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK; + val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT; + } + + of_property_read_u32_array(np, "arm,data-latency", + data, ARRAY_SIZE(data)); + if (data[0] && data[1]) { + mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK | + L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK; + val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) | + ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT); + } + + of_property_read_u32(np, "arm,dirty-latency", &dirty); + if (dirty) { + mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK; + val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT; + } + + *aux_val &= ~mask; + *aux_val |= val; + *aux_mask &= ~mask; +} + +static void l2x0_resume(void) +{ + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + /* restore aux ctrl and enable l2 */ + l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); + + writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + + L2X0_AUX_CTRL); + + l2x0_inv_all(); + + writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); + } +} + +static const struct l2c_init_data of_l2x0_data __initconst = { + .of_parse = l2x0_of_parse, + .outer_cache = { + .inv_range = l2x0_inv_range, + .clean_range = l2x0_clean_range, + .flush_range = l2x0_flush_range, + .flush_all = l2x0_flush_all, + .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = l2x0_resume, + }, +}; + +static void __init pl310_of_parse(const struct device_node *np, + u32 *aux_val, u32 *aux_mask) +{ + u32 data[3] = { 0, 0, 0 }; + u32 tag[3] = { 0, 0, 0 }; + u32 filter[2] = { 0, 0 }; + + of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); + if (tag[0] && tag[1] && tag[2]) + writel_relaxed( + ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | + ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | + ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), + l2x0_base + L2X0_TAG_LATENCY_CTRL); + + of_property_read_u32_array(np, "arm,data-latency", + data, ARRAY_SIZE(data)); + if (data[0] && data[1] && data[2]) + writel_relaxed( + ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | + ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | + ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), + l2x0_base + L2X0_DATA_LATENCY_CTRL); + + of_property_read_u32_array(np, "arm,filter-ranges", + filter, ARRAY_SIZE(filter)); + if (filter[1]) { + writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), + l2x0_base + L2X0_ADDR_FILTER_END); + writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN, + l2x0_base + L2X0_ADDR_FILTER_START); + } +} + +static void __init pl310_save(void __iomem *base) +{ + u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; + + l2x0_saved_regs.tag_latency = readl_relaxed(base + + L2X0_TAG_LATENCY_CTRL); + l2x0_saved_regs.data_latency = readl_relaxed(base + + L2X0_DATA_LATENCY_CTRL); + l2x0_saved_regs.filter_end = readl_relaxed(base + + L2X0_ADDR_FILTER_END); + l2x0_saved_regs.filter_start = readl_relaxed(base + + L2X0_ADDR_FILTER_START); + + if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { + /* + * From r2p0, there is Prefetch offset/control register + */ + l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + + L2X0_PREFETCH_CTRL); + /* + * From r3p0, there is Power control register + */ + if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) + l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + + L2X0_POWER_CTRL); + } +} + +static void pl310_resume(void) +{ + u32 l2x0_revision; + + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + /* restore pl310 setup */ + writel_relaxed(l2x0_saved_regs.tag_latency, + l2x0_base + L2X0_TAG_LATENCY_CTRL); + writel_relaxed(l2x0_saved_regs.data_latency, + l2x0_base + L2X0_DATA_LATENCY_CTRL); + writel_relaxed(l2x0_saved_regs.filter_end, + l2x0_base + L2X0_ADDR_FILTER_END); + writel_relaxed(l2x0_saved_regs.filter_start, + l2x0_base + L2X0_ADDR_FILTER_START); + + l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; + + if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { + writel_relaxed(l2x0_saved_regs.prefetch_ctrl, + l2x0_base + L2X0_PREFETCH_CTRL); + if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) + writel_relaxed(l2x0_saved_regs.pwr_ctrl, + l2x0_base + L2X0_POWER_CTRL); + } + } + + l2x0_resume(); +} + +static const struct l2c_init_data of_pl310_data __initconst = { + .of_parse = pl310_of_parse, + .save = pl310_save, + .outer_cache = { + .inv_range = l2x0_inv_range, + .clean_range = l2x0_clean_range, + .flush_range = l2x0_flush_range, + .flush_all = l2x0_flush_all, + .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = pl310_resume, + }, +}; + /* * Note that the end addresses passed to Linux primitives are * noninclusive, while the hardware cache range operations use @@ -562,6 +731,75 @@ static void aurora_flush_range(unsigned long start, unsigned long end) } } +static void aurora_save(void __iomem *base) +{ + l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL); + l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL); +} + +static void aurora_resume(void) +{ + if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + writel_relaxed(l2x0_saved_regs.aux_ctrl, + l2x0_base + L2X0_AUX_CTRL); + writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL); + } +} + +static void __init aurora_broadcast_l2_commands(void) +{ + __u32 u; + /* Enable Broadcasting of cache commands to L2*/ + __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u)); + u |= AURORA_CTRL_FW; /* Set the FW bit */ + __asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u)); + isb(); +} + +static void __init aurora_of_parse(const struct device_node *np, + u32 *aux_val, u32 *aux_mask) +{ + u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU; + u32 mask = AURORA_ACR_REPLACEMENT_MASK; + + of_property_read_u32(np, "cache-id-part", + &cache_id_part_number_from_dt); + + /* Determine and save the write policy */ + l2_wt_override = of_property_read_bool(np, "wt-override"); + + if (l2_wt_override) { + val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY; + mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK; + } + + *aux_val &= ~mask; + *aux_val |= val; + *aux_mask &= ~mask; +} + +static const struct l2c_init_data of_aurora_with_outer_data __initconst = { + .of_parse = aurora_of_parse, + .save = aurora_save, + .outer_cache = { + .inv_range = aurora_inv_range, + .clean_range = aurora_clean_range, + .flush_range = aurora_flush_range, + .flush_all = l2x0_flush_all, + .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = aurora_resume, + }, +}; + +static const struct l2c_init_data of_aurora_no_outer_data __initconst = { + .of_parse = aurora_of_parse, + .save = aurora_save, + .outer_cache = { + .resume = aurora_resume, + }, +}; + /* * For certain Broadcom SoCs, depending on the address range, different offsets * need to be added to the address before passing it to L2 for @@ -703,108 +941,19 @@ static void bcm_flush_range(unsigned long start, unsigned long end) new_end); } -static void __init l2x0_of_parse(const struct device_node *np, - u32 *aux_val, u32 *aux_mask) -{ - u32 data[2] = { 0, 0 }; - u32 tag = 0; - u32 dirty = 0; - u32 val = 0, mask = 0; - - of_property_read_u32(np, "arm,tag-latency", &tag); - if (tag) { - mask |= L2X0_AUX_CTRL_TAG_LATENCY_MASK; - val |= (tag - 1) << L2X0_AUX_CTRL_TAG_LATENCY_SHIFT; - } - - of_property_read_u32_array(np, "arm,data-latency", - data, ARRAY_SIZE(data)); - if (data[0] && data[1]) { - mask |= L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK | - L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK; - val |= ((data[0] - 1) << L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT) | - ((data[1] - 1) << L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT); - } - - of_property_read_u32(np, "arm,dirty-latency", &dirty); - if (dirty) { - mask |= L2X0_AUX_CTRL_DIRTY_LATENCY_MASK; - val |= (dirty - 1) << L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT; - } - - *aux_val &= ~mask; - *aux_val |= val; - *aux_mask &= ~mask; -} - -static void __init pl310_of_parse(const struct device_node *np, - u32 *aux_val, u32 *aux_mask) -{ - u32 data[3] = { 0, 0, 0 }; - u32 tag[3] = { 0, 0, 0 }; - u32 filter[2] = { 0, 0 }; - - of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); - if (tag[0] && tag[1] && tag[2]) - writel_relaxed( - ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | - ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | - ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), - l2x0_base + L2X0_TAG_LATENCY_CTRL); - - of_property_read_u32_array(np, "arm,data-latency", - data, ARRAY_SIZE(data)); - if (data[0] && data[1] && data[2]) - writel_relaxed( - ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | - ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | - ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), - l2x0_base + L2X0_DATA_LATENCY_CTRL); - - of_property_read_u32_array(np, "arm,filter-ranges", - filter, ARRAY_SIZE(filter)); - if (filter[1]) { - writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), - l2x0_base + L2X0_ADDR_FILTER_END); - writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN, - l2x0_base + L2X0_ADDR_FILTER_START); - } -} - -static void __init pl310_save(void __iomem *base) -{ - u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; - - l2x0_saved_regs.tag_latency = readl_relaxed(base + - L2X0_TAG_LATENCY_CTRL); - l2x0_saved_regs.data_latency = readl_relaxed(base + - L2X0_DATA_LATENCY_CTRL); - l2x0_saved_regs.filter_end = readl_relaxed(base + - L2X0_ADDR_FILTER_END); - l2x0_saved_regs.filter_start = readl_relaxed(base + - L2X0_ADDR_FILTER_START); - - if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { - /* - * From r2p0, there is Prefetch offset/control register - */ - l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + - L2X0_PREFETCH_CTRL); - /* - * From r3p0, there is Power control register - */ - if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + - L2X0_POWER_CTRL); - } -} - -static void aurora_save(void __iomem *base) -{ - l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL); - l2x0_saved_regs.aux_ctrl = readl_relaxed(base + L2X0_AUX_CTRL); -} +static const struct l2c_init_data of_bcm_l2x0_data __initconst = { + .of_parse = pl310_of_parse, + .save = pl310_save, + .outer_cache = { + .inv_range = bcm_inv_range, + .clean_range = bcm_clean_range, + .flush_range = bcm_flush_range, + .flush_all = l2x0_flush_all, + .disable = l2x0_disable, + .sync = l2x0_cache_sync, + .resume = pl310_resume, + }, +}; static void __init tauros3_save(void __iomem *base) { @@ -814,60 +963,6 @@ static void __init tauros3_save(void __iomem *base) readl_relaxed(base + L2X0_PREFETCH_CTRL); } -static void l2x0_resume(void) -{ - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - /* restore aux ctrl and enable l2 */ - l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); - - writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + - L2X0_AUX_CTRL); - - l2x0_inv_all(); - - writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); - } -} - -static void pl310_resume(void) -{ - u32 l2x0_revision; - - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - /* restore pl310 setup */ - writel_relaxed(l2x0_saved_regs.tag_latency, - l2x0_base + L2X0_TAG_LATENCY_CTRL); - writel_relaxed(l2x0_saved_regs.data_latency, - l2x0_base + L2X0_DATA_LATENCY_CTRL); - writel_relaxed(l2x0_saved_regs.filter_end, - l2x0_base + L2X0_ADDR_FILTER_END); - writel_relaxed(l2x0_saved_regs.filter_start, - l2x0_base + L2X0_ADDR_FILTER_START); - - l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; - - if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { - writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - l2x0_base + L2X0_PREFETCH_CTRL); - if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - writel_relaxed(l2x0_saved_regs.pwr_ctrl, - l2x0_base + L2X0_POWER_CTRL); - } - } - - l2x0_resume(); -} - -static void aurora_resume(void) -{ - if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - writel_relaxed(l2x0_saved_regs.aux_ctrl, - l2x0_base + L2X0_AUX_CTRL); - writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL); - } -} - static void tauros3_resume(void) { if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { @@ -880,87 +975,6 @@ static void tauros3_resume(void) l2x0_resume(); } -static void __init aurora_broadcast_l2_commands(void) -{ - __u32 u; - /* Enable Broadcasting of cache commands to L2*/ - __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u)); - u |= AURORA_CTRL_FW; /* Set the FW bit */ - __asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u)); - isb(); -} - -static void __init aurora_of_parse(const struct device_node *np, - u32 *aux_val, u32 *aux_mask) -{ - u32 val = AURORA_ACR_REPLACEMENT_TYPE_SEMIPLRU; - u32 mask = AURORA_ACR_REPLACEMENT_MASK; - - of_property_read_u32(np, "cache-id-part", - &cache_id_part_number_from_dt); - - /* Determine and save the write policy */ - l2_wt_override = of_property_read_bool(np, "wt-override"); - - if (l2_wt_override) { - val |= AURORA_ACR_FORCE_WRITE_THRO_POLICY; - mask |= AURORA_ACR_FORCE_WRITE_POLICY_MASK; - } - - *aux_val &= ~mask; - *aux_val |= val; - *aux_mask &= ~mask; -} - -static const struct l2c_init_data of_pl310_data __initconst = { - .of_parse = pl310_of_parse, - .save = pl310_save, - .outer_cache = { - .inv_range = l2x0_inv_range, - .clean_range = l2x0_clean_range, - .flush_range = l2x0_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, - .resume = pl310_resume, - }, -}; - -static const struct l2c_init_data of_l2x0_data __initconst = { - .of_parse = l2x0_of_parse, - .outer_cache = { - .inv_range = l2x0_inv_range, - .clean_range = l2x0_clean_range, - .flush_range = l2x0_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, - .resume = l2x0_resume, - }, -}; - -static const struct l2c_init_data of_aurora_with_outer_data __initconst = { - .of_parse = aurora_of_parse, - .save = aurora_save, - .outer_cache = { - .inv_range = aurora_inv_range, - .clean_range = aurora_clean_range, - .flush_range = aurora_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, - .resume = aurora_resume, - }, -}; - -static const struct l2c_init_data of_aurora_no_outer_data __initconst = { - .of_parse = aurora_of_parse, - .save = aurora_save, - .outer_cache = { - .resume = aurora_resume, - }, -}; - static const struct l2c_init_data of_tauros3_data __initconst = { .save = tauros3_save, /* Tauros3 broadcasts L1 cache operations to L2 */ @@ -969,20 +983,6 @@ static const struct l2c_init_data of_tauros3_data __initconst = { }, }; -static const struct l2c_init_data of_bcm_l2x0_data __initconst = { - .of_parse = pl310_of_parse, - .save = pl310_save, - .outer_cache = { - .inv_range = bcm_inv_range, - .clean_range = bcm_clean_range, - .flush_range = bcm_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, - .resume = pl310_resume, - }, -}; - #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } static const struct of_device_id l2x0_ids[] __initconst = { L2C_ID("arm,l210-cache", of_l2x0_data), From 50c3263706593fca5db20008f47a8e9cc5cf997e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0518/1983] ARM: l2c: provide enable method Providing an enable method gives L2 cache controllers a chance to do special handling at enable time. This allows us to remove a hack in l2x0_unlock() for Marvell Aurora L2 caches. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 80 +++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 09fe0f5eada536..2adb82e7f4b368 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -29,7 +29,9 @@ #include "cache-aurora-l2.h" struct l2c_init_data { + unsigned num_lock; void (*of_parse)(const struct device_node *, u32 *, u32 *); + void (*enable)(void __iomem *, u32, unsigned); void (*save)(void __iomem *); struct outer_cache_fns outer_cache; }; @@ -82,6 +84,36 @@ static inline void l2c_unlock(void __iomem *base, unsigned num) } } +/* + * Enable the L2 cache controller. This function must only be + * called when the cache controller is known to be disabled. + */ +static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) +{ + unsigned long flags; + + l2c_unlock(base, num_lock); + + writel_relaxed(aux, base + L2X0_AUX_CTRL); + + local_irq_save(flags); + __l2c_op_way(base + L2X0_INV_WAY); + writel_relaxed(0, base + sync_reg_offset); + l2c_wait_mask(base + sync_reg_offset, 1); + local_irq_restore(flags); + + writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL); +} + +static void l2c_disable(void) +{ + void __iomem *base = l2x0_base; + + outer_cache.flush_all(); + writel_relaxed(0, base + L2X0_CTRL); + dsb(st); +} + #ifdef CONFIG_CACHE_PL310 static inline void cache_wait(void __iomem *reg, unsigned long mask) { @@ -325,9 +357,6 @@ static void l2x0_unlock(u32 cache_id) case L2X0_CACHE_ID_PART_L310: lockregs = 8; break; - case AURORA_CACHE_ID: - lockregs = 4; - break; default: /* L210 and unknown types */ lockregs = 1; @@ -337,7 +366,22 @@ static void l2x0_unlock(u32 cache_id) l2c_unlock(l2x0_base, lockregs); } +static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) +{ + /* Make sure that I&D is not locked down when starting */ + l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID)); + + /* l2x0 controller is disabled */ + writel_relaxed(aux, base + L2X0_AUX_CTRL); + + l2x0_inv_all(); + + /* enable L2X0 */ + writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL); +} + static const struct l2c_init_data l2x0_init_fns __initconst = { + .enable = l2x0_enable, .outer_cache = { .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, @@ -412,22 +456,11 @@ static void __init __l2c_init(const struct l2c_init_data *data, l2x0_size = ways * way_size * SZ_1K; /* - * Check if l2x0 controller is already enabled. - * If you are booting from non-secure mode - * accessing the below registers will fault. + * Check if l2x0 controller is already enabled. If we are booting + * in non-secure mode accessing the below registers will fault. */ - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - /* Make sure that I&D is not locked down when starting */ - l2x0_unlock(cache_id); - - /* l2x0 controller is disabled */ - writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); - - l2x0_inv_all(); - - /* enable L2X0 */ - writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); - } + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) + data->enable(l2x0_base, aux, data->num_lock); /* Re-read it in case some bits are reserved. */ aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); @@ -515,6 +548,7 @@ static void l2x0_resume(void) static const struct l2c_init_data of_l2x0_data __initconst = { .of_parse = l2x0_of_parse, + .enable = l2x0_enable, .outer_cache = { .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, @@ -620,7 +654,9 @@ static void pl310_resume(void) } static const struct l2c_init_data of_pl310_data __initconst = { + .num_lock = 8, .of_parse = pl310_of_parse, + .enable = l2c_enable, .save = pl310_save, .outer_cache = { .inv_range = l2x0_inv_range, @@ -779,7 +815,9 @@ static void __init aurora_of_parse(const struct device_node *np, } static const struct l2c_init_data of_aurora_with_outer_data __initconst = { + .num_lock = 4, .of_parse = aurora_of_parse, + .enable = l2c_enable, .save = aurora_save, .outer_cache = { .inv_range = aurora_inv_range, @@ -793,7 +831,9 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = { }; static const struct l2c_init_data of_aurora_no_outer_data __initconst = { + .num_lock = 4, .of_parse = aurora_of_parse, + .enable = l2c_enable, .save = aurora_save, .outer_cache = { .resume = aurora_resume, @@ -942,7 +982,9 @@ static void bcm_flush_range(unsigned long start, unsigned long end) } static const struct l2c_init_data of_bcm_l2x0_data __initconst = { + .num_lock = 8, .of_parse = pl310_of_parse, + .enable = l2c_enable, .save = pl310_save, .outer_cache = { .inv_range = bcm_inv_range, @@ -976,6 +1018,8 @@ static void tauros3_resume(void) } static const struct l2c_init_data of_tauros3_data __initconst = { + .num_lock = 8, + .enable = l2c_enable, .save = tauros3_save, /* Tauros3 broadcasts L1 cache operations to L2 */ .outer_cache = { From 1be1f16c54a98f4bad0deebb1f4a0ed79af25c15 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0519/1983] ARM: l2c: write auxctrl register before unlocking We should write the auxillary control register before unlocking: the write may be necessary to enable non-secure access to the lock registers. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 2adb82e7f4b368..fc609550b7fa53 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -92,10 +92,10 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) { unsigned long flags; - l2c_unlock(base, num_lock); - writel_relaxed(aux, base + L2X0_AUX_CTRL); + l2c_unlock(base, num_lock); + local_irq_save(flags); __l2c_op_way(base + L2X0_INV_WAY); writel_relaxed(0, base + sync_reg_offset); @@ -368,12 +368,12 @@ static void l2x0_unlock(u32 cache_id) static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) { - /* Make sure that I&D is not locked down when starting */ - l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID)); - /* l2x0 controller is disabled */ writel_relaxed(aux, base + L2X0_AUX_CTRL); + /* Make sure that I&D is not locked down when starting */ + l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID)); + l2x0_inv_all(); /* enable L2X0 */ From b74417ff81bf90c1b22fbb3794cf92141f66067b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0520/1983] ARM: l2c: only write the auxiliary control register if required Avoid unnecessary writes to the auxiliary control register if the register already contains the required value. This allows us to avoid invoking the platforms secure monitor code unnecessarily. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index fc609550b7fa53..1c947b4c7f05e7 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -92,7 +92,9 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) { unsigned long flags; - writel_relaxed(aux, base + L2X0_AUX_CTRL); + /* Only write the aux register if it needs changing */ + if (readl_relaxed(base + L2X0_AUX_CTRL) != aux) + writel_relaxed(aux, base + L2X0_AUX_CTRL); l2c_unlock(base, num_lock); From 8d1911629f8d1c129f93036b4e1ce167f48744f7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0521/1983] ARM: l2c: move aurora broadcast setup to enable function Rather than having this hacked into the OF initialiation function, we can handle this via the enable function instead. While here, clean up that code and comments a little. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 1c947b4c7f05e7..5f381af1a7a429 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -784,14 +784,22 @@ static void aurora_resume(void) } } -static void __init aurora_broadcast_l2_commands(void) +/* + * For Aurora cache in no outer mode, enable via the CP15 coprocessor + * broadcasting of cache commands to L2. + */ +static void __init aurora_enable_no_outer(void __iomem *base, u32 aux, + unsigned num_lock) { - __u32 u; - /* Enable Broadcasting of cache commands to L2*/ - __asm__ __volatile__("mrc p15, 1, %0, c15, c2, 0" : "=r"(u)); + u32 u; + + asm volatile("mrc p15, 1, %0, c15, c2, 0" : "=r" (u)); u |= AURORA_CTRL_FW; /* Set the FW bit */ - __asm__ __volatile__("mcr p15, 1, %0, c15, c2, 0\n" : : "r"(u)); + asm volatile("mcr p15, 1, %0, c15, c2, 0" : : "r" (u)); + isb(); + + l2c_enable(base, aux, num_lock); } static void __init aurora_of_parse(const struct device_node *np, @@ -835,7 +843,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = { static const struct l2c_init_data of_aurora_no_outer_data __initconst = { .num_lock = 4, .of_parse = aurora_of_parse, - .enable = l2c_enable, + .enable = aurora_enable_no_outer, .save = aurora_save, .outer_cache = { .resume = aurora_resume, @@ -1066,16 +1074,10 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) data = of_match_node(l2x0_ids, np)->data; /* L2 configuration can only be changed if the cache is disabled */ - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) if (data->of_parse) data->of_parse(np, &aux_val, &aux_mask); - /* For aurora cache in no outer mode select the - * correct mode using the coprocessor*/ - if (data == &of_aurora_no_outer_data) - aurora_broadcast_l2_commands(); - } - if (cache_id_part_number_from_dt) cache_id = cache_id_part_number_from_dt; else From ff1c5aa74450af5c30c8d94f934e1f91f744a4c0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0522/1983] ARM: l2c: implement fixups for L2 cache controller quirks/errata Rather than putting quirk handling in __l2c_init(), move it out to a separate function which individual implementations can specify. This helps to localise the quirks to those implementations which require them. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 112 +++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 11 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 5f381af1a7a429..a544f19c448fc5 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -32,6 +32,7 @@ struct l2c_init_data { unsigned num_lock; void (*of_parse)(const struct device_node *, u32 *, u32 *); void (*enable)(void __iomem *, u32, unsigned); + void (*fixup)(void __iomem *, u32, struct outer_cache_fns *); void (*save)(void __iomem *); struct outer_cache_fns outer_cache; }; @@ -394,9 +395,80 @@ static const struct l2c_init_data l2x0_init_fns __initconst = { }, }; +/* + * L2C-310 specific code. + * + * Errata: + * 588369: PL310 R0P0->R1P0, fixed R2P0. + * Affects: all clean+invalidate operations + * clean and invalidate skips the invalidate step, so we need to issue + * separate operations. We also require the above debug workaround + * enclosing this code fragment on affected parts. On unaffected parts, + * we must not use this workaround without the debug register writes + * to avoid exposing a problem similar to 727915. + * + * 727915: PL310 R2P0->R3P0, fixed R3P1. + * Affects: clean+invalidate by way + * clean and invalidate by way runs in the background, and a store can + * hit the line between the clean operation and invalidate operation, + * resulting in the store being lost. + * + * 753970: PL310 R3P0, fixed R3P1. + * Affects: sync + * prevents merging writes after the sync operation, until another L2C + * operation is performed (or a number of other conditions.) + * + * 769419: PL310 R0P0->R3P1, fixed R3P2. + * Affects: store buffer + * store buffer is not automatically drained. + */ +static void __init l2c310_fixup(void __iomem *base, u32 cache_id, + struct outer_cache_fns *fns) +{ + unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK; + const char *errata[4]; + unsigned n = 0; + + if (revision <= L310_CACHE_ID_RTL_R3P0) + fns->set_debug = pl310_set_debug; + + if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) && + revision == L310_CACHE_ID_RTL_R3P0) { + sync_reg_offset = L2X0_DUMMY_REG; + errata[n++] = "753970"; + } + + if (IS_ENABLED(CONFIG_PL310_ERRATA_769419)) + errata[n++] = "769419"; + + if (n) { + unsigned i; + + pr_info("L2C-310 errat%s", n > 1 ? "a" : "um"); + for (i = 0; i < n; i++) + pr_cont(" %s", errata[i]); + pr_cont(" enabled\n"); + } +} + +static const struct l2c_init_data l2c310_init_fns __initconst = { + .num_lock = 8, + .enable = l2c_enable, + .fixup = l2c310_fixup, + .outer_cache = { + .inv_range = l2x0_inv_range, + .clean_range = l2x0_clean_range, + .flush_range = l2x0_flush_range, + .flush_all = l2x0_flush_all, + .disable = l2x0_disable, + .sync = l2x0_cache_sync, + }, +}; + static void __init __l2c_init(const struct l2c_init_data *data, u32 aux_val, u32 aux_mask, u32 cache_id) { + struct outer_cache_fns fns; u32 aux; u32 way_size = 0; int ways; @@ -423,23 +495,20 @@ static void __init __l2c_init(const struct l2c_init_data *data, else ways = 8; type = "L310"; -#ifdef CONFIG_PL310_ERRATA_753970 - /* Unmapped register. */ - sync_reg_offset = L2X0_DUMMY_REG; -#endif break; + case L2X0_CACHE_ID_PART_L210: ways = (aux >> 13) & 0xf; type = "L210"; break; case AURORA_CACHE_ID: - sync_reg_offset = AURORA_SYNC_REG; ways = (aux >> 13) & 0xf; ways = 2 << ((ways + 1) >> 2); way_size_shift = AURORA_WAY_SIZE_SHIFT; type = "Aurora"; break; + default: /* Assume unknown chips have 8 ways */ ways = 8; @@ -457,6 +526,10 @@ static void __init __l2c_init(const struct l2c_init_data *data, l2x0_size = ways * way_size * SZ_1K; + fns = data->outer_cache; + if (data->fixup) + data->fixup(l2x0_base, cache_id, &fns); + /* * Check if l2x0 controller is already enabled. If we are booting * in non-secure mode accessing the below registers will fault. @@ -470,11 +543,7 @@ static void __init __l2c_init(const struct l2c_init_data *data, /* Save the value for resuming. */ l2x0_saved_regs.aux_ctrl = aux; - outer_cache = data->outer_cache; - - if ((cache_id & L2X0_CACHE_ID_PART_MASK) == L2X0_CACHE_ID_PART_L310 && - (cache_id & L2X0_CACHE_ID_RTL_MASK) <= L310_CACHE_ID_RTL_R3P0) - outer_cache.set_debug = pl310_set_debug; + outer_cache = fns; pr_info("%s cache controller enabled\n", type); pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n", @@ -483,13 +552,24 @@ static void __init __l2c_init(const struct l2c_init_data *data, void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) { + const struct l2c_init_data *data; u32 cache_id; l2x0_base = base; cache_id = readl_relaxed(base + L2X0_CACHE_ID); - __l2c_init(&l2x0_init_fns, aux_val, aux_mask, cache_id); + switch (cache_id & L2X0_CACHE_ID_PART_MASK) { + default: + data = &l2x0_init_fns; + break; + + case L2X0_CACHE_ID_PART_L310: + data = &l2c310_init_fns; + break; + } + + __l2c_init(data, aux_val, aux_mask, cache_id); } #ifdef CONFIG_OF @@ -659,6 +739,7 @@ static const struct l2c_init_data of_pl310_data __initconst = { .num_lock = 8, .of_parse = pl310_of_parse, .enable = l2c_enable, + .fixup = l2c310_fixup, .save = pl310_save, .outer_cache = { .inv_range = l2x0_inv_range, @@ -802,6 +883,12 @@ static void __init aurora_enable_no_outer(void __iomem *base, u32 aux, l2c_enable(base, aux, num_lock); } +static void __init aurora_fixup(void __iomem *base, u32 cache_id, + struct outer_cache_fns *fns) +{ + sync_reg_offset = AURORA_SYNC_REG; +} + static void __init aurora_of_parse(const struct device_node *np, u32 *aux_val, u32 *aux_mask) { @@ -828,6 +915,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = { .num_lock = 4, .of_parse = aurora_of_parse, .enable = l2c_enable, + .fixup = aurora_fixup, .save = aurora_save, .outer_cache = { .inv_range = aurora_inv_range, @@ -844,6 +932,7 @@ static const struct l2c_init_data of_aurora_no_outer_data __initconst = { .num_lock = 4, .of_parse = aurora_of_parse, .enable = aurora_enable_no_outer, + .fixup = aurora_fixup, .save = aurora_save, .outer_cache = { .resume = aurora_resume, @@ -995,6 +1084,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .num_lock = 8, .of_parse = pl310_of_parse, .enable = l2c_enable, + .fixup = l2c310_fixup, .save = pl310_save, .outer_cache = { .inv_range = bcm_inv_range, From 194f4efd50aa38d843e4923c3d61e82536c8ada6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0523/1983] ARM: l2c: clean up L2 cache initialisation messages Make one of them purely "English", and the other purely technical. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index a544f19c448fc5..713cdcef25d117 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -545,9 +545,10 @@ static void __init __l2c_init(const struct l2c_init_data *data, outer_cache = fns; - pr_info("%s cache controller enabled\n", type); - pr_info("l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d kB\n", - ways, cache_id, aux, l2x0_size >> 10); + pr_info("%s cache controller enabled, %d ways, %d kB\n", + type, ways, l2x0_size >> 10); + pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n", + type, cache_id, aux); } void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) From 525f20ea5b2c8d850611e259f38be23a5970b495 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0524/1983] ARM: l2c: move and add ARM L2C-2x0/L2C-310 save/resume code to non-OF Add the save/resume code hooks to the non-OF implementations as well. There's no reason for the non-OF implementations to be any different from the OF implementations. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 151 ++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 713cdcef25d117..4d985c17291c67 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -383,6 +383,21 @@ static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL); } +static void l2x0_resume(void) +{ + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + /* restore aux ctrl and enable l2 */ + l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); + + writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + + L2X0_AUX_CTRL); + + l2x0_inv_all(); + + writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); + } +} + static const struct l2c_init_data l2x0_init_fns __initconst = { .enable = l2x0_enable, .outer_cache = { @@ -392,6 +407,7 @@ static const struct l2c_init_data l2x0_init_fns __initconst = { .flush_all = l2x0_flush_all, .disable = l2x0_disable, .sync = l2x0_cache_sync, + .resume = l2x0_resume, }, }; @@ -422,6 +438,65 @@ static const struct l2c_init_data l2x0_init_fns __initconst = { * Affects: store buffer * store buffer is not automatically drained. */ +static void __init pl310_save(void __iomem *base) +{ + u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; + + l2x0_saved_regs.tag_latency = readl_relaxed(base + + L2X0_TAG_LATENCY_CTRL); + l2x0_saved_regs.data_latency = readl_relaxed(base + + L2X0_DATA_LATENCY_CTRL); + l2x0_saved_regs.filter_end = readl_relaxed(base + + L2X0_ADDR_FILTER_END); + l2x0_saved_regs.filter_start = readl_relaxed(base + + L2X0_ADDR_FILTER_START); + + if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { + /* + * From r2p0, there is Prefetch offset/control register + */ + l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + + L2X0_PREFETCH_CTRL); + /* + * From r3p0, there is Power control register + */ + if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) + l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + + L2X0_POWER_CTRL); + } +} + +static void pl310_resume(void) +{ + u32 l2x0_revision; + + if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + /* restore pl310 setup */ + writel_relaxed(l2x0_saved_regs.tag_latency, + l2x0_base + L2X0_TAG_LATENCY_CTRL); + writel_relaxed(l2x0_saved_regs.data_latency, + l2x0_base + L2X0_DATA_LATENCY_CTRL); + writel_relaxed(l2x0_saved_regs.filter_end, + l2x0_base + L2X0_ADDR_FILTER_END); + writel_relaxed(l2x0_saved_regs.filter_start, + l2x0_base + L2X0_ADDR_FILTER_START); + + l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; + + if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { + writel_relaxed(l2x0_saved_regs.prefetch_ctrl, + l2x0_base + L2X0_PREFETCH_CTRL); + if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) + writel_relaxed(l2x0_saved_regs.pwr_ctrl, + l2x0_base + L2X0_POWER_CTRL); + } + } + + l2x0_resume(); +} + static void __init l2c310_fixup(void __iomem *base, u32 cache_id, struct outer_cache_fns *fns) { @@ -455,6 +530,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .num_lock = 8, .enable = l2c_enable, .fixup = l2c310_fixup, + .save = pl310_save, .outer_cache = { .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, @@ -462,6 +538,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .flush_all = l2x0_flush_all, .disable = l2x0_disable, .sync = l2x0_cache_sync, + .resume = pl310_resume, }, }; @@ -614,21 +691,6 @@ static void __init l2x0_of_parse(const struct device_node *np, *aux_mask &= ~mask; } -static void l2x0_resume(void) -{ - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - /* restore aux ctrl and enable l2 */ - l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); - - writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + - L2X0_AUX_CTRL); - - l2x0_inv_all(); - - writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); - } -} - static const struct l2c_init_data of_l2x0_data __initconst = { .of_parse = l2x0_of_parse, .enable = l2x0_enable, @@ -677,65 +739,6 @@ static void __init pl310_of_parse(const struct device_node *np, } } -static void __init pl310_save(void __iomem *base) -{ - u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; - - l2x0_saved_regs.tag_latency = readl_relaxed(base + - L2X0_TAG_LATENCY_CTRL); - l2x0_saved_regs.data_latency = readl_relaxed(base + - L2X0_DATA_LATENCY_CTRL); - l2x0_saved_regs.filter_end = readl_relaxed(base + - L2X0_ADDR_FILTER_END); - l2x0_saved_regs.filter_start = readl_relaxed(base + - L2X0_ADDR_FILTER_START); - - if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { - /* - * From r2p0, there is Prefetch offset/control register - */ - l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + - L2X0_PREFETCH_CTRL); - /* - * From r3p0, there is Power control register - */ - if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + - L2X0_POWER_CTRL); - } -} - -static void pl310_resume(void) -{ - u32 l2x0_revision; - - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - /* restore pl310 setup */ - writel_relaxed(l2x0_saved_regs.tag_latency, - l2x0_base + L2X0_TAG_LATENCY_CTRL); - writel_relaxed(l2x0_saved_regs.data_latency, - l2x0_base + L2X0_DATA_LATENCY_CTRL); - writel_relaxed(l2x0_saved_regs.filter_end, - l2x0_base + L2X0_ADDR_FILTER_END); - writel_relaxed(l2x0_saved_regs.filter_start, - l2x0_base + L2X0_ADDR_FILTER_START); - - l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; - - if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { - writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - l2x0_base + L2X0_PREFETCH_CTRL); - if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - writel_relaxed(l2x0_saved_regs.pwr_ctrl, - l2x0_base + L2X0_POWER_CTRL); - } - } - - l2x0_resume(); -} - static const struct l2c_init_data of_pl310_data __initconst = { .num_lock = 8, .of_parse = pl310_of_parse, From 290e7a927ad1aa1774fab5006037b923fd57c4a0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0525/1983] ARM: l2c: clean up save/resume functions Rename the pl310 save/resume functions to have a l2c310 prefix - this is it's official name. Use a local cached copy of the l2x0_base virtual address, and also realise that many of the resume function tails are the same as the enable functions, so make a call to the enable function instead of duplicating that code. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 109 +++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 4d985c17291c67..e3f4fcbcc88b77 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -385,17 +385,10 @@ static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) static void l2x0_resume(void) { - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - /* restore aux ctrl and enable l2 */ - l2x0_unlock(readl_relaxed(l2x0_base + L2X0_CACHE_ID)); - - writel_relaxed(l2x0_saved_regs.aux_ctrl, l2x0_base + - L2X0_AUX_CTRL); - - l2x0_inv_all(); + void __iomem *base = l2x0_base; - writel_relaxed(L2X0_CTRL_EN, l2x0_base + L2X0_CTRL); - } + if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) + l2x0_enable(base, l2x0_saved_regs.aux_ctrl, 0); } static const struct l2c_init_data l2x0_init_fns __initconst = { @@ -438,10 +431,9 @@ static const struct l2c_init_data l2x0_init_fns __initconst = { * Affects: store buffer * store buffer is not automatically drained. */ -static void __init pl310_save(void __iomem *base) +static void __init l2c310_save(void __iomem *base) { - u32 l2x0_revision = readl_relaxed(base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; + unsigned revision; l2x0_saved_regs.tag_latency = readl_relaxed(base + L2X0_TAG_LATENCY_CTRL); @@ -452,49 +444,49 @@ static void __init pl310_save(void __iomem *base) l2x0_saved_regs.filter_start = readl_relaxed(base + L2X0_ADDR_FILTER_START); - if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { - /* - * From r2p0, there is Prefetch offset/control register - */ + revision = readl_relaxed(base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; + + /* From r2p0, there is Prefetch offset/control register */ + if (revision >= L310_CACHE_ID_RTL_R2P0) l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + - L2X0_PREFETCH_CTRL); - /* - * From r3p0, there is Power control register - */ - if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + - L2X0_POWER_CTRL); - } + L2X0_PREFETCH_CTRL); + + /* From r3p0, there is Power control register */ + if (revision >= L310_CACHE_ID_RTL_R3P0) + l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + + L2X0_POWER_CTRL); } -static void pl310_resume(void) +static void l2c310_resume(void) { - u32 l2x0_revision; + void __iomem *base = l2x0_base; + + if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) { + unsigned revision; - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { /* restore pl310 setup */ writel_relaxed(l2x0_saved_regs.tag_latency, - l2x0_base + L2X0_TAG_LATENCY_CTRL); + base + L2X0_TAG_LATENCY_CTRL); writel_relaxed(l2x0_saved_regs.data_latency, - l2x0_base + L2X0_DATA_LATENCY_CTRL); + base + L2X0_DATA_LATENCY_CTRL); writel_relaxed(l2x0_saved_regs.filter_end, - l2x0_base + L2X0_ADDR_FILTER_END); + base + L2X0_ADDR_FILTER_END); writel_relaxed(l2x0_saved_regs.filter_start, - l2x0_base + L2X0_ADDR_FILTER_START); + base + L2X0_ADDR_FILTER_START); - l2x0_revision = readl_relaxed(l2x0_base + L2X0_CACHE_ID) & - L2X0_CACHE_ID_RTL_MASK; + revision = readl_relaxed(base + L2X0_CACHE_ID) & + L2X0_CACHE_ID_RTL_MASK; - if (l2x0_revision >= L310_CACHE_ID_RTL_R2P0) { + if (revision >= L310_CACHE_ID_RTL_R2P0) writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - l2x0_base + L2X0_PREFETCH_CTRL); - if (l2x0_revision >= L310_CACHE_ID_RTL_R3P0) - writel_relaxed(l2x0_saved_regs.pwr_ctrl, - l2x0_base + L2X0_POWER_CTRL); - } - } + base + L2X0_PREFETCH_CTRL); + if (revision >= L310_CACHE_ID_RTL_R3P0) + writel_relaxed(l2x0_saved_regs.pwr_ctrl, + base + L2X0_POWER_CTRL); - l2x0_resume(); + l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); + } } static void __init l2c310_fixup(void __iomem *base, u32 cache_id, @@ -530,7 +522,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .num_lock = 8, .enable = l2c_enable, .fixup = l2c310_fixup, - .save = pl310_save, + .save = l2c310_save, .outer_cache = { .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, @@ -538,7 +530,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .flush_all = l2x0_flush_all, .disable = l2x0_disable, .sync = l2x0_cache_sync, - .resume = pl310_resume, + .resume = l2c310_resume, }, }; @@ -744,7 +736,7 @@ static const struct l2c_init_data of_pl310_data __initconst = { .of_parse = pl310_of_parse, .enable = l2c_enable, .fixup = l2c310_fixup, - .save = pl310_save, + .save = l2c310_save, .outer_cache = { .inv_range = l2x0_inv_range, .clean_range = l2x0_clean_range, @@ -752,7 +744,7 @@ static const struct l2c_init_data of_pl310_data __initconst = { .flush_all = l2x0_flush_all, .disable = l2x0_disable, .sync = l2x0_cache_sync, - .resume = pl310_resume, + .resume = l2c310_resume, }, }; @@ -862,10 +854,11 @@ static void aurora_save(void __iomem *base) static void aurora_resume(void) { - if (!(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { - writel_relaxed(l2x0_saved_regs.aux_ctrl, - l2x0_base + L2X0_AUX_CTRL); - writel_relaxed(l2x0_saved_regs.ctrl, l2x0_base + L2X0_CTRL); + void __iomem *base = l2x0_base; + + if (!(readl(base + L2X0_CTRL) & L2X0_CTRL_EN)) { + writel_relaxed(l2x0_saved_regs.aux_ctrl, base + L2X0_AUX_CTRL); + writel_relaxed(l2x0_saved_regs.ctrl, base + L2X0_CTRL); } } @@ -1089,7 +1082,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .of_parse = pl310_of_parse, .enable = l2c_enable, .fixup = l2c310_fixup, - .save = pl310_save, + .save = l2c310_save, .outer_cache = { .inv_range = bcm_inv_range, .clean_range = bcm_clean_range, @@ -1097,7 +1090,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .flush_all = l2x0_flush_all, .disable = l2x0_disable, .sync = l2x0_cache_sync, - .resume = pl310_resume, + .resume = l2c310_resume, }, }; @@ -1111,14 +1104,16 @@ static void __init tauros3_save(void __iomem *base) static void tauros3_resume(void) { - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) { + void __iomem *base = l2x0_base; + + if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) { writel_relaxed(l2x0_saved_regs.aux2_ctrl, - l2x0_base + TAUROS3_AUX2_CTRL); + base + TAUROS3_AUX2_CTRL); writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - l2x0_base + L2X0_PREFETCH_CTRL); - } + base + L2X0_PREFETCH_CTRL); - l2x0_resume(); + l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); + } } static const struct l2c_init_data of_tauros3_data __initconst = { From e2fe484a2dea3b88355bc1ba59be84436d78780b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0526/1983] ARM: l2c: simplify l2x0 unlocking code The l2x0 unlocking code is only called from l2x0_enable() now, so move the logic entirely into that function and simplify it. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index e3f4fcbcc88b77..157fd7ae331a92 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -352,30 +352,21 @@ static void l2x0_disable(void) raw_spin_unlock_irqrestore(&l2x0_lock, flags); } -static void l2x0_unlock(u32 cache_id) +static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) { - int lockregs; + unsigned id; - switch (cache_id & L2X0_CACHE_ID_PART_MASK) { - case L2X0_CACHE_ID_PART_L310: - lockregs = 8; - break; - default: - /* L210 and unknown types */ - lockregs = 1; - break; - } - - l2c_unlock(l2x0_base, lockregs); -} + id = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK; + if (id == L2X0_CACHE_ID_PART_L310) + num_lock = 8; + else + num_lock = 1; -static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) -{ /* l2x0 controller is disabled */ writel_relaxed(aux, base + L2X0_AUX_CTRL); /* Make sure that I&D is not locked down when starting */ - l2x0_unlock(readl_relaxed(base + L2X0_CACHE_ID)); + l2c_unlock(base, num_lock); l2x0_inv_all(); From e022c572c6e701cf50f07b7a7e2b90b5083d5d68 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0527/1983] ARM: l2c: move pl310_set_debug() into l2c-310 code Move the pl310_set_debug() into the l2c-310 code area, and don't hide it with ifdefs. Rename it to l2c310_set_debug(). Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 157fd7ae331a92..9586be73ca4f94 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -154,18 +154,11 @@ static inline void debug_writel(unsigned long val) if (outer_cache.set_debug) l2c_set_debug(l2x0_base, val); } - -static void pl310_set_debug(unsigned long val) -{ - writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); -} #else /* Optimised out for non-errata case */ static inline void debug_writel(unsigned long val) { } - -#define pl310_set_debug NULL #endif #ifdef CONFIG_PL310_ERRATA_588369 @@ -422,6 +415,11 @@ static const struct l2c_init_data l2x0_init_fns __initconst = { * Affects: store buffer * store buffer is not automatically drained. */ +static void l2c310_set_debug(unsigned long val) +{ + writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); +} + static void __init l2c310_save(void __iomem *base) { unsigned revision; @@ -488,7 +486,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, unsigned n = 0; if (revision <= L310_CACHE_ID_RTL_R3P0) - fns->set_debug = pl310_set_debug; + fns->set_debug = l2c310_set_debug; if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) && revision == L310_CACHE_ID_RTL_R3P0) { From 2e88f0c12907dd127a7ac83a307a2549dc9e91f7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:23 +0200 Subject: [PATCH 0528/1983] ARM: l2c: add L2C-210 specific handlers Add L2C-210 specific cache operation handlers. These are tailored to the requirements of the L2C-210 cache controller, which doesn't require any workarounds. We avoid using the way operations during normal operation, which means we can avoid locking: the only time we use the way operations are during initialisation, and when disabling the cache. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 123 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 9586be73ca4f94..d07fa4fc95a31a 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -388,6 +388,108 @@ static const struct l2c_init_data l2x0_init_fns __initconst = { }, }; +/* + * L2C-210 specific code. + * + * The L2C-2x0 PA, set/way and sync operations are atomic, but we must + * ensure that no background operation is running. The way operations + * are all background tasks. + * + * While a background operation is in progress, any new operation is + * ignored (unspecified whether this causes an error.) Thankfully, not + * used on SMP. + * + * Never has a different sync register other than L2X0_CACHE_SYNC, but + * we use sync_reg_offset here so we can share some of this with L2C-310. + */ +static void __l2c210_cache_sync(void __iomem *base) +{ + writel_relaxed(0, base + sync_reg_offset); +} + +static void __l2c210_op_pa_range(void __iomem *reg, unsigned long start, + unsigned long end) +{ + while (start < end) { + writel_relaxed(start, reg); + start += CACHE_LINE_SIZE; + } +} + +static void l2c210_inv_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + if (start & (CACHE_LINE_SIZE - 1)) { + start &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + + if (end & (CACHE_LINE_SIZE - 1)) { + end &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA); + } + + __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end); + __l2c210_cache_sync(base); +} + +static void l2c210_clean_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + start &= ~(CACHE_LINE_SIZE - 1); + __l2c210_op_pa_range(base + L2X0_CLEAN_LINE_PA, start, end); + __l2c210_cache_sync(base); +} + +static void l2c210_flush_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + start &= ~(CACHE_LINE_SIZE - 1); + __l2c210_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, start, end); + __l2c210_cache_sync(base); +} + +static void l2c210_flush_all(void) +{ + void __iomem *base = l2x0_base; + + BUG_ON(!irqs_disabled()); + + __l2c_op_way(base + L2X0_CLEAN_INV_WAY); + __l2c210_cache_sync(base); +} + +static void l2c210_sync(void) +{ + __l2c210_cache_sync(l2x0_base); +} + +static void l2c210_resume(void) +{ + void __iomem *base = l2x0_base; + + if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) + l2c_enable(base, l2x0_saved_regs.aux_ctrl, 1); +} + +static const struct l2c_init_data l2c210_data __initconst = { + .num_lock = 1, + .enable = l2c_enable, + .outer_cache = { + .inv_range = l2c210_inv_range, + .clean_range = l2c210_clean_range, + .flush_range = l2c210_flush_range, + .flush_all = l2c210_flush_all, + .disable = l2c_disable, + .sync = l2c210_sync, + .resume = l2c210_resume, + }, +}; + /* * L2C-310 specific code. * @@ -623,6 +725,10 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) data = &l2x0_init_fns; break; + case L2X0_CACHE_ID_PART_L210: + data = &l2c210_data; + break; + case L2X0_CACHE_ID_PART_L310: data = &l2c310_init_fns; break; @@ -672,6 +778,21 @@ static void __init l2x0_of_parse(const struct device_node *np, *aux_mask &= ~mask; } +static const struct l2c_init_data of_l2c210_data __initconst = { + .num_lock = 1, + .of_parse = l2x0_of_parse, + .enable = l2c_enable, + .outer_cache = { + .inv_range = l2c210_inv_range, + .clean_range = l2c210_clean_range, + .flush_range = l2c210_flush_range, + .flush_all = l2c210_flush_all, + .disable = l2c_disable, + .sync = l2c210_sync, + .resume = l2c210_resume, + }, +}; + static const struct l2c_init_data of_l2x0_data __initconst = { .of_parse = l2x0_of_parse, .enable = l2x0_enable, @@ -1117,7 +1238,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = { #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } static const struct of_device_id l2x0_ids[] __initconst = { - L2C_ID("arm,l210-cache", of_l2x0_data), + L2C_ID("arm,l210-cache", of_l2c210_data), L2C_ID("arm,l220-cache", of_l2x0_data), L2C_ID("arm,pl310-cache", of_pl310_data), L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), From 173a49c038753dba0900365f07351986b95bfa1d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0529/1983] ARM: l2c: implement L2C-310 erratum 727915 as a method override Implement L2C-310 erratum 727915 by overriding the flush_all method in the outer_cache operations structure. This allows us to sensibly contain the erratum code in one place without affecting other locations or implementations. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index d07fa4fc95a31a..6161232c8a850f 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -522,6 +522,19 @@ static void l2c310_set_debug(unsigned long val) writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); } +static void l2c310_flush_all_erratum(void) +{ + void __iomem *base = l2x0_base; + unsigned long flags; + + raw_spin_lock_irqsave(&l2x0_lock, flags); + l2c_set_debug(base, 0x03); + __l2c_op_way(base + L2X0_CLEAN_INV_WAY); + l2c_set_debug(base, 0x00); + __l2c210_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); +} + static void __init l2c310_save(void __iomem *base) { unsigned revision; @@ -590,6 +603,13 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, if (revision <= L310_CACHE_ID_RTL_R3P0) fns->set_debug = l2c310_set_debug; + if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) && + revision >= L310_CACHE_ID_RTL_R2P0 && + revision < L310_CACHE_ID_RTL_R3P1) { + fns->flush_all = l2c310_flush_all_erratum; + errata[n++] = "727915"; + } + if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) && revision == L310_CACHE_ID_RTL_R3P0) { sync_reg_offset = L2X0_DUMMY_REG; From 41dd04fd9507b4a756d78a9724162fb508024c8a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0530/1983] ARM: l2c: implement L2C-310 erratum 588369 as a method override Implement L2C-310 erratum 588369 by overriding the invalidate range and flush range methods in the outer_cache operations structure. This allows us to sensibly contain the erratum code in one place without affecting other locations/implemetations. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 6161232c8a850f..79ff08db204d97 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -522,6 +522,65 @@ static void l2c310_set_debug(unsigned long val) writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); } +static void l2c310_inv_range_erratum(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + + if ((start | end) & (CACHE_LINE_SIZE - 1)) { + unsigned long flags; + + /* Erratum 588369 for both clean+invalidate operations */ + raw_spin_lock_irqsave(&l2x0_lock, flags); + l2c_set_debug(base, 0x03); + + if (start & (CACHE_LINE_SIZE - 1)) { + start &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(start, base + L2X0_CLEAN_LINE_PA); + writel_relaxed(start, base + L2X0_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + + if (end & (CACHE_LINE_SIZE - 1)) { + end &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(end, base + L2X0_CLEAN_LINE_PA); + writel_relaxed(end, base + L2X0_INV_LINE_PA); + } + + l2c_set_debug(base, 0x00); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); + } + + __l2c210_op_pa_range(base + L2X0_INV_LINE_PA, start, end); + __l2c210_cache_sync(base); +} + +static void l2c310_flush_range_erratum(unsigned long start, unsigned long end) +{ + raw_spinlock_t *lock = &l2x0_lock; + unsigned long flags; + void __iomem *base = l2x0_base; + + raw_spin_lock_irqsave(lock, flags); + while (start < end) { + unsigned long blk_end = start + min(end - start, 4096UL); + + l2c_set_debug(base, 0x03); + while (start < blk_end) { + writel_relaxed(start, base + L2X0_CLEAN_LINE_PA); + writel_relaxed(start, base + L2X0_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + l2c_set_debug(base, 0x00); + + if (blk_end < end) { + raw_spin_unlock_irqrestore(lock, flags); + raw_spin_lock_irqsave(lock, flags); + } + } + raw_spin_unlock_irqrestore(lock, flags); + __l2c210_cache_sync(base); +} + static void l2c310_flush_all_erratum(void) { void __iomem *base = l2x0_base; @@ -600,9 +659,19 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, const char *errata[4]; unsigned n = 0; + /* For compatibility */ if (revision <= L310_CACHE_ID_RTL_R3P0) fns->set_debug = l2c310_set_debug; + if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) && + revision < L310_CACHE_ID_RTL_R2P0 && + /* For bcm compatibility */ + fns->inv_range == l2x0_inv_range) { + fns->inv_range = l2c310_inv_range_erratum; + fns->flush_range = l2c310_flush_range_erratum; + errata[n++] = "588369"; + } + if (IS_ENABLED(CONFIG_PL310_ERRATA_727915) && revision >= L310_CACHE_ID_RTL_R2P0 && revision < L310_CACHE_ID_RTL_R3P1) { From 9a5db095d9481d593f45224076fab237e0428590 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0531/1983] ARM: l2c: use L2C-210 handlers for L2C-310 errata-less implementations Where no errata affect the L2C-310 handlers, they are functionally equivalent to L2C-210. Re-use the L2C-210 handlers for the L2C-310 part. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 58 +++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 79ff08db204d97..49ddff972cb373 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -493,6 +493,18 @@ static const struct l2c_init_data l2c210_data __initconst = { /* * L2C-310 specific code. * + * Very similar to L2C-210, the PA, set/way and sync operations are atomic, + * and the way operations are all background tasks. However, issuing an + * operation while a background operation is in progress results in a + * SLVERR response. We can reuse: + * + * __l2c210_cache_sync (using sync_reg_offset) + * l2c210_sync + * l2c210_inv_range (if 588369 is not applicable) + * l2c210_clean_range + * l2c210_flush_range (if 588369 is not applicable) + * l2c210_flush_all (if 727915 is not applicable) + * * Errata: * 588369: PL310 R0P0->R1P0, fixed R2P0. * Affects: all clean+invalidate operations @@ -666,7 +678,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) && revision < L310_CACHE_ID_RTL_R2P0 && /* For bcm compatibility */ - fns->inv_range == l2x0_inv_range) { + fns->inv_range == l2c210_inv_range) { fns->inv_range = l2c310_inv_range_erratum; fns->flush_range = l2c310_flush_range_erratum; errata[n++] = "588369"; @@ -704,12 +716,13 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .fixup = l2c310_fixup, .save = l2c310_save, .outer_cache = { - .inv_range = l2x0_inv_range, - .clean_range = l2x0_clean_range, - .flush_range = l2x0_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, + .inv_range = l2c210_inv_range, + .clean_range = l2c210_clean_range, + .flush_range = l2c210_flush_range, + .flush_all = l2c210_flush_all, + .disable = l2c_disable, + .sync = l2c210_sync, + .set_debug = l2c310_set_debug, .resume = l2c310_resume, }, }; @@ -896,8 +909,8 @@ static const struct l2c_init_data of_l2x0_data __initconst = { }, }; -static void __init pl310_of_parse(const struct device_node *np, - u32 *aux_val, u32 *aux_mask) +static void __init l2c310_of_parse(const struct device_node *np, + u32 *aux_val, u32 *aux_mask) { u32 data[3] = { 0, 0, 0 }; u32 tag[3] = { 0, 0, 0 }; @@ -930,19 +943,20 @@ static void __init pl310_of_parse(const struct device_node *np, } } -static const struct l2c_init_data of_pl310_data __initconst = { +static const struct l2c_init_data of_l2c310_data __initconst = { .num_lock = 8, - .of_parse = pl310_of_parse, + .of_parse = l2c310_of_parse, .enable = l2c_enable, .fixup = l2c310_fixup, .save = l2c310_save, .outer_cache = { - .inv_range = l2x0_inv_range, - .clean_range = l2x0_clean_range, - .flush_range = l2x0_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, + .inv_range = l2c210_inv_range, + .clean_range = l2c210_clean_range, + .flush_range = l2c210_flush_range, + .flush_all = l2c210_flush_all, + .disable = l2c_disable, + .sync = l2c210_sync, + .set_debug = l2c310_set_debug, .resume = l2c310_resume, }, }; @@ -1278,7 +1292,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end) static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .num_lock = 8, - .of_parse = pl310_of_parse, + .of_parse = l2c310_of_parse, .enable = l2c_enable, .fixup = l2c310_fixup, .save = l2c310_save, @@ -1286,9 +1300,9 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .inv_range = bcm_inv_range, .clean_range = bcm_clean_range, .flush_range = bcm_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, + .flush_all = l2c210_flush_all, + .disable = l2c_disable, + .sync = l2c210_sync, .resume = l2c310_resume, }, }; @@ -1329,7 +1343,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = { static const struct of_device_id l2x0_ids[] __initconst = { L2C_ID("arm,l210-cache", of_l2c210_data), L2C_ID("arm,l220-cache", of_l2x0_data), - L2C_ID("arm,pl310-cache", of_pl310_data), + L2C_ID("arm,pl310-cache", of_l2c310_data), L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data), L2C_ID("marvell,aurora-system-cache", of_aurora_no_outer_data), From 537c81bc085356ca83bad2a5105fe14a3fab77de Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0532/1983] ARM: l2c: add L2C-220 specific handlers The L2C-220 is different from the L2C-210 and L2C-310 in that every operation is a background operation: this means we have to use spinlocks to protect all operations, and we have to wait for every operation to complete. Should a second operation be attempted while a previous operation is in progress, the response will be an imprecise abort. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 167 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 157 insertions(+), 10 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 49ddff972cb373..751c3d7a22b38c 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -490,6 +490,148 @@ static const struct l2c_init_data l2c210_data __initconst = { }, }; +/* + * L2C-220 specific code. + * + * All operations are background operations: they have to be waited for. + * Conflicting requests generate a slave error (which will cause an + * imprecise abort.) Never uses sync_reg_offset, so we hard-code the + * sync register here. + * + * However, we can re-use the l2c210_resume call. + */ +static inline void __l2c220_cache_sync(void __iomem *base) +{ + writel_relaxed(0, base + L2X0_CACHE_SYNC); + l2c_wait_mask(base + L2X0_CACHE_SYNC, 1); +} + +static void l2c220_op_way(void __iomem *base, unsigned reg) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&l2x0_lock, flags); + __l2c_op_way(base + reg); + __l2c220_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); +} + +static unsigned long l2c220_op_pa_range(void __iomem *reg, unsigned long start, + unsigned long end, unsigned long flags) +{ + raw_spinlock_t *lock = &l2x0_lock; + + while (start < end) { + unsigned long blk_end = start + min(end - start, 4096UL); + + while (start < blk_end) { + l2c_wait_mask(reg, 1); + writel_relaxed(start, reg); + start += CACHE_LINE_SIZE; + } + + if (blk_end < end) { + raw_spin_unlock_irqrestore(lock, flags); + raw_spin_lock_irqsave(lock, flags); + } + } + + return flags; +} + +static void l2c220_inv_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + unsigned long flags; + + raw_spin_lock_irqsave(&l2x0_lock, flags); + if ((start | end) & (CACHE_LINE_SIZE - 1)) { + if (start & (CACHE_LINE_SIZE - 1)) { + start &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(start, base + L2X0_CLEAN_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + + if (end & (CACHE_LINE_SIZE - 1)) { + end &= ~(CACHE_LINE_SIZE - 1); + l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); + writel_relaxed(end, base + L2X0_CLEAN_INV_LINE_PA); + } + } + + flags = l2c220_op_pa_range(base + L2X0_INV_LINE_PA, + start, end, flags); + l2c_wait_mask(base + L2X0_INV_LINE_PA, 1); + __l2c220_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); +} + +static void l2c220_clean_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + unsigned long flags; + + start &= ~(CACHE_LINE_SIZE - 1); + if ((end - start) >= l2x0_size) { + l2c220_op_way(base, L2X0_CLEAN_WAY); + return; + } + + raw_spin_lock_irqsave(&l2x0_lock, flags); + flags = l2c220_op_pa_range(base + L2X0_CLEAN_LINE_PA, + start, end, flags); + l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); + __l2c220_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); +} + +static void l2c220_flush_range(unsigned long start, unsigned long end) +{ + void __iomem *base = l2x0_base; + unsigned long flags; + + start &= ~(CACHE_LINE_SIZE - 1); + if ((end - start) >= l2x0_size) { + l2c220_op_way(base, L2X0_CLEAN_INV_WAY); + return; + } + + raw_spin_lock_irqsave(&l2x0_lock, flags); + flags = l2c220_op_pa_range(base + L2X0_CLEAN_INV_LINE_PA, + start, end, flags); + l2c_wait_mask(base + L2X0_CLEAN_INV_LINE_PA, 1); + __l2c220_cache_sync(base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); +} + +static void l2c220_flush_all(void) +{ + l2c220_op_way(l2x0_base, L2X0_CLEAN_INV_WAY); +} + +static void l2c220_sync(void) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&l2x0_lock, flags); + __l2c220_cache_sync(l2x0_base); + raw_spin_unlock_irqrestore(&l2x0_lock, flags); +} + +static const struct l2c_init_data l2c220_data = { + .num_lock = 1, + .enable = l2c_enable, + .outer_cache = { + .inv_range = l2c220_inv_range, + .clean_range = l2c220_clean_range, + .flush_range = l2c220_flush_range, + .flush_all = l2c220_flush_all, + .disable = l2c_disable, + .sync = l2c220_sync, + .resume = l2c210_resume, + }, +}; + /* * L2C-310 specific code. * @@ -831,6 +973,10 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) data = &l2c210_data; break; + case L2X0_CACHE_ID_PART_L220: + data = &l2c220_data; + break; + case L2X0_CACHE_ID_PART_L310: data = &l2c310_init_fns; break; @@ -895,17 +1041,18 @@ static const struct l2c_init_data of_l2c210_data __initconst = { }, }; -static const struct l2c_init_data of_l2x0_data __initconst = { +static const struct l2c_init_data of_l2c220_data __initconst = { + .num_lock = 1, .of_parse = l2x0_of_parse, - .enable = l2x0_enable, + .enable = l2c_enable, .outer_cache = { - .inv_range = l2x0_inv_range, - .clean_range = l2x0_clean_range, - .flush_range = l2x0_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, - .resume = l2x0_resume, + .inv_range = l2c220_inv_range, + .clean_range = l2c220_clean_range, + .flush_range = l2c220_flush_range, + .flush_all = l2c220_flush_all, + .disable = l2c_disable, + .sync = l2c220_sync, + .resume = l2c210_resume, }, }; @@ -1342,7 +1489,7 @@ static const struct l2c_init_data of_tauros3_data __initconst = { #define L2C_ID(name, fns) { .compatible = name, .data = (void *)&fns } static const struct of_device_id l2x0_ids[] __initconst = { L2C_ID("arm,l210-cache", of_l2c210_data), - L2C_ID("arm,l220-cache", of_l2x0_data), + L2C_ID("arm,l220-cache", of_l2c220_data), L2C_ID("arm,pl310-cache", of_l2c310_data), L2C_ID("brcm,bcm11351-a2-pl310-cache", of_bcm_l2x0_data), L2C_ID("marvell,aurora-outer-cache", of_aurora_with_outer_data), From fc069aecc73009a83fea3c35c838c88588f852e7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0533/1983] ARM: l2c: convert Broadcom L2C-310 to new code The Broadcom L2C-310 devices use ARMs L2C-310 R2P3 or later. These require no errata workarounds, and so we can directly call the l2c210 functions from their methods. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 751c3d7a22b38c..57680e03da848a 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -1360,16 +1360,16 @@ static void bcm_inv_range(unsigned long start, unsigned long end) /* normal case, no cross section between start and end */ if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { - l2x0_inv_range(new_start, new_end); + l2c210_inv_range(new_start, new_end); return; } /* They cross sections, so it can only be a cross from section * 2 to section 3 */ - l2x0_inv_range(new_start, + l2c210_inv_range(new_start, bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); - l2x0_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + l2c210_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), new_end); } @@ -1382,26 +1382,21 @@ static void bcm_clean_range(unsigned long start, unsigned long end) if (unlikely(end <= start)) return; - if ((end - start) >= l2x0_size) { - l2x0_clean_all(); - return; - } - new_start = bcm_l2_phys_addr(start); new_end = bcm_l2_phys_addr(end); /* normal case, no cross section between start and end */ if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { - l2x0_clean_range(new_start, new_end); + l2c210_clean_range(new_start, new_end); return; } /* They cross sections, so it can only be a cross from section * 2 to section 3 */ - l2x0_clean_range(new_start, + l2c210_clean_range(new_start, bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); - l2x0_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + l2c210_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), new_end); } @@ -1415,7 +1410,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end) return; if ((end - start) >= l2x0_size) { - l2x0_flush_all(); + outer_cache.flush_all(); return; } @@ -1424,24 +1419,24 @@ static void bcm_flush_range(unsigned long start, unsigned long end) /* normal case, no cross section between start and end */ if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { - l2x0_flush_range(new_start, new_end); + l2c210_flush_range(new_start, new_end); return; } /* They cross sections, so it can only be a cross from section * 2 to section 3 */ - l2x0_flush_range(new_start, + l2c210_flush_range(new_start, bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); - l2x0_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + l2c210_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), new_end); } +/* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .num_lock = 8, .of_parse = l2c310_of_parse, .enable = l2c_enable, - .fixup = l2c310_fixup, .save = l2c310_save, .outer_cache = { .inv_range = bcm_inv_range, From 95790b012d18900ed78cb1644060a328dc4b46a8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0534/1983] ARM: l2c: remove obsolete l2x0 ops for non-OF init non-OF initialisation has never been used with any cache controller which isn't an ARM cache controller, so we can safely get rid of the old (and buggy) l2x0_*-based operations structure. This is also the last reference to: - l2x0_clean_line() - l2x0_inv_line() - l2x0_flush_line() - l2x0_flush_all() - l2x0_clean_all() - l2x0_inv_all() - l2x0_inv_range() - l2x0_clean_range() - l2x0_flush_range() - l2x0_enable() - l2x0_resume() so kill those functions too. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 206 --------------------------------------- 1 file changed, 206 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 57680e03da848a..c5d754912f9648 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -134,20 +134,6 @@ static inline void cache_sync(void) cache_wait(base + L2X0_CACHE_SYNC, 1); } -static inline void l2x0_clean_line(unsigned long addr) -{ - void __iomem *base = l2x0_base; - cache_wait(base + L2X0_CLEAN_LINE_PA, 1); - writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA); -} - -static inline void l2x0_inv_line(unsigned long addr) -{ - void __iomem *base = l2x0_base; - cache_wait(base + L2X0_INV_LINE_PA, 1); - writel_relaxed(addr, base + L2X0_INV_LINE_PA); -} - #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) static inline void debug_writel(unsigned long val) { @@ -161,27 +147,6 @@ static inline void debug_writel(unsigned long val) } #endif -#ifdef CONFIG_PL310_ERRATA_588369 -static inline void l2x0_flush_line(unsigned long addr) -{ - void __iomem *base = l2x0_base; - - /* Clean by PA followed by Invalidate by PA */ - cache_wait(base + L2X0_CLEAN_LINE_PA, 1); - writel_relaxed(addr, base + L2X0_CLEAN_LINE_PA); - cache_wait(base + L2X0_INV_LINE_PA, 1); - writel_relaxed(addr, base + L2X0_INV_LINE_PA); -} -#else - -static inline void l2x0_flush_line(unsigned long addr) -{ - void __iomem *base = l2x0_base; - cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1); - writel_relaxed(addr, base + L2X0_CLEAN_INV_LINE_PA); -} -#endif - static void l2x0_cache_sync(void) { unsigned long flags; @@ -209,131 +174,6 @@ static void l2x0_flush_all(void) raw_spin_unlock_irqrestore(&l2x0_lock, flags); } -static void l2x0_clean_all(void) -{ - unsigned long flags; - - /* clean all ways */ - raw_spin_lock_irqsave(&l2x0_lock, flags); - __l2c_op_way(l2x0_base + L2X0_CLEAN_WAY); - cache_sync(); - raw_spin_unlock_irqrestore(&l2x0_lock, flags); -} - -static void l2x0_inv_all(void) -{ - unsigned long flags; - - /* invalidate all ways */ - raw_spin_lock_irqsave(&l2x0_lock, flags); - /* Invalidating when L2 is enabled is a nono */ - BUG_ON(readl(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN); - __l2c_op_way(l2x0_base + L2X0_INV_WAY); - cache_sync(); - raw_spin_unlock_irqrestore(&l2x0_lock, flags); -} - -static void l2x0_inv_range(unsigned long start, unsigned long end) -{ - void __iomem *base = l2x0_base; - unsigned long flags; - - raw_spin_lock_irqsave(&l2x0_lock, flags); - if (start & (CACHE_LINE_SIZE - 1)) { - start &= ~(CACHE_LINE_SIZE - 1); - debug_writel(0x03); - l2x0_flush_line(start); - debug_writel(0x00); - start += CACHE_LINE_SIZE; - } - - if (end & (CACHE_LINE_SIZE - 1)) { - end &= ~(CACHE_LINE_SIZE - 1); - debug_writel(0x03); - l2x0_flush_line(end); - debug_writel(0x00); - } - - while (start < end) { - unsigned long blk_end = start + min(end - start, 4096UL); - - while (start < blk_end) { - l2x0_inv_line(start); - start += CACHE_LINE_SIZE; - } - - if (blk_end < end) { - raw_spin_unlock_irqrestore(&l2x0_lock, flags); - raw_spin_lock_irqsave(&l2x0_lock, flags); - } - } - cache_wait(base + L2X0_INV_LINE_PA, 1); - cache_sync(); - raw_spin_unlock_irqrestore(&l2x0_lock, flags); -} - -static void l2x0_clean_range(unsigned long start, unsigned long end) -{ - void __iomem *base = l2x0_base; - unsigned long flags; - - if ((end - start) >= l2x0_size) { - l2x0_clean_all(); - return; - } - - raw_spin_lock_irqsave(&l2x0_lock, flags); - start &= ~(CACHE_LINE_SIZE - 1); - while (start < end) { - unsigned long blk_end = start + min(end - start, 4096UL); - - while (start < blk_end) { - l2x0_clean_line(start); - start += CACHE_LINE_SIZE; - } - - if (blk_end < end) { - raw_spin_unlock_irqrestore(&l2x0_lock, flags); - raw_spin_lock_irqsave(&l2x0_lock, flags); - } - } - cache_wait(base + L2X0_CLEAN_LINE_PA, 1); - cache_sync(); - raw_spin_unlock_irqrestore(&l2x0_lock, flags); -} - -static void l2x0_flush_range(unsigned long start, unsigned long end) -{ - void __iomem *base = l2x0_base; - unsigned long flags; - - if ((end - start) >= l2x0_size) { - l2x0_flush_all(); - return; - } - - raw_spin_lock_irqsave(&l2x0_lock, flags); - start &= ~(CACHE_LINE_SIZE - 1); - while (start < end) { - unsigned long blk_end = start + min(end - start, 4096UL); - - debug_writel(0x03); - while (start < blk_end) { - l2x0_flush_line(start); - start += CACHE_LINE_SIZE; - } - debug_writel(0x00); - - if (blk_end < end) { - raw_spin_unlock_irqrestore(&l2x0_lock, flags); - raw_spin_lock_irqsave(&l2x0_lock, flags); - } - } - cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1); - cache_sync(); - raw_spin_unlock_irqrestore(&l2x0_lock, flags); -} - static void l2x0_disable(void) { unsigned long flags; @@ -345,49 +185,6 @@ static void l2x0_disable(void) raw_spin_unlock_irqrestore(&l2x0_lock, flags); } -static void l2x0_enable(void __iomem *base, u32 aux, unsigned num_lock) -{ - unsigned id; - - id = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK; - if (id == L2X0_CACHE_ID_PART_L310) - num_lock = 8; - else - num_lock = 1; - - /* l2x0 controller is disabled */ - writel_relaxed(aux, base + L2X0_AUX_CTRL); - - /* Make sure that I&D is not locked down when starting */ - l2c_unlock(base, num_lock); - - l2x0_inv_all(); - - /* enable L2X0 */ - writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL); -} - -static void l2x0_resume(void) -{ - void __iomem *base = l2x0_base; - - if (!(readl_relaxed(base + L2X0_CTRL) & L2X0_CTRL_EN)) - l2x0_enable(base, l2x0_saved_regs.aux_ctrl, 0); -} - -static const struct l2c_init_data l2x0_init_fns __initconst = { - .enable = l2x0_enable, - .outer_cache = { - .inv_range = l2x0_inv_range, - .clean_range = l2x0_clean_range, - .flush_range = l2x0_flush_range, - .flush_all = l2x0_flush_all, - .disable = l2x0_disable, - .sync = l2x0_cache_sync, - .resume = l2x0_resume, - }, -}; - /* * L2C-210 specific code. * @@ -966,9 +763,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) switch (cache_id & L2X0_CACHE_ID_PART_MASK) { default: - data = &l2x0_init_fns; - break; - case L2X0_CACHE_ID_PART_L210: data = &l2c210_data; break; From 2d5e3cc44080363f28f04613ca5415bee1295d15 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0535/1983] ARM: l2c: move type string into l2c_init_data structure Rather than decoding this from the ID register, store it in the l2c_init_data structure. This simplifies things some more, and allows us to better provide further details as to how we're driving the cache. We print the cache ID value anyway should we need to precisely identify the cache hardware. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index c5d754912f9648..b4dd2f4b491b7d 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -29,6 +29,7 @@ #include "cache-aurora-l2.h" struct l2c_init_data { + const char *type; unsigned num_lock; void (*of_parse)(const struct device_node *, u32 *, u32 *); void (*enable)(void __iomem *, u32, unsigned); @@ -274,6 +275,7 @@ static void l2c210_resume(void) } static const struct l2c_init_data l2c210_data __initconst = { + .type = "L2C-210", .num_lock = 1, .enable = l2c_enable, .outer_cache = { @@ -416,6 +418,7 @@ static void l2c220_sync(void) } static const struct l2c_init_data l2c220_data = { + .type = "L2C-220", .num_lock = 1, .enable = l2c_enable, .outer_cache = { @@ -650,6 +653,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, } static const struct l2c_init_data l2c310_init_fns __initconst = { + .type = "L2C-310", .num_lock = 8, .enable = l2c_enable, .fixup = l2c310_fixup, @@ -674,7 +678,6 @@ static void __init __l2c_init(const struct l2c_init_data *data, u32 way_size = 0; int ways; int way_size_shift = L2X0_WAY_SIZE_SHIFT; - const char *type; /* * It is strange to save the register state before initialisation, @@ -695,25 +698,21 @@ static void __init __l2c_init(const struct l2c_init_data *data, ways = 16; else ways = 8; - type = "L310"; break; case L2X0_CACHE_ID_PART_L210: ways = (aux >> 13) & 0xf; - type = "L210"; break; case AURORA_CACHE_ID: ways = (aux >> 13) & 0xf; ways = 2 << ((ways + 1) >> 2); way_size_shift = AURORA_WAY_SIZE_SHIFT; - type = "Aurora"; break; default: /* Assume unknown chips have 8 ways */ ways = 8; - type = "L2x0 series"; break; } @@ -747,9 +746,9 @@ static void __init __l2c_init(const struct l2c_init_data *data, outer_cache = fns; pr_info("%s cache controller enabled, %d ways, %d kB\n", - type, ways, l2x0_size >> 10); + data->type, ways, l2x0_size >> 10); pr_info("%s: CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n", - type, cache_id, aux); + data->type, cache_id, aux); } void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask) @@ -821,6 +820,7 @@ static void __init l2x0_of_parse(const struct device_node *np, } static const struct l2c_init_data of_l2c210_data __initconst = { + .type = "L2C-210", .num_lock = 1, .of_parse = l2x0_of_parse, .enable = l2c_enable, @@ -836,6 +836,7 @@ static const struct l2c_init_data of_l2c210_data __initconst = { }; static const struct l2c_init_data of_l2c220_data __initconst = { + .type = "L2C-220", .num_lock = 1, .of_parse = l2x0_of_parse, .enable = l2c_enable, @@ -885,6 +886,7 @@ static void __init l2c310_of_parse(const struct device_node *np, } static const struct l2c_init_data of_l2c310_data __initconst = { + .type = "L2C-310", .num_lock = 8, .of_parse = l2c310_of_parse, .enable = l2c_enable, @@ -1063,6 +1065,7 @@ static void __init aurora_of_parse(const struct device_node *np, } static const struct l2c_init_data of_aurora_with_outer_data __initconst = { + .type = "Aurora", .num_lock = 4, .of_parse = aurora_of_parse, .enable = l2c_enable, @@ -1080,6 +1083,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = { }; static const struct l2c_init_data of_aurora_no_outer_data __initconst = { + .type = "Aurora", .num_lock = 4, .of_parse = aurora_of_parse, .enable = aurora_enable_no_outer, @@ -1228,6 +1232,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end) /* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { + .type = "BCM-L2C-310", .num_lock = 8, .of_parse = l2c310_of_parse, .enable = l2c_enable, @@ -1266,6 +1271,7 @@ static void tauros3_resume(void) } static const struct l2c_init_data of_tauros3_data __initconst = { + .type = "Tauros3", .num_lock = 8, .enable = l2c_enable, .save = tauros3_save, From 85f0f362c6bece4f24f317f53d957da6c17e3884 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0536/1983] ARM: l2c: add decode for L2C-220 cache ways Rather than assuming these are always 8-way, it can be decoded from the auxillary register in the same manner as L2C-210. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b4dd2f4b491b7d..69a18316b23936 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -701,6 +701,7 @@ static void __init __l2c_init(const struct l2c_init_data *data, break; case L2X0_CACHE_ID_PART_L210: + case L2X0_CACHE_ID_PART_L220: ways = (aux >> 13) & 0xf; break; From 12a1a3d387716c769e67205a1f3e78888f168dc3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0537/1983] ARM: l2c: move way size calculation data into l2c_init_data Move the way size calculation data (base of way size) out of the switch statement into the provided initialisation data. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 69a18316b23936..b4d373ab1a5ce5 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -30,6 +30,7 @@ struct l2c_init_data { const char *type; + unsigned way_size_0; unsigned num_lock; void (*of_parse)(const struct device_node *, u32 *, u32 *); void (*enable)(void __iomem *, u32, unsigned); @@ -276,6 +277,7 @@ static void l2c210_resume(void) static const struct l2c_init_data l2c210_data __initconst = { .type = "L2C-210", + .way_size_0 = SZ_8K, .num_lock = 1, .enable = l2c_enable, .outer_cache = { @@ -419,6 +421,7 @@ static void l2c220_sync(void) static const struct l2c_init_data l2c220_data = { .type = "L2C-220", + .way_size_0 = SZ_8K, .num_lock = 1, .enable = l2c_enable, .outer_cache = { @@ -654,6 +657,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, static const struct l2c_init_data l2c310_init_fns __initconst = { .type = "L2C-310", + .way_size_0 = SZ_8K, .num_lock = 8, .enable = l2c_enable, .fixup = l2c310_fixup, @@ -674,10 +678,8 @@ static void __init __l2c_init(const struct l2c_init_data *data, u32 aux_val, u32 aux_mask, u32 cache_id) { struct outer_cache_fns fns; + unsigned way_size_bits, ways; u32 aux; - u32 way_size = 0; - int ways; - int way_size_shift = L2X0_WAY_SIZE_SHIFT; /* * It is strange to save the register state before initialisation, @@ -708,7 +710,6 @@ static void __init __l2c_init(const struct l2c_init_data *data, case AURORA_CACHE_ID: ways = (aux >> 13) & 0xf; ways = 2 << ((ways + 1) >> 2); - way_size_shift = AURORA_WAY_SIZE_SHIFT; break; default: @@ -720,12 +721,15 @@ static void __init __l2c_init(const struct l2c_init_data *data, l2x0_way_mask = (1 << ways) - 1; /* - * L2 cache Size = Way size * Number of ways + * way_size_0 is the size that a way_size value of zero would be + * given the calculation: way_size = way_size_0 << way_size_bits. + * So, if way_size_bits=0 is reserved, but way_size_bits=1 is 16k, + * then way_size_0 would be 8k. + * + * L2 cache size = number of ways * way size. */ - way_size = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; - way_size = 1 << (way_size + way_size_shift); - - l2x0_size = ways * way_size * SZ_1K; + way_size_bits = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; + l2x0_size = ways * (data->way_size_0 << way_size_bits); fns = data->outer_cache; if (data->fixup) @@ -822,6 +826,7 @@ static void __init l2x0_of_parse(const struct device_node *np, static const struct l2c_init_data of_l2c210_data __initconst = { .type = "L2C-210", + .way_size_0 = SZ_8K, .num_lock = 1, .of_parse = l2x0_of_parse, .enable = l2c_enable, @@ -838,6 +843,7 @@ static const struct l2c_init_data of_l2c210_data __initconst = { static const struct l2c_init_data of_l2c220_data __initconst = { .type = "L2C-220", + .way_size_0 = SZ_8K, .num_lock = 1, .of_parse = l2x0_of_parse, .enable = l2c_enable, @@ -888,6 +894,7 @@ static void __init l2c310_of_parse(const struct device_node *np, static const struct l2c_init_data of_l2c310_data __initconst = { .type = "L2C-310", + .way_size_0 = SZ_8K, .num_lock = 8, .of_parse = l2c310_of_parse, .enable = l2c_enable, @@ -1067,6 +1074,7 @@ static void __init aurora_of_parse(const struct device_node *np, static const struct l2c_init_data of_aurora_with_outer_data __initconst = { .type = "Aurora", + .way_size_0 = SZ_4K, .num_lock = 4, .of_parse = aurora_of_parse, .enable = l2c_enable, @@ -1085,6 +1093,7 @@ static const struct l2c_init_data of_aurora_with_outer_data __initconst = { static const struct l2c_init_data of_aurora_no_outer_data __initconst = { .type = "Aurora", + .way_size_0 = SZ_4K, .num_lock = 4, .of_parse = aurora_of_parse, .enable = aurora_enable_no_outer, @@ -1234,6 +1243,7 @@ static void bcm_flush_range(unsigned long start, unsigned long end) /* Broadcom L2C-310 start from ARMs R3P2 or later, and require no fixups */ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .type = "BCM-L2C-310", + .way_size_0 = SZ_8K, .num_lock = 8, .of_parse = l2c310_of_parse, .enable = l2c_enable, @@ -1273,6 +1283,7 @@ static void tauros3_resume(void) static const struct l2c_init_data of_tauros3_data __initconst = { .type = "Tauros3", + .way_size_0 = SZ_8K, .num_lock = 8, .enable = l2c_enable, .save = tauros3_save, From 63d26dbf8a4967a0b0556ca8bfa46c7041e2c5ea Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0538/1983] ARM: l2c: move errata configuration options to arch/arm/mm/Kconfig Move the L2C-310 errata configuration options to arch/arm/mm/Kconfig along side the option which enables support for this device. Signed-off-by: Russell King --- arch/arm/Kconfig | 51 --------------------------------------------- arch/arm/mm/Kconfig | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ff4f55b70d52a1..7aaf7fc20382ae 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1213,19 +1213,6 @@ config ARM_ERRATA_742231 register of the Cortex-A9 which reduces the linefill issuing capabilities of the processor. -config PL310_ERRATA_588369 - bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines" - depends on CACHE_L2X0 - help - The PL310 L2 cache controller implements three types of Clean & - Invalidate maintenance operations: by Physical Address - (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC). - They are architecturally defined to behave as the execution of a - clean operation followed immediately by an invalidate operation, - both performing to the same memory location. This functionality - is not correctly implemented in PL310 as clean lines are not - invalidated as a result of these operations. - config ARM_ERRATA_643719 bool "ARM errata: LoUIS bit field in CLIDR register is incorrect" depends on CPU_V7 && SMP @@ -1248,17 +1235,6 @@ config ARM_ERRATA_720789 tables. The workaround changes the TLB flushing routines to invalidate entries regardless of the ASID. -config PL310_ERRATA_727915 - bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption" - depends on CACHE_L2X0 - help - PL310 implements the Clean & Invalidate by Way L2 cache maintenance - operation (offset 0x7FC). This operation runs in background so that - PL310 can handle normal accesses while it is in progress. Under very - rare circumstances, due to this erratum, write data can be lost when - PL310 treats a cacheable write transaction during a Clean & - Invalidate by Way operation. - config ARM_ERRATA_743622 bool "ARM errata: Faulty hazard checking in the Store Buffer may lead to data corruption" depends on CPU_V7 @@ -1284,21 +1260,6 @@ config ARM_ERRATA_751472 operation is received by a CPU before the ICIALLUIS has completed, potentially leading to corrupted entries in the cache or TLB. -config PL310_ERRATA_753970 - bool "PL310 errata: cache sync operation may be faulty" - depends on CACHE_PL310 - help - This option enables the workaround for the 753970 PL310 (r3p0) erratum. - - Under some condition the effect of cache sync operation on - the store buffer still remains when the operation completes. - This means that the store buffer is always asked to drain and - this prevents it from merging any further writes. The workaround - is to replace the normal offset of cache sync operation (0x730) - by another offset targeting an unmapped PL310 register 0x740. - This has the same effect as the cache sync operation: store buffer - drain and waiting for all buffers empty. - config ARM_ERRATA_754322 bool "ARM errata: possible faulty MMU translations following an ASID switch" depends on CPU_V7 @@ -1347,18 +1308,6 @@ config ARM_ERRATA_764369 relevant cache maintenance functions and sets a specific bit in the diagnostic control register of the SCU. -config PL310_ERRATA_769419 - bool "PL310 errata: no automatic Store Buffer drain" - depends on CACHE_L2X0 - help - On revisions of the PL310 prior to r3p2, the Store Buffer does - not automatically drain. This can cause normal, non-cacheable - writes to be retained when the memory system is idle, leading - to suboptimal I/O performance for drivers using coherent DMA. - This option adds a write barrier to the cpu_idle loop so that, - on systems with an outer cache, the store buffer is drained - explicitly. - config ARM_ERRATA_775420 bool "ARM errata: A data cache maintenance operation which aborts, might lead to deadlock" depends on CPU_V7 diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index e9c290c21744eb..57b96502c3c059 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -898,6 +898,57 @@ config CACHE_PL310 This option enables optimisations for the PL310 cache controller. +config PL310_ERRATA_588369 + bool "PL310 errata: Clean & Invalidate maintenance operations do not invalidate clean lines" + depends on CACHE_L2X0 + help + The PL310 L2 cache controller implements three types of Clean & + Invalidate maintenance operations: by Physical Address + (offset 0x7F0), by Index/Way (0x7F8) and by Way (0x7FC). + They are architecturally defined to behave as the execution of a + clean operation followed immediately by an invalidate operation, + both performing to the same memory location. This functionality + is not correctly implemented in PL310 as clean lines are not + invalidated as a result of these operations. + +config PL310_ERRATA_727915 + bool "PL310 errata: Background Clean & Invalidate by Way operation can cause data corruption" + depends on CACHE_L2X0 + help + PL310 implements the Clean & Invalidate by Way L2 cache maintenance + operation (offset 0x7FC). This operation runs in background so that + PL310 can handle normal accesses while it is in progress. Under very + rare circumstances, due to this erratum, write data can be lost when + PL310 treats a cacheable write transaction during a Clean & + Invalidate by Way operation. + +config PL310_ERRATA_753970 + bool "PL310 errata: cache sync operation may be faulty" + depends on CACHE_PL310 + help + This option enables the workaround for the 753970 PL310 (r3p0) erratum. + + Under some condition the effect of cache sync operation on + the store buffer still remains when the operation completes. + This means that the store buffer is always asked to drain and + this prevents it from merging any further writes. The workaround + is to replace the normal offset of cache sync operation (0x730) + by another offset targeting an unmapped PL310 register 0x740. + This has the same effect as the cache sync operation: store buffer + drain and waiting for all buffers empty. + +config PL310_ERRATA_769419 + bool "PL310 errata: no automatic Store Buffer drain" + depends on CACHE_L2X0 + help + On revisions of the PL310 prior to r3p2, the Store Buffer does + not automatically drain. This can cause normal, non-cacheable + writes to be retained when the memory system is idle, leading + to suboptimal I/O performance for drivers using coherent DMA. + This option adds a write barrier to the cpu_idle loop so that, + on systems with an outer cache, the store buffer is drained + explicitly. + config CACHE_TAUROS2 bool "Enable the Tauros2 L2 cache controller" depends on (ARCH_DOVE || ARCH_MMP || CPU_PJ4) From 6145fcc68c64bc460080ae1a463d4546b56b3d8a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0539/1983] ARM: l2c: provide generic hook to intercept writes to secure registers When Linux is running in the non-secure world, any write to a secure L2C register will generate an abort. Platforms normally have to call firmware to work around this. Provide a hook for them to intercept any L2C secure register write. l2c_write_sec() avoids writes to secure registers which are already set to the appropriate value, thus avoiding the overhead of needlessly calling into the secure monitor. Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 5 +++- arch/arm/mm/cache-l2x0.c | 42 ++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index e9a0797fe18890..5066fa2f73abe0 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -33,8 +33,11 @@ struct outer_cache_fns { #ifdef CONFIG_OUTER_CACHE_SYNC void (*sync)(void); #endif - void (*set_debug)(unsigned long); void (*resume)(void); + + /* This is an ARM L2C thing */ + void (*set_debug)(unsigned long); + void (*write_sec)(unsigned long, unsigned); }; extern struct outer_cache_fns outer_cache; diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b4d373ab1a5ce5..369a9d01d94f72 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -59,6 +59,20 @@ static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask) cpu_relax(); } +/* + * By default, we write directly to secure registers. Platforms must + * override this if they are running non-secure. + */ +static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg) +{ + if (val == readl_relaxed(base + reg)) + return; + if (outer_cache.write_sec) + outer_cache.write_sec(val, reg); + else + writel_relaxed(val, base + reg); +} + /* * This should only be called when we have a requirement that the * register be written due to a work-around, as platforms running @@ -66,7 +80,10 @@ static inline void l2c_wait_mask(void __iomem *reg, unsigned long mask) */ static inline void l2c_set_debug(void __iomem *base, unsigned long val) { - outer_cache.set_debug(val); + if (outer_cache.set_debug) + outer_cache.set_debug(val); + else + l2c_write_sec(val, base, L2X0_DEBUG_CTRL); } static void __l2c_op_way(void __iomem *reg) @@ -95,9 +112,7 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) { unsigned long flags; - /* Only write the aux register if it needs changing */ - if (readl_relaxed(base + L2X0_AUX_CTRL) != aux) - writel_relaxed(aux, base + L2X0_AUX_CTRL); + l2c_write_sec(aux, base, L2X0_AUX_CTRL); l2c_unlock(base, num_lock); @@ -107,7 +122,7 @@ static void l2c_enable(void __iomem *base, u32 aux, unsigned num_lock) l2c_wait_mask(base + sync_reg_offset, 1); local_irq_restore(flags); - writel_relaxed(L2X0_CTRL_EN, base + L2X0_CTRL); + l2c_write_sec(L2X0_CTRL_EN, base, L2X0_CTRL); } static void l2c_disable(void) @@ -115,7 +130,7 @@ static void l2c_disable(void) void __iomem *base = l2x0_base; outer_cache.flush_all(); - writel_relaxed(0, base + L2X0_CTRL); + l2c_write_sec(0, base, L2X0_CTRL); dsb(st); } @@ -139,7 +154,7 @@ static inline void cache_sync(void) #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) static inline void debug_writel(unsigned long val) { - if (outer_cache.set_debug) + if (outer_cache.set_debug || outer_cache.write_sec) l2c_set_debug(l2x0_base, val); } #else @@ -182,7 +197,7 @@ static void l2x0_disable(void) raw_spin_lock_irqsave(&l2x0_lock, flags); __l2x0_flush_all(); - writel_relaxed(0, l2x0_base + L2X0_CTRL); + l2c_write_sec(0, l2x0_base, L2X0_CTRL); dsb(st); raw_spin_unlock_irqrestore(&l2x0_lock, flags); } @@ -599,11 +614,11 @@ static void l2c310_resume(void) L2X0_CACHE_ID_RTL_MASK; if (revision >= L310_CACHE_ID_RTL_R2P0) - writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - base + L2X0_PREFETCH_CTRL); + l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, + L2X0_PREFETCH_CTRL); if (revision >= L310_CACHE_ID_RTL_R3P0) - writel_relaxed(l2x0_saved_regs.pwr_ctrl, - base + L2X0_POWER_CTRL); + l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, + L2X0_POWER_CTRL); l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); } @@ -732,8 +747,11 @@ static void __init __l2c_init(const struct l2c_init_data *data, l2x0_size = ways * (data->way_size_0 << way_size_bits); fns = data->outer_cache; + fns.write_sec = outer_cache.write_sec; if (data->fixup) data->fixup(l2x0_base, cache_id, &fns); + if (fns.write_sec) + fns.set_debug = NULL; /* * Check if l2x0 controller is already enabled. If we are booting From 212f8337212c260721c4082358a2a10c79d0eb0c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:24 +0200 Subject: [PATCH 0540/1983] ARM: l2c: omap2: implement new write_sec method With the write_sec method, we no longer need to override the default L2C disable method, and we no longer need the L2C set_debug method. Both of these can be handled via the write_sec method. Acked-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap2/omap4-common.c | 42 ++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 9f8d506f511dd0..7abc1eb15bf95e 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -166,17 +166,33 @@ void __iomem *omap4_get_l2cache_base(void) return l2cache_base; } -static void omap4_l2x0_disable(void) +static void omap4_l2c310_write_sec(unsigned long val, unsigned reg) { - outer_flush_all(); - /* Disable PL310 L2 Cache controller */ - omap_smc1(0x102, 0x0); -} + unsigned smc_op; -static void omap4_l2x0_set_debug(unsigned long val) -{ - /* Program PL310 L2 Cache controller debug register */ - omap_smc1(0x100, val); + switch (reg) { + case L2X0_CTRL: + smc_op = OMAP4_MON_L2X0_CTRL_INDEX; + break; + + case L2X0_AUX_CTRL: + smc_op = OMAP4_MON_L2X0_AUXCTRL_INDEX; + break; + + case L2X0_DEBUG_CTRL: + smc_op = OMAP4_MON_L2X0_DBG_CTRL_INDEX; + break; + + case L310_PREFETCH_CTRL: + smc_op = OMAP4_MON_L2X0_PREFETCH_INDEX; + break; + + default: + WARN_ONCE(1, "OMAP L2C310: ignoring write to reg 0x%x\n", reg); + return; + } + + omap_smc1(smc_op, val); } static int __init omap_l2_cache_init(void) @@ -211,18 +227,12 @@ static int __init omap_l2_cache_init(void) /* Enable PL310 L2 Cache controller */ omap_smc1(0x102, 0x1); + outer_cache.write_sec = omap4_l2c310_write_sec; if (of_have_populated_dt()) l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); else l2x0_init(l2cache_base, aux_ctrl, L2X0_AUX_CTRL_MASK); - /* - * Override default outer_cache.disable with a OMAP4 - * specific one - */ - outer_cache.disable = omap4_l2x0_disable; - outer_cache.set_debug = omap4_l2x0_set_debug; - return 0; } omap_early_initcall(omap_l2_cache_init); From 966fbde3f37bc0c9b9ecd60d1f48fd76c10d583e Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:25 +0200 Subject: [PATCH 0541/1983] ARM: l2c: omap2: remove explicit SMI calls to enable L2 cache Now that OMAP2 uses the write_sec method, we don't need to enable the L2 cache in OMAP2 specific code; this can be done via the normal mechanisms in the L2C code. Remove the OMAP2 specific code. Acked-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap2/omap4-common.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 7abc1eb15bf95e..343c354ae6f5fc 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -222,11 +222,6 @@ static int __init omap_l2_cache_init(void) (1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | (1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT); - omap_smc1(0x109, aux_ctrl); - - /* Enable PL310 L2 Cache controller */ - omap_smc1(0x102, 0x1); - outer_cache.write_sec = omap4_l2c310_write_sec; if (of_have_populated_dt()) l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); From 52c894dfebd30c69302a8b4d1bc1c14323156343 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:25 +0200 Subject: [PATCH 0542/1983] ARM: l2c: highbank: implement new write_sec method With the write_sec method, we no longer need to override the default L2C disable method. This can be handled via the write_sec method instead. Signed-off-by: Russell King --- arch/arm/mach-highbank/highbank.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index 38e1dc3b4c6e91..4712aed3d9f606 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -51,11 +51,13 @@ static void __init highbank_scu_map_io(void) } -static void highbank_l2x0_disable(void) +static void highbank_l2c310_write_sec(unsigned long val, unsigned reg) { - outer_flush_all(); - /* Disable PL310 L2 Cache controller */ - highbank_smc1(0x102, 0x0); + if (reg == L2X0_CTRL) + highbank_smc1(0x102, val); + else + WARN_ONCE(1, "Highbank L2C310: ignoring write to reg 0x%x\n", + reg); } static void __init highbank_init_irq(void) @@ -69,8 +71,8 @@ static void __init highbank_init_irq(void) if (IS_ENABLED(CONFIG_CACHE_L2X0) && of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) { highbank_smc1(0x102, 0x1); + outer_cache.write_sec = highbank_l2c310_write_sec; l2x0_of_init(0, ~0); - outer_cache.disable = highbank_l2x0_disable; } } From b87cf829c04549486e72a960507f0764281ef15d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:25 +0200 Subject: [PATCH 0543/1983] ARM: l2c: highbank: remove explicit SMI call in L2 cache initialisation Now that highbank uses the write_sec method, we don't need to enable the L2 cache in SoC specific code; this can be done via the normal mechanisms in the L2C code. Checking with Rob Herring: > > Can we kill the "highbank_smc1(0x102, 0x1);" here? That means > > l2x0_of_init() will see the L2 cache disabled, and will try to enable > > it via the write_sec hook, so it should do the right thing. > > Yes, that should work. You should be able to just call l2x0_of_init > unconditionally. The condition was really to just avoid the smc on > Midway which does get handled on h/w, but not if running virtualized. So also drop the DT check too. I'm leaving the config check in place so that if L2 is disabled, the write_sec hook can be optimised away. Signed-off-by: Russell King --- arch/arm/mach-highbank/highbank.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c index 4712aed3d9f606..245e588859ec54 100644 --- a/arch/arm/mach-highbank/highbank.c +++ b/arch/arm/mach-highbank/highbank.c @@ -68,9 +68,7 @@ static void __init highbank_init_irq(void) highbank_scu_map_io(); /* Enable PL310 L2 Cache controller */ - if (IS_ENABLED(CONFIG_CACHE_L2X0) && - of_find_compatible_node(NULL, NULL, "arm,pl310-cache")) { - highbank_smc1(0x102, 0x1); + if (IS_ENABLED(CONFIG_CACHE_L2X0)) { outer_cache.write_sec = highbank_l2c310_write_sec; l2x0_of_init(0, ~0); } From 889e844cab07f025e74ff7525713b4d0b191a330 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:25 +0200 Subject: [PATCH 0544/1983] ARM: l2c: ux500: implement dummy write_sec method ux500 can't write to any of the secure registers on the L2C controllers, so provide a dummy handler which ignores all writes. Acked-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/mach-ux500/cache-l2x0.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index 264f894c0e3d22..5cc7e3625d8ccf 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -35,6 +35,14 @@ static int __init ux500_l2x0_unlock(void) return 0; } +static void ux500_l2c310_write_sec(unsigned long val, unsigned reg) +{ + /* + * We can't write to secure registers as we are in non-secure + * mode, until we have some SMI service available. + */ +} + static int __init ux500_l2x0_init(void) { u32 aux_val = 0x3e000000; @@ -56,21 +64,14 @@ static int __init ux500_l2x0_init(void) /* 64KB way size */ aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); + outer_cache.write_sec = ux500_l2c310_write_sec; + /* 64KB way size, 8 way associativity, force WA */ if (of_have_populated_dt()) l2x0_of_init(aux_val, 0xc0000fff); else l2x0_init(l2x0_base, aux_val, 0xc0000fff); - /* - * We can't disable l2 as we are in non secure mode, currently - * this seems be called only during kexec path. So let's - * override outer.disable with nasty assignment until we have - * some SMI service available. - */ - outer_cache.disable = NULL; - outer_cache.set_debug = NULL; - return 0; } From de13ad522f9e2700364dc0ce0f6591050c9a218d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:25 +0200 Subject: [PATCH 0545/1983] ARM: l2c: remove old .set_debug method We no longer need or require the .set_debug method; we handle everything it used to do via the .write_sec method instead. Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 1 - arch/arm/mm/cache-l2x0.c | 21 ++------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index 5066fa2f73abe0..eaa8a28c687111 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -36,7 +36,6 @@ struct outer_cache_fns { void (*resume)(void); /* This is an ARM L2C thing */ - void (*set_debug)(unsigned long); void (*write_sec)(unsigned long, unsigned); }; diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 369a9d01d94f72..b13276c9971bf0 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -80,10 +80,7 @@ static void l2c_write_sec(unsigned long val, void __iomem *base, unsigned reg) */ static inline void l2c_set_debug(void __iomem *base, unsigned long val) { - if (outer_cache.set_debug) - outer_cache.set_debug(val); - else - l2c_write_sec(val, base, L2X0_DEBUG_CTRL); + l2c_write_sec(val, base, L2X0_DEBUG_CTRL); } static void __l2c_op_way(void __iomem *reg) @@ -154,8 +151,7 @@ static inline void cache_sync(void) #if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) static inline void debug_writel(unsigned long val) { - if (outer_cache.set_debug || outer_cache.write_sec) - l2c_set_debug(l2x0_base, val); + l2c_set_debug(l2x0_base, val); } #else /* Optimised out for non-errata case */ @@ -489,11 +485,6 @@ static const struct l2c_init_data l2c220_data = { * Affects: store buffer * store buffer is not automatically drained. */ -static void l2c310_set_debug(unsigned long val) -{ - writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); -} - static void l2c310_inv_range_erratum(unsigned long start, unsigned long end) { void __iomem *base = l2x0_base; @@ -631,10 +622,6 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, const char *errata[4]; unsigned n = 0; - /* For compatibility */ - if (revision <= L310_CACHE_ID_RTL_R3P0) - fns->set_debug = l2c310_set_debug; - if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) && revision < L310_CACHE_ID_RTL_R2P0 && /* For bcm compatibility */ @@ -684,7 +671,6 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .flush_all = l2c210_flush_all, .disable = l2c_disable, .sync = l2c210_sync, - .set_debug = l2c310_set_debug, .resume = l2c310_resume, }, }; @@ -750,8 +736,6 @@ static void __init __l2c_init(const struct l2c_init_data *data, fns.write_sec = outer_cache.write_sec; if (data->fixup) data->fixup(l2x0_base, cache_id, &fns); - if (fns.write_sec) - fns.set_debug = NULL; /* * Check if l2x0 controller is already enabled. If we are booting @@ -925,7 +909,6 @@ static const struct l2c_init_data of_l2c310_data __initconst = { .flush_all = l2c210_flush_all, .disable = l2c_disable, .sync = l2c210_sync, - .set_debug = l2c310_set_debug, .resume = l2c310_resume, }, }; From 816804f019a064bf0f1d8a705da3fa2176281876 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:46:25 +0200 Subject: [PATCH 0546/1983] ARM: l2c: implement L2C-310 erratum 752271 in core L2C code Rather than having SoCs work around L2C erratum themselves, move them into core code. This erratum affects the double linefill feature which needs to be disabled for r3p0 to r3p1-50rel0. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b13276c9971bf0..b1fa825c133f7a 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -476,6 +476,11 @@ static const struct l2c_init_data l2c220_data = { * hit the line between the clean operation and invalidate operation, * resulting in the store being lost. * + * 752271: PL310 R3P0->R3P1-50REL0, fixed R3P2. + * Affects: 8x64-bit (double fill) line fetches + * double fill line fetches can fail to cause dirty data to be evicted + * from the cache before the new data overwrites the second line. + * * 753970: PL310 R3P0, fixed R3P1. * Affects: sync * prevents merging writes after the sync operation, until another L2C @@ -619,7 +624,7 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, struct outer_cache_fns *fns) { unsigned revision = cache_id & L2X0_CACHE_ID_RTL_MASK; - const char *errata[4]; + const char *errata[8]; unsigned n = 0; if (IS_ENABLED(CONFIG_PL310_ERRATA_588369) && @@ -638,6 +643,17 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, errata[n++] = "727915"; } + if (revision >= L310_CACHE_ID_RTL_R3P0 && + revision < L310_CACHE_ID_RTL_R3P2) { + u32 val = readl_relaxed(base + L2X0_PREFETCH_CTRL); + /* I don't think bit23 is required here... but iMX6 does so */ + if (val & (BIT(30) | BIT(23))) { + val &= ~(BIT(30) | BIT(23)); + l2c_write_sec(val, base, L2X0_PREFETCH_CTRL); + errata[n++] = "752271"; + } + } + if (IS_ENABLED(CONFIG_PL310_ERRATA_753970) && revision == L310_CACHE_ID_RTL_R3P0) { sync_reg_offset = L2X0_DUMMY_REG; From 727225e38b769662df372f6aca8f4773fe2c6464 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:49:43 +0200 Subject: [PATCH 0547/1983] ARM: l2c: fix register naming We have a mixture of different devices with different register layouts, but we group all the bits together in an opaque mess. Split them out into those which are L2C-310 specific and ones which refer to earlier devices. Provide full auxiliary control register definitions. Acked-by: Tony Lindgren Acked-by: Linus Walleij Acked-by: Shawn Guo Signed-off-by: Russell King --- arch/arm/include/asm/hardware/cache-l2x0.h | 73 ++++++++++++++-------- arch/arm/mach-cns3xxx/core.c | 8 +-- arch/arm/mach-imx/system.c | 8 +-- arch/arm/mach-omap2/omap-mpuss-lowpower.c | 2 +- arch/arm/mach-omap2/omap4-common.c | 18 +++--- arch/arm/mach-prima2/l2x0.c | 5 +- arch/arm/mach-realview/realview_pbx.c | 4 +- arch/arm/mach-spear/spear13xx.c | 6 +- arch/arm/mach-sti/board-dt.c | 8 +-- arch/arm/mach-tegra/sleep.h | 8 +-- arch/arm/mach-ux500/cache-l2x0.c | 4 +- arch/arm/mach-vexpress/ct-ca9x4.c | 4 +- arch/arm/mm/cache-l2x0.c | 57 ++++++++--------- 13 files changed, 114 insertions(+), 91 deletions(-) diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 3af45734b51420..b3ee122c6f24ff 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -26,8 +26,8 @@ #define L2X0_CACHE_TYPE 0x004 #define L2X0_CTRL 0x100 #define L2X0_AUX_CTRL 0x104 -#define L2X0_TAG_LATENCY_CTRL 0x108 -#define L2X0_DATA_LATENCY_CTRL 0x10C +#define L310_TAG_LATENCY_CTRL 0x108 +#define L310_DATA_LATENCY_CTRL 0x10C #define L2X0_EVENT_CNT_CTRL 0x200 #define L2X0_EVENT_CNT1_CFG 0x204 #define L2X0_EVENT_CNT0_CFG 0x208 @@ -54,16 +54,16 @@ #define L2X0_LOCKDOWN_WAY_D_BASE 0x900 #define L2X0_LOCKDOWN_WAY_I_BASE 0x904 #define L2X0_LOCKDOWN_STRIDE 0x08 -#define L2X0_ADDR_FILTER_START 0xC00 -#define L2X0_ADDR_FILTER_END 0xC04 +#define L310_ADDR_FILTER_START 0xC00 +#define L310_ADDR_FILTER_END 0xC04 #define L2X0_TEST_OPERATION 0xF00 #define L2X0_LINE_DATA 0xF10 #define L2X0_LINE_TAG 0xF30 #define L2X0_DEBUG_CTRL 0xF40 -#define L2X0_PREFETCH_CTRL 0xF60 -#define L2X0_POWER_CTRL 0xF80 -#define L2X0_DYNAMIC_CLK_GATING_EN (1 << 1) -#define L2X0_STNDBY_MODE_EN (1 << 0) +#define L310_PREFETCH_CTRL 0xF60 +#define L310_POWER_CTRL 0xF80 +#define L310_DYNAMIC_CLK_GATING_EN (1 << 1) +#define L310_STNDBY_MODE_EN (1 << 0) /* Registers shifts and masks */ #define L2X0_CACHE_ID_PART_MASK (0xf << 6) @@ -88,29 +88,52 @@ #define L310_CACHE_ID_RTL_R3P3 0x09 #define L2X0_AUX_CTRL_MASK 0xc0000fff +/* L2C auxiliary control register - bits common to L2C-210/220/310 */ +#define L2C_AUX_CTRL_WAY_SIZE_SHIFT 17 +#define L2C_AUX_CTRL_WAY_SIZE_MASK (7 << 17) +#define L2C_AUX_CTRL_WAY_SIZE(n) ((n) << 17) +#define L2C_AUX_CTRL_EVTMON_ENABLE BIT(20) +#define L2C_AUX_CTRL_PARITY_ENABLE BIT(21) +#define L2C_AUX_CTRL_SHARED_OVERRIDE BIT(22) +/* L2C-210/220 common bits */ #define L2X0_AUX_CTRL_DATA_RD_LATENCY_SHIFT 0 -#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK 0x7 +#define L2X0_AUX_CTRL_DATA_RD_LATENCY_MASK (7 << 0) #define L2X0_AUX_CTRL_DATA_WR_LATENCY_SHIFT 3 -#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK (0x7 << 3) +#define L2X0_AUX_CTRL_DATA_WR_LATENCY_MASK (7 << 3) #define L2X0_AUX_CTRL_TAG_LATENCY_SHIFT 6 -#define L2X0_AUX_CTRL_TAG_LATENCY_MASK (0x7 << 6) +#define L2X0_AUX_CTRL_TAG_LATENCY_MASK (7 << 6) #define L2X0_AUX_CTRL_DIRTY_LATENCY_SHIFT 9 -#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK (0x7 << 9) -#define L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT 16 -#define L2X0_AUX_CTRL_WAY_SIZE_SHIFT 17 -#define L2X0_AUX_CTRL_WAY_SIZE_MASK (0x7 << 17) -#define L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT 22 -#define L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT 26 -#define L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT 27 -#define L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT 28 -#define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT 29 -#define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT 30 +#define L2X0_AUX_CTRL_DIRTY_LATENCY_MASK (7 << 9) +#define L2X0_AUX_CTRL_ASSOC_SHIFT 13 +#define L2X0_AUX_CTRL_ASSOC_MASK (15 << 13) +/* L2C-210 specific bits */ +#define L210_AUX_CTRL_WRAP_DISABLE BIT(12) +#define L210_AUX_CTRL_WA_OVERRIDE BIT(23) +#define L210_AUX_CTRL_EXCLUSIVE_ABORT BIT(24) +/* L2C-220 specific bits */ +#define L220_AUX_CTRL_EXCLUSIVE_CACHE BIT(12) +#define L220_AUX_CTRL_FWA_SHIFT 23 +#define L220_AUX_CTRL_FWA_MASK (3 << 23) +#define L220_AUX_CTRL_NS_LOCKDOWN BIT(26) +#define L220_AUX_CTRL_NS_INT_CTRL BIT(27) +/* L2C-310 specific bits */ +#define L310_AUX_CTRL_FULL_LINE_ZERO BIT(0) /* R2P0+ */ +#define L310_AUX_CTRL_HIGHPRIO_SO_DEV BIT(10) /* R2P0+ */ +#define L310_AUX_CTRL_STORE_LIMITATION BIT(11) /* R2P0+ */ +#define L310_AUX_CTRL_EXCLUSIVE_CACHE BIT(12) +#define L310_AUX_CTRL_ASSOCIATIVITY_16 BIT(16) +#define L310_AUX_CTRL_CACHE_REPLACE_RR BIT(25) /* R2P0+ */ +#define L310_AUX_CTRL_NS_LOCKDOWN BIT(26) +#define L310_AUX_CTRL_NS_INT_CTRL BIT(27) +#define L310_AUX_CTRL_DATA_PREFETCH BIT(28) +#define L310_AUX_CTRL_INSTR_PREFETCH BIT(29) +#define L310_AUX_CTRL_EARLY_BRESP BIT(30) /* R2P0+ */ -#define L2X0_LATENCY_CTRL_SETUP_SHIFT 0 -#define L2X0_LATENCY_CTRL_RD_SHIFT 4 -#define L2X0_LATENCY_CTRL_WR_SHIFT 8 +#define L310_LATENCY_CTRL_SETUP(n) ((n) << 0) +#define L310_LATENCY_CTRL_RD(n) ((n) << 4) +#define L310_LATENCY_CTRL_WR(n) ((n) << 8) -#define L2X0_ADDR_FILTER_EN 1 +#define L310_ADDR_FILTER_EN 1 #define L2X0_CTRL_EN 1 diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c index e38b279f402c46..dd2b9926a76c6d 100644 --- a/arch/arm/mach-cns3xxx/core.c +++ b/arch/arm/mach-cns3xxx/core.c @@ -240,9 +240,9 @@ void __init cns3xxx_l2x0_init(void) * * 1 cycle of latency for setup, read and write accesses */ - val = readl(base + L2X0_TAG_LATENCY_CTRL); + val = readl(base + L310_TAG_LATENCY_CTRL); val &= 0xfffff888; - writel(val, base + L2X0_TAG_LATENCY_CTRL); + writel(val, base + L310_TAG_LATENCY_CTRL); /* * Data RAM Control register @@ -253,9 +253,9 @@ void __init cns3xxx_l2x0_init(void) * * 1 cycle of latency for setup, read and write accesses */ - val = readl(base + L2X0_DATA_LATENCY_CTRL); + val = readl(base + L310_DATA_LATENCY_CTRL); val &= 0xfffff888; - writel(val, base + L2X0_DATA_LATENCY_CTRL); + writel(val, base + L310_DATA_LATENCY_CTRL); /* 32 KiB, 8-way, parity disable */ l2x0_init(base, 0x00540000, 0xfe000fff); diff --git a/arch/arm/mach-imx/system.c b/arch/arm/mach-imx/system.c index eb9fc8c08a59ae..d6d700c7ce16ae 100644 --- a/arch/arm/mach-imx/system.c +++ b/arch/arm/mach-imx/system.c @@ -153,7 +153,7 @@ void __init imx_init_l2cache(void) } /* Configure the L2 PREFETCH and POWER registers */ - val = readl_relaxed(l2x0_base + L2X0_PREFETCH_CTRL); + val = readl_relaxed(l2x0_base + L310_PREFETCH_CTRL); val |= 0x70800000; /* * The L2 cache controller(PL310) version on the i.MX6D/Q is r3p1-50rel0 @@ -166,9 +166,9 @@ void __init imx_init_l2cache(void) */ if (cpu_is_imx6q()) val &= ~(1 << 30 | 1 << 23); - writel_relaxed(val, l2x0_base + L2X0_PREFETCH_CTRL); - val = L2X0_DYNAMIC_CLK_GATING_EN | L2X0_STNDBY_MODE_EN; - writel_relaxed(val, l2x0_base + L2X0_POWER_CTRL); + writel_relaxed(val, l2x0_base + L310_PREFETCH_CTRL); + val = L310_DYNAMIC_CLK_GATING_EN | L310_STNDBY_MODE_EN; + writel_relaxed(val, l2x0_base + L310_POWER_CTRL); iounmap(l2x0_base); of_node_put(np); diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c index 667915d236f3db..ba43f49fbb59b2 100644 --- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c @@ -194,7 +194,7 @@ static void save_l2x0_context(void) if (l2x0_base) { val = __raw_readl(l2x0_base + L2X0_AUX_CTRL); __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET); - val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL); + val = __raw_readl(l2x0_base + L310_PREFETCH_CTRL); __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET); } } diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 343c354ae6f5fc..84e097f5fdf40a 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -212,15 +212,15 @@ static int __init omap_l2_cache_init(void) return -ENOMEM; /* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */ - aux_ctrl = (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT) | - (0x1 << 25) | - (0x1 << L2X0_AUX_CTRL_NS_LOCKDOWN_SHIFT) | - (0x1 << L2X0_AUX_CTRL_NS_INT_CTRL_SHIFT)) | - (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | - (1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | - (1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) | - (1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | - (1 << L2X0_AUX_CTRL_EARLY_BRESP_SHIFT); + aux_ctrl = L310_AUX_CTRL_ASSOCIATIVITY_16 | + L310_AUX_CTRL_CACHE_REPLACE_RR | + L310_AUX_CTRL_NS_LOCKDOWN | + L310_AUX_CTRL_NS_INT_CTRL | + L2C_AUX_CTRL_WAY_SIZE(3) | + L2C_AUX_CTRL_SHARED_OVERRIDE | + L310_AUX_CTRL_DATA_PREFETCH | + L310_AUX_CTRL_INSTR_PREFETCH | + L310_AUX_CTRL_EARLY_BRESP; outer_cache.write_sec = omap4_l2c310_write_sec; if (of_have_populated_dt()) diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c index cbcbe9cb094c48..3a4eda45171ed5 100644 --- a/arch/arm/mach-prima2/l2x0.c +++ b/arch/arm/mach-prima2/l2x0.c @@ -18,13 +18,12 @@ struct l2x0_aux }; static struct l2x0_aux prima2_l2x0_aux __initconst = { - .val = 2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT, + .val = L2C_AUX_CTRL_WAY_SIZE(2), .mask = 0, }; static struct l2x0_aux marco_l2x0_aux __initconst = { - .val = (2 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT) | - (1 << L2X0_AUX_CTRL_ASSOCIATIVITY_SHIFT), + .val = L2C_AUX_CTRL_WAY_SIZE(2) | L310_AUX_CTRL_ASSOCIATIVITY_16, .mask = L2X0_AUX_CTRL_MASK, }; diff --git a/arch/arm/mach-realview/realview_pbx.c b/arch/arm/mach-realview/realview_pbx.c index 9d75493e3f0cc1..f0cfd7e7e569a4 100644 --- a/arch/arm/mach-realview/realview_pbx.c +++ b/arch/arm/mach-realview/realview_pbx.c @@ -370,8 +370,8 @@ static void __init realview_pbx_init(void) __io_address(REALVIEW_PBX_TILE_L220_BASE); /* set RAM latencies to 1 cycle for eASIC */ - writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL); - writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL); + writel(0, l2x0_base + L310_TAG_LATENCY_CTRL); + writel(0, l2x0_base + L310_DATA_LATENCY_CTRL); /* 16KB way size, 8-way associativity, parity disabled * Bits: .. 0 0 0 0 1 00 1 0 1 001 0 000 0 .... .... .... */ diff --git a/arch/arm/mach-spear/spear13xx.c b/arch/arm/mach-spear/spear13xx.c index 7aa6e8cf830f5e..92860fa01668c5 100644 --- a/arch/arm/mach-spear/spear13xx.c +++ b/arch/arm/mach-spear/spear13xx.c @@ -38,14 +38,14 @@ void __init spear13xx_l2x0_init(void) if (!IS_ENABLED(CONFIG_CACHE_L2X0)) return; - writel_relaxed(0x06, VA_L2CC_BASE + L2X0_PREFETCH_CTRL); + writel_relaxed(0x06, VA_L2CC_BASE + L310_PREFETCH_CTRL); /* * Program following latencies in order to make * SPEAr1340 work at 600 MHz */ - writel_relaxed(0x221, VA_L2CC_BASE + L2X0_TAG_LATENCY_CTRL); - writel_relaxed(0x441, VA_L2CC_BASE + L2X0_DATA_LATENCY_CTRL); + writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL); + writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL); l2x0_init(VA_L2CC_BASE, 0x70A60001, 0xfe00ffff); } diff --git a/arch/arm/mach-sti/board-dt.c b/arch/arm/mach-sti/board-dt.c index 1217fb598cfdc7..dc8669efc12d68 100644 --- a/arch/arm/mach-sti/board-dt.c +++ b/arch/arm/mach-sti/board-dt.c @@ -19,10 +19,10 @@ void __init stih41x_l2x0_init(void) u32 way_size = 0x4; u32 aux_ctrl; /* may be this can be encoded in macros like BIT*() */ - aux_ctrl = (0x1 << L2X0_AUX_CTRL_SHARE_OVERRIDE_SHIFT) | - (0x1 << L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT) | - (0x1 << L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT) | - (way_size << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); + aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE | + L310_AUX_CTRL_DATA_PREFETCH | + L310_AUX_CTRL_INSTR_PREFETCH | + L2C_AUX_CTRL_WAY_SIZE(way_size); l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); } diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index a4edbb3abd3d17..a032820d2fac77 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -134,13 +134,13 @@ tst \tmp3, #L2X0_CTRL_EN bne exit_l2_resume ldr \tmp3, [\tmp1, #L2X0_R_TAG_LATENCY] - str \tmp3, [\tmp2, #L2X0_TAG_LATENCY_CTRL] + str \tmp3, [\tmp2, #L310_TAG_LATENCY_CTRL] ldr \tmp3, [\tmp1, #L2X0_R_DATA_LATENCY] - str \tmp3, [\tmp2, #L2X0_DATA_LATENCY_CTRL] + str \tmp3, [\tmp2, #L310_DATA_LATENCY_CTRL] ldr \tmp3, [\tmp1, #L2X0_R_PREFETCH_CTRL] - str \tmp3, [\tmp2, #L2X0_PREFETCH_CTRL] + str \tmp3, [\tmp2, #L310_PREFETCH_CTRL] ldr \tmp3, [\tmp1, #L2X0_R_PWR_CTRL] - str \tmp3, [\tmp2, #L2X0_POWER_CTRL] + str \tmp3, [\tmp2, #L310_POWER_CTRL] ldr \tmp3, [\tmp1, #L2X0_R_AUX_CTRL] str \tmp3, [\tmp2, #L2X0_AUX_CTRL] mov \tmp3, #L2X0_CTRL_EN diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index 5cc7e3625d8ccf..067c37a054fbb6 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -59,10 +59,10 @@ static int __init ux500_l2x0_init(void) /* DBx540's L2 has 128KB way size */ if (cpu_is_ux540_family()) /* 128KB way size */ - aux_val |= (0x4 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); + aux_val |= L2C_AUX_CTRL_WAY_SIZE(4); else /* 64KB way size */ - aux_val |= (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); + aux_val |= L2C_AUX_CTRL_WAY_SIZE(3); outer_cache.write_sec = ux500_l2c310_write_sec; diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index 6f34497a42451e..6c4ffb6c5ad83d 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -145,8 +145,8 @@ static void __init ct_ca9x4_init(void) void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K); /* set RAM latencies to 1 cycle for this core tile. */ - writel(0, l2x0_base + L2X0_TAG_LATENCY_CTRL); - writel(0, l2x0_base + L2X0_DATA_LATENCY_CTRL); + writel(0, l2x0_base + L310_TAG_LATENCY_CTRL); + writel(0, l2x0_base + L310_DATA_LATENCY_CTRL); l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff); #endif diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index b1fa825c133f7a..3e2c22a12d87e7 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -567,13 +567,13 @@ static void __init l2c310_save(void __iomem *base) unsigned revision; l2x0_saved_regs.tag_latency = readl_relaxed(base + - L2X0_TAG_LATENCY_CTRL); + L310_TAG_LATENCY_CTRL); l2x0_saved_regs.data_latency = readl_relaxed(base + - L2X0_DATA_LATENCY_CTRL); + L310_DATA_LATENCY_CTRL); l2x0_saved_regs.filter_end = readl_relaxed(base + - L2X0_ADDR_FILTER_END); + L310_ADDR_FILTER_END); l2x0_saved_regs.filter_start = readl_relaxed(base + - L2X0_ADDR_FILTER_START); + L310_ADDR_FILTER_START); revision = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK; @@ -581,12 +581,12 @@ static void __init l2c310_save(void __iomem *base) /* From r2p0, there is Prefetch offset/control register */ if (revision >= L310_CACHE_ID_RTL_R2P0) l2x0_saved_regs.prefetch_ctrl = readl_relaxed(base + - L2X0_PREFETCH_CTRL); + L310_PREFETCH_CTRL); /* From r3p0, there is Power control register */ if (revision >= L310_CACHE_ID_RTL_R3P0) l2x0_saved_regs.pwr_ctrl = readl_relaxed(base + - L2X0_POWER_CTRL); + L310_POWER_CTRL); } static void l2c310_resume(void) @@ -598,23 +598,23 @@ static void l2c310_resume(void) /* restore pl310 setup */ writel_relaxed(l2x0_saved_regs.tag_latency, - base + L2X0_TAG_LATENCY_CTRL); + base + L310_TAG_LATENCY_CTRL); writel_relaxed(l2x0_saved_regs.data_latency, - base + L2X0_DATA_LATENCY_CTRL); + base + L310_DATA_LATENCY_CTRL); writel_relaxed(l2x0_saved_regs.filter_end, - base + L2X0_ADDR_FILTER_END); + base + L310_ADDR_FILTER_END); writel_relaxed(l2x0_saved_regs.filter_start, - base + L2X0_ADDR_FILTER_START); + base + L310_ADDR_FILTER_START); revision = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_RTL_MASK; if (revision >= L310_CACHE_ID_RTL_R2P0) l2c_write_sec(l2x0_saved_regs.prefetch_ctrl, base, - L2X0_PREFETCH_CTRL); + L310_PREFETCH_CTRL); if (revision >= L310_CACHE_ID_RTL_R3P0) l2c_write_sec(l2x0_saved_regs.pwr_ctrl, base, - L2X0_POWER_CTRL); + L310_POWER_CTRL); l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); } @@ -645,11 +645,11 @@ static void __init l2c310_fixup(void __iomem *base, u32 cache_id, if (revision >= L310_CACHE_ID_RTL_R3P0 && revision < L310_CACHE_ID_RTL_R3P2) { - u32 val = readl_relaxed(base + L2X0_PREFETCH_CTRL); + u32 val = readl_relaxed(base + L310_PREFETCH_CTRL); /* I don't think bit23 is required here... but iMX6 does so */ if (val & (BIT(30) | BIT(23))) { val &= ~(BIT(30) | BIT(23)); - l2c_write_sec(val, base, L2X0_PREFETCH_CTRL); + l2c_write_sec(val, base, L310_PREFETCH_CTRL); errata[n++] = "752271"; } } @@ -745,7 +745,8 @@ static void __init __l2c_init(const struct l2c_init_data *data, * * L2 cache size = number of ways * way size. */ - way_size_bits = (aux & L2X0_AUX_CTRL_WAY_SIZE_MASK) >> 17; + way_size_bits = (aux & L2C_AUX_CTRL_WAY_SIZE_MASK) >> + L2C_AUX_CTRL_WAY_SIZE_SHIFT; l2x0_size = ways * (data->way_size_0 << way_size_bits); fns = data->outer_cache; @@ -886,27 +887,27 @@ static void __init l2c310_of_parse(const struct device_node *np, of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); if (tag[0] && tag[1] && tag[2]) writel_relaxed( - ((tag[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | - ((tag[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | - ((tag[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), - l2x0_base + L2X0_TAG_LATENCY_CTRL); + L310_LATENCY_CTRL_RD(tag[0] - 1) | + L310_LATENCY_CTRL_WR(tag[1] - 1) | + L310_LATENCY_CTRL_SETUP(tag[2] - 1), + l2x0_base + L310_TAG_LATENCY_CTRL); of_property_read_u32_array(np, "arm,data-latency", data, ARRAY_SIZE(data)); if (data[0] && data[1] && data[2]) writel_relaxed( - ((data[0] - 1) << L2X0_LATENCY_CTRL_RD_SHIFT) | - ((data[1] - 1) << L2X0_LATENCY_CTRL_WR_SHIFT) | - ((data[2] - 1) << L2X0_LATENCY_CTRL_SETUP_SHIFT), - l2x0_base + L2X0_DATA_LATENCY_CTRL); + L310_LATENCY_CTRL_RD(data[0] - 1) | + L310_LATENCY_CTRL_WR(data[1] - 1) | + L310_LATENCY_CTRL_SETUP(data[2] - 1), + l2x0_base + L310_DATA_LATENCY_CTRL); of_property_read_u32_array(np, "arm,filter-ranges", filter, ARRAY_SIZE(filter)); if (filter[1]) { writel_relaxed(ALIGN(filter[0] + filter[1], SZ_1M), - l2x0_base + L2X0_ADDR_FILTER_END); - writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L2X0_ADDR_FILTER_EN, - l2x0_base + L2X0_ADDR_FILTER_START); + l2x0_base + L310_ADDR_FILTER_END); + writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN, + l2x0_base + L310_ADDR_FILTER_START); } } @@ -1281,7 +1282,7 @@ static void __init tauros3_save(void __iomem *base) l2x0_saved_regs.aux2_ctrl = readl_relaxed(base + TAUROS3_AUX2_CTRL); l2x0_saved_regs.prefetch_ctrl = - readl_relaxed(base + L2X0_PREFETCH_CTRL); + readl_relaxed(base + L310_PREFETCH_CTRL); } static void tauros3_resume(void) @@ -1292,7 +1293,7 @@ static void tauros3_resume(void) writel_relaxed(l2x0_saved_regs.aux2_ctrl, base + TAUROS3_AUX2_CTRL); writel_relaxed(l2x0_saved_regs.prefetch_ctrl, - base + L2X0_PREFETCH_CTRL); + base + L310_PREFETCH_CTRL); l2c_enable(base, l2x0_saved_regs.aux_ctrl, 8); } From 5f2eee580fb5787e66b33fb8e3f8c0481ce3e3bd Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:49:43 +0200 Subject: [PATCH 0548/1983] ARM: l2c: add automatic enable of early BRESP The AXI bus protocol requires that a write response should only be sent back to the master when the last write has been accepted. Early BRESP allows the L2C-310 to send the write response as soon as the store buffer accepts the write address. Cortex-A9 processors can signal to the L2C-310 that they wish to be notified early, and if this optimisation is enabled, the L2C-310 can signal an early write response. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 3e2c22a12d87e7..db3f18a968b2bd 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -24,6 +24,7 @@ #include #include +#include #include #include "cache-tauros3.h" #include "cache-aurora-l2.h" @@ -620,6 +621,24 @@ static void l2c310_resume(void) } } +static void __init l2c310_enable(void __iomem *base, u32 aux, unsigned num_lock) +{ + unsigned rev = readl_relaxed(base + L2X0_CACHE_ID) & L2X0_CACHE_ID_PART_MASK; + bool cortex_a9 = read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9; + + if (rev >= L310_CACHE_ID_RTL_R2P0) { + if (cortex_a9) { + aux |= L310_AUX_CTRL_EARLY_BRESP; + pr_info("L2C-310 enabling early BRESP for Cortex-A9\n"); + } else if (aux & L310_AUX_CTRL_EARLY_BRESP) { + pr_warn("L2C-310 early BRESP only supported with Cortex-A9\n"); + aux &= ~L310_AUX_CTRL_EARLY_BRESP; + } + } + + l2c_enable(base, aux, num_lock); +} + static void __init l2c310_fixup(void __iomem *base, u32 cache_id, struct outer_cache_fns *fns) { @@ -677,7 +696,7 @@ static const struct l2c_init_data l2c310_init_fns __initconst = { .type = "L2C-310", .way_size_0 = SZ_8K, .num_lock = 8, - .enable = l2c_enable, + .enable = l2c310_enable, .fixup = l2c310_fixup, .save = l2c310_save, .outer_cache = { @@ -916,7 +935,7 @@ static const struct l2c_init_data of_l2c310_data __initconst = { .way_size_0 = SZ_8K, .num_lock = 8, .of_parse = l2c310_of_parse, - .enable = l2c_enable, + .enable = l2c310_enable, .fixup = l2c310_fixup, .save = l2c310_save, .outer_cache = { @@ -1264,7 +1283,7 @@ static const struct l2c_init_data of_bcm_l2x0_data __initconst = { .way_size_0 = SZ_8K, .num_lock = 8, .of_parse = l2c310_of_parse, - .enable = l2c_enable, + .enable = l2c310_enable, .save = l2c310_save, .outer_cache = { .inv_range = bcm_inv_range, From 0211417a4e18a11255bae2bed851cd755076f739 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:49:59 +0200 Subject: [PATCH 0549/1983] ARM: l2c: remove platforms/SoCs setting early BRESP Since we now automatically enable early BRESP in core L2C-310 code when we detect a Cortex-A9, we don't need platforms/SoCs to set this bit explicitly. Instead, they should seek to preserve the value of bit 30 in the auxiliary control register. Acked-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-berlin/berlin.c | 2 +- arch/arm/mach-omap2/omap4-common.c | 3 +-- arch/arm/mach-shmobile/board-armadillo800eva-reference.c | 4 ++-- arch/arm/mach-shmobile/board-armadillo800eva.c | 4 ++-- arch/arm/mach-shmobile/board-kzm9g-reference.c | 4 ++-- arch/arm/mach-shmobile/board-kzm9g.c | 4 ++-- arch/arm/mach-shmobile/setup-r8a7778.c | 4 ++-- arch/arm/mach-shmobile/setup-r8a7779.c | 4 ++-- arch/arm/mach-spear/spear13xx.c | 2 +- arch/arm/mach-tegra/tegra.c | 4 ++-- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-berlin/berlin.c b/arch/arm/mach-berlin/berlin.c index 025bcb5473eb56..6709d2a6bec891 100644 --- a/arch/arm/mach-berlin/berlin.c +++ b/arch/arm/mach-berlin/berlin.c @@ -24,7 +24,7 @@ static void __init berlin_init_machine(void) * with DT probing for L2CCs, berlin_init_machine can be removed. * Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc */ - l2x0_of_init(0x70c00000, 0xfeffffff); + l2x0_of_init(0x30c00000, 0xfeffffff); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index 84e097f5fdf40a..ce2fad84a43c70 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -219,8 +219,7 @@ static int __init omap_l2_cache_init(void) L2C_AUX_CTRL_WAY_SIZE(3) | L2C_AUX_CTRL_SHARED_OVERRIDE | L310_AUX_CTRL_DATA_PREFETCH | - L310_AUX_CTRL_INSTR_PREFETCH | - L310_AUX_CTRL_EARLY_BRESP; + L310_AUX_CTRL_INSTR_PREFETCH; outer_cache.write_sec = omap4_l2c310_write_sec; if (of_have_populated_dt()) diff --git a/arch/arm/mach-shmobile/board-armadillo800eva-reference.c b/arch/arm/mach-shmobile/board-armadillo800eva-reference.c index 57d1a78367b6aa..34e7f3c17dd2ea 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva-reference.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva-reference.c @@ -164,8 +164,8 @@ static void __init eva_init(void) r8a7740_meram_workaround(); #ifdef CONFIG_CACHE_L2X0 - /* Early BRESP enable, Shared attribute override enable, 32K*8way */ - l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff); + /* Shared attribute override enable, 32K*8way */ + l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff); #endif r8a7740_add_standard_devices_dt(); diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 93533e2710a839..69ec71038ec78b 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -1270,8 +1270,8 @@ static void __init eva_init(void) #ifdef CONFIG_CACHE_L2X0 - /* Early BRESP enable, Shared attribute override enable, 32K*8way */ - l2x0_init(IOMEM(0xf0002000), 0x40440000, 0x82000fff); + /* Shared attribute override enable, 32K*8way */ + l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff); #endif i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); diff --git a/arch/arm/mach-shmobile/board-kzm9g-reference.c b/arch/arm/mach-shmobile/board-kzm9g-reference.c index 598e32488410f2..85873f186d777d 100644 --- a/arch/arm/mach-shmobile/board-kzm9g-reference.c +++ b/arch/arm/mach-shmobile/board-kzm9g-reference.c @@ -36,8 +36,8 @@ static void __init kzm_init(void) sh73a0_add_standard_devices_dt(); #ifdef CONFIG_CACHE_L2X0 - /* Early BRESP enable, Shared attribute override enable, 64K*8way */ - l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff); + /* Shared attribute override enable, 64K*8way */ + l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff); #endif } diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index bc40b853ffd3cf..bd9931f223ae92 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -878,8 +878,8 @@ static void __init kzm_init(void) gpio_request_one(223, GPIOF_IN, NULL); /* IRQ8 */ #ifdef CONFIG_CACHE_L2X0 - /* Early BRESP enable, Shared attribute override enable, 64K*8way */ - l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff); + /* Shared attribute override enable, 64K*8way */ + l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff); #endif i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); diff --git a/arch/arm/mach-shmobile/setup-r8a7778.c b/arch/arm/mach-shmobile/setup-r8a7778.c index 6d694526e4ca1b..3a8e5316671e7c 100644 --- a/arch/arm/mach-shmobile/setup-r8a7778.c +++ b/arch/arm/mach-shmobile/setup-r8a7778.c @@ -298,10 +298,10 @@ void __init r8a7778_add_dt_devices(void) void __iomem *base = ioremap_nocache(0xf0100000, 0x1000); if (base) { /* - * Early BRESP enable, Shared attribute override enable, 64K*16way + * Shared attribute override enable, 64K*16way * don't call iounmap(base) */ - l2x0_init(base, 0x40470000, 0x82000fff); + l2x0_init(base, 0x00470000, 0xc2000fff); } #endif diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c index 8e860b36997a67..91c90bf0ae8372 100644 --- a/arch/arm/mach-shmobile/setup-r8a7779.c +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -700,8 +700,8 @@ static struct platform_device *r8a7779_standard_devices[] __initdata = { void __init r8a7779_add_standard_devices(void) { #ifdef CONFIG_CACHE_L2X0 - /* Early BRESP enable, Shared attribute override enable, 64K*16way */ - l2x0_init(IOMEM(0xf0100000), 0x40470000, 0x82000fff); + /* Shared attribute override enable, 64K*16way */ + l2x0_init(IOMEM(0xf0100000), 0x00470000, 0xc2000fff); #endif r8a7779_pm_init(); diff --git a/arch/arm/mach-spear/spear13xx.c b/arch/arm/mach-spear/spear13xx.c index 92860fa01668c5..dcb300443b6677 100644 --- a/arch/arm/mach-spear/spear13xx.c +++ b/arch/arm/mach-spear/spear13xx.c @@ -46,7 +46,7 @@ void __init spear13xx_l2x0_init(void) */ writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL); writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL); - l2x0_init(VA_L2CC_BASE, 0x70A60001, 0xfe00ffff); + l2x0_init(VA_L2CC_BASE, 0x30a60001, 0xfe00ffff); } /* diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 6191603379e13c..ecbb5411a1045e 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -89,9 +89,9 @@ static void __init tegra_init_cache(void) cache_type = readl(p + L2X0_CACHE_TYPE); aux_ctrl = (cache_type & 0x700) << (17-8); - aux_ctrl |= 0x7C400001; + aux_ctrl |= 0x3c400001; - ret = l2x0_of_init(aux_ctrl, 0x8200c3fe); + ret = l2x0_of_init(aux_ctrl, 0xc200c3fe); if (!ret) l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs); #endif From c3bf5d4fa1af1160367a65c825f05142bdd99032 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:50:00 +0200 Subject: [PATCH 0550/1983] ARM: l2c: tegra: remove associativity and way size from aux_ctrl Signed-off-by: Russell King --- arch/arm/mach-tegra/tegra.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index ecbb5411a1045e..fb802e24b64706 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -73,25 +73,9 @@ u32 tegra_uart_config[3] = { static void __init tegra_init_cache(void) { #ifdef CONFIG_CACHE_L2X0 - static const struct of_device_id pl310_ids[] __initconst = { - { .compatible = "arm,pl310-cache", }, - {} - }; - - struct device_node *np; int ret; - void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; - u32 aux_ctrl, cache_type; - - np = of_find_matching_node(NULL, pl310_ids); - if (!np) - return; - - cache_type = readl(p + L2X0_CACHE_TYPE); - aux_ctrl = (cache_type & 0x700) << (17-8); - aux_ctrl |= 0x3c400001; - ret = l2x0_of_init(aux_ctrl, 0xc200c3fe); + ret = l2x0_of_init(0x3c400001, 0xc20fc3fe); if (!ret) l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs); #endif From f5942b851dffbfada9c18347e14bd3b8f9438a5f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:50:00 +0200 Subject: [PATCH 0551/1983] ARM: l2c: ux500: remove associativity and way size from aux_ctrl Acked-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/mach-ux500/cache-l2x0.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index 067c37a054fbb6..5b891d05105451 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -45,8 +45,6 @@ static void ux500_l2c310_write_sec(unsigned long val, unsigned reg) static int __init ux500_l2x0_init(void) { - u32 aux_val = 0x3e000000; - if (cpu_is_u8500_family() || cpu_is_ux540_family()) l2x0_base = __io_address(U8500_L2CC_BASE); else @@ -56,21 +54,12 @@ static int __init ux500_l2x0_init(void) /* Unlock before init */ ux500_l2x0_unlock(); - /* DBx540's L2 has 128KB way size */ - if (cpu_is_ux540_family()) - /* 128KB way size */ - aux_val |= L2C_AUX_CTRL_WAY_SIZE(4); - else - /* 64KB way size */ - aux_val |= L2C_AUX_CTRL_WAY_SIZE(3); - outer_cache.write_sec = ux500_l2c310_write_sec; - /* 64KB way size, 8 way associativity, force WA */ if (of_have_populated_dt()) - l2x0_of_init(aux_val, 0xc0000fff); + l2x0_of_init(0x3e000000, 0xc00f0fff); else - l2x0_init(l2x0_base, aux_val, 0xc0000fff); + l2x0_init(l2x0_base, 0x3e000000, 0xc00f0fff); return 0; } From cfb565311327d631809c2f6d55178733d017e399 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:50:00 +0200 Subject: [PATCH 0552/1983] ARM: l2c: ux500: don't try to change the L2 cache auxiliary control register ux500 can't change the auxiliary control register, so there's no point passing values to try and modify it to the l2x0 init functions. Acked-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/mach-ux500/cache-l2x0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c index 5b891d05105451..842ebedbdd1c3d 100644 --- a/arch/arm/mach-ux500/cache-l2x0.c +++ b/arch/arm/mach-ux500/cache-l2x0.c @@ -57,9 +57,9 @@ static int __init ux500_l2x0_init(void) outer_cache.write_sec = ux500_l2c310_write_sec; if (of_have_populated_dt()) - l2x0_of_init(0x3e000000, 0xc00f0fff); + l2x0_of_init(0, ~0); else - l2x0_init(l2x0_base, 0x3e000000, 0xc00f0fff); + l2x0_init(l2x0_base, 0, ~0); return 0; } From 2de88a7e53d4c2f6817f89152df08fbb1d26ef3a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:50:00 +0200 Subject: [PATCH 0553/1983] ARM: l2c: cns3xxx: remove cache size override Signed-off-by: Russell King --- arch/arm/mach-cns3xxx/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c index dd2b9926a76c6d..bce1d36d0ffb3b 100644 --- a/arch/arm/mach-cns3xxx/core.c +++ b/arch/arm/mach-cns3xxx/core.c @@ -258,7 +258,7 @@ void __init cns3xxx_l2x0_init(void) writel(val, base + L310_DATA_LATENCY_CTRL); /* 32 KiB, 8-way, parity disable */ - l2x0_init(base, 0x00540000, 0xfe000fff); + l2x0_init(base, 0x00500000, 0xfe0f0fff); } #endif /* CONFIG_CACHE_L2X0 */ From d35bc6fcd841d5d4b1ad1f73a39a1b3daff6db55 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:03 +0200 Subject: [PATCH 0554/1983] ARM: l2c: exynos: remove cache size override Signed-off-by: Russell King --- arch/arm/mach-exynos/common.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index f18be40e5b2129..0d19a1b0444e47 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -45,9 +45,6 @@ #include "common.h" #include "regs-pmu.h" -#define L2_AUX_VAL 0x7C470001 -#define L2_AUX_MASK 0xC200ffff - static const char name_exynos4210[] = "EXYNOS4210"; static const char name_exynos4212[] = "EXYNOS4212"; static const char name_exynos4412[] = "EXYNOS4412"; @@ -400,7 +397,7 @@ static int __init exynos4_l2x0_cache_init(void) { int ret; - ret = l2x0_of_init(L2_AUX_VAL, L2_AUX_MASK); + ret = l2x0_of_init(0x3c400001, 0xc20fffff); if (ret) return ret; From e5edf96e306ce34b33706f2b566b524218ecbc87 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:03 +0200 Subject: [PATCH 0555/1983] ARM: l2c: nomadik: remove cache size override Acked-by: Linus Walleij Signed-off-by: Russell King --- arch/arm/mach-nomadik/cpu-8815.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-nomadik/cpu-8815.c b/arch/arm/mach-nomadik/cpu-8815.c index 4a1065e41e9c5a..0f245bcc6b7ed1 100644 --- a/arch/arm/mach-nomadik/cpu-8815.c +++ b/arch/arm/mach-nomadik/cpu-8815.c @@ -147,7 +147,7 @@ static void __init cpu8815_init_of(void) { #ifdef CONFIG_CACHE_L2X0 /* At full speed latency must be >=2, so 0x249 in low bits */ - l2x0_of_init(0x00730249, 0xfe000fff); + l2x0_of_init(0x00700249, 0xfe0fefff); #endif of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } From c43f588ae02224a3e676b88fba2c8fd3d24f34b4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:03 +0200 Subject: [PATCH 0556/1983] ARM: l2c: omap2: remove cache size override Acked-by: Tony Lindgren Signed-off-by: Russell King --- arch/arm/mach-omap2/omap4-common.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index ce2fad84a43c70..c0f9a81a2d3289 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -212,20 +212,18 @@ static int __init omap_l2_cache_init(void) return -ENOMEM; /* 16-way associativity, parity disabled, way size - 64KB (es2.0 +) */ - aux_ctrl = L310_AUX_CTRL_ASSOCIATIVITY_16 | - L310_AUX_CTRL_CACHE_REPLACE_RR | + aux_ctrl = L310_AUX_CTRL_CACHE_REPLACE_RR | L310_AUX_CTRL_NS_LOCKDOWN | L310_AUX_CTRL_NS_INT_CTRL | - L2C_AUX_CTRL_WAY_SIZE(3) | L2C_AUX_CTRL_SHARED_OVERRIDE | L310_AUX_CTRL_DATA_PREFETCH | L310_AUX_CTRL_INSTR_PREFETCH; outer_cache.write_sec = omap4_l2c310_write_sec; if (of_have_populated_dt()) - l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); + l2x0_of_init(aux_ctrl, 0xc19fffff); else - l2x0_init(l2cache_base, aux_ctrl, L2X0_AUX_CTRL_MASK); + l2x0_init(l2cache_base, aux_ctrl, 0xc19fffff); return 0; } From 7a43f1dd05fcc3743da0a724ac4b8b2ccf753403 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:53 +0200 Subject: [PATCH 0557/1983] ARM: l2c: prima2: remove cache size override Signed-off-by: Russell King --- arch/arm/boot/dts/marco.dtsi | 2 +- arch/arm/boot/dts/prima2.dtsi | 2 +- arch/arm/mach-prima2/l2x0.c | 34 +--------------------------------- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/arch/arm/boot/dts/marco.dtsi b/arch/arm/boot/dts/marco.dtsi index 1579c3491ccd5f..247879aa1224d5 100644 --- a/arch/arm/boot/dts/marco.dtsi +++ b/arch/arm/boot/dts/marco.dtsi @@ -36,7 +36,7 @@ ranges = <0x40000000 0x40000000 0xa0000000>; l2-cache-controller@c0030000 { - compatible = "sirf,marco-pl310-cache", "arm,pl310-cache"; + compatible = "arm,pl310-cache"; reg = <0xc0030000 0x1000>; interrupts = <0 59 0>; arm,tag-latency = <1 1 1>; diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index 0e219932d7cce3..29b578727b0a02 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -48,7 +48,7 @@ ranges = <0x40000000 0x40000000 0x80000000>; l2-cache-controller@80040000 { - compatible = "arm,pl310-cache", "sirf,prima2-pl310-cache"; + compatible = "arm,pl310-cache"; reg = <0x80040000 0x1000>; interrupts = <59>; arm,tag-latency = <1 1 1>; diff --git a/arch/arm/mach-prima2/l2x0.c b/arch/arm/mach-prima2/l2x0.c index 3a4eda45171ed5..09f68f046830f1 100644 --- a/arch/arm/mach-prima2/l2x0.c +++ b/arch/arm/mach-prima2/l2x0.c @@ -8,42 +8,10 @@ #include #include -#include #include -struct l2x0_aux -{ - u32 val; - u32 mask; -}; - -static struct l2x0_aux prima2_l2x0_aux __initconst = { - .val = L2C_AUX_CTRL_WAY_SIZE(2), - .mask = 0, -}; - -static struct l2x0_aux marco_l2x0_aux __initconst = { - .val = L2C_AUX_CTRL_WAY_SIZE(2) | L310_AUX_CTRL_ASSOCIATIVITY_16, - .mask = L2X0_AUX_CTRL_MASK, -}; - -static struct of_device_id sirf_l2x0_ids[] __initconst = { - { .compatible = "sirf,prima2-pl310-cache", .data = &prima2_l2x0_aux, }, - { .compatible = "sirf,marco-pl310-cache", .data = &marco_l2x0_aux, }, - {}, -}; - static int __init sirfsoc_l2x0_init(void) { - struct device_node *np; - const struct l2x0_aux *aux; - - np = of_find_matching_node(NULL, sirf_l2x0_ids); - if (np) { - aux = of_match_node(sirf_l2x0_ids, np)->data; - return l2x0_of_init(aux->val, aux->mask); - } - - return 0; + return l2x0_of_init(0, ~0); } early_initcall(sirfsoc_l2x0_init); From e4d1820b2ea1d8e5398ed03ce9fe494057447f4c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:53 +0200 Subject: [PATCH 0558/1983] ARM: l2c: shmobile: remove cache size override Signed-off-by: Russell King --- arch/arm/mach-shmobile/board-armadillo800eva-reference.c | 2 +- arch/arm/mach-shmobile/board-armadillo800eva.c | 2 +- arch/arm/mach-shmobile/board-kzm9g-reference.c | 2 +- arch/arm/mach-shmobile/board-kzm9g.c | 2 +- arch/arm/mach-shmobile/setup-r8a7778.c | 2 +- arch/arm/mach-shmobile/setup-r8a7779.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-shmobile/board-armadillo800eva-reference.c b/arch/arm/mach-shmobile/board-armadillo800eva-reference.c index 34e7f3c17dd2ea..39e11f48e8bce8 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva-reference.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva-reference.c @@ -165,7 +165,7 @@ static void __init eva_init(void) #ifdef CONFIG_CACHE_L2X0 /* Shared attribute override enable, 32K*8way */ - l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff); + l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff); #endif r8a7740_add_standard_devices_dt(); diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 69ec71038ec78b..179f455d96c728 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -1271,7 +1271,7 @@ static void __init eva_init(void) #ifdef CONFIG_CACHE_L2X0 /* Shared attribute override enable, 32K*8way */ - l2x0_init(IOMEM(0xf0002000), 0x00440000, 0xc2000fff); + l2x0_init(IOMEM(0xf0002000), 0x00400000, 0xc20f0fff); #endif i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); diff --git a/arch/arm/mach-shmobile/board-kzm9g-reference.c b/arch/arm/mach-shmobile/board-kzm9g-reference.c index 85873f186d777d..a735a1d80c285b 100644 --- a/arch/arm/mach-shmobile/board-kzm9g-reference.c +++ b/arch/arm/mach-shmobile/board-kzm9g-reference.c @@ -37,7 +37,7 @@ static void __init kzm_init(void) #ifdef CONFIG_CACHE_L2X0 /* Shared attribute override enable, 64K*8way */ - l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff); + l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); #endif } diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index bd9931f223ae92..4c4cb64e99fefc 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -879,7 +879,7 @@ static void __init kzm_init(void) #ifdef CONFIG_CACHE_L2X0 /* Shared attribute override enable, 64K*8way */ - l2x0_init(IOMEM(0xf0100000), 0x00460000, 0xc2000fff); + l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); #endif i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices)); diff --git a/arch/arm/mach-shmobile/setup-r8a7778.c b/arch/arm/mach-shmobile/setup-r8a7778.c index 3a8e5316671e7c..6dd7ddf8874169 100644 --- a/arch/arm/mach-shmobile/setup-r8a7778.c +++ b/arch/arm/mach-shmobile/setup-r8a7778.c @@ -301,7 +301,7 @@ void __init r8a7778_add_dt_devices(void) * Shared attribute override enable, 64K*16way * don't call iounmap(base) */ - l2x0_init(base, 0x00470000, 0xc2000fff); + l2x0_init(base, 0x00400000, 0xc20f0fff); } #endif diff --git a/arch/arm/mach-shmobile/setup-r8a7779.c b/arch/arm/mach-shmobile/setup-r8a7779.c index 91c90bf0ae8372..a6630fccfc4577 100644 --- a/arch/arm/mach-shmobile/setup-r8a7779.c +++ b/arch/arm/mach-shmobile/setup-r8a7779.c @@ -701,7 +701,7 @@ void __init r8a7779_add_standard_devices(void) { #ifdef CONFIG_CACHE_L2X0 /* Shared attribute override enable, 64K*16way */ - l2x0_init(IOMEM(0xf0100000), 0x00470000, 0xc2000fff); + l2x0_init(IOMEM(0xf0100000), 0x00400000, 0xc20f0fff); #endif r8a7779_pm_init(); From 58bb217e3618caa047da8b5a11147391035a6a7a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:53 +0200 Subject: [PATCH 0559/1983] ARM: l2c: spear13xx: remove cache size override Signed-off-by: Russell King --- arch/arm/mach-spear/spear13xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-spear/spear13xx.c b/arch/arm/mach-spear/spear13xx.c index dcb300443b6677..c9897ea3898005 100644 --- a/arch/arm/mach-spear/spear13xx.c +++ b/arch/arm/mach-spear/spear13xx.c @@ -46,7 +46,7 @@ void __init spear13xx_l2x0_init(void) */ writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL); writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL); - l2x0_init(VA_L2CC_BASE, 0x30a60001, 0xfe00ffff); + l2x0_init(VA_L2CC_BASE, 0x30a00001, 0xfe0fffff); } /* From 2a81e78453a5934c7c2b896459dd7ce5b6199424 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:53 +0200 Subject: [PATCH 0560/1983] ARM: l2c: sti: remove cache size override Signed-off-by: Russell King --- arch/arm/mach-sti/board-dt.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-sti/board-dt.c b/arch/arm/mach-sti/board-dt.c index dc8669efc12d68..cf716ae1072619 100644 --- a/arch/arm/mach-sti/board-dt.c +++ b/arch/arm/mach-sti/board-dt.c @@ -16,15 +16,9 @@ void __init stih41x_l2x0_init(void) { - u32 way_size = 0x4; - u32 aux_ctrl; - /* may be this can be encoded in macros like BIT*() */ - aux_ctrl = L2C_AUX_CTRL_SHARED_OVERRIDE | - L310_AUX_CTRL_DATA_PREFETCH | - L310_AUX_CTRL_INSTR_PREFETCH | - L2C_AUX_CTRL_WAY_SIZE(way_size); - - l2x0_of_init(aux_ctrl, L2X0_AUX_CTRL_MASK); + l2x0_of_init(L2C_AUX_CTRL_SHARED_OVERRIDE | + L310_AUX_CTRL_DATA_PREFETCH | + L310_AUX_CTRL_INSTR_PREFETCH, 0xc00f0fff); } static void __init stih41x_machine_init(void) From ad1f8a911d1bbad2d3cfd79cda7e037e254964fe Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:53 +0200 Subject: [PATCH 0561/1983] ARM: l2c: zynq: remove cache size override Signed-off-by: Russell King --- arch/arm/mach-zynq/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 8c09a8393fb630..b58f1717800604 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c @@ -67,7 +67,7 @@ static void __init zynq_init_machine(void) /* * 64KB way size, 8-way associativity, parity disabled */ - l2x0_of_init(0x02060000, 0xF0F0FFFF); + l2x0_of_init(0x02000000, 0xf0ffffff); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); From f34989fda802712d7d896a2d675ced161e3d49a2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:53 +0200 Subject: [PATCH 0562/1983] ARM: l2c: realview: improve commentry about the L2 cache requirements Add better commentry about the L2 cache requirements on these platforms. Unfortunately, the auxiliary control register is not pre-set to indicate the correct cache parameters, so we have to manually program these. Signed-off-by: Russell King --- arch/arm/mach-realview/realview_eb.c | 9 +++++++-- arch/arm/mach-realview/realview_pb1176.c | 8 +++++++- arch/arm/mach-realview/realview_pb11mp.c | 9 +++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c index c85ddb2a0ad090..b575895037b8f7 100644 --- a/arch/arm/mach-realview/realview_eb.c +++ b/arch/arm/mach-realview/realview_eb.c @@ -442,8 +442,13 @@ static void __init realview_eb_init(void) realview_eb11mp_fixup(); #ifdef CONFIG_CACHE_L2X0 - /* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled - * Bits: .... ...0 0111 1001 0000 .... .... .... */ + /* + * The PL220 needs to be manually configured as the hardware + * doesn't report the correct sizes. + * 1MB (128KB/way), 8-way associativity, event monitor and + * parity enabled, ignore share bit, no force write allocate + * Bits: .... ...0 0111 1001 0000 .... .... .... + */ l2x0_init(__io_address(REALVIEW_EB11MP_L220_BASE), 0x00790000, 0xfe000fff); #endif platform_device_register(&pmu_device); diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c index c5eade76461be3..e3bddb5ab10fba 100644 --- a/arch/arm/mach-realview/realview_pb1176.c +++ b/arch/arm/mach-realview/realview_pb1176.c @@ -355,7 +355,13 @@ static void __init realview_pb1176_init(void) int i; #ifdef CONFIG_CACHE_L2X0 - /* 128Kb (16Kb/way) 8-way associativity. evmon/parity/share enabled. */ + /* + * The PL220 needs to be manually configured as the hardware + * doesn't report the correct sizes. + * 128kB (16kB/way), 8-way associativity, event monitor and + * parity enabled, ignore share bit, no force write allocate + * Bits: .... ...0 0111 0011 0000 .... .... .... + */ l2x0_init(__io_address(REALVIEW_PB1176_L220_BASE), 0x00730000, 0xfe000fff); #endif diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c index f4b0962578feb9..101deaf2630bd1 100644 --- a/arch/arm/mach-realview/realview_pb11mp.c +++ b/arch/arm/mach-realview/realview_pb11mp.c @@ -337,8 +337,13 @@ static void __init realview_pb11mp_init(void) int i; #ifdef CONFIG_CACHE_L2X0 - /* 1MB (128KB/way), 8-way associativity, evmon/parity/share enabled - * Bits: .... ...0 0111 1001 0000 .... .... .... */ + /* + * The PL220 needs to be manually configured as the hardware + * doesn't report the correct sizes. + * 1MB (128KB/way), 8-way associativity, event monitor and + * parity enabled, ignore share bit, no force write allocate + * Bits: .... ...0 0111 1001 0000 .... .... .... + */ l2x0_init(__io_address(REALVIEW_TC11MP_L220_BASE), 0x00790000, 0xfe000fff); #endif From e58f0dc6e61cc8d6530d9f35bd496a55aefbb35f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:54 +0200 Subject: [PATCH 0563/1983] ARM: l2c: kill L2X0_AUX_CTRL_MASK before anyone else makes use of this L2X0_AUX_CTRL_MASK is not useful for PL310s. It would be better if people thought about their value for this rather than cargo-cult programming. Signed-off-by: Russell King --- arch/arm/include/asm/hardware/cache-l2x0.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index b3ee122c6f24ff..84bbd31b89103d 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -87,7 +87,6 @@ #define L310_CACHE_ID_RTL_R3P2 0x08 #define L310_CACHE_ID_RTL_R3P3 0x09 -#define L2X0_AUX_CTRL_MASK 0xc0000fff /* L2C auxiliary control register - bits common to L2C-210/220/310 */ #define L2C_AUX_CTRL_WAY_SIZE_SHIFT 17 #define L2C_AUX_CTRL_WAY_SIZE_MASK (7 << 17) From 69218eacc0cf80d128794e50c5ff200eb5d920ee Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:54 +0200 Subject: [PATCH 0564/1983] ARM: l2c: print a warning with L2C-310 caches if the cache size is modified As we have now removed all instances of the L2C-310 having its cache size "modified" via platform/SoC code, discourage new cases showing up by printing a warning. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index db3f18a968b2bd..48b826e759cbe2 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -732,6 +732,8 @@ static void __init __l2c_init(const struct l2c_init_data *data, /* Determine the number of ways */ switch (cache_id & L2X0_CACHE_ID_PART_MASK) { case L2X0_CACHE_ID_PART_L310: + if ((aux_val | ~aux_mask) & (L2C_AUX_CTRL_WAY_SIZE_MASK | L310_AUX_CTRL_ASSOCIATIVITY_16)) + pr_warn("L2C: DT/platform tries to modify or specify cache size\n"); if (aux & (1 << 16)) ways = 16; else From 6f4290e95d925f2ff64f13b3ec21d683acc377d5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:54 +0200 Subject: [PATCH 0565/1983] ARM: l2c: vexpress ca9x4: move L2 cache initialisation earlier It is beneficial to have the L2 cache up and running earlier in the system boot. Not only will this allow for simpler code when we come to enable some features, but it also means that we get a more accurate bogomips value for the udelay() loop. Calibrating the loop with the L2 cache off, and then running with the L2 cache on is not the best idea. Signed-off-by: Russell King --- arch/arm/mach-vexpress/ct-ca9x4.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index 6c4ffb6c5ad83d..204038ef37950e 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -45,6 +45,23 @@ static void __init ct_ca9x4_map_io(void) iotable_init(ct_ca9x4_io_desc, ARRAY_SIZE(ct_ca9x4_io_desc)); } +static void __init ca9x4_l2_init(void) +{ +#ifdef CONFIG_CACHE_L2X0 + void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K); + + if (l2x0_base) { + /* set RAM latencies to 1 cycle for this core tile. */ + writel(0, l2x0_base + L310_TAG_LATENCY_CTRL); + writel(0, l2x0_base + L310_DATA_LATENCY_CTRL); + + l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff); + } else { + pr_err("L2C: unable to map L2 cache controller\n"); + } +#endif +} + #ifdef CONFIG_HAVE_ARM_TWD static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, A9_MPCORE_TWD, IRQ_LOCALTIMER); @@ -63,6 +80,7 @@ static void __init ct_ca9x4_init_irq(void) gic_init(0, 29, ioremap(A9_MPCORE_GIC_DIST, SZ_4K), ioremap(A9_MPCORE_GIC_CPU, SZ_256)); ca9x4_twd_init(); + ca9x4_l2_init(); } static int ct_ca9x4_clcd_setup(struct clcd_fb *fb) @@ -141,16 +159,6 @@ static void __init ct_ca9x4_init(void) { int i; -#ifdef CONFIG_CACHE_L2X0 - void __iomem *l2x0_base = ioremap(CT_CA9X4_L2CC, SZ_4K); - - /* set RAM latencies to 1 cycle for this core tile. */ - writel(0, l2x0_base + L310_TAG_LATENCY_CTRL); - writel(0, l2x0_base + L310_DATA_LATENCY_CTRL); - - l2x0_init(l2x0_base, 0x00400000, 0xfe0fffff); -#endif - for (i = 0; i < ARRAY_SIZE(ct_ca9x4_amba_devs); i++) amba_device_register(ct_ca9x4_amba_devs[i], &iomem_resource); From e475930799a2b4e71f90a17b26fa8935e39b5f70 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:54 +0200 Subject: [PATCH 0566/1983] ARM: l2c: add L2C-310 power control DT properties Add two new properties for setting thte L3 power control register. Two new properties are added: arm,dynamic-clk-gating arm,standby-mode iMX6 sets both these, add the properties there. Acked-by: Shawn Guo Signed-off-by: Russell King --- Documentation/devicetree/bindings/arm/l2cc.txt | 2 ++ arch/arm/boot/dts/imx6qdl.dtsi | 2 ++ arch/arm/boot/dts/imx6sl.dtsi | 2 ++ arch/arm/mm/cache-l2x0.c | 10 ++++++++++ 4 files changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index b513cb8196fefd..e0dd400ecea65b 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -40,6 +40,8 @@ Optional properties: - arm,filter-ranges : Starting address and length of window to filter. Addresses in the filter window are directed to the M1 port. Other addresses will go to the M0 port. +- arm,dynamic-clk-gating : Enables dynamic clock gating (PL310) +- arm,standby-mode : Enables standby mode (PL310) - interrupts : 1 combined interrupt. - cache-id-part: cache id part number to be used if it is not present on hardware diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index 732f2d247a7f16..c775098bb143d5 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -141,6 +141,8 @@ cache-level = <2>; arm,tag-latency = <4 2 3>; arm,data-latency = <4 2 3>; + arm,dynamic-clk-gating; + arm,standby-mode; }; pcie: pcie@0x01000000 { diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi index 1b1e5f319c9372..cc758e77df53c9 100644 --- a/arch/arm/boot/dts/imx6sl.dtsi +++ b/arch/arm/boot/dts/imx6sl.dtsi @@ -156,6 +156,8 @@ cache-level = <2>; arm,tag-latency = <4 2 3>; arm,data-latency = <4 2 3>; + arm,dynamic-clk-gating; + arm,standby-mode; }; pmu { diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 48b826e759cbe2..8a5110deb6574a 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -904,6 +904,7 @@ static void __init l2c310_of_parse(const struct device_node *np, u32 data[3] = { 0, 0, 0 }; u32 tag[3] = { 0, 0, 0 }; u32 filter[2] = { 0, 0 }; + u32 val; of_property_read_u32_array(np, "arm,tag-latency", tag, ARRAY_SIZE(tag)); if (tag[0] && tag[1] && tag[2]) @@ -930,6 +931,15 @@ static void __init l2c310_of_parse(const struct device_node *np, writel_relaxed((filter[0] & ~(SZ_1M - 1)) | L310_ADDR_FILTER_EN, l2x0_base + L310_ADDR_FILTER_START); } + + val = 0; + if (of_property_read_bool(np, "arm,dynamic-clk-gating")) + val |= L310_DYNAMIC_CLK_GATING_EN; + if (of_property_read_bool(np, "arm,standby-mode")) + val |= L310_STNDBY_MODE_EN; + + if (val) + l2c_write_sec(val, l2x0_base, L310_POWER_CTRL); } static const struct l2c_init_data of_l2c310_data __initconst = { From 621aa4718119a578ec431afb3d2db1a538c46ab5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:54 +0200 Subject: [PATCH 0567/1983] ARM: l2c: check that DT files specify the required "cache-unified" property This is a required property, and should always be specified. Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 8a5110deb6574a..96a1374127de20 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -1378,6 +1378,10 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) data = of_match_node(l2x0_ids, np)->data; + /* All L2 caches are unified, so this property should be specified */ + if (!of_property_read_bool(np, "cache-unified")) + pr_err("L2C: device tree omits to specify unified cache\n"); + /* L2 configuration can only be changed if the cache is disabled */ if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & L2X0_CTRL_EN)) if (data->of_parse) From 8c41a8afdf5bd09536ada33cf64ff2825d86b6e8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Jul 2014 20:51:54 +0200 Subject: [PATCH 0568/1983] ARM: l2c: add warnings for stuff modifying aux_ctrl register values Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 96a1374127de20..5de7f6c5630695 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -715,7 +715,15 @@ static void __init __l2c_init(const struct l2c_init_data *data, { struct outer_cache_fns fns; unsigned way_size_bits, ways; - u32 aux; + u32 aux, old_aux; + + /* + * Sanity check the aux values. aux_mask is the bits we preserve + * from reading the hardware register, and aux_val is the bits we + * set. + */ + if (aux_val & aux_mask) + pr_alert("L2C: platform provided aux values permit register corruption.\n"); /* * It is strange to save the register state before initialisation, @@ -724,11 +732,14 @@ static void __init __l2c_init(const struct l2c_init_data *data, if (data->save) data->save(l2x0_base); - aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); - + old_aux = aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; aux |= aux_val; + if (old_aux != aux) + pr_warn("L2C: DT/platform modifies aux control register: 0x%08x -> 0x%08x\n", + old_aux, aux); + /* Determine the number of ways */ switch (cache_id & L2X0_CACHE_ID_PART_MASK) { case L2X0_CACHE_ID_PART_L310: @@ -1361,7 +1372,7 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) const struct l2c_init_data *data; struct device_node *np; struct resource res; - u32 cache_id; + u32 cache_id, old_aux; np = of_find_matching_node(NULL, l2x0_ids); if (!np) @@ -1378,6 +1389,14 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask) data = of_match_node(l2x0_ids, np)->data; + old_aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); + if (old_aux != ((old_aux & aux_mask) | aux_val)) { + pr_warn("L2C: platform modifies aux control register: 0x%08x -> 0x%08x\n", + old_aux, (old_aux & aux_mask) | aux_val); + } else if (aux_mask != ~0U && aux_val != 0) { + pr_alert("L2C: platform provided aux values match the hardware, so have no effect. Please remove them.\n"); + } + /* All L2 caches are unified, so this property should be specified */ if (!of_property_read_bool(np, "cache-unified")) pr_err("L2C: device tree omits to specify unified cache\n"); From f5fc1d7a061e876eee657b37ee8d0ca6ee00b95f Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 2 Aug 2015 16:03:12 +0200 Subject: [PATCH 0569/1983] gpu: galcore: Rely on linux to do proper cache handling With modern kernels all these corner cases should be handled using the dma_* interfaces. Get rid of all these options and simplify the code to just use proper kernel api. Any kernel case implementations that may come up should now be handled through the drivers platform customization interface. --- drivers/gpu/galcore/Kbuild | 6 - drivers/gpu/galcore/config | 2 - drivers/gpu/galcore/gc_hal_kernel_allocator.c | 13 -- drivers/gpu/galcore/gc_hal_kernel_os.c | 192 +----------------- drivers/gpu/galcore/inc/gc_hal_options.h | 9 - 5 files changed, 9 insertions(+), 213 deletions(-) diff --git a/drivers/gpu/galcore/Kbuild b/drivers/gpu/galcore/Kbuild index 7695af8aaf24a0..79c98505e5e2df 100644 --- a/drivers/gpu/galcore/Kbuild +++ b/drivers/gpu/galcore/Kbuild @@ -197,12 +197,6 @@ else EXTRA_CFLAGS += -DgcdENABLE_VG=1 endif -ifeq ($(ENABLE_OUTER_CACHE_PATCH), 1) -EXTRA_CFLAGS += -DgcdENABLE_OUTER_CACHE_PATCH=1 -else -EXTRA_CFLAGS += -DgcdENABLE_OUTER_CACHE_PATCH=0 -endif - ifeq ($(USE_BANK_ALIGNMENT), 1) EXTRA_CFLAGS += -DgcdENABLE_BANK_ALIGNMENT=1 ifneq ($(BANK_BIT_START), 0) diff --git a/drivers/gpu/galcore/config b/drivers/gpu/galcore/config index ce586d7c776c0b..5c1fed61d0a3c7 100644 --- a/drivers/gpu/galcore/config +++ b/drivers/gpu/galcore/config @@ -27,8 +27,6 @@ VIVANTE_ENABLE_VG ?= 1 FORCE_ALL_VIDEO_MEMORY_CACHED ?= 0 NONPAGED_MEMORY_CACHEABLE ?= 0 NONPAGED_MEMORY_BUFFERABLE ?= 1 -CACHE_FUNCTION_UNIMPLEMENTED ?= 1 -ENABLE_OUTER_CACHE_PATCH ?= 0 USE_BANK_ALIGNMENT ?= 1 BANK_BIT_START ?= 13 BANK_BIT_END ?= 15 diff --git a/drivers/gpu/galcore/gc_hal_kernel_allocator.c b/drivers/gpu/galcore/gc_hal_kernel_allocator.c index 9e7be0a0f28805..9d9d03a6ccda15 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_allocator.c +++ b/drivers/gpu/galcore/gc_hal_kernel_allocator.c @@ -367,19 +367,6 @@ _DefaultAlloc( { flush_dcache_page(page); -#if !gcdCACHE_FUNCTION_UNIMPLEMENTED && defined(CONFIG_OUTER_CACHE) && gcdENABLE_OUTER_CACHE_PATCH - if (page_to_phys(page)) - { - _HandleOuterCache( - Allocator->os, - page_to_phys(page), - gcvNULL, - PAGE_SIZE, - gcvCACHE_FLUSH - ); - } -#endif - priv->high += PAGE_SIZE; } } diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index bfe388683a6744..19aa4d291cd513 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -376,113 +376,6 @@ _QueryProcessPageTable( return gcvSTATUS_OK; } -#if !gcdCACHE_FUNCTION_UNIMPLEMENTED && defined(CONFIG_OUTER_CACHE) -static inline gceSTATUS -outer_func( - gceCACHEOPERATION Type, - unsigned long Start, - unsigned long End - ) -{ - switch (Type) - { - case gcvCACHE_CLEAN: - outer_clean_range(Start, End); - break; - case gcvCACHE_INVALIDATE: - outer_inv_range(Start, End); - break; - case gcvCACHE_FLUSH: - outer_flush_range(Start, End); - break; - default: - return gcvSTATUS_INVALID_ARGUMENT; - break; - } - return gcvSTATUS_OK; -} - -#if gcdENABLE_OUTER_CACHE_PATCH -/******************************************************************************* -** _HandleOuterCache -** -** Handle the outer cache for the specified addresses. -** -** ARGUMENTS: -** -** gckOS Os -** Pointer to gckOS object. -** -** gctPOINTER Physical -** Physical address to flush. -** -** gctPOINTER Logical -** Logical address to flush. -** -** gctSIZE_T Bytes -** Size of the address range in bytes to flush. -** -** gceOUTERCACHE_OPERATION Type -** Operation need to be execute. -*/ -gceSTATUS -_HandleOuterCache( - IN gckOS Os, - IN gctUINT32 Physical, - IN gctPOINTER Logical, - IN gctSIZE_T Bytes, - IN gceCACHEOPERATION Type - ) -{ - gceSTATUS status; - unsigned long paddr; - gctPOINTER vaddr; - gctUINT32 offset, bytes, left; - - gcmkHEADER_ARG("Os=0x%X Logical=0x%X Bytes=%lu", - Os, Logical, Bytes); - - if (Physical != gcvINVALID_ADDRESS) - { - /* Non paged memory or gcvPOOL_USER surface */ - paddr = (unsigned long) Physical; - gcmkONERROR(outer_func(Type, paddr, paddr + Bytes)); - } - else - { - /* Non contiguous virtual memory */ - vaddr = Logical; - left = Bytes; - - while (left) - { - /* Handle (part of) current page. */ - offset = (gctUINTPTR_T)vaddr & ~PAGE_MASK; - - bytes = gcmMIN(left, PAGE_SIZE - offset); - - gcmkONERROR(_QueryProcessPageTable(vaddr, (gctUINT32*)&paddr)); - gcmkONERROR(outer_func(Type, paddr, paddr + bytes)); - - vaddr = (gctUINT8_PTR)vaddr + bytes; - left -= bytes; - } - } - - mb(); - - /* Success. */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - -OnError: - /* Return the status. */ - gcmkFOOTER(); - return status; -} -#endif -#endif - gctBOOL _AllowAccess( IN gckOS Os, @@ -3641,7 +3534,7 @@ gckOS_LockPages( PLINUX_MDL_MAP mdlMap; gckALLOCATOR allocator; - gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%lu", Os, Physical, Logical); + gcmkHEADER_ARG("Os=0x%X Physical=0x%X Bytes=%u", Os, Physical, Bytes); /* Verify the arguments. */ gcmkVERIFY_OBJECT(Os, gcvOBJ_OS); @@ -3688,7 +3581,6 @@ gckOS_LockPages( *Logical = mdlMap->vmaAddr; /* Return the page number according to the GPU page size. */ - gcmkASSERT((PAGE_SIZE % 4096) == 0); gcmkASSERT((PAGE_SIZE / 4096) >= 1); *PageCount = mdl->numPages * (PAGE_SIZE / 4096); @@ -5490,37 +5382,11 @@ gckOS_CacheClean( return gcvSTATUS_OK; } -#if !gcdCACHE_FUNCTION_UNIMPLEMENTED -#ifdef CONFIG_ARM - - /* Inner cache. */ - dmac_map_area(Logical, Bytes, DMA_TO_DEVICE); - -#if defined(CONFIG_OUTER_CACHE) - /* Outer cache. */ -#if gcdENABLE_OUTER_CACHE_PATCH - _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_CLEAN); -#else - outer_clean_range((unsigned long) Handle, (unsigned long) Handle + Bytes); -#endif -#endif - -#elif defined(CONFIG_MIPS) - - dma_cache_wback((unsigned long) Logical, Bytes); - -#elif defined(CONFIG_PPC) - - /* TODO */ - -#else dma_sync_single_for_device( gcvNULL, (dma_addr_t)Physical, Bytes, DMA_TO_DEVICE); -#endif -#endif /* Success. */ gcmkFOOTER_NO(); @@ -5590,33 +5456,11 @@ gckOS_CacheInvalidate( return gcvSTATUS_OK; } -#if !gcdCACHE_FUNCTION_UNIMPLEMENTED -#ifdef CONFIG_ARM - - /* Inner cache. */ - dmac_map_area(Logical, Bytes, DMA_FROM_DEVICE); - -#if defined(CONFIG_OUTER_CACHE) - /* Outer cache. */ -#if gcdENABLE_OUTER_CACHE_PATCH - _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_INVALIDATE); -#else - outer_inv_range((unsigned long) Handle, (unsigned long) Handle + Bytes); -#endif -#endif - -#elif defined(CONFIG_MIPS) - dma_cache_inv((unsigned long) Logical, Bytes); -#elif defined(CONFIG_PPC) - /* TODO */ -#else dma_sync_single_for_device( gcvNULL, (dma_addr_t)Physical, Bytes, DMA_FROM_DEVICE); -#endif -#endif /* Success. */ gcmkFOOTER_NO(); @@ -5686,32 +5530,14 @@ gckOS_CacheFlush( return gcvSTATUS_OK; } -#if !gcdCACHE_FUNCTION_UNIMPLEMENTED -#ifdef CONFIG_ARM - /* Inner cache. */ - dmac_flush_range(Logical, Logical + Bytes); - -#if defined(CONFIG_OUTER_CACHE) - /* Outer cache. */ -#if gcdENABLE_OUTER_CACHE_PATCH - _HandleOuterCache(Os, Physical, Logical, Bytes, gcvCACHE_FLUSH); -#else - outer_flush_range((unsigned long) Handle, (unsigned long) Handle + Bytes); -#endif -#endif - -#elif defined(CONFIG_MIPS) - dma_cache_wback_inv((unsigned long) Logical, Bytes); -#elif defined(CONFIG_PPC) - /* TODO */ -#else - dma_sync_single_for_device( - gcvNULL, - (dma_addr_t)Physical, - Bytes, - DMA_BIDIRECTIONAL); -#endif -#endif + if (Physical != gcvINVALID_ADDRESS) + { + dma_sync_single_for_device( + gcvNULL, + (dma_addr_t)Physical, + Bytes, + DMA_BIDIRECTIONAL); + } /* Success. */ gcmkFOOTER_NO(); diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h index 1bd3feb38aff5f..58162dd0d8f793 100644 --- a/drivers/gpu/galcore/inc/gc_hal_options.h +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -714,15 +714,6 @@ # define gcdUSE_TRIANGLE_STRIP_PATCH 1 #endif -/* - gcdENABLE_OUTER_CACHE_PATCH - - Enable the outer cache patch. -*/ -#ifndef gcdENABLE_OUTER_CACHE_PATCH -# define gcdENABLE_OUTER_CACHE_PATCH 0 -#endif - /* gcdPROCESS_ADDRESS_SPACE From 89d457ca63ecdb17e07512248b6344c961c12b1d Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 3 Aug 2015 13:31:58 +0200 Subject: [PATCH 0570/1983] gpu: galcore: Do not identify GPU every time we check isPresent We identify the GPU when it is first intialized and check its features. After that we use _isPresent to verify the hardware is running again after powering down. We do not need to re-identify the hardware during this step because it will not have magically changed at this point. --- .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 7e277fd58ae722..6ea034e5486e50 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -790,7 +790,6 @@ _IsGPUPresent( ) { gceSTATUS status; - gcsHAL_QUERY_CHIP_IDENTITY identity; gctUINT32 control; gcmkHEADER_ARG("Hardware=0x%x", Hardware); @@ -811,24 +810,6 @@ _IsGPUPresent( 0x00000, control)); - /* Identify the hardware. */ - gcmkONERROR(_IdentifyHardware(Hardware->os, - Hardware->core, - &identity)); - - /* Check if these are the same values as saved before. */ - if ((Hardware->identity.chipModel != identity.chipModel) - || (Hardware->identity.chipRevision != identity.chipRevision) - || (Hardware->identity.chipFeatures != identity.chipFeatures) - || (Hardware->identity.chipMinorFeatures != identity.chipMinorFeatures) - || (Hardware->identity.chipMinorFeatures1 != identity.chipMinorFeatures1) - || (Hardware->identity.chipMinorFeatures2 != identity.chipMinorFeatures2) - ) - { - gcmkPRINT("[galcore]: GPU is not present."); - gcmkONERROR(gcvSTATUS_GPU_NOT_RESPONDING); - } - /* Success. */ gcmkFOOTER_NO(); return gcvSTATUS_OK; From d76c0df9fd8e12248891cab5080d47969c9cc36a Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 3 Aug 2015 13:34:31 +0200 Subject: [PATCH 0571/1983] gpu: galcore: remove semaphore locking from debugfs This locking is generally all wrong. It has functions that can sleep while being called from an atomic state, the locking blocks so you can't watch a trace while the gpu is rendering. Just removing it all allows things to work properly even when debugging drawing with multiple cores. --- drivers/gpu/galcore/gc_hal_kernel_debugfs.c | 37 ++------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_debugfs.c b/drivers/gpu/galcore/gc_hal_kernel_debugfs.c index 23e67977b858b8..bed95135532e83 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_debugfs.c +++ b/drivers/gpu/galcore/gc_hal_kernel_debugfs.c @@ -138,7 +138,6 @@ struct _gcsDEBUGFS_Node /*Synchronization primitives*/ #define gcmkNODE_READQ(node) (&((node)->read_q)) #define gcmkNODE_WRITEQ(node) (&((node)->write_q)) -#define gcmkNODE_SEM(node) (&((node)->sem)) /*Utilities*/ #define gcmkMIN(x, y) ((x) < (y) ? (x) : y) @@ -440,7 +439,7 @@ _AppendString ( n = gcmkMIN ( Length , Node->size - 1 ) ; /* make sure we have the memory for it */ - if ( ( message = kmalloc ( n , GFP_KERNEL ) ) == NULL ) + if ( ( message = kmalloc ( n , GFP_ATOMIC ) ) == NULL ) return - ENOMEM ; /* copy into our temp buffer */ @@ -475,12 +474,6 @@ _DebugFSPrint ( return - ERESTARTSYS ; } -/* - if(down_interruptible( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ) - { - return - ERESTARTSYS ; - } -*/ len = vsnprintf ( buffer , sizeof (buffer ) , Message , *( va_list * ) Arguments ) ; buffer[len] = '\0' ; @@ -491,10 +484,7 @@ _DebugFSPrint ( buffer[len] = '\0' ; } res = _AppendString ( gc_dbgfs.currentNode , buffer , len ) ; - up ( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ; -#if 0 - wake_up_interruptible ( gcmkNODE_READQ ( gc_dbgfs.currentNode ) ) ; /* blocked in read*/ -#endif + wake_up ( gcmkNODE_READQ ( gc_dbgfs.currentNode ) ) ; /* blocked in read*/ return res; } @@ -550,15 +540,9 @@ _DebugFSRead ( return - EIO ; } - if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) - { - return - ERESTARTSYS ; - } - /* wait until there's data available (unless we do nonblocking reads) */ while ( *offset >= gcmkNODE_FIRST_EMPTY_BYTE ( node ) ) { - up ( gcmkNODE_SEM ( node ) ) ; if ( file->f_flags & O_NONBLOCK ) { return - EAGAIN ; @@ -567,11 +551,6 @@ _DebugFSRead ( { return - ERESTARTSYS ; /* signal: tell the fs layer to handle it */ } - /* otherwise loop, but first reacquire the lock */ - if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) - { - return - ERESTARTSYS ; - } } data_to_return = _ReadFromNode ( node , &length , offset ) ; if ( data_to_return == NULL ) @@ -589,7 +568,6 @@ _DebugFSRead ( } kfree ( data_to_return ) ; unlock: - up ( gcmkNODE_SEM ( node ) ) ; wake_up_interruptible ( gcmkNODE_WRITEQ ( node ) ) ; return retval ; } @@ -617,11 +595,6 @@ _DebugFSWrite ( return - EIO ; } - if ( down_interruptible ( gcmkNODE_SEM ( node ) ) ) - { - return - ERESTARTSYS ; - } - /* if the message is longer than the buffer, just take the beginning * of it, in hopes that the reader (if any) will have time to read * before we wrap around and obliterate it */ @@ -630,7 +603,6 @@ _DebugFSWrite ( /* make sure we have the memory for it */ if ( ( message = kmalloc ( n , GFP_KERNEL ) ) == NULL ) { - up ( gcmkNODE_SEM ( node ) ) ; return - ENOMEM ; } @@ -638,7 +610,6 @@ _DebugFSWrite ( /* copy into our temp buffer */ if ( copy_from_user ( message , buffer , n ) > 0 ) { - up ( gcmkNODE_SEM ( node ) ) ; kfree ( message ) ; return - EFAULT ; } @@ -647,7 +618,6 @@ _DebugFSWrite ( _WriteToNode ( node , message , n ) ; kfree ( message ) ; - up ( gcmkNODE_SEM ( node ) ) ; /* wake up any readers that might be waiting for the data. we call * schedule in the vague hope that a reader will run before the @@ -1020,7 +990,6 @@ gckDEBUGFS_CreateNode ( #else init_waitqueue ( gcmkNODE_WRITEQ ( node ) ) ; #endif - sema_init ( gcmkNODE_SEM ( node ) , 1 ) ; /*End the sync primitives*/ /*creating the debug file system*/ @@ -1081,7 +1050,6 @@ gckDEBUGFS_FreeNode ( return ; } - down ( gcmkNODE_SEM ( Node ) ) ; /*free data*/ vfree ( Node->data ) ; @@ -1109,7 +1077,6 @@ gckDEBUGFS_FreeNode ( ptr = & ( ( **ptr ).next ) ; } *ptr = Node->next ; - up ( gcmkNODE_SEM ( Node ) ) ; } /******************************************************************************* From c986e8055a815896539b813a26e820bb765f8761 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 3 Aug 2015 13:36:31 +0200 Subject: [PATCH 0572/1983] gpu: galcore: clean up some debug output These are spelling fixes and type fixes so debug output is accurate. --- drivers/gpu/galcore/gc_hal_kernel_video_memory.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c index a255d7d5fd29e2..f9564040f5acb6 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_video_memory.c +++ b/drivers/gpu/galcore/gc_hal_kernel_video_memory.c @@ -2165,7 +2165,7 @@ gckVIDMEM_HANDLE_Reference( gctINT32 oldValue = 0; gctBOOL acquired = gcvFALSE; - gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID); + gcmkHEADER_ARG("Handle=%d ProcessID=%d", Handle, ProcessID); gcmkONERROR( gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex)); @@ -2210,7 +2210,7 @@ gckVIDMEM_HANDLE_Dereference( gckVIDMEM_HANDLE handleObject = gcvNULL; gctBOOL acquired = gcvFALSE; - gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID); + gcmkHEADER_ARG("Handle=%d ProcessID=%d", Handle, ProcessID); gcmkONERROR( gckKERNEL_FindHandleDatbase(Kernel, @@ -2296,7 +2296,7 @@ gckVIDMEM_HANDLE_LookupAndReference( /* Return result. */ *Node = node; - gcmkFOOTER_ARG("*Node=%d", *Node); + gcmkFOOTER_ARG("*Node=%X", *Node); return gcvSTATUS_OK; OnError: @@ -2343,7 +2343,7 @@ gckVIDMEM_HANDLE_Lookup( *Node = node; - gcmkFOOTER_ARG("*Node=%d", *Node); + gcmkFOOTER_ARG("*Node=%X", *Node); return gcvSTATUS_OK; OnError: From fb53a62f21bafe99de8bf559b0e69ee82778f431 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 3 Aug 2015 13:47:15 +0200 Subject: [PATCH 0573/1983] video: mxcfb: Pad memory allocations for odd resolutions. This should be properly handled in userspace but I have seen a couple of media applications that are not properly handling resolutions that aren't aligned to 8 pixels like the gpu and vpu prefer. This bug presented itself in memory corruption when displaying output on a monitor that has a 1600x900 resolution. I will chase after userspace, but since userspace tools that I can't change expect this alignment I will allocate memory that is aligned to provide enough padding so they won't corrupt system memory if used carelessly. --- drivers/video/mxc/mxc_ipuv3_fb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 1fbfc9dd980aec..5fb7155aada884 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -499,7 +499,7 @@ static int mxcfb_set_par(struct fb_info *fbi) mxcfb_set_fix(fbi); - mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + mem_len = ALIGN(fbi->var.yres_virtual, 16) * fbi->fix.line_length; if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { if (fbi->fix.smem_start) mxcfb_unmap_video_memory(fbi); @@ -1744,8 +1744,8 @@ static int mxcfb_resume(struct platform_device *pdev) */ static int mxcfb_map_video_memory(struct fb_info *fbi) { - if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) - fbi->fix.smem_len = fbi->var.yres_virtual * + if (fbi->fix.smem_len < ALIGN(fbi->var.yres_virtual, 16) * fbi->fix.line_length) + fbi->fix.smem_len = ALIGN(fbi->var.yres_virtual, 16) * fbi->fix.line_length; fbi->screen_base = dma_alloc_writecombine(fbi->device, From b1b5285b7a2853bbd2f959cd1128b61bfe59b920 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 5 Aug 2015 23:42:56 +0200 Subject: [PATCH 0574/1983] video: mxcfb: Fix hdmi edid parsing This re-implements my code I wrote to originally fix the freescale edid parsing. This should fix LG and Sony TV's not choosing the correct resolution on init. --- drivers/video/mxc/mxc_hdmi.c | 43 ++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index cd08d1a92c1863..ec9bcb4264417c 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -1591,7 +1591,7 @@ static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi) { int extblknum; - int i, j, ret; + int i, ret; unsigned char *ediddata = edid; unsigned char tmpedid[EDID_LENGTH]; @@ -1641,10 +1641,10 @@ static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid, } extblknum = edid[0x7E]; - if (extblknum == 255) - extblknum = 0; - if (extblknum) { + if (extblknum < 0) { + return extblknum; + } else { ediddata = edid + EDID_LENGTH; for (i = 0; i < 128; i++) { *ediddata = hdmi_edid_i2c_read(hdmi, i, 1); @@ -1656,25 +1656,25 @@ static int mxc_edid_read_internal(struct mxc_hdmi *hdmi, unsigned char *edid, memset(&fbi->monspecs, 0, sizeof(fbi->monspecs)); fb_edid_to_monspecs(edid, &fbi->monspecs); - if (extblknum) { - ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH, - cfg, &fbi->monspecs); - if (ret < 0) - fb_edid_add_monspecs(edid + EDID_LENGTH, &fbi->monspecs); - if (fbi->monspecs.modedb_len > 0) - hdmi->edid_cfg.hdmi_cap = false; - else + ret = mxc_edid_parse_ext_blk(edid + EDID_LENGTH, + cfg, &fbi->monspecs); + if (ret < 0) { + fb_edid_add_monspecs(edid + EDID_LENGTH, &fbi->monspecs); + if (fbi->monspecs.modedb_len > 0) + hdmi->edid_cfg.hdmi_cap = false; + else return -ENOENT; } /* need read segment block? */ if (extblknum > 1) { - for (j = 2; j <= extblknum; j++) { + int j; + for (j = 1; j <= extblknum; j++) { for (i = 0; i < 128; i++) - tmpedid[i] = hdmi_edid_i2c_read(hdmi, i, j); + *(tmpedid + 1) = hdmi_edid_i2c_read(hdmi, i, j); /* edid ext block parsing */ - ret = mxc_edid_parse_ext_blk(tmpedid, + ret = mxc_edid_parse_ext_blk(tmpedid + EDID_LENGTH, cfg, &fbi->monspecs); if (ret < 0) return -ENOENT; @@ -1717,11 +1717,11 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) clkdis &= ~HDMI_MC_CLKDIS_HDCPCLK_DISABLE; hdmi_writeb(clkdis, HDMI_MC_CLKDIS); } + } - if (ret < 0) { - dev_dbg(&hdmi->pdev->dev, "read failed\n"); + + if (ret < 0) return HDMI_EDID_FAIL; - } dev_info(&hdmi->pdev->dev, "%s HDMI in %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap?"HDMI":"DVI"); hdmi->plug_event = hdmi->edid_cfg.hdmi_cap?HDMI_IH_PHY_STAT0_HPD:HDMI_DVI_IH_STAT; @@ -1890,9 +1890,9 @@ static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi) */ mode = &hdmi->fbi->monspecs.modedb[i]; - if (hdmi->edid_cfg.hdmi_cap && - (mode->vmode & FB_VMODE_INTERLACED) && - (mxc_edid_mode_to_vic(mode) == 0)) + if ((mode->vmode & FB_VMODE_INTERLACED) || + (hdmi->edid_cfg.hdmi_cap && + (mxc_edid_mode_to_vic(mode) == 0))) continue; dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); @@ -1926,6 +1926,7 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* If not EDID data read, set up default modelist */ + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); dev_info(&hdmi->pdev->dev, "create default modelist\n"); console_lock(); From 06859291ef0b076853e3bf52510d3f3c178949c8 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 5 Aug 2015 23:50:41 +0200 Subject: [PATCH 0575/1983] arm: imx: Clock VPU from pll2_pfd0_352m There is no reason to reduce the clock for pll2_pfd2_396m and effect everything else using that when we can just use pll2_pfd0_352m for the VPU which is the only user. This allows us to actually play with a full range of clock speeds, up to 352 for the VPU. --- arch/arm/mach-imx/clk-imx6q.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 4430e42eb483ed..7ec0c4a8305457 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -579,10 +579,10 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) * all modules that sourceing clk from PLL2_PFD2 will * be impacted. */ - imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD2_396M], 352000000); - imx_clk_set_parent(clk[IMX6QDL_CLK_VPU_AXI_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]); + imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD0_352M], 352000000); pr_info("VPU 352M is enabled!\n"); #endif + imx_clk_set_parent(clk[IMX6QDL_CLK_VPU_AXI_SEL], clk[IMX6QDL_CLK_PLL2_PFD0_352M]); /* Set initial power mode */ imx6q_set_lpm(WAIT_CLOCKED); From f216f881dc7cf73a4ad68ee22795bd139ee3a2dd Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 5 Aug 2015 23:54:57 +0200 Subject: [PATCH 0576/1983] arm: imx: clock the axi bus for all chipsets from the 540Mhz pll Instead of just the iMX6S/DL use the 540Mhz pll to clock all the axi bus clocks. This gives us a slight bump in performance for the axi bus to the gpu, pci etc. --- arch/arm/mach-imx/clk-imx6q.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 7ec0c4a8305457..61a0a25889a4e7 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -497,15 +497,14 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) imx_clk_set_parent(clk[IMX6QDL_CLK_IPU1_DI1_SEL], clk[IMX6QDL_CLK_IPU1_DI1_PRE]); imx_clk_set_parent(clk[IMX6QDL_CLK_IPU2_DI0_SEL], clk[IMX6QDL_CLK_IPU2_DI0_PRE]); imx_clk_set_parent(clk[IMX6QDL_CLK_IPU2_DI1_SEL], clk[IMX6QDL_CLK_IPU2_DI1_PRE]); - if (cpu_is_imx6dl()) { - imx_clk_set_rate(clk[IMX6QDL_CLK_PLL3_PFD1_540M], 540000000); - imx_clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]); - imx_clk_set_parent(clk[IMX6QDL_CLK_AXI_ALT_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]); - imx_clk_set_parent(clk[IMX6QDL_CLK_AXI_SEL], clk[IMX6QDL_CLK_AXI_ALT_SEL]); - /* set epdc/pxp axi clock to 200Mhz */ - imx_clk_set_parent(clk[IMX6QDL_CLK_IPU2_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]); - imx_clk_set_rate(clk[IMX6QDL_CLK_IPU2], 200000000); - } else if (cpu_is_imx6q()) { + imx_clk_set_rate(clk[IMX6QDL_CLK_PLL3_PFD1_540M], 540000000); + imx_clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]); + imx_clk_set_parent(clk[IMX6QDL_CLK_AXI_ALT_SEL], clk[IMX6QDL_CLK_PLL3_PFD1_540M]); + imx_clk_set_parent(clk[IMX6QDL_CLK_AXI_SEL], clk[IMX6QDL_CLK_AXI_ALT_SEL]); + /* set epdc/pxp axi clock to 200Mhz */ + imx_clk_set_parent(clk[IMX6QDL_CLK_IPU2_SEL], clk[IMX6QDL_CLK_PLL2_PFD2_396M]); + imx_clk_set_rate(clk[IMX6QDL_CLK_IPU2], 200000000); + if (cpu_is_imx6q()) { imx_clk_set_parent(clk[IMX6QDL_CLK_IPU1_SEL], clk[IMX6QDL_CLK_MMDC_CH0_AXI]); imx_clk_set_parent(clk[IMX6QDL_CLK_IPU2_SEL], clk[IMX6QDL_CLK_MMDC_CH0_AXI]); } From 3010fb04e38c5952d156afb1290e69a44c3017b8 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 11 Aug 2015 13:36:09 +0200 Subject: [PATCH 0577/1983] gpu: galcore: Don't abuse the dma_* apis There is no reason we can't pass the device to the dma_* api's, so do it. This does not follow the traditional vivante syntax but it is more correct for in kernel drivers. --- drivers/gpu/galcore/gc_hal_kernel_device.c | 3 +++ drivers/gpu/galcore/gc_hal_kernel_device.h | 3 +++ drivers/gpu/galcore/gc_hal_kernel_os.c | 20 ++++++++++---------- drivers/gpu/galcore/gc_hal_kernel_probe.c | 5 +++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.c b/drivers/gpu/galcore/gc_hal_kernel_device.c index e99d29f3763f33..42a0b242055dfe 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.c +++ b/drivers/gpu/galcore/gc_hal_kernel_device.c @@ -683,6 +683,7 @@ gckGALDEVICE_Construct( IN gctINT PowerManagement, IN gctINT GpuProfiler, IN gcsDEVICE_CONSTRUCT_ARGS * Args, + IN struct device *dev, OUT gckGALDEVICE *Device ) { @@ -729,6 +730,8 @@ gckGALDEVICE_Construct( device->platform = Args->platform; + device->dev = dev; + gcmkONERROR(_DebugfsInit(device)); if (gckDEBUGFS_CreateNode( diff --git a/drivers/gpu/galcore/gc_hal_kernel_device.h b/drivers/gpu/galcore/gc_hal_kernel_device.h index c94d67ccf76276..4d66a6ac31be63 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_device.h +++ b/drivers/gpu/galcore/gc_hal_kernel_device.h @@ -30,6 +30,8 @@ typedef struct _gckGALDEVICE { + struct device *dev; + /* Objects. */ gckOS os; gckKERNEL kernels[gcdMAX_GPU_COUNT]; @@ -175,6 +177,7 @@ gceSTATUS gckGALDEVICE_Construct( IN gctINT PowerManagement, IN gctINT GpuProfiler, IN gcsDEVICE_CONSTRUCT_ARGS * Args, + IN struct device *dev, OUT gckGALDEVICE *Device ); diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index 19aa4d291cd513..ab373288c59126 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -1092,7 +1092,7 @@ gckOS_MapMemory( } #ifndef NO_DMA_COHERENT - if (dma_mmap_writecombine(gcvNULL, + if (dma_mmap_writecombine(Os->device->dev, mdlMap->vma, mdl->addr, mdl->dmaHandle, @@ -1411,9 +1411,9 @@ gckOS_AllocateNonPagedMemory( #ifndef NO_DMA_COHERENT #ifdef CONFIG_ARM64 - addr = dma_alloc_coherent(gcvNULL, + addr = dma_alloc_coherent(Os->device->dev, #else - addr = dma_alloc_writecombine(gcvNULL, + addr = dma_alloc_writecombine(Os->device->dev, #endif mdl->numPages * PAGE_SIZE, &mdl->dmaHandle, @@ -1442,7 +1442,7 @@ gckOS_AllocateNonPagedMemory( #if !defined(CONFIG_PPC) /* Cache invalidate. */ dma_sync_single_for_device( - gcvNULL, + Os->device->dev, page_to_phys(page), bytes, DMA_FROM_DEVICE); @@ -1523,7 +1523,7 @@ gckOS_AllocateNonPagedMemory( } #ifndef NO_DMA_COHERENT - if (dma_mmap_coherent(gcvNULL, + if (dma_mmap_coherent(Os->device->dev, mdlMap->vma, mdl->addr, mdl->dmaHandle, @@ -1681,9 +1681,9 @@ gceSTATUS gckOS_FreeNonPagedMemory( #ifndef NO_DMA_COHERENT #ifdef CONFIG_ARM64 - dma_free_coherent(gcvNULL, + dma_free_coherent(Os->device->dev, #else - dma_free_writecombine(gcvNULL, + dma_free_writecombine(Os->device->dev, #endif mdl->numPages * PAGE_SIZE, mdl->addr, @@ -5383,7 +5383,7 @@ gckOS_CacheClean( } dma_sync_single_for_device( - gcvNULL, + Os->device->dev, (dma_addr_t)Physical, Bytes, DMA_TO_DEVICE); @@ -5457,7 +5457,7 @@ gckOS_CacheInvalidate( } dma_sync_single_for_device( - gcvNULL, + Os->device->dev, (dma_addr_t)Physical, Bytes, DMA_FROM_DEVICE); @@ -5533,7 +5533,7 @@ gckOS_CacheFlush( if (Physical != gcvINVALID_ADDRESS) { dma_sync_single_for_device( - gcvNULL, + Os->device->dev, (dma_addr_t)Physical, Bytes, DMA_BIDIRECTIONAL); diff --git a/drivers/gpu/galcore/gc_hal_kernel_probe.c b/drivers/gpu/galcore/gc_hal_kernel_probe.c index 10098c3ef2c8ac..9c904bd8bf7ef5 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_probe.c +++ b/drivers/gpu/galcore/gc_hal_kernel_probe.c @@ -758,7 +758,7 @@ static int drv_mmap( } -static int drv_init(void) +static int drv_init(struct platform_device *pdev) { int ret; int result = -EINVAL; @@ -812,6 +812,7 @@ static int drv_init(void) powerManagement, gpuProfiler, &args, + &pdev->dev, &device ); @@ -975,7 +976,7 @@ static int gpu_probe(struct platform_device *pdev) _UpdateModuleParam(&moduleParam); } - ret = drv_init(); + ret = drv_init(pdev); if (!ret) { From 3bb44618b504a91cf958545c10f1caa3a021f84b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 12 Aug 2015 12:25:44 +0200 Subject: [PATCH 0578/1983] dts: cbi/hb: Enable dcic support in the device-tree files This enables dcic support for all models of the SolidRun lineup. --- arch/arm/boot/dts/imx6qdl-cubox-i.dtsi | 6 ++++++ arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 12 ++++++++++++ arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi index 3457b1a90435ea..d7c85a0fef921b 100644 --- a/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi @@ -149,6 +149,12 @@ }; }; +&dcic1 { + dcic_id = <0>; + dcic_mux = "dcic-hdmi"; + status = "okay"; +}; + &hdmi_core { ipu_id = <0>; disp_id = <0>; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index 7dcae4297b200e..ccac1c3ee70c55 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -165,6 +165,18 @@ status = "okay"; }; +&dcic1 { + dcic_id = <0>; + dcic_mux = "dcic-hdmi"; + status = "okay"; +}; + +&dcic2 { + dcic_id = <1>; + dcic_mux = "dcic-lvds1"; + status = "okay"; +}; + &flexcan1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hummingboard_flexcan1>; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi index 650f5b2054cf37..edc95beb968630 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi @@ -190,6 +190,18 @@ status = "okay"; }; +&dcic1 { + dcic_id = <0>; + dcic_mux = "dcic-hdmi"; + status = "okay"; +}; + +&dcic2 { + dcic_id = <1>; + dcic_mux = "dcic-lvds1"; + status = "okay"; +}; + &ecspi2 { fsl,spi-num-chipselects = <1>; cs-gpios = <&gpio2 26 0>; From a92b3683ee8fab16b6250359375eafe831e05c59 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 24 Feb 2014 14:51:50 +0100 Subject: [PATCH 0579/1983] ARM i.MX6q: Mark VPU and IPU AXI transfers as cacheable, increase IPU priority Upstream-commit: 7ea653efa98d8144345227576fc084ed7a356cf8 This is needed so that the IPU framebuffer scanout cannot be starved by VPU or GPU activity. Some boards like the SabreLite and SabreSD seem to set this in the DCD already, but the documented register reset values do not contain the necessary settings. Signed-off-by: Philipp Zabel Signed-off-by: Shawn Guo --- arch/arm/mach-imx/mach-imx6q.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 59b282fca1b003..07fe35bba9f0cc 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -366,6 +366,39 @@ static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = { { /* sentinel */ } }; +static void __init imx6q_axi_init(void) +{ + struct regmap *gpr; + unsigned int mask; + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + /* + * Enable the cacheable attribute of VPU and IPU + * AXI transactions. + */ + mask = IMX6Q_GPR4_VPU_WR_CACHE_SEL | + IMX6Q_GPR4_VPU_RD_CACHE_SEL | + IMX6Q_GPR4_VPU_P_WR_CACHE_VAL | + IMX6Q_GPR4_VPU_P_RD_CACHE_VAL_MASK | + IMX6Q_GPR4_IPU_WR_CACHE_CTL | + IMX6Q_GPR4_IPU_RD_CACHE_CTL; + regmap_update_bits(gpr, IOMUXC_GPR4, mask, mask); + + /* Increase IPU read QoS priority */ + regmap_update_bits(gpr, IOMUXC_GPR6, + IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK | + IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK, + (0xf << 16) | (0x7 << 20)); + regmap_update_bits(gpr, IOMUXC_GPR7, + IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK | + IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK, + (0xf << 16) | (0x7 << 20)); + } else { + pr_warn("failed to find fsl,imx6q-iomuxc-gpr regmap\n"); + } +} + static void __init imx6q_init_machine(void) { struct device *parent; @@ -386,6 +419,7 @@ static void __init imx6q_init_machine(void) imx_anatop_init(); imx6q_csi_mux_init(); cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init(); + imx6q_axi_init(); } #define OCOTP_CFG3 0x440 From 4b9a3a0642a679de4b490027bd3f892fa120169a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 24 Feb 2014 14:51:49 +0100 Subject: [PATCH 0580/1983] ARM: imx6q: Add GPR6 and GPR7 register definitions for iomuxc gpr Upstream-commit: ef3adc187ca6418a376774ebf55d1258d1dc2c31 Masks for IPU AXI transaction QoS settings Signed-off-by: Philipp Zabel Signed-off-by: Shawn Guo --- include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index f6515b5dd36916..2e15a7b44fed76 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -244,6 +244,24 @@ #define IMX6Q_GPR5_L2_CLK_STOP BIT(8) +#define IMX6Q_GPR6_IPU1_ID00_WR_QOS_MASK (0xf << 0) +#define IMX6Q_GPR6_IPU1_ID01_WR_QOS_MASK (0xf << 4) +#define IMX6Q_GPR6_IPU1_ID10_WR_QOS_MASK (0xf << 8) +#define IMX6Q_GPR6_IPU1_ID11_WR_QOS_MASK (0xf << 12) +#define IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK (0xf << 16) +#define IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK (0xf << 20) +#define IMX6Q_GPR6_IPU1_ID10_RD_QOS_MASK (0xf << 24) +#define IMX6Q_GPR6_IPU1_ID11_RD_QOS_MASK (0xf << 28) + +#define IMX6Q_GPR7_IPU2_ID00_WR_QOS_MASK (0xf << 0) +#define IMX6Q_GPR7_IPU2_ID01_WR_QOS_MASK (0xf << 4) +#define IMX6Q_GPR7_IPU2_ID10_WR_QOS_MASK (0xf << 8) +#define IMX6Q_GPR7_IPU2_ID11_WR_QOS_MASK (0xf << 12) +#define IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK (0xf << 16) +#define IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK (0xf << 20) +#define IMX6Q_GPR7_IPU2_ID10_RD_QOS_MASK (0xf << 24) +#define IMX6Q_GPR7_IPU2_ID11_RD_QOS_MASK (0xf << 28) + #define IMX6Q_GPR8_TX_SWING_LOW (0x7f << 25) #define IMX6Q_GPR8_TX_SWING_FULL (0x7f << 18) #define IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB (0x3f << 12) From 591588baeeede101326da55c4a915c554827e8fe Mon Sep 17 00:00:00 2001 From: Daniel Tang Date: Wed, 11 Feb 2015 12:44:58 +0800 Subject: [PATCH 0581/1983] Chipidea: Set connect-at-fullspeed bit when entering host mode if CI_HDRC_FORCE_FULLSPEED is set in the platform data Upstream-commit: 905276c4319174d52de0e45fc9b22f599497be47 PORTSC_PFSC is not set on entering host mode which means the USB OTG controller will attempt to enumerate USB devices at high speed even when the CI_HDRC_FORCE_FULLSPEED flag is set in the platform data. This patch ensures it is set right before host mode operations begin if needed. Signed-off-by: Daniel Tang Signed-off-by: Peter Chen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/host.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 5a874387729754..432063a32c7e79 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -292,6 +292,9 @@ static int host_start(struct ci_hdrc *ci) if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING) hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); + if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) + hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC); + ci_hdrc_ahb_config(ci); return ret; From 28d539c581dacb8fc5417631da17189a3ee4a2fd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 26 May 2014 10:34:20 -0300 Subject: [PATCH 0582/1983] ASoC: sgtl5000: Fix the cache handling Upstream-commit: 29aa37cddfb9b721013ff28608200d73a9426368 Since commit e5d80e82e32e (ASoC: sgtl5000: Convert to use regmap directly) a kernel oops is observed after a suspend/resume sequence. The kernel oops happens inside sgtl5000_restore_regs() as codec->reg_cache is no longer a valid pointer. Add the remaining register entries into sgtl5000_reg_defaults[] and remove sgtl5000_restore_regs() completely, which allows suspend/resume to work fine and make the code simpler. Tested on a im53-qsb board. Reported-by: Shawn Guo Signed-off-by: Fabio Estevam Tested-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 75 ++++++++----------------------------- 1 file changed, 15 insertions(+), 60 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index e93c36fd3073c4..ec663294493e5c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -36,18 +36,32 @@ /* default value of sgtl5000 registers */ static const struct reg_default sgtl5000_reg_defaults[] = { + { SGTL5000_CHIP_DIG_POWER, 0x0000 }, { SGTL5000_CHIP_CLK_CTRL, 0x0008 }, { SGTL5000_CHIP_I2S_CTRL, 0x0010 }, { SGTL5000_CHIP_SSS_CTRL, 0x0010 }, + { SGTL5000_CHIP_ADCDAC_CTRL, 0x020c }, { SGTL5000_CHIP_DAC_VOL, 0x3c3c }, { SGTL5000_CHIP_PAD_STRENGTH, 0x015f }, + { SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 }, { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 }, { SGTL5000_CHIP_ANA_CTRL, 0x0111 }, + { SGTL5000_CHIP_LINREG_CTRL, 0x0000 }, + { SGTL5000_CHIP_REF_CTRL, 0x0000 }, + { SGTL5000_CHIP_MIC_CTRL, 0x0000 }, + { SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 }, { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 }, { SGTL5000_CHIP_ANA_POWER, 0x7060 }, { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, + { SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_STATUS, 0x0000 }, + { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_TEST2, 0x0000 }, + { SGTL5000_DAP_CTRL, 0x0000 }, + { SGTL5000_DAP_PEQ, 0x0000 }, { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f }, + { SGTL5000_DAP_AUDIO_EQ, 0x0000 }, { SGTL5000_DAP_SURROUND, 0x0040 }, { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f }, @@ -55,6 +69,7 @@ static const struct reg_default sgtl5000_reg_defaults[] = { { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f }, { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f }, { SGTL5000_DAP_MAIN_CHAN, 0x8000 }, + { SGTL5000_DAP_MIX_CHAN, 0x0000 }, { SGTL5000_DAP_AVC_CTRL, 0x0510 }, { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 }, { SGTL5000_DAP_AVC_ATTACK, 0x0028 }, @@ -1066,71 +1081,11 @@ static int sgtl5000_suspend(struct snd_soc_codec *codec) return 0; } -/* - * restore all sgtl5000 registers, - * since a big hole between dap and regular registers, - * we will restore them respectively. - */ -static int sgtl5000_restore_regs(struct snd_soc_codec *codec) -{ - u16 *cache = codec->reg_cache; - u16 reg; - - /* restore regular registers */ - for (reg = 0; reg <= SGTL5000_CHIP_SHORT_CTRL; reg += 2) { - - /* These regs should restore in particular order */ - if (reg == SGTL5000_CHIP_ANA_POWER || - reg == SGTL5000_CHIP_CLK_CTRL || - reg == SGTL5000_CHIP_LINREG_CTRL || - reg == SGTL5000_CHIP_LINE_OUT_CTRL || - reg == SGTL5000_CHIP_REF_CTRL) - continue; - - snd_soc_write(codec, reg, cache[reg]); - } - - /* restore dap registers */ - for (reg = SGTL5000_DAP_REG_OFFSET; reg < SGTL5000_MAX_REG_OFFSET; reg += 2) - snd_soc_write(codec, reg, cache[reg]); - - /* - * restore these regs according to the power setting sequence in - * sgtl5000_set_power_regs() and clock setting sequence in - * sgtl5000_set_clock(). - * - * The order of restore is: - * 1. SGTL5000_CHIP_CLK_CTRL MCLK_FREQ bits (1:0) should be restore after - * SGTL5000_CHIP_ANA_POWER PLL bits set - * 2. SGTL5000_CHIP_LINREG_CTRL should be set before - * SGTL5000_CHIP_ANA_POWER LINREG_D restored - * 3. SGTL5000_CHIP_REF_CTRL controls Analog Ground Voltage, - * prefer to resotre it after SGTL5000_CHIP_ANA_POWER restored - */ - snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, - cache[SGTL5000_CHIP_LINREG_CTRL]); - - snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, - cache[SGTL5000_CHIP_ANA_POWER]); - - snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, - cache[SGTL5000_CHIP_CLK_CTRL]); - - snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, - cache[SGTL5000_CHIP_REF_CTRL]); - - snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, - cache[SGTL5000_CHIP_LINE_OUT_CTRL]); - return 0; -} - static int sgtl5000_resume(struct snd_soc_codec *codec) { /* Bring the codec back up to standby to enable regulators */ sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - /* Restore registers by cached in memory */ - sgtl5000_restore_regs(codec); return 0; } #else From 98fc69d973ec4c3bc38ed4ef13bf20658592d496 Mon Sep 17 00:00:00 2001 From: Fu Zhonghui Date: Mon, 18 Aug 2014 10:48:14 +0800 Subject: [PATCH 0583/1983] mmc: core: sdio: Fix unconditional wake_up_process() on sdio thread 781e989cf59 ("mmc: sdhci: convert to new SDIO IRQ handling") and bf3b5ec66bd ("mmc: sdio_irq: rework sdio irq handling") disabled the use of our own custom threaded IRQ handler, but left in an unconditional wake_up_process() on that handler at resume-time. Link: https://bugzilla.kernel.org/show_bug.cgi?id=80151 In addition, the check for MMC_CAP_SDIO_IRQ capability is added before enable sdio IRQ. Signed-off-by: Jaehoon Chung Signed-off-by: Chris Ball Signed-off-by: Fu Zhonghui Cc: # v3.16+ Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio.c | 12 ++++++++++-- drivers/mmc/core/sdio_irq.c | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 4d721c6e2af010..7c338b741ed0e1 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1015,8 +1015,16 @@ static int mmc_sdio_resume(struct mmc_host *host) } } - if (!err && host->sdio_irqs) - wake_up_process(host->sdio_irq_thread); + if (!err && host->sdio_irqs) { + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + wake_up_process(host->sdio_irq_thread); + } else if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 1); + mmc_host_clk_release(host); + } + } + mmc_release_host(host); /* diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 5cc13c8d35bbf3..696eca49384480 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -208,7 +208,7 @@ static int sdio_card_irq_get(struct mmc_card *card) host->sdio_irqs--; return err; } - } else { + } else if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); @@ -229,7 +229,7 @@ static int sdio_card_irq_put(struct mmc_card *card) if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { atomic_set(&host->sdio_irq_thread_abort, 1); kthread_stop(host->sdio_irq_thread); - } else { + } else if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); From ecb72c8793b608896298a37e08660bc2d02c8ae8 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 14 Aug 2015 21:22:10 +0200 Subject: [PATCH 0584/1983] PCI: imx: Fix suspend/resume for iMX6S/DL/D/Q We need to do a full assert_core_reset/deassert_core_reset when doing a suspend/resume so power_on gpios are toggled and everything is put in a known good state for resume. This fixes the problem where PCI would not exit D3 state on resume. --- drivers/pci/host/pci-imx6.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index 8e7960b743cefd..6d59785ad78c60 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -927,9 +927,7 @@ static int pci_imx_suspend_noirq(struct device *dev) * So, toggle bit18 of GPR1, used as a workaround of errata * "PCIe PCIe does not support L2 Power Down" */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_TEST_PD, - IMX6Q_GPR1_PCIE_TEST_PD); + imx6_pcie_assert_core_reset(pp); } return 0; @@ -993,8 +991,9 @@ static int pci_imx_resume_noirq(struct device *dev) * So, toggle bit18 of GPR1, used as a workaround of errata * "PCIe PCIe does not support L2 Power Down" */ - regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, - IMX6Q_GPR1_PCIE_TEST_PD, 0); + ret = imx6_pcie_deassert_core_reset(pp); + if (ret < 0) + return ret; } return 0; From b1c2741fa157d530003c4cafcf65d11479260688 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 15 Aug 2015 10:40:09 +0200 Subject: [PATCH 0585/1983] gpu: galcore: re-enable the busfreq management for now This ifdef was from the upstream kernel. I have not implemented this functionality for this kernel yet so remove these settings and re-enable busfrequency support. --- .../platform/freescale/gc_hal_kernel_platform_imx6q14.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c index b5c83a87db277f..6ea8803f0c1a05 100644 --- a/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c +++ b/drivers/gpu/galcore/platform/freescale/gc_hal_kernel_platform_imx6q14.c @@ -34,10 +34,7 @@ #include #include -#ifdef CONFIG_MXC_BUSFREQ #include -#endif - #ifdef CONFIG_DEVICE_THERMAL #include @@ -620,17 +617,13 @@ _SetClock( #ifdef CONFIG_PM static int gpu_runtime_suspend(struct device *dev) { -#ifdef CONFIG_MXC_BUSFREQ release_bus_freq(BUS_FREQ_HIGH); -#endif return 0; } static int gpu_runtime_resume(struct device *dev) { -#ifdef CONFIG_MXC_BUSFREQ request_bus_freq(BUS_FREQ_HIGH); -#endif return 0; } From ef133f1176f8372c398f3f39b77a54949ec484a1 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 15 Aug 2015 10:42:10 +0200 Subject: [PATCH 0586/1983] net: fec: Make runtime pm dependant on link status Previously the runtime pm, which just notifies the bus frequency driver if it can go into low mode, was using open and close to signal if the device could go into low power mode. Instead switch this to see if there is link or not. --- drivers/net/ethernet/freescale/fec_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index aa610df47f9126..e7d9e9ce3d91c7 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1722,6 +1722,7 @@ static void fec_enet_adjust_link(struct net_device *ndev) if (!fep->link) { fep->link = phy_dev->link; status_change = 1; + pm_runtime_get_sync(ndev->dev.parent); } if (fep->full_duplex != phy_dev->duplex) { @@ -1752,6 +1753,7 @@ static void fec_enet_adjust_link(struct net_device *ndev) napi_enable(&fep->napi); fep->link = phy_dev->link; status_change = 1; + pm_runtime_put_sync_suspend(ndev->dev.parent); } } @@ -2801,7 +2803,6 @@ fec_enet_open(struct net_device *ndev) phy_start(fep->phy_dev); netif_tx_start_all_queues(ndev); - pm_runtime_get_sync(ndev->dev.parent); if ((id_entry->driver_data & FEC_QUIRK_BUG_WAITMODE) && !fec_enet_irq_workaround(fep)) pm_qos_add_request(&ndev->pm_qos_req, From d78240d86b2876b20bfad41e32eecd465694cc67 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 16 Aug 2015 10:33:56 +0200 Subject: [PATCH 0587/1983] video: mxcfb: Minor cleanup Small batch of spacing, spelling, and working problems. --- drivers/video/mxc/mxc_hdmi.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index ec9bcb4264417c..66f489b7c319bb 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -170,14 +170,13 @@ struct mxc_hdmi { u8 edid[HDMI_EDID_LEN]; bool fb_reg; bool cable_plugin; - u8 blank; + u8 blank; bool dft_mode_set; char *dft_mode_str; int default_bpp; u8 latest_intr_stat; u8 plug_event; u8 plug_mask; - bool irq_enabled; spinlock_t irq_lock; bool phy_enabled; struct fb_videomode default_mode; @@ -1723,11 +1722,11 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) if (ret < 0) return HDMI_EDID_FAIL; - dev_info(&hdmi->pdev->dev, "%s HDMI in %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap?"HDMI":"DVI"); + dev_info(&hdmi->pdev->dev, "%s reports %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap?"HDMI":"DVI"); hdmi->plug_event = hdmi->edid_cfg.hdmi_cap?HDMI_IH_PHY_STAT0_HPD:HDMI_DVI_IH_STAT; hdmi->plug_mask = hdmi->edid_cfg.hdmi_cap?HDMI_PHY_HPD:HDMI_DVI_STAT; - if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) { + if (memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN) == 0) { dev_info(&hdmi->pdev->dev, "same edid\n"); return HDMI_EDID_SAME; } @@ -2013,7 +2012,7 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) if (hdmi->edid_status == HDMI_EDID_NO_MODES || hdmi->edid_status == HDMI_EDID_FAIL) { int retry_status; - dev_info(&hdmi->pdev->dev, "Read EDID again\n"); + dev_warn(&hdmi->pdev->dev, "Read EDID again\n"); msleep(200); retry_status = mxc_hdmi_read_edid(hdmi); /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we @@ -2199,7 +2198,7 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) if (intr_stat & hdmi->plug_event) { dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); - dev_dbg(&hdmi->pdev->dev, "intr_stat %u plug_event %u\n", intr_stat, hdmi->plug_event); + dev_dbg(&hdmi->pdev->dev, "intr_stat 0x%x plug_event 0x%x\n", intr_stat, hdmi->plug_event); hdmi->latest_intr_stat = intr_stat; /* Mute interrupts until handled */ @@ -2319,7 +2318,7 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) /* HDMI Initialization Step B.1 */ hdmi_av_composer(hdmi); - /* HDMI Initializateion Step B.2 */ + /* HDMI Initialization Step B.2 */ mxc_hdmi_phy_init(hdmi); /* HDMI Initialization Step B.3 */ @@ -2336,6 +2335,8 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) /* HDMI Initialization Step F - Configure AVI InfoFrame */ hdmi_config_AVI(hdmi); + } else { + dev_dbg(&hdmi->pdev->dev, "%s DVI mode\n", __func__); } hdmi_video_packetize(hdmi); From 2428c8ea6a4f239a2e5dee9a917e3ef56215bf41 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 16 Aug 2015 10:51:27 +0200 Subject: [PATCH 0588/1983] video: mxcfb: Better hotplug state tracking Instead of tracking edid_status, type of display, and hotplug status in three different places, add a single enum that can track all of it. This makes our code more consistent and allows us to better know the state of our connection. --- drivers/video/mxc/mxc_hdmi.c | 88 +++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 66f489b7c319bb..6e674b076104cb 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -149,6 +149,13 @@ struct hdmi_phy_reg_config { u16 reg_cksymtx; }; +enum hotplug_state { + HDMI_HOTPLUG_DISCONNECTED, + HDMI_HOTPLUG_CONNECTED_HDMI, + HDMI_HOTPLUG_CONNECTED_DVI, + HDMI_HOTPLUG_CONNECTED_NO_EDID, +}; + struct mxc_hdmi { struct platform_device *pdev; struct platform_device *core_pdev; @@ -165,11 +172,10 @@ struct mxc_hdmi { struct hdmi_data_info hdmi_data; int vic; - int edid_status; struct mxc_edid_cfg edid_cfg; u8 edid[HDMI_EDID_LEN]; bool fb_reg; - bool cable_plugin; + enum hotplug_state hp_state; u8 blank; bool dft_mode_set; char *dft_mode_str; @@ -268,10 +274,21 @@ static ssize_t mxc_hdmi_show_state(struct device *dev, { struct mxc_hdmi *hdmi = dev_get_drvdata(dev); - if (hdmi->cable_plugin == false) + switch (hdmi->hp_state) + { + case HDMI_HOTPLUG_CONNECTED_HDMI: + strcpy(buf, "plugin HDMI\n"); + break; + case HDMI_HOTPLUG_CONNECTED_DVI: + strcpy(buf, "plugin DVI\n"); + break; + case HDMI_HOTPLUG_CONNECTED_NO_EDID: + strcpy(buf, "plugin NO EDID\n"); + break; + case HDMI_HOTPLUG_DISCONNECTED: + default: strcpy(buf, "plugout\n"); - else - strcpy(buf, "plugin\n"); + } return strlen(buf); } @@ -1342,13 +1359,14 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) * Otherwise HDMI PHY will get messed up and generate an overflow * interrupt that can't be cleared or detected by accessing the * status register. */ - if (!hdmi->fb_reg || !hdmi->cable_plugin - || (hdmi->blank != FB_BLANK_UNBLANK)) + if (!hdmi->fb_reg || + hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED || + hdmi->blank != FB_BLANK_UNBLANK) return; /*check csc whether needed activated in HDMI mode */ cscon = (isColorSpaceConversion(hdmi) && - !hdmi->hdmi_data.video_mode.mDVI); + (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_HDMI)); /* HDMI Phy spec says to do the phy initialization sequence twice */ for (i = 0 ; i < 2 ; i++) { @@ -1362,7 +1380,7 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) } hdmi->phy_enabled = true; - if (!hdmi->hdmi_data.video_mode.mDVI) + if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_HDMI) hdmi_enable_overflow_interrupts(); } @@ -1544,7 +1562,7 @@ static void hdmi_av_composer(struct mxc_hdmi *hdmi) HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE); - inv_val |= (vmode->mDVI ? + inv_val |= ((hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_DVI) ? HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE : HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE); @@ -1723,6 +1741,7 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) return HDMI_EDID_FAIL; dev_info(&hdmi->pdev->dev, "%s reports %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap?"HDMI":"DVI"); + hdmi->hp_state = hdmi->edid_cfg.hdmi_cap?HDMI_HOTPLUG_CONNECTED_HDMI:HDMI_HOTPLUG_CONNECTED_DVI; hdmi->plug_event = hdmi->edid_cfg.hdmi_cap?HDMI_IH_PHY_STAT0_HPD:HDMI_DVI_IH_STAT; hdmi->plug_mask = hdmi->edid_cfg.hdmi_cap?HDMI_PHY_HPD:HDMI_DVI_STAT; @@ -1791,7 +1810,8 @@ static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi) hdmi_writeb(clkdis, HDMI_MC_CLKDIS); /* Enable csc path */ - if (isColorSpaceConversion(hdmi) && !hdmi->hdmi_data.video_mode.mDVI) { + if (isColorSpaceConversion(hdmi) && + (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_HDMI)) { clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE; hdmi_writeb(clkdis, HDMI_MC_CLKDIS); } @@ -2001,16 +2021,18 @@ static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi) static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) { + int edid_status; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - hdmi->cable_plugin = true; + hdmi->hp_state = HDMI_HOTPLUG_CONNECTED_NO_EDID; /* HDMI Initialization Step C */ - hdmi->edid_status = mxc_hdmi_read_edid(hdmi); + edid_status = mxc_hdmi_read_edid(hdmi); /* Read EDID again if first EDID read failed */ - if (hdmi->edid_status == HDMI_EDID_NO_MODES || - hdmi->edid_status == HDMI_EDID_FAIL) { + if (edid_status == HDMI_EDID_NO_MODES || + edid_status == HDMI_EDID_FAIL) { int retry_status; dev_warn(&hdmi->pdev->dev, "Read EDID again\n"); msleep(200); @@ -2018,11 +2040,11 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we * want NO_MODES as final result. */ if (retry_status != HDMI_EDID_SAME) - hdmi->edid_status = retry_status; + edid_status = retry_status; } /* HDMI Initialization Steps D, E, F */ - switch (hdmi->edid_status) { + switch (edid_status) { case HDMI_EDID_SUCCESS: mxc_hdmi_edid_rebuild_modelist(hdmi); break; @@ -2041,7 +2063,7 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) } /* Save edid cfg for audio driver */ - hdmi_set_edid_cfg(hdmi->edid_status, &hdmi->edid_cfg); + hdmi_set_edid_cfg(edid_status, &hdmi->edid_cfg); /* Setting video mode */ mxc_hdmi_set_mode(hdmi); @@ -2082,7 +2104,7 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) hdmi_disable_overflow_interrupts(); - hdmi->cable_plugin = false; + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; } static void hotplug_worker(struct work_struct *work) @@ -2264,14 +2286,13 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) hdmi_disable_overflow_interrupts(); - dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); - if (hdmi->edid_cfg.hdmi_cap || !hdmi->edid_status) { + if ((hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_HDMI) || + (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_NO_EDID)) { + dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); hdmi_set_dvi_mode(0); - hdmi->hdmi_data.video_mode.mDVI = false; } else { + dev_dbg(&hdmi->pdev->dev, "VESA mode used vic=%d\n", hdmi->vic); hdmi_set_dvi_mode(1); - dev_dbg(&hdmi->pdev->dev, "CEA mode vic=%d work in DVI\n", hdmi->vic); - hdmi->hdmi_data.video_mode.mDVI = true; } if ((hdmi->vic == 6) || (hdmi->vic == 7) || @@ -2302,7 +2323,7 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) hdmi->hdmi_data.enc_out_format = RGB; /* YCbCr only enabled in HDMI mode */ - if (!hdmi->hdmi_data.video_mode.mDVI && + if ((hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_HDMI) && !hdmi->hdmi_data.rgb_out_enable) { if (hdmi->edid_cfg.cea_ycbcr444) hdmi->hdmi_data.enc_out_format = YCBCR444; @@ -2325,9 +2346,7 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) mxc_hdmi_enable_video_path(hdmi); /* not for DVI mode */ - if (hdmi->hdmi_data.video_mode.mDVI) - dev_dbg(&hdmi->pdev->dev, "%s DVI mode\n", __func__); - else { + if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_HDMI) { dev_dbg(&hdmi->pdev->dev, "%s CEA mode\n", __func__); /* HDMI Initialization Step E - Configure audio */ @@ -2412,6 +2431,12 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, break; case FB_EVENT_BLANK: + if (!hdmi->fb_reg || hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED) { + dev_dbg(&hdmi->pdev->dev, + "event=FB_EVENT_BLANK - NOOP\n"); + break; + } + if ((*((int *)event->data) == FB_BLANK_UNBLANK) && (*((int *)event->data) != hdmi->blank)) { dev_dbg(&hdmi->pdev->dev, @@ -2427,8 +2452,7 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, /* Unmute interrupts */ hdmi_writeb(~hdmi->plug_event, HDMI_IH_MUTE_PHY_STAT0); - if (hdmi->fb_reg && hdmi->cable_plugin) - mxc_hdmi_setup(hdmi, val); + mxc_hdmi_setup(hdmi, val); hdmi_set_blank_state(1); } else if (*((int *)event->data) != hdmi->blank) { dev_dbg(&hdmi->pdev->dev, @@ -2438,7 +2462,7 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, mxc_hdmi_phy_disable(hdmi); - if(hdmi->plug_mask == HDMI_DVI_STAT) { + if(hdmi->hp_state == HDMI_HOTPLUG_CONNECTED_DVI) { u8 val; pr_debug("In DVI Mode, disabling hotplug interrupts until unblanked\n"); val = hdmi_readb(HDMI_IH_MUTE_PHY_STAT0); @@ -2721,7 +2745,7 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, hdmi->fbi->mode = (struct fb_videomode *)mode; /* Default setting HDMI working in HDMI mode*/ - hdmi->edid_cfg.hdmi_cap = true; + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; hdmi->plug_event = HDMI_DVI_IH_STAT; hdmi->plug_mask = HDMI_DVI_STAT; From b559cb66b050dee54657a7a5d14c1a923a511f2d Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 16 Aug 2015 10:54:42 +0200 Subject: [PATCH 0589/1983] video: mxcfb: Do a better job of finding a working resolution. This makes sure to reset our state on disconnect, and when looking for a new resolution falling back to the preferred monitor resolution if our default or last modes are undefined. --- drivers/video/mxc/mxc_hdmi.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 6e674b076104cb..f0013c0295725c 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -1989,13 +1989,20 @@ static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi) if (!hdmi->dft_mode_set) { fb_videomode_to_var(&var, &hdmi->default_mode); hdmi->dft_mode_set = true; - } else + } else { fb_videomode_to_var(&var, &hdmi->previous_non_vga_mode); + } - fb_var_to_videomode(&m, &var); - dump_fb_videomode(&m); + if (var.xres) { + fb_var_to_videomode(&m, &var); + dump_fb_videomode(&m); + mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + } else { + dev_dbg(&hdmi->pdev->dev, + "No default mode using the best for the display\n"); + mode = fb_find_best_display(&hdmi->fbi->monspecs, &hdmi->fbi->modelist); + } - mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); if (!mode) { pr_err("%s: could not find mode in modelist\n", __func__); return; @@ -2105,6 +2112,10 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) hdmi_disable_overflow_interrupts(); hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; + + /* Prepare driver for next connection */ + hdmi->dft_mode_set = false; + memset(&hdmi->previous_non_vga_mode, 0, sizeof(struct fb_videomode)); } static void hotplug_worker(struct work_struct *work) From 056b42c28cd2c9123f5279f4f5a1d105f91f0853 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 16 Aug 2015 10:58:28 +0200 Subject: [PATCH 0590/1983] video: mxcfb: blank/unblank to save power This patch does two things. 1) It makes sure the display is unblanked when first connected. This gives us a good user experience where if the framebuffer is blanked and then a device is plugged in nothing turns on. 2) After the hdmi device is registered we tell the IPU to blank the display, then on subsequent connect/disconnect events we unblank/blank the screens as needed. This makes sure that the ipu is always disabled if we aren't using it and if you don't have a monitor attached saves about 80mA's of constant power usage. Overall this provides a significnat power savings for headless systems, and also doesn't block the bus-frequency driver from kicking in if no display is attached. --- drivers/video/mxc/mxc_hdmi.c | 12 ++++++++++++ drivers/video/mxc/mxc_ipuv3_fb.c | 5 +++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index f0013c0295725c..65c85caee9465b 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -211,6 +211,8 @@ static bool hdcp_init; extern const struct fb_videomode mxc_cea_mode[64]; extern void mxc_hdmi_cec_handle(u16 cec_stat); +extern int mxcfb_blank(int blank, struct fb_info *info); + static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event); static void hdmi_enable_overflow_interrupts(void); static void hdmi_disable_overflow_interrupts(void); @@ -2032,6 +2034,10 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + console_lock(); + fb_blank(hdmi->fbi, FB_BLANK_UNBLANK); + console_unlock(); + hdmi->hp_state = HDMI_HOTPLUG_CONNECTED_NO_EDID; /* HDMI Initialization Step C */ @@ -2116,6 +2122,10 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) /* Prepare driver for next connection */ hdmi->dft_mode_set = false; memset(&hdmi->previous_non_vga_mode, 0, sizeof(struct fb_videomode)); + + console_lock(); + fb_blank(hdmi->fbi, FB_BLANK_POWERDOWN); + console_unlock(); } static void hotplug_worker(struct work_struct *work) @@ -2387,6 +2397,8 @@ static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) if (hdmi->fb_reg) return; + mxcfb_blank(FB_BLANK_POWERDOWN, hdmi->fbi); + spin_lock_irqsave(&hdmi->irq_lock, flags); dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 5fb7155aada884..5da874c28870a3 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -252,7 +252,7 @@ static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id) static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id); static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id); -static int mxcfb_blank(int blank, struct fb_info *info); +int mxcfb_blank(int blank, struct fb_info *info); static int mxcfb_map_video_memory(struct fb_info *fbi); static int mxcfb_unmap_video_memory(struct fb_info *fbi); @@ -1363,7 +1363,7 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) * mxcfb_blank(): * Blank the display. */ -static int mxcfb_blank(int blank, struct fb_info *info) +int mxcfb_blank(int blank, struct fb_info *info) { struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par; int ret = 0; @@ -1397,6 +1397,7 @@ static int mxcfb_blank(int blank, struct fb_info *info) mxc_fbi->cur_blank = blank; return ret; } +EXPORT_SYMBOL(mxcfb_blank); /* * Pan or Wrap the Display From e865de4757caa504c3d651be7e02686136fba0b2 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 16 Aug 2015 11:16:40 +0200 Subject: [PATCH 0591/1983] mxc: ipu3: demote non fatal errors to warning and info Make messages that are non-fatal only report in higher debug levels. There is no reason to send this to the console for the average user. --- drivers/mxc/ipu3/ipu_disp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c index f3affe7f48be16..2fe936e3843d65 100644 --- a/drivers/mxc/ipu3/ipu_disp.c +++ b/drivers/mxc/ipu3/ipu_disp.c @@ -200,7 +200,7 @@ void _ipu_dmfc_set_burst_size(struct ipu_soc *ipu, int dma_chan, int burst_size) dmfc_bs = 0xc0; break; default: - dev_err(ipu->dev, "Unsupported burst size %d\n", + dev_warn(ipu->dev, "Unsupported burst size %d\n", burst_size); return; } @@ -258,7 +258,7 @@ static void _ipu_di_sync_config(struct ipu_soc *ipu, if ((run_count >= 0x1000) || (offset_count >= 0x1000) || (repeat_count >= 0x1000) || (cnt_up >= 0x400) || (cnt_down >= 0x400)) { - dev_err(ipu->dev, "DI%d counters out of range.\n", di); + dev_warn(ipu->dev, "DI%d counters out of range.\n", di); return; } @@ -992,8 +992,8 @@ void adapt_panel_to_ipu_restricitions(struct ipu_soc *ipu, uint16_t *v_start_wid *v_end_width = 2; *v_sync_width = *v_sync_width - diff; } else - dev_err(ipu->dev, "WARNING: try to adapt timming, but failed\n"); - dev_err(ipu->dev, "WARNING: adapt panel end blank lines\n"); + dev_warn(ipu->dev, "WARNING: try to adapt timming, but failed\n"); + dev_info(ipu->dev, "WARNING: adapt panel end blank lines\n"); } } From fc97b6797d80a656f5eaefa18b5b7f4467449b0b Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Mon, 17 Aug 2015 15:04:51 +0200 Subject: [PATCH 0592/1983] mxc-hdmi: change order of hdmi->hp_state settings and fb_(un)blank() calls in cable_connected/disconnected functions) (previous order was making blanking and unblanking not work as it checks for hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED and returns immediately) --- drivers/video/mxc/mxc_hdmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 65c85caee9465b..6d2fd2d22caba0 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -2034,12 +2034,12 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + hdmi->hp_state = HDMI_HOTPLUG_CONNECTED_NO_EDID; + console_lock(); fb_blank(hdmi->fbi, FB_BLANK_UNBLANK); console_unlock(); - hdmi->hp_state = HDMI_HOTPLUG_CONNECTED_NO_EDID; - /* HDMI Initialization Step C */ edid_status = mxc_hdmi_read_edid(hdmi); @@ -2117,8 +2117,6 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) hdmi_disable_overflow_interrupts(); - hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; - /* Prepare driver for next connection */ hdmi->dft_mode_set = false; memset(&hdmi->previous_non_vga_mode, 0, sizeof(struct fb_videomode)); @@ -2126,6 +2124,8 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) console_lock(); fb_blank(hdmi->fbi, FB_BLANK_POWERDOWN); console_unlock(); + + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; } static void hotplug_worker(struct work_struct *work) From 805856e53a56917852fd10977ef246e510e65ff2 Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Wed, 5 Aug 2015 20:13:22 +0200 Subject: [PATCH 0593/1983] video: mxc_edid: Parse Video Capability Data Block The information from this block is used to detect if a sink supports overriding of the RGB quantisation range. Signed-off-by: Rudi --- drivers/video/mxc/mxc_edid.c | 16 ++++++++++++++++ include/video/mxc_edid.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index d791bfea5609b4..59e157882315d1 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -547,6 +547,22 @@ int mxc_edid_parse_ext_blk(unsigned char *edid, break; } case 0x7: /*User extended block*/ + if (blklen >= 2 && edid[index + 1] == 0) { /*Video Capability Data Block*/ + u8 data = edid[index + 2]; + + cfg->cea_scan_mode_ce = (data >> 0) & 3; + cfg->cea_scan_mode_it = (data >> 2) & 3; + cfg->cea_scan_mode_pt = (data >> 4) & 3; + cfg->cea_rgb_range_selectable = (data >> 6) & 1; + + DPRINTK("VCDB over/underscan behavior (CE) %d\n", cfg->cea_scan_mode_ce); + DPRINTK("VCDB over/underscan behavior (IT) %d\n", cfg->cea_scan_mode_it); + DPRINTK("VCDB over/underscan behavior (PT) %d\n", cfg->cea_scan_mode_pt); + DPRINTK("VCDB RGB quant. range selectable %d\n", cfg->cea_rgb_range_selectable); + + index += blklen; + break; + } default: /* skip */ DPRINTK("Not handle block, tagcode = 0x%x\n", tagcode); diff --git a/include/video/mxc_edid.h b/include/video/mxc_edid.h index 34e797cc80b4d8..9d34f22835f8eb 100644 --- a/include/video/mxc_edid.h +++ b/include/video/mxc_edid.h @@ -64,6 +64,10 @@ struct mxc_edid_cfg { bool cea_ycbcr444; bool cea_ycbcr422; bool hdmi_cap; + bool cea_rgb_range_selectable; + u8 cea_scan_mode_ce; + u8 cea_scan_mode_it; + u8 cea_scan_mode_pt; /*VSD*/ bool vsd_support_ai; From 60251e8643f6dc17effbea2630bdcf019b53b910 Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Wed, 5 Aug 2015 20:16:44 +0200 Subject: [PATCH 0594/1983] video: mxc_hdmi: Add new option 'auto' for RGB Quant Range setting This new option, which is now the default, will automatically use full range, when the sink announces that it supports quantisation overriding. Otherwise, the default range (i.e. limited) is used. Signed-off-by: Rudi --- drivers/video/mxc/mxc_hdmi.c | 41 ++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 65c85caee9465b..208c798b1942e6 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -216,10 +216,11 @@ extern int mxcfb_blank(int blank, struct fb_info *info); static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event); static void hdmi_enable_overflow_interrupts(void); static void hdmi_disable_overflow_interrupts(void); +static unsigned int getRGBQuantRange(struct mxc_hdmi *hdmi); -static char *rgb_quant_range = "default"; +static char *rgb_quant_range = "auto"; module_param(rgb_quant_range, charp, S_IRUGO); -MODULE_PARM_DESC(rgb_quant_range, "RGB Quant Range (default, limited, full)"); +MODULE_PARM_DESC(rgb_quant_range, "RGB Quant Range (auto, default, limited, full)"); static struct platform_device_id imx_hdmi_devtype[] = { { @@ -355,8 +356,9 @@ static ssize_t mxc_hdmi_show_rgb_quant_range(struct device *dev, struct device_attribute *attr, char *buf) { struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + int n; - switch (hdmi->hdmi_data.rgb_quant_range) { + switch (getRGBQuantRange(hdmi)) { case HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE: strcpy(buf, "limited\n"); break; @@ -369,7 +371,14 @@ static ssize_t mxc_hdmi_show_rgb_quant_range(struct device *dev, break; }; - return strlen(buf); + n = strlen(buf); + + if (hdmi->hdmi_data.rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_MASK) { + strcpy(buf + n - 1, " (auto)\n"); + n += 7; + } + + return n; } static ssize_t mxc_hdmi_store_rgb_quant_range(struct device *dev, @@ -384,6 +393,8 @@ static ssize_t mxc_hdmi_store_rgb_quant_range(struct device *dev, hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE; } else if (sysfs_streq("default", buf)) { hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT; + } else if (sysfs_streq("auto", buf)) { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_MASK; } else { ret = -EINVAL; goto out; @@ -510,12 +521,23 @@ static void hdmi_video_sample(struct mxc_hdmi *hdmi) hdmi_writeb(0x0, HDMI_TX_BCBDATA1); } +static unsigned int getRGBQuantRange(struct mxc_hdmi *hdmi) +{ + if (hdmi->hdmi_data.rgb_quant_range != HDMI_FC_AVICONF2_RGB_QUANT_MASK) + return hdmi->hdmi_data.rgb_quant_range; + + return hdmi->edid_cfg.cea_rgb_range_selectable ? + HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE : HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT; +} + static int isColorSpaceConversion(struct mxc_hdmi *hdmi) { + unsigned int rgb_quant_range = getRGBQuantRange(hdmi); + return (hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format) || (hdmi->hdmi_data.enc_out_format == RGB && - ((hdmi->hdmi_data.rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE) || - (hdmi->hdmi_data.rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT && hdmi->vic > 1))); + ((rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE) || + (rgb_quant_range == HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT && hdmi->vic > 1))); } static int isColorSpaceDecimation(struct mxc_hdmi *hdmi) @@ -1475,8 +1497,7 @@ static void hdmi_config_AVI(struct mxc_hdmi *hdmi) ********************************************/ val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry | - hdmi->hdmi_data.rgb_quant_range | - HDMI_FC_AVICONF2_SCALING_NONE; + getRGBQuantRange(hdmi) | HDMI_FC_AVICONF2_SCALING_NONE; hdmi_writeb(val, HDMI_FC_AVICONF2); /******************************************** @@ -2798,8 +2819,10 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE; } else if (!strcasecmp(rgb_quant_range, "full")) { hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE; - } else { + } else if (!strcasecmp(rgb_quant_range, "default")) { hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT; + } else { + hdmi->hdmi_data.rgb_quant_range = HDMI_FC_AVICONF2_RGB_QUANT_MASK; } ret = devm_request_irq(&hdmi->pdev->dev, irq, mxc_hdmi_hotplug, IRQF_SHARED, From f2c34aa14dfd77ad0bf6d0ccb7224fdf528ca193 Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Thu, 6 Aug 2015 08:33:58 +0200 Subject: [PATCH 0595/1983] video: mxc_hdmi: Add command line switch to disable EDID processing. Allow the use of the fallback videomode list even in case a valid EDID is obtainable. This is done by adding 'mxc_hdmi.ignore_edid=1' to the kernel command line. Signed-off-by: Rudi --- drivers/video/mxc/mxc_hdmi.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 208c798b1942e6..87acdb48d59e2d 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -222,6 +222,10 @@ static char *rgb_quant_range = "auto"; module_param(rgb_quant_range, charp, S_IRUGO); MODULE_PARM_DESC(rgb_quant_range, "RGB Quant Range (auto, default, limited, full)"); +static bool ignore_edid = 0; +module_param(ignore_edid, bool, S_IRUGO); +MODULE_PARM_DESC(ignore_edid, "Ignore EDID (default=0)"); + static struct platform_device_id imx_hdmi_devtype[] = { { .name = "hdmi-imx6DL", @@ -2062,19 +2066,23 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) hdmi->hp_state = HDMI_HOTPLUG_CONNECTED_NO_EDID; /* HDMI Initialization Step C */ - edid_status = mxc_hdmi_read_edid(hdmi); - - /* Read EDID again if first EDID read failed */ - if (edid_status == HDMI_EDID_NO_MODES || - edid_status == HDMI_EDID_FAIL) { - int retry_status; - dev_warn(&hdmi->pdev->dev, "Read EDID again\n"); - msleep(200); - retry_status = mxc_hdmi_read_edid(hdmi); - /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we - * want NO_MODES as final result. */ - if (retry_status != HDMI_EDID_SAME) - edid_status = retry_status; + if (ignore_edid) { + edid_status = HDMI_EDID_FAIL; + } else { + edid_status = mxc_hdmi_read_edid(hdmi); + + /* Read EDID again if first EDID read failed */ + if (edid_status == HDMI_EDID_NO_MODES || + edid_status == HDMI_EDID_FAIL) { + int retry_status; + dev_warn(&hdmi->pdev->dev, "Read EDID again\n"); + msleep(200); + retry_status = mxc_hdmi_read_edid(hdmi); + /* If we get NO_MODES on the 1st and SAME on the 2nd attempt we + * want NO_MODES as final result. */ + if (retry_status != HDMI_EDID_SAME) + edid_status = retry_status; + } } /* HDMI Initialization Steps D, E, F */ From e828201b66fe638ac099b5401bec4f5a0c477806 Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Thu, 6 Aug 2015 08:38:00 +0200 Subject: [PATCH 0596/1983] video: mxc_hdmi: Flag video modes from the fallback list as standard. This patch changes the fallback modes from 'User' to 'Standard' (i.e. they get listed as S:xxxx instead of U:xxxx). Signed-off-by: Rudi --- drivers/video/mxc/mxc_hdmi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 65c85caee9465b..2f04f8937cbe52 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -2742,8 +2742,11 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, /*Add all no interlaced CEA mode to default modelist */ for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { mode = &mxc_cea_mode[i]; - if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0)) - fb_add_videomode(mode, &hdmi->fbi->modelist); + if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0)) { + struct fb_videomode m = *mode; + m.flag |= FB_MODE_IS_STANDARD; + fb_add_videomode(&m, &hdmi->fbi->modelist); + } } console_unlock(); From 88811551480b0f2202461a793674a2355024f8f7 Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Wed, 5 Aug 2015 20:21:17 +0200 Subject: [PATCH 0597/1983] video: mxc_edid: Parse and store audio rates/sizes by channel count. A sink may have different audio restrictions depending on the maximum channel count. This patch changes the EDID parsing so that sample_rates and sample_sizes are stored separately for 2, 4, 6 and 8 channels modes. Signed-off-by: Rudi --- drivers/video/mxc/mxc_edid.c | 27 +++++++++++++++------------ include/video/mxc_edid.h | 5 ++--- sound/soc/fsl/fsl_hdmi.c | 14 +++++++++----- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index d791bfea5609b4..e09e796551c8e2 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -286,12 +286,20 @@ int mxc_edid_parse_ext_blk(unsigned char *edid, detail_timing_desc_offset = edid[index++]; + memset(cfg->sample_rates, 0, sizeof(cfg->sample_rates)); + memset(cfg->sample_sizes, 0, sizeof(cfg->sample_sizes)); + if (revision >= 2) { cfg->cea_underscan = (edid[index] >> 7) & 0x1; cfg->cea_basicaudio = (edid[index] >> 6) & 0x1; cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1; cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1; + if (cfg->cea_basicaudio) { + cfg->sample_rates[0] = 0x07; + cfg->sample_sizes[0] = 0x01; + } + DPRINTK("CEA underscan %d\n", cfg->cea_underscan); DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio); DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444); @@ -500,13 +508,10 @@ int mxc_edid_parse_ext_blk(unsigned char *edid, } case 0x1: /*Audio data block*/ { - u8 audio_format, max_ch, byte1, byte2, byte3; + u8 audio_format, byte1, byte2, byte3; + int ch_idx; i = 0; - cfg->max_channels = 0; - cfg->sample_rates = 0; - cfg->sample_sizes = 0; - while (i < blklen) { byte1 = edid[index + 1]; byte2 = edid[index + 2]; @@ -515,20 +520,18 @@ int mxc_edid_parse_ext_blk(unsigned char *edid, i += 3; audio_format = byte1 >> 3; - max_ch = (byte1 & 0x07) + 1; DPRINTK("Audio Format Descriptor : %2d\n", audio_format); - DPRINTK("Max Number of Channels : %2d\n", max_ch); + DPRINTK("Max Number of Channels : %2d\n", (byte1 & 0x07) + 1); DPRINTK("Sample Rates : %02x\n", byte2); /* ALSA can't specify specific compressed * formats, so only care about PCM for now. */ if (audio_format == AUDIO_CODING_TYPE_LPCM) { - if (max_ch > cfg->max_channels) - cfg->max_channels = max_ch; - - cfg->sample_rates |= byte2; - cfg->sample_sizes |= byte3 & 0x7; + for (ch_idx = (byte1 & 0x07) / 2; ch_idx >= 0; ch_idx--) { + cfg->sample_rates[ch_idx] |= byte2; + cfg->sample_sizes[ch_idx] |= byte3 & 0x7; + } DPRINTK("Sample Sizes : %02x\n", byte3 & 0x7); } diff --git a/include/video/mxc_edid.h b/include/video/mxc_edid.h index 34e797cc80b4d8..771ccdf224b982 100644 --- a/include/video/mxc_edid.h +++ b/include/video/mxc_edid.h @@ -90,9 +90,8 @@ struct mxc_edid_cfg { u16 hdmi_3d_struct_all; u32 vsd_max_tmdsclk_rate; - u8 max_channels; - u8 sample_sizes; - u8 sample_rates; + u8 sample_sizes[4]; + u8 sample_rates[4]; u8 speaker_alloc; }; diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index 13f358e4b45255..3385221edca7e3 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -277,10 +277,11 @@ static int cea_audio_rates[HDMI_MAX_RATES] = { static void fsl_hdmi_get_playback_rates(void) { int i, count = 0; - u8 rates; + u8 rates = edid_cfg.sample_rates[0] | edid_cfg.sample_rates[1] | + edid_cfg.sample_rates[2] | edid_cfg.sample_rates[3]; /* Always assume basic audio support */ - rates = edid_cfg.sample_rates | 0x7; + rates |= 0x07; for (i = 0 ; i < HDMI_MAX_RATES ; i++) if ((rates & (1 << i)) != 0) @@ -296,11 +297,13 @@ static void fsl_hdmi_get_playback_rates(void) static void fsl_hdmi_get_playback_sample_size(void) { int i = 0; + u8 sizes = edid_cfg.sample_sizes[0] | edid_cfg.sample_sizes[1] | + edid_cfg.sample_sizes[2] | edid_cfg.sample_sizes[3]; /* Always assume basic audio support */ playback_sample_size[i++] = 16; - if (edid_cfg.sample_sizes & 0x4) + if (sizes & 0x4) playback_sample_size[i++] = 32; playback_constraint_bits.list = playback_sample_size; @@ -318,8 +321,9 @@ static void fsl_hdmi_get_playback_channels(void) playback_channels[i++] = channels; channels += 2; - while ((i < HDMI_MAX_CHANNEL_CONSTRAINTS) && - (channels <= edid_cfg.max_channels)) { + while (i < HDMI_MAX_CHANNEL_CONSTRAINTS && + i < ARRAY_SIZE(edid_cfg.sample_rates) && + edid_cfg.sample_rates[i] && edid_cfg.sample_sizes[i]) { playback_channels[i++] = channels; channels += 2; } From 040f5acd0778e5e254496aab60fd74d8232a5654 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 9 Aug 2015 16:34:20 +0200 Subject: [PATCH 0598/1983] sound: fsl_hdmi: Fix return value handling when setting constraints. Some of the snd_pcm_hw_constraint_xxx() functions may return non-zero positive values in case of success. So in order to detect errors we should check for negatives. Signed-off-by: Rudi --- sound/soc/fsl/fsl_hdmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index 3385221edca7e3..3d0280ade10602 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -348,23 +348,23 @@ static int fsl_hdmi_update_constraints(struct snd_pcm_substream *substream) fsl_hdmi_get_playback_rates(); ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &playback_constraint_rates); - if (ret) + if (ret < 0) return ret; fsl_hdmi_get_playback_sample_size(); ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, &playback_constraint_bits); - if (ret) + if (ret < 0) return ret; fsl_hdmi_get_playback_channels(); ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &playback_constraint_channels); - if (ret) + if (ret < 0) return ret; ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - if (ret) + if (ret < 0) return ret; return 0; From df32d21d4d06d9db76d5a9a1474db8877c1365a2 Mon Sep 17 00:00:00 2001 From: Rudi Date: Sun, 9 Aug 2015 16:45:03 +0200 Subject: [PATCH 0599/1983] sound: fsl_hdmi: Add rules to adjust constraints dynamically. This patch will modify the sample rate and channel count constraints depending on each other. This handles sinks, that report different supported sample rates for stereo and multi-channel. Signed-off-by: Rudi --- sound/soc/fsl/fsl_hdmi.c | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/sound/soc/fsl/fsl_hdmi.c b/sound/soc/fsl/fsl_hdmi.c index 3d0280ade10602..5a51702883dc73 100644 --- a/sound/soc/fsl/fsl_hdmi.c +++ b/sound/soc/fsl/fsl_hdmi.c @@ -335,6 +335,83 @@ static void fsl_hdmi_get_playback_channels(void) pr_debug("%s: constraint = %d channels\n", __func__, playback_channels[i]); } +static int fsl_hw_rule_channels_by_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int i; + u8 m; + struct snd_interval n; + + if (snd_interval_single(r)) { + m = 0; + for (i = 0; i < HDMI_MAX_RATES; i++) { + if (snd_interval_min(r) == cea_audio_rates[i]) { + m = 1 << i; + break; + } + } + + if (m) { + snd_interval_any(&n); + n.min = n.max = 2; + + for (i = 1; i < ARRAY_SIZE(edid_cfg.sample_rates); i++) { + if (!(edid_cfg.sample_rates[i] & m)) + break; + n.max += 2; + } + + pr_debug("%s: rate = %d, channels = %d..%d\n", + __func__, r->min, n.min, n.max); + + return snd_interval_refine(c, &n); + } + } + + return 0; +} + +static int fsl_hw_rule_rate_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int i, rate; + u8 m; + struct snd_interval n; + + if (snd_interval_single(c)) { + i = (snd_interval_min(c) - 1) / 2; + m = edid_cfg.sample_rates[i]; + + if (m) { + snd_interval_any(&n); + n.min = 192000; + n.max = 32000; + + for (i = 0; i < HDMI_MAX_RATES; i++, m >>= 1) { + if (!(m & 1)) + continue; + + rate = cea_audio_rates[i]; + if ( rate < n.min) + n.min = rate; + if ( rate > n.max) + n.max = rate; + } + + pr_debug("%s: channels = %d, rates = %d..%d\n", + __func__, c->min, n.min, n.max); + + return snd_interval_refine(r, &n); + } + } + + return 0; +} + static int fsl_hdmi_update_constraints(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -367,6 +444,18 @@ static int fsl_hdmi_update_constraints(struct snd_pcm_substream *substream) if (ret < 0) return ret; + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + fsl_hw_rule_channels_by_rate, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + fsl_hw_rule_rate_by_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret < 0) + return ret; + return 0; } From 9ac05c5fc49a6496927e7bef40c4014c6b21b8f1 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 12 May 2015 14:46:11 -0700 Subject: [PATCH 0600/1983] mmc: core: Increase delay for voltage to stabilize from 3.3V to 1.8V Upstream-commit: 7c5209c315ea0f3102413ed1d6309be94b1e792f Since the regulator used for the SDMMC IO voltage is not expected to draw a lot of current, most systems will probably use an inexpensive LDO for it. LDO regulators apparently have the feature that they don't actively drive the voltage down--they wait for other components in the system to drag the voltage down. Thus they will transition faster under heavy loads and slower under light loads. During an SDMMC voltage change from 3.3V to 1.8V, we are almost certainly under a light load. To be specific: * The regulator is hooked through pulls to CMD0-3 and DAT. Probably the CMD pulls are something like 47K and the DAT is something like 10K. * The card is supposed to be driving DAT0-3 low during voltage change which will draw _some_ current, but not a lot. * The regulator is also provided to the SDMMC host controller, but the SDMMC host controller is in open drain mode during the voltage change and so shouldn't be drawing much current. In order to keep the SDMMC host working properly (or for noise reasons), there might also be a capacitor attached to the SDMMC IO regulator. This also will have the effect of slowing down transitions of the regulator, especially under light loads. From experimental evidence, we've seen the voltage change fail if the card doesn't detect that the voltage fell to less than about 2.3V when we turn on the clock. On one device (that admittedly had a 47K CMD pullup instead of a 10K CMD pullup) we saw that the voltage was just about 2.3V after 5ms and thus the voltage change would sometimes fail. Doubling the delay gave margin and made the voltage change work 100% of the time, despite the slightly weaker CMD pull. At the moment submitting this as an RFC patch since my problem _could_ be fixed by increasing the pull strength (or using a smaller capacitor). However being a little bit more lenient to strange hardware could also be a good thing. Signed-off-by: Doug Anderson Acked-by: Mark Brown Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 069e9c43b54b30..11fdefede308f8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1473,8 +1473,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) goto power_cycle; } - /* Keep clock gated for at least 5 ms */ - mmc_delay(5); + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ + mmc_delay(10); host->ios.clock = clock; mmc_set_ios(host); From 75a67496bf2ca405a3c00131c91a73343374d462 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 17 Aug 2015 13:22:09 +0200 Subject: [PATCH 0601/1983] power: reset: Add snvs rtc power-off driver snvs-poweroff supports using the SNVS RTC from certain Freescale SOCs to power off the board. It also removes the hack of adding this functionality directly to the rtc driver. If a board needs this support it should add an entry like restart_poweroff { compatible = "fsl,snvs-poweroff"; }; to their device-tree board file. --- drivers/power/reset/Kconfig | 8 ++++ drivers/power/reset/Makefile | 1 + drivers/power/reset/snvs-poweroff.c | 59 +++++++++++++++++++++++++++++ drivers/rtc/rtc-snvs.c | 10 ++--- 4 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 drivers/power/reset/snvs-poweroff.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 6d452a78b19c7a..1868c3b229e98e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -43,6 +43,14 @@ config POWER_RESET_RESTART Instead they restart, and u-boot holds the SoC until the user presses a key. u-boot then boots into Linux. +config POWER_RESET_SNVS + bool "Freescale SNVS RTC power-off driver" + depends on ARM + depends on RTC_DRV_SNVS + help + Power off support for Freescale SOCs that rely on the SNVS + RTC to poweroff the SOC. + config POWER_RESET_VEXPRESS bool "ARM Versatile Express power-off and reset driver" depends on ARM || ARM64 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a5b4a77d1a4139..c691281c87af03 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o +obj-$(CONFIG_POWER_RESET_SNVS) += snvs-poweroff.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o diff --git a/drivers/power/reset/snvs-poweroff.c b/drivers/power/reset/snvs-poweroff.c new file mode 100644 index 00000000000000..f50de4099b9abf --- /dev/null +++ b/drivers/power/reset/snvs-poweroff.c @@ -0,0 +1,59 @@ +/* + * Power off by restarting and let u-boot keep hold of the machine + * until the user presses a button for example. + * + * Andrew Lunn + * + * Copyright (C) 2012 Andrew Lunn + * Copyright (C) 2015 Jon Nettleton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +extern void snvs_poweroff(void); + +static int snvs_poweroff_probe(struct platform_device *pdev) +{ + /* + * if no specific power off function in board file, power off system by + * SNVS + */ + if (!pm_power_off) + pm_power_off = snvs_poweroff; + + return 0; +} + +static int snvs_poweroff_remove(struct platform_device *pdev) +{ + if (pm_power_off == &snvs_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id of_snvs_poweroff_match[] = { + { .compatible = "fsl,snvs-poweroff", }, + {}, +}; + +static struct platform_driver snvs_poweroff_driver = { + .probe = snvs_poweroff_probe, + .remove = snvs_poweroff_remove, + .driver = { + .name = "snvs-poweroff", + .owner = THIS_MODULE, + .of_match_table = of_snvs_poweroff_match, + }, +}; +module_platform_driver(snvs_poweroff_driver); diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index 3505cfc7d55e72..cd4bc694a6a931 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -43,6 +43,8 @@ struct snvs_rtc_data { static void __iomem *snvs_base; +void snvs_poweroff(void); + static u32 rtc_read_lp_counter(void __iomem *ioaddr) { u64 read1, read2; @@ -243,7 +245,7 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) return events ? IRQ_HANDLED : IRQ_NONE; } -static void snvs_poweroff(void) +void snvs_poweroff(void) { u32 value; @@ -303,12 +305,6 @@ static int snvs_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register rtc: %d\n", ret); return ret; } - /* - * if no specific power off function in board file, power off system by - * SNVS - */ - if (!pm_power_off) - pm_power_off = snvs_poweroff; return 0; } From cddeaf18510aca3a65ca2666fc9d32a3a329180d Mon Sep 17 00:00:00 2001 From: Peter Vicman Date: Sun, 16 Aug 2015 11:42:12 +0200 Subject: [PATCH 0602/1983] dts: udoo: Add the device-tree support for both Udoo Quad and Udoo Dual-lite copied from official Udoo kernel 3.14.28: https://github.com/UDOOboard/linux_kernel shutdown is done by gpio-poweroff in device tree (thanks to @linux4kix) --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx6dl-udoo.dts | 19 + arch/arm/boot/dts/imx6q-udoo.dts | 40 +- arch/arm/boot/dts/imx6qdl-udoo.dtsi | 587 ++++++++++++++++++++++++++++ 4 files changed, 608 insertions(+), 39 deletions(-) create mode 100644 arch/arm/boot/dts/imx6dl-udoo.dts create mode 100644 arch/arm/boot/dts/imx6qdl-udoo.dtsi diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index c0119d62785c58..7244693ecbc7f8 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -165,6 +165,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6dl-sabresd-ldo.dtb \ imx6dl-sabresd-pf200.dtb \ imx6dl-sabresd-hdcp.dtb \ + imx6dl-udoo.dtb \ imx6dl-wandboard.dtb \ imx6q-arm2.dtb \ imx6q-cubox-i.dtb \ diff --git a/arch/arm/boot/dts/imx6dl-udoo.dts b/arch/arm/boot/dts/imx6dl-udoo.dts new file mode 100644 index 00000000000000..85c7d494f24bce --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-udoo.dts @@ -0,0 +1,19 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Author: Fabio Estevam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/dts-v1/; +#include "imx6dl.dtsi" +#include "imx6qdl-udoo.dtsi" + +/ { + model = "Udoo i.MX6 Dual-lite Board"; + compatible = "udoo,imx6dl-udoo", "fsl,imx6dl"; +}; diff --git a/arch/arm/boot/dts/imx6q-udoo.dts b/arch/arm/boot/dts/imx6q-udoo.dts index b663f5df70eb31..39dd993206a78d 100644 --- a/arch/arm/boot/dts/imx6q-udoo.dts +++ b/arch/arm/boot/dts/imx6q-udoo.dts @@ -11,51 +11,13 @@ /dts-v1/; #include "imx6q.dtsi" +#include "imx6qdl-udoo.dtsi" / { model = "Udoo i.MX6 Quad Board"; compatible = "udoo,imx6q-udoo", "fsl,imx6q"; - - memory { - reg = <0x10000000 0x40000000>; - }; -}; - -&iomuxc { - imx6q-udoo { - pinctrl_uart2: uart2grp { - fsl,pins = < - MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 - MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 - >; - }; - - pinctrl_usdhc3: usdhc3grp { - fsl,pins = < - MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 - >; - }; - }; }; &sata { status = "okay"; }; - -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2>; - status = "okay"; -}; - -&usdhc3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3>; - non-removable; - status = "okay"; -}; diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi new file mode 100644 index 00000000000000..2ca138fa674e1f --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi @@ -0,0 +1,587 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * Copyright (C) 2014 Jasbir + * Copyright 2015 Aidilab, Srl. + * + * Author: Fabio Estevam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/ { + aliases { + mxcfb0 = &mxcfb1; +// reg_can_xcvr = ®_can_xcvr; + mmc0 = &usdhc3; + mmc1 = &usdhc2; + mmc2 = &usdhc1; + mmc3 = &usdhc4; + ssi0 = &ssi1; + }; + + memory { + reg = <0x10000000 0x40000000>; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_usb_h1_vbus: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + startup-delay-us = <2>; /* USB2415 requires a POR of 1 us minimum */ + gpio = <&gpio7 12 0>; + }; + + reg_lcd0_pwr: regulator@1 { + compatible = "regulator-fixed"; + regulator-name = "LCD0 POWER"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 2 0>; + enable-active-high; + regulator-boot-on; + regulator-always-on; + status = "disabled"; + }; + + reg_lcd0_backlight: regulator@2 { + compatible = "regulator-fixed"; + regulator-name = "LCD0 BACKLIGHT"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 4 0>; + enable-active-high; + regulator-boot-on; + regulator-always-on; + status = "disabled"; + }; + +// reg_can_xcvr: regulator@3 { +// compatible = "regulator-fixed"; +// reg = <3>; +// regulator-name = "CAN XCVR"; +// regulator-min-microvolt = <3300000>; +// regulator-max-microvolt = <3300000>; +// regulator-always-on; +// enable-active-low; +// }; + + reg_usb_otg_vbus: regulator@4 { + compatible = "regulator-fixed"; + reg = <4>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + enable-active-high; + }; + + reg_2p5v: regulator@5 { + compatible = "regulator-fixed"; + reg = <5>; + regulator-name = "2P5V"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + }; + + gpio-poweroff { + compatible = "gpio-poweroff"; + gpios = <&gpio2 4 GPIO_ACTIVE_HIGH>; + }; + + codec: vt1613 { + compatible = "via,vt1613"; + }; + + sound { + compatible = "udoo,imx-vt1613-audio"; + ssi-controller = <&ssi1>; + audio-codec = <&codec>; + mux-int-port = <1>; + mux-ext-port = <6>; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + +// sound-spdif { +// compatible = "fsl,imx-audio-spdif", +// "fsl,imx-sabreauto-spdif"; +// model = "imx-spdif"; +// spdif-controller = <&spdif>; +// spdif-in; +// status = "disabled"; +// }; + + v4l2_cap_0 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <0>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_cap_1 { + compatible = "fsl,imx6q-v4l2-capture"; + ipu_id = <0>; + csi_id = <1>; + mclk_source = <0>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; + + udoo_ard: udoo_ard_manager { + compatible = "udoo,imx6q-udoo-ard"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_udoo_ard_alt>; + bossac-clk-gpio = <&gpio6 3 GPIO_ACTIVE_LOW>; + bossac-dat-gpio = <&gpio5 18 GPIO_ACTIVE_LOW>; + bossac-erase-gpio = <&gpio4 21 GPIO_ACTIVE_LOW>; + bossac-reset-gpio = <&gpio1 0 GPIO_ACTIVE_LOW>; + status = "okay"; + }; +}; + +&audmux { + status = "okay"; +}; + +&dcic1 { + dcic_id = <0>; + dcic_mux = "dcic-hdmi"; + status = "okay"; +}; + +&dcic2 { + dcic_id = <1>; + dcic_mux = "dcic-lvds1"; + status = "okay"; +}; + +&gpc { // General power controller + /* use ldo-bypass, u-boot will check it and configure */ + fsl,ldo-bypass = <1>; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_core { + ipu_id = <0>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&ldb { // LVDS display bridge + status = "disabled"; + primary; + lvds-channel@0 { + reg = <0>; + fsl,data-mapping = "spwg"; + status = "disabled"; + primary; + crtc = "ipu1-di0"; + }; +}; + +&i2c1 { // external pinout pin 20-21 + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; +}; + +&i2c2 { // internal (hdmi) + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + hdmi: edid@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; +}; + +&i2c3 { // CSI camera (CN11) and LVDS 7 inches touch panel (CN13) + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; +}; + +&fec { //ethernet + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + /* doesn't work with solidrun's kernel 3.14.48+ + phy-reset-gpios = <&gpio3 23 0>; + phy-reset-duration = <50>;*/ + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog &external_hog>; + + imx6q-udoo { + pinctrl_hog: hoggrp { + fsl,pins = < + // Internal GPIOs + MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x80000000 // 5v enable + MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 // Vtt enable + MX6QDL_PAD_SD2_DAT0__GPIO1_IO15 0x80000000 // Touch panel reset + MX6QDL_PAD_SD2_DAT2__GPIO1_IO13 0x80000000 // Touch panel interrupt + MX6QDL_PAD_DISP0_DAT5__GPIO4_IO26 0x80000000 // Debug UART (J18) + + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 // USB hub reset + MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0 // USB hub clock + MX6QDL_PAD_EIM_WAIT__GPIO5_IO00 0xb0b1 // USB OTG select + + MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x80000000 // SD card power + MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 // SD card detect + + MX6QDL_PAD_GPIO_16__GPIO7_IO11 0xb0b1 // SAM3X OTG vbus_en + MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 // SAM3X usb host + MX6QDL_PAD_GPIO_3__GPIO1_IO03 0x30b1 // Arduino pinout pin 12 + + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 // LVDS panel on (CN13) + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 // LVDS backlight on (CN13) + + MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x80000000 // CSI camera reset (CN11) + MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x80000000 // CSI camera enable (CN11) + >; + }; + + external_hog: hoggrp-2 { + fsl,pins = < + // External Pinout GPIOs + MX6QDL_PAD_CSI0_DAT11__GPIO5_IO29 0x80000000 // pin 00 + MX6QDL_PAD_CSI0_DAT10__GPIO5_IO28 0x80000000 // pin 01 + MX6QDL_PAD_SD1_CLK__GPIO1_IO20 0x80000000 // pin 02 + MX6QDL_PAD_SD1_DAT0__GPIO1_IO16 0x80000000 // pin 03 + MX6QDL_PAD_SD1_DAT1__GPIO1_IO17 0x80000000 // pin 04 + MX6QDL_PAD_SD1_CMD__GPIO1_IO18 0x80000000 // pin 05 + MX6QDL_PAD_SD4_DAT1__GPIO2_IO09 0x80000000 // pin 06 + MX6QDL_PAD_SD4_DAT2__GPIO2_IO10 0x80000000 // pin 07 + MX6QDL_PAD_SD1_DAT3__GPIO1_IO21 0x80000000 // pin 08 + MX6QDL_PAD_SD1_DAT2__GPIO1_IO19 0x80000000 // pin 09 + MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x80000000 // pin 10 + MX6QDL_PAD_GPIO_9__GPIO1_IO09 0x80000000 // pin 11 + MX6QDL_PAD_GPIO_3__GPIO1_IO03 0x80000000 // pin 12 + MX6QDL_PAD_SD4_DAT0__GPIO2_IO08 0x80000000 // pin 13 + MX6QDL_PAD_CSI0_DAT4__GPIO5_IO22 0x80000000 // pin 14 + MX6QDL_PAD_CSI0_DAT16__GPIO6_IO02 0x80000000 // pin 15 + MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x80000000 // pin 16 + MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x80000000 // pin 17 + MX6QDL_PAD_CSI0_DAT12__GPIO5_IO30 0x80000000 // pin 18 + MX6QDL_PAD_CSI0_DAT13__GPIO5_IO31 0x80000000 // pin 19 + // pin 20, 21 in pinctrl_i2c1 + MX6QDL_PAD_DISP0_DAT6__GPIO4_IO27 0x80000000 // pin 22 + MX6QDL_PAD_DISP0_DAT7__GPIO4_IO28 0x80000000 // pin 23 + MX6QDL_PAD_DISP0_DAT8__GPIO4_IO29 0x80000000 // pin 24 + MX6QDL_PAD_DISP0_DAT9__GPIO4_IO30 0x80000000 // pin 25 + MX6QDL_PAD_DISP0_DAT10__GPIO4_IO31 0x80000000 // pin 26 + MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x80000000 // pin 27 + MX6QDL_PAD_DISP0_DAT12__GPIO5_IO06 0x80000000 // pin 28 + MX6QDL_PAD_DISP0_DAT13__GPIO5_IO07 0x80000000 // pin 29 + MX6QDL_PAD_DISP0_DAT14__GPIO5_IO08 0x80000000 // pin 30 + MX6QDL_PAD_DISP0_DAT15__GPIO5_IO09 0x80000000 // pin 31 + MX6QDL_PAD_DISP0_DAT16__GPIO5_IO10 0x80000000 // pin 32 + MX6QDL_PAD_DISP0_DAT17__GPIO5_IO11 0x80000000 // pin 33 + MX6QDL_PAD_DISP0_DAT18__GPIO5_IO12 0x80000000 // pin 34 + MX6QDL_PAD_DISP0_DAT19__GPIO5_IO13 0x80000000 // pin 35 + MX6QDL_PAD_DISP0_DAT20__GPIO5_IO14 0x80000000 // pin 36 + MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15 0x80000000 // pin 37 + MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x80000000 // pin 38 + MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x80000000 // pin 39 KEY_VOL_UP + MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x80000000 // pin 40 HOME + MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x80000000 // pin 41 SEARCH + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 // pin 42 BACK + MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x80000000 // pin 43 MENU + MX6QDL_PAD_GPIO_19__GPIO4_IO05 0x80000000 // pin 44 KEY_VOL_DOWN + MX6QDL_PAD_DISP0_DAT22__GPIO5_IO16 0x80000000 // pin 45 + MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17 0x80000000 // pin 46 + MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x80000000 // pin 47 + MX6QDL_PAD_KEY_ROW1__GPIO4_IO09 0x80000000 // pin 48 + MX6QDL_PAD_KEY_COL1__GPIO4_IO08 0x80000000 // pin 49 + MX6QDL_PAD_EIM_OE__GPIO2_IO25 0x80000000 // pin 50 + MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 // pin 51 + MX6QDL_PAD_EIM_CS0__GPIO2_IO23 0x80000000 // pin 52 + MX6QDL_PAD_EIM_D24__GPIO3_IO24 0x80000000 // pin 53 + >; + }; + + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 /* RGMII_nRST */ + MX6QDL_PAD_EIM_EB3__GPIO2_IO31 0x80000000 /* EN_ETH_PWR */ + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 // or 0x100b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 // or 0x100b1 + >; + }; + + pinctrl_udoo_ard_alt: udooard2grp { + fsl,pins = < + MX6QDL_PAD_DISP0_DAT0__GPIO4_IO21 0x80000000 + MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03 0x80000000 + MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x80000000 + MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x80000000 + >; + }; + +// pinctrl_flexcan1: can1grp { +// fsl,pins = < +// MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x1b0b1 +// MX6QDL_PAD_GPIO_8__FLEXCAN1_RX 0x1b0b1 +// >; +// }; + + +// pinctrl_pwm4: pwm4grp { +// fls,pins = < +// MX6QDL_PAD_SD4_DAT2__PWM4_OUT 0x1b0b1 +// >; +// }; +// +// pinctrl_pwm3: pwm3grp { +// fls,pins = < +// MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 +// >; +// }; +// +// pinctrl_pwm1: pwm1grp { +// fsl,pins = < +// MX6QDL_PAD_GPIO_9__PWM1_OUT 0x1b0b1 +// >; +// }; +// +// pinctrl_pwm2: pwm2grp { +// fsl,pins = < +// MX6QDL_PAD_GPIO_1__PWM2_OUT 0x1b0b1 +// >; +// }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + + + pinctrl_spdif_1: spdifgrp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__SPDIF_IN 0x1b0b0 + >; + }; + + ac97link_running: ac97link_runninggrp { + fsl,pins = < + MX6QDL_PAD_DI0_PIN2__AUD6_TXD 0x80000000 + MX6QDL_PAD_DI0_PIN3__AUD6_TXFS 0x80000000 + MX6QDL_PAD_DI0_PIN4__AUD6_RXD 0x80000000 + MX6QDL_PAD_DI0_PIN15__AUD6_TXC 0x80000000 + >; + }; + + ac97link_reset: ac97link_resetgrp { + fsl,pins = < + MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x80000000 + MX6QDL_PAD_DI0_PIN3__GPIO4_IO19 0x80000000 + MX6QDL_PAD_DI0_PIN2__GPIO4_IO18 0x80000000 + >; + }; + + ac97link_warm_reset: ac97link_warm_resetgrp { + fsl,pins = < + MX6QDL_PAD_DI0_PIN3__GPIO4_IO19 0x80000000 + >; + }; + + }; +}; + +&ssi1 { + fsl,mode = "ac97-slave"; + pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; + pinctrl-0 = <&ac97link_running>; + pinctrl-1 = <&ac97link_running>; + pinctrl-2 = <&ac97link_reset>; + pinctrl-3 = <&ac97link_warm_reset>; + /* sync, sdata (output), reset */ + ac97-gpios = <&gpio4 19 0 &gpio4 18 0 &gpio2 30 0>; + status = "okay"; +}; + +&uart2 { // iMX6 serial debug port - ttymxc1, available on micro USB CN6 (jumper J18 plugged) + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + status = "okay"; +}; + +&uart4 { // iMX6-Arduino internal serial port - ttymxc3 + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + non-removable; + keep-power-in-suspend; + no-1-8-v; + enable-sdio-wakeup; + status = "okay"; +}; + +&usbotg { + pinctrl-names = "default"; + vbus-supply = <®_usb_otg_vbus>; + status = "okay"; +}; + +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + clocks = <&clks 201>; + clock-names = "phy"; + status = "okay"; +}; + +/* + * &pwm1 { + * pinctrl-names = "default"; + * pinctrl-0 = <&pinctrl_pwm1>; + * #pwm-cells = <3>; + * status = "okay"; + * }; + * + * &pwm2 { + * pinctrl-names = "default"; + * pinctrl-0 = <&pinctrl_pwm2>; + * #pwm-cells = <3>; + * status = "okay"; + * }; + * + * &pwm3 { + * pinctrl-names = "default"; + * pinctrl-0 = <&pinctrl_pwm3>; + * #pwm-cells = <3>; + * status = "okay"; + * }; + * + * &pwm4 { + * pinctrl-names = "default"; + * pinctrl-0 = <&pinctrl_pwm4>; + * #pwm-cells = <3>; + * status = "okay"; + * }; + */ + +/* + * &mipi_csi { + * status = "okay"; + * ipu_id = <0>; + * csi_id = <1>; + * v_channel = <0>; + * lanes = <1>; + * }; + * + * &spdif { + * pinctrl-names = "default"; + * pinctrl-0 = <&pinctrl_spdif_1>; + * status = "disabled"; + * }; + */ + From 3f306b0520284f1e4e7d8a09881268d41c0505cb Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Tue, 18 Aug 2015 07:01:19 +0200 Subject: [PATCH 0603/1983] arm: imx: Clean up busfreq logging Switch to using pr_* functions and removing erroneous "\n" in the beginning of the messages to clean up the logging. --- arch/arm/mach-imx/busfreq_ddr3.c | 8 ++++---- arch/arm/mach-imx/busfreq_lpddr2.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-imx/busfreq_ddr3.c b/arch/arm/mach-imx/busfreq_ddr3.c index 379847ce1c37bf..4307ede98d47c2 100644 --- a/arch/arm/mach-imx/busfreq_ddr3.c +++ b/arch/arm/mach-imx/busfreq_ddr3.c @@ -250,7 +250,7 @@ int update_ddr_freq_imx6sx(int ddr_rate) if (ddr_rate == curr_ddr_rate) return 0; - printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate); + pr_debug("Bus freq set to %d start...\n", ddr_rate); if (low_bus_freq_mode || audio_bus_freq_mode) dll_off = true; @@ -280,7 +280,7 @@ int update_ddr_freq_imx6sx(int ddr_rate) local_irq_enable(); - printk(KERN_DEBUG "Bus freq set to %d done!\n", ddr_rate); + pr_debug("Bus freq set to %d done!\n", ddr_rate); return 0; } @@ -303,7 +303,7 @@ int update_ddr_freq_imx6q(int ddr_rate) if (ddr_rate == curr_ddr_rate) return 0; - printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate); + pr_debug("Bus freq set to %d start...\n", ddr_rate); if (low_bus_freq_mode || audio_bus_freq_mode) dll_off = true; @@ -397,7 +397,7 @@ int update_ddr_freq_imx6q(int ddr_rate) local_irq_enable(); - printk(KERN_DEBUG "Bus freq set to %d done! cpu=%d\n", ddr_rate, me); + pr_debug("Bus freq set to %d done! cpu=%d\n", ddr_rate, me); return 0; } diff --git a/arch/arm/mach-imx/busfreq_lpddr2.c b/arch/arm/mach-imx/busfreq_lpddr2.c index 9f5469871604ea..3b68eadee619cc 100644 --- a/arch/arm/mach-imx/busfreq_lpddr2.c +++ b/arch/arm/mach-imx/busfreq_lpddr2.c @@ -70,7 +70,7 @@ int update_lpddr2_freq(int ddr_rate) if (ddr_rate == curr_ddr_rate) return 0; - printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate); + pr_debug("Bus freq set to %d start...\n", ddr_rate); spin_lock_irqsave(&freq_lock, flags); /* @@ -87,7 +87,7 @@ int update_lpddr2_freq(int ddr_rate) curr_ddr_rate = ddr_rate; spin_unlock_irqrestore(&freq_lock, flags); - printk(KERN_DEBUG "\nBus freq set to %d done...\n", ddr_rate); + pr_debug("Bus freq set to %d done...\n", ddr_rate); return 0; } From 6266e63d51003e37eeac76ec433881eb152314d8 Mon Sep 17 00:00:00 2001 From: Rudi Date: Wed, 19 Aug 2015 07:28:21 +0200 Subject: [PATCH 0604/1983] net: fec: Fix kernel oops when no phy is detected --- drivers/net/ethernet/freescale/fec_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index e7d9e9ce3d91c7..9d9c4cf475ad85 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3412,7 +3412,7 @@ fec_probe(struct platform_device *pdev) ret = fec_enet_mii_probe(ndev); if (ret) - goto failed_register; + goto failed_mii_probe; phy_start_aneg(fep->phy_dev); @@ -3426,6 +3426,8 @@ fec_probe(struct platform_device *pdev) INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work); return 0; +failed_mii_probe: + unregister_netdev(ndev); failed_register: fec_enet_mii_remove(fep); failed_mii_init: From 2dbf6c1b8738c780ffe618491fe3ac29bbc8aabe Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 28 Jan 2015 11:14:55 +0100 Subject: [PATCH 0605/1983] cdc-acm: add sanity checks Upstream-commit: 7e860a6e7aa62b337a61110430cd633db5b0d2dd Check the special CDC headers for a plausible minimum length. Another big operating systems ignores such garbage. Signed-off-by: Oliver Neukum CC: stable@vger.kernel.org Reviewed-by: Adam Lee Tested-by: Adam Lee Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d7049c393a33c0..77403241d0380d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1063,6 +1063,7 @@ static int acm_probe(struct usb_interface *intf, unsigned long quirks; int num_rx_buf; int i; + unsigned int elength = 0; int combined_interfaces = 0; struct device *tty_dev; int rv = -ENOMEM; @@ -1108,9 +1109,12 @@ static int acm_probe(struct usb_interface *intf, dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } + elength = buffer[0]; switch (buffer[2]) { case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; if (union_header) { dev_err(&intf->dev, "More than one " "union descriptor, skipping ...\n"); @@ -1119,31 +1123,38 @@ static int acm_probe(struct usb_interface *intf, union_header = (struct usb_cdc_union_desc *)buffer; break; case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; cfd = (struct usb_cdc_country_functional_desc *)buffer; break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: + if (elength < 4) + goto next_desc; ac_management_function = buffer[3]; break; case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < 5) + goto next_desc; call_management_function = buffer[3]; call_interface_num = buffer[4]; if ((quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3) dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n"); break; default: - /* there are LOTS more CDC descriptors that + /* + * there are LOTS more CDC descriptors that * could legitimately be found here. */ dev_dbg(&intf->dev, "Ignoring descriptor: " - "type %02x, length %d\n", - buffer[2], buffer[0]); + "type %02x, length %ud\n", + buffer[2], elength); break; } next_desc: - buflen -= buffer[0]; - buffer += buffer[0]; + buflen -= elength; + buffer += elength; } if (!union_header) { From ca8e809fb020ce956be8dc0f0f46ccb47840e4ad Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 28 Jan 2015 16:56:24 +0100 Subject: [PATCH 0606/1983] cdc-acm: kill unnecessary messages Upstream-commit: 17136d4984b37d2a9acb67e1da1a3e95e3fed918 Memory allocation failures are reported by a central facility. No need to repeat the job. Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 77403241d0380d..0f5098f7398e36 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1272,10 +1272,8 @@ static int acm_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "interfaces are valid\n"); acm = kzalloc(sizeof(struct acm), GFP_KERNEL); - if (acm == NULL) { - dev_err(&intf->dev, "out of memory (acm kzalloc)\n"); + if (acm == NULL) goto alloc_fail; - } minor = acm_alloc_minor(acm); if (minor == ACM_TTY_MINORS) { @@ -1313,42 +1311,32 @@ static int acm_probe(struct usb_interface *intf, init_usb_anchor(&acm->delayed); buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); - if (!buf) { - dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n"); + if (!buf) goto alloc_fail2; - } acm->ctrl_buffer = buf; - if (acm_write_buffers_alloc(acm) < 0) { - dev_err(&intf->dev, "out of memory (write buffer alloc)\n"); + if (acm_write_buffers_alloc(acm) < 0) goto alloc_fail4; - } acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL); - if (!acm->ctrlurb) { - dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); + if (!acm->ctrlurb) goto alloc_fail5; - } + for (i = 0; i < num_rx_buf; i++) { struct acm_rb *rb = &(acm->read_buffers[i]); struct urb *urb; rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL, &rb->dma); - if (!rb->base) { - dev_err(&intf->dev, "out of memory " - "(read bufs usb_alloc_coherent)\n"); + if (!rb->base) goto alloc_fail6; - } rb->index = i; rb->instance = acm; urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - dev_err(&intf->dev, - "out of memory (read urbs usb_alloc_urb)\n"); + if (!urb) goto alloc_fail6; - } + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = rb->dma; if (acm->is_int_ep) { @@ -1373,11 +1361,8 @@ static int acm_probe(struct usb_interface *intf, struct acm_wb *snd = &(acm->wb[i]); snd->urb = usb_alloc_urb(0, GFP_KERNEL); - if (snd->urb == NULL) { - dev_err(&intf->dev, - "out of memory (write urbs usb_alloc_urb)\n"); + if (snd->urb == NULL) goto alloc_fail7; - } if (usb_endpoint_xfer_int(epwrite)) usb_fill_int_urb(snd->urb, usb_dev, From 80e52f5ec8c9c49115d1d7e04022ffe480b00f9d Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 20 Mar 2015 09:24:24 +0100 Subject: [PATCH 0607/1983] cdc-acm: fix race between callback and unthrottle Upstream-commit: 36e59e0d70d6150e7a2155c54612ea875e88ce8d Abn URB may be may marked free only after the buffer has been processed or there is a small window during which it could be submitted on another CPU and overwrite an unprocessed buffer Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0f5098f7398e36..cbe50ab988c10c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -407,23 +407,31 @@ static void acm_read_bulk_callback(struct urb *urb) struct acm_rb *rb = urb->context; struct acm *acm = rb->instance; unsigned long flags; + int status = urb->status; dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, rb->index, urb->actual_length); - set_bit(rb->index, &acm->read_urbs_free); if (!acm->dev) { + set_bit(rb->index, &acm->read_urbs_free); dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); return; } usb_mark_last_busy(acm->dev); if (urb->status) { + set_bit(rb->index, &acm->read_urbs_free); dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", - __func__, urb->status); + __func__, status); return; } acm_process_read_urb(acm, urb); + /* + * Unthrottle may run on another CPU which needs to see events + * in the same order. Submission has an implict barrier + */ + smp_mb__before_clear_bit(); + set_bit(rb->index, &acm->read_urbs_free); /* throttle device if requested by tty */ spin_lock_irqsave(&acm->read_lock, flags); From dc69ade8f9cd15909a7f363092bb3322da16d6fe Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 20 Mar 2015 11:25:17 +0100 Subject: [PATCH 0608/1983] cdc-acm: surpress misleading message Upstream-commit: 6c8074e90c7350f5e38caf1e8d73e98df4115403 During the entry intro suspend a misleading message can be printed. Surpress it by checking the specific error. Signed-off-by: Oliver Neukum 0 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index cbe50ab988c10c..ef764da945f4ac 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -350,7 +350,7 @@ static void acm_ctrl_irq(struct urb *urb) } exit: retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) + if (retval && retval != -EPERM) dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n", __func__, retval); } From 46bc834b7f93773d02433bfdeb6e5f74304b5556 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 20 Mar 2015 11:41:06 +0100 Subject: [PATCH 0609/1983] cdc-acm: convert to not directly using urb->status Upstream-commit: 4132cd02db180d018325e26bd145a509a14fcd6b A step on the road to passing status as a parameter Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ef764da945f4ac..b60e1962cc2ac8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -419,7 +419,7 @@ static void acm_read_bulk_callback(struct urb *urb) } usb_mark_last_busy(acm->dev); - if (urb->status) { + if (status) { set_bit(rb->index, &acm->read_urbs_free); dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", __func__, status); @@ -450,13 +450,14 @@ static void acm_write_bulk(struct urb *urb) struct acm_wb *wb = urb->context; struct acm *acm = wb->instance; unsigned long flags; + int status = urb->status; - if (urb->status || (urb->actual_length != urb->transfer_buffer_length)) + if (status || (urb->actual_length != urb->transfer_buffer_length)) dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n", __func__, urb->actual_length, urb->transfer_buffer_length, - urb->status); + status); spin_lock_irqsave(&acm->write_lock, flags); acm_write_done(acm, wb); From e20267b762f5e8b9726004136a4a47e97edf6b21 Mon Sep 17 00:00:00 2001 From: Quentin Casasnovas Date: Tue, 14 Apr 2015 11:25:43 +0200 Subject: [PATCH 0610/1983] cdc-acm: prevent infinite loop when parsing CDC headers. Upstream-commit: 0d3bba0287d4e284c3ec7d3397e81eec920d5e7e Phil and I found out a problem with commit: 7e860a6e7aa6 ("cdc-acm: add sanity checks") It added some sanity checks to ignore potential garbage in CDC headers but also introduced a potential infinite loop. This can happen at the first loop iteration (elength = 0 in that case) if the description isn't a DT_CS_INTERFACE or later if 'buffer[0]' is zero. It should also be noted that the wrong length was being added to 'buffer' in case 'buffer[1]' was not a DT_CS_INTERFACE descriptor, since elength was assigned after that check in the loop. A specially crafted USB device could be used to trigger this infinite loop. Fixes: 7e860a6e7aa6 ("cdc-acm: add sanity checks") Signed-off-by: Phil Turnbull Signed-off-by: Quentin Casasnovas CC: Sergei Shtylyov CC: Oliver Neukum CC: Adam Lee CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index b60e1962cc2ac8..8d61fc6fc15090 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1114,11 +1114,16 @@ static int acm_probe(struct usb_interface *intf, } while (buflen > 0) { + elength = buffer[0]; + if (!elength) { + dev_err(&intf->dev, "skipping garbage byte\n"); + elength = 1; + goto next_desc; + } if (buffer[1] != USB_DT_CS_INTERFACE) { dev_err(&intf->dev, "skipping garbage\n"); goto next_desc; } - elength = buffer[0]; switch (buffer[2]) { case USB_CDC_UNION_TYPE: /* we've found it */ From e33f62aabc35259200eeea71dbcae0757d7a0225 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 20 Aug 2015 08:43:16 +0200 Subject: [PATCH 0611/1983] dts: hb/hb2: Add basic support for the lvds port This adds very basic support for the single channel lvds port that is on the end of the Hummingboard and Hummingboard edge. It is disabled by default because it can not be hotplug detected and we don't need memory reserved for something that isn't going to be used by many people. To enable it you need to enable the second framebuffer device and then enable the ldb. You should also take note to change the fb aliases and the crtc depending on the SOC variant you are using and whether this should be the primary framebuffer device or not. By default I have it aliased to be /dev/fb2. --- arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 37 ++++++++++++++++++++ arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi | 37 ++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index ccac1c3ee70c55..9d99b68afc34cb 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -47,6 +47,7 @@ mmc0 = &usdhc2; mmc1 = &usdhc1; mxcfb0 = &mxcfb1; + mxcfb2 = &mxcfb2; }; chosen { @@ -73,6 +74,16 @@ status = "okay"; }; + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + regulators { compatible = "simple-bus"; @@ -422,6 +433,32 @@ }; }; +&ldb { + status = "disabled"; + + lvds-channel@0 { + fsl,data-mapping = "spwg"; + fsl,data-width = <18>; + crtc = "ipu2-di0"; + primary; + + display-timings { + native-mode = <&timing0>; + timing0: hsd100pxn1 { + clock-frequency = <65000000>; + hactive = <1024>; + vactive = <768>; + hback-porch = <220>; + hfront-porch = <40>; + vback-porch = <21>; + vfront-porch = <7>; + hsync-len = <60>; + vsync-len = <10>; + }; + }; + }; +}; + &mipi_csi { ipu_id = <0>; csi_id = <1>; diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi index edc95beb968630..89e64fd692afb1 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard2.dtsi @@ -48,6 +48,7 @@ mmc0 = &usdhc2; mmc1 = &usdhc1; mxcfb0 = &mxcfb1; + mxcfb2 = &mxcfb2; }; chosen { @@ -74,6 +75,16 @@ status = "okay"; }; + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + regulators { compatible = "simple-bus"; @@ -573,6 +584,32 @@ }; }; +&ldb { + status = "disabled"; + + lvds-channel@0 { + fsl,data-mapping = "spwg"; + fsl,data-width = <18>; + crtc = "ipu2-di0"; + primary; + + display-timings { + native-mode = <&timing0>; + timing0: hsd100pxn1 { + clock-frequency = <65000000>; + hactive = <1024>; + vactive = <768>; + hback-porch = <220>; + hfront-porch = <40>; + vback-porch = <21>; + vfront-porch = <7>; + hsync-len = <60>; + vsync-len = <10>; + }; + }; + }; +}; + &mipi_csi { ipu_id = <0>; csi_id = <1>; From 8205934bc0f8e696add343c8aa7dae5a61055fc2 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 20 Aug 2015 08:58:07 +0200 Subject: [PATCH 0612/1983] arm: configs: Update cbi/hb defconfigs This makes some default changes to better reflect the best options in this kernel for our hardware. --- arch/arm/configs/imx_v7_cbi_hb_base_defconfig | 14 ++++++-------- arch/arm/configs/imx_v7_cbi_hb_defconfig | 15 ++++++--------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig index 5e1ebb64509fb2..f354734c1fd387 100644 --- a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig +++ b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig @@ -11,9 +11,6 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y CONFIG_CLEANCACHE=y -CONFIG_FRONTSWAP=y -CONFIG_ZSWAP=y -CONFIG_ZSMALLOC=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_MODULES=y @@ -25,20 +22,21 @@ CONFIG_GPIO_PCA953X=y CONFIG_ARCH_MXC=y CONFIG_MXC_DEBUG_BOARD=y CONFIG_SOC_IMX6Q=y -CONFIG_SOC_IMX6SL=y +# CONFIG_SOC_IMX6SL is not set +# CONFIG_SOC_IMX6SX is not set # CONFIG_SWP_EMULATE is not set CONFIG_PCI=y CONFIG_PCIE_DW=y CONFIG_PCI_IMX6=y CONFIG_SMP=y -CONFIG_VMSPLIT_2G=y +CONFIG_VMSPLIT_3G=y CONFIG_PREEMPT_VOLUNTARY=y CONFIG_AEABI=y # CONFIG_OABI_COMPAT is not set CONFIG_HIGHMEM=y CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y @@ -52,8 +50,8 @@ CONFIG_NEON=y CONFIG_KERNEL_MODE_NEON=y CONFIG_BINFMT_MISC=m CONFIG_PM_RUNTIME=y -CONFIG_PM_DEBUG=y -CONFIG_PM_TEST_SUSPEND=y +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y CONFIG_DEFAULT_BFQ=y diff --git a/arch/arm/configs/imx_v7_cbi_hb_defconfig b/arch/arm/configs/imx_v7_cbi_hb_defconfig index d3dbb90f65ba47..68d664d2f9c587 100644 --- a/arch/arm/configs/imx_v7_cbi_hb_defconfig +++ b/arch/arm/configs/imx_v7_cbi_hb_defconfig @@ -91,19 +91,20 @@ CONFIG_GPIO_PCA953X=y CONFIG_ARCH_MXC=y CONFIG_MXC_DEBUG_BOARD=y CONFIG_SOC_IMX6Q=y -CONFIG_SOC_IMX6SL=y +# CONFIG_SOC_IMX6SL is not set +# CONFIG_SOC_IMX6SX is not set # CONFIG_SWP_EMULATE is not set CONFIG_PCI=y CONFIG_PCIE_DW=y CONFIG_PCI_IMX6=y CONFIG_SMP=y -CONFIG_VMSPLIT_2G=y +CONFIG_VMSPLIT_3G=y CONFIG_AEABI=y # CONFIG_OABI_COMPAT is not set CONFIG_HIGHMEM=y CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y @@ -116,9 +117,8 @@ CONFIG_VFPv3=y CONFIG_NEON=y CONFIG_KERNEL_MODE_NEON=y CONFIG_BINFMT_MISC=m -CONFIG_PM_RUNTIME=y -CONFIG_PM_DEBUG=y -CONFIG_PM_TEST_SUSPEND=y +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -5010,9 +5010,6 @@ CONFIG_PTP_1588_CLOCK=m CONFIG_PTP_1588_CLOCK_PCH=m CONFIG_CLEANCACHE=y -CONFIG_FRONTSWAP=y -CONFIG_ZSWAP=y -CONFIG_ZSMALLOC=y # CONFIG_PGTABLE_MAPPING is not set # CONFIG_MDIO_GPIO is not set From 42f51235be3ed2288e0aa411ce89b354beb6d0e9 Mon Sep 17 00:00:00 2001 From: Rudi Date: Fri, 21 Aug 2015 12:46:47 +0200 Subject: [PATCH 0613/1983] watchdog: imx2_wdt: Add support for WDIOC_SETOPTIONS This adds an ioctl WDIOC_SETOPTIONS which fixes support for many userspace watchdog daemons that expect this ioctl to exist, including systemd. --- drivers/watchdog/imx2_wdt.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 11bda8079b12fd..8e385b01c590fa 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -205,7 +205,7 @@ static int imx2_wdt_close(struct inode *inode, struct file *file) { if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout) imx2_wdt_stop(); - else { + else if (!timer_pending(&imx2_wdt.timer)) { dev_crit(imx2_wdt_miscdev.parent, "Unexpected close: Expect reboot!\n"); imx2_wdt_ping(); @@ -265,6 +265,15 @@ static long imx2_wdt_ioctl(struct file *file, unsigned int cmd, case WDIOC_GETPRETIMEOUT: return put_user(imx2_wdt.pretimeout, p); + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + if (new_value & WDIOS_DISABLECARD) + imx2_wdt_stop(); + if (new_value & WDIOS_ENABLECARD) + imx2_wdt_start(); + return 0; + default: return -ENOTTY; } From 35b45591c701042464ab1a6b3baa6dca135700df Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 22 Aug 2015 13:02:10 +0200 Subject: [PATCH 0614/1983] mach: imx: Clock down the default VPU settings Some of the iMX6 chips have problems encoding when the VPU is clocked up to 352Mhz. We drop this down to 327Mhz which seems to be stable for both encoding and decoding. Luckily now that the VPU is on its own pfd we can change this clocking at will. --- arch/arm/mach-imx/clk-imx6q.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 61a0a25889a4e7..5868988a1abe56 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -568,6 +568,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) /*Set enet_ref clock to 125M to supply for RGMII tx_clk */ clk_set_rate(clk[IMX6QDL_CLK_ENET_REF], 125000000); + imx_clk_set_parent(clk[IMX6QDL_CLK_VPU_AXI_SEL], clk[IMX6QDL_CLK_PLL2_PFD0_352M]); + imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD0_352M], 327000000); #ifdef CONFIG_MX6_VPU_352M /* * If VPU 352M is enabled, then PLL2_PDF2 need to be @@ -581,7 +583,6 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD0_352M], 352000000); pr_info("VPU 352M is enabled!\n"); #endif - imx_clk_set_parent(clk[IMX6QDL_CLK_VPU_AXI_SEL], clk[IMX6QDL_CLK_PLL2_PFD0_352M]); /* Set initial power mode */ imx6q_set_lpm(WAIT_CLOCKED); From 9bfa9d0370c398dc4ce0b4d065933bf029274070 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 22 Aug 2015 13:04:56 +0200 Subject: [PATCH 0615/1983] mach: imx: Re-enable SmartEEE in the Atheros Phy I am testing the atheros phy's built in power savings features, and did find that just enabling them was causing link disconnects as reported initially. However since increasing the tx twd time out I have not experienced any more disconnects. This is a feature that I want to get working as it saves about 60mA's of power when the network is idle. --- arch/arm/mach-imx/mach-imx6q.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 07fe35bba9f0cc..2a3492df7bcf2a 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -214,7 +214,15 @@ static int ar8031_phy_fixup(struct phy_device *dev) phy_write(dev, 0xe, 0x805d); phy_write(dev, 0xd, 0x4003); val = phy_read(dev, 0xe); - val &= ~(0x1 << 8); + val |= (0x1 << 8); + phy_write(dev, 0xe, val); + + /* Increase 1000BT tx time for SmartEEE. */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805b); + phy_write(dev, 0xd, 0x4003); + val = phy_read(dev, 0xe); + val = 0x1317; phy_write(dev, 0xe, val); /* To enable AR8031 output a 125MHz clk from CLK_25M */ From 749daef1f59128646be44de544c0e7e0e4a0857c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 22 Aug 2015 14:45:19 +0200 Subject: [PATCH 0616/1983] arm: configs: cbi/hb update config files This enables suspend, pm_runtime, and disables some debugging and the noisy camm tests in our default configs. --- arch/arm/configs/imx_v7_cbi_hb_base_defconfig | 4 +++- arch/arm/configs/imx_v7_cbi_hb_defconfig | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig index f354734c1fd387..24f8031fe7dcb1 100644 --- a/arch/arm/configs/imx_v7_cbi_hb_base_defconfig +++ b/arch/arm/configs/imx_v7_cbi_hb_base_defconfig @@ -50,6 +50,8 @@ CONFIG_NEON=y CONFIG_KERNEL_MODE_NEON=y CONFIG_BINFMT_MISC=m CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_SUSPEND=y # CONFIG_PM_DEBUG is not set # CONFIG_PM_TEST_SUSPEND is not set CONFIG_IOSCHED_BFQ=y @@ -328,7 +330,7 @@ CONFIG_CRYPTO_TWOFISH=y # CONFIG_CRYPTO_ANSI_CPRNG is not set CONFIG_CRYPTO_DEV_FSL_CAAM=y CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y -CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +# CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST is not set CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_CRC_CCITT=m diff --git a/arch/arm/configs/imx_v7_cbi_hb_defconfig b/arch/arm/configs/imx_v7_cbi_hb_defconfig index 68d664d2f9c587..7798dc85bda9cf 100644 --- a/arch/arm/configs/imx_v7_cbi_hb_defconfig +++ b/arch/arm/configs/imx_v7_cbi_hb_defconfig @@ -390,7 +390,7 @@ CONFIG_CRYPTO_TWOFISH=y # CONFIG_CRYPTO_ANSI_CPRNG is not set CONFIG_CRYPTO_DEV_FSL_CAAM=y CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y -CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +# CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST is not set CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y CONFIG_CRYPTO_AES_ARM_BS=y CONFIG_CRC_CCITT=m @@ -4563,6 +4563,9 @@ CONFIG_PROC_EVENTS=y CONFIG_IBMASR=m +CONFIG_PM_RUNTIME=y +# CONFIG_PM_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set CONFIG_PM=y CONFIG_PM_STD_PARTITION="" # CONFIG_DPM_WATCHDOG is not set # revisit this in debug @@ -4571,7 +4574,7 @@ CONFIG_PM_TRACE_RTC=y # CONFIG_PM_OPP is not set # CONFIG_PM_AUTOSLEEP is not set # CONFIG_PM_WAKELOCKS is not set -CONFIG_HIBERNATION=y +# CONFIG_HIBERNATION is not set # CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set CONFIG_SUSPEND=y @@ -5024,7 +5027,7 @@ CONFIG_CLEANCACHE=y # CONFIG_GPIO_IT8761E is not set # CONFIG SB105x is not set # CONFIG_GPIO_TS5500 is not set -CONFIG_GPIO_VIPERBOARD=m +# CONFIG_GPIO_VIPERBOARD is not set # CONFIG_UCB1400_CORE is not set # CONFIG_TPS6105X is not set # CONFIG_RADIO_MIROPCM20 is not set From b78f7c5abaa1429a6cf3dbf949217f8011ab05fd Mon Sep 17 00:00:00 2001 From: Rudi Date: Sat, 22 Aug 2015 20:59:42 +0200 Subject: [PATCH 0617/1983] leds: Fix support for low-active PWM LED --- drivers/leds/leds-pwm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index a7b369fc355491..d761527aad302d 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -70,6 +70,10 @@ static void led_pwm_set(struct led_classdev *led_cdev, duty *= brightness; do_div(duty, max); + + if (led_dat->active_low) + duty = led_dat->period - duty; + led_dat->duty = duty; if (led_dat->can_sleep) @@ -117,6 +121,7 @@ static int led_pwm_create_of(struct platform_device *pdev, led_dat->cdev.default_trigger = of_get_property(child, "linux,default-trigger", NULL); + led_dat->active_low = of_property_read_bool(child, "active-low"); of_property_read_u32(child, "max-brightness", &led_dat->cdev.max_brightness); From 48e164ac6da650a68c5b4bce2bb875e4685f9808 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 24 Aug 2015 16:09:33 +0200 Subject: [PATCH 0618/1983] Revert "mmc: core: Extend mmc_delay in mmc_power_cycle for UHS transition" This reverts commit b6f7377fb68fec63035c72253e2d3f214340235a. This was fixed upstream and I have backported that patch. Having both patches in the tree causes problems with some SanDisk cards. --- drivers/mmc/core/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 11fdefede308f8..7d1a3722a53992 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1647,7 +1647,7 @@ void mmc_power_cycle(struct mmc_host *host, u32 ocr) { mmc_power_off(host); /* Wait at least 1 ms according to SD spec */ - mmc_delay(3); + mmc_delay(1); mmc_power_up(host, ocr); } From 83cf61928cab24f6aebf16ed868b00a06573ba09 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 24 Aug 2015 16:51:15 +0100 Subject: [PATCH 0619/1983] net: fec: add support for ethtool eee operations Add support for the ethtool 802.3az EEE operations by coupling the phy ethtool functions into our ethtool_ops structure. We don't need MAC level support with a phy such as the Atheros AR8035, which takes care of EEE itself. Signed-off-by: Russell King --- drivers/net/ethernet/freescale/fec_main.c | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 9d9c4cf475ad85..668b08dfd25159 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2503,6 +2503,28 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) return 0; } +static int fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *eee) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct phy_device *phydev = fep->phy_dev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_get_eee(phydev, eee); +} + +static int fec_enet_set_eee(struct net_device *ndev, struct ethtool_eee *eee) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + struct phy_device *phydev = fep->phy_dev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_set_eee(phydev, eee); +} + static const struct ethtool_ops fec_enet_ethtool_ops = { .get_settings = fec_enet_get_settings, .set_settings = fec_enet_set_settings, @@ -2523,6 +2545,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .set_tunable = fec_enet_set_tunable, .get_wol = fec_enet_get_wol, .set_wol = fec_enet_set_wol, + .get_eee = fec_enet_get_eee, + .set_eee = fec_enet_set_eee, }; static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) From 29e220795bd86fb26ee43e09f7733a2995421f6d Mon Sep 17 00:00:00 2001 From: warped-rudi Date: Tue, 25 Aug 2015 08:29:28 +0200 Subject: [PATCH 0620/1983] leds: Update device tree binding documentation --- Documentation/devicetree/bindings/leds/leds-pwm.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/leds/leds-pwm.txt b/Documentation/devicetree/bindings/leds/leds-pwm.txt index 7297107cf83285..6c70d03b98c0fa 100644 --- a/Documentation/devicetree/bindings/leds/leds-pwm.txt +++ b/Documentation/devicetree/bindings/leds/leds-pwm.txt @@ -17,6 +17,8 @@ LED sub-node properties: see Documentation/devicetree/bindings/leds/common.txt - linux,default-trigger : (optional) see Documentation/devicetree/bindings/leds/common.txt +- active-low : (optional) + Inverts the duty cycle to reflect a low-active output pin Example: @@ -38,6 +40,7 @@ pwmleds { label = "omap4::keypad"; pwms = <&twl_pwm 0 7812500>; max-brightness = <127>; + active-low; }; charging { From 64784921a6db29e9fd7be016ed04f6a6f63f3a1a Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 20 Jan 2015 16:41:59 -0800 Subject: [PATCH 0621/1983] net: phy: fixed: allow setting no update_link callback fixed_phy_set_link_update() contains an early check against a NULL callback pointer, which basically prevents us from removing any previous callback we may have set. The users of the fp->link_update callback deal with a NULL callback just fine, so we really want to allow "removing" a link_update callback to avoid dangling callback pointers during e.g: module removal. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/fixed_phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index d60d875cb4450a..595a65871b66fa 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -157,7 +157,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev, struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; - if (!link_update || !phydev || !phydev->bus) + if (!phydev || !phydev->bus) return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { From 99d5d28c964ccbbf7524a8bcc7c1214619f9355b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Aug 2015 14:39:44 +0200 Subject: [PATCH 0622/1983] dts: hummingboard: Move gpio controlled regulator to card reset Some sdhc cards would hard lock in the firmware because on shutdown the regulator would be turned off, however as soon as the bootloader firmware would start it would be turned on. This time period was short enough that it would crash some cards firmware. Instead we will set up that regulator as a reset. This allows both voltage transitioning and fallback for 1.8V cards to work, as well as consistent shutdowns. --- arch/arm/boot/dts/imx6qdl-hummingboard.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi index 9d99b68afc34cb..49567ed0feeef3 100644 --- a/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-hummingboard.dtsi @@ -526,7 +526,7 @@ &pinctrl_hummingboard_usdhc2_200mhz >; - vmmc-supply = <®_usdhc2_vbus>; + card-external-vcc-supply = <®_usdhc2_vbus>; cd-gpios = <&gpio1 4 0>; status = "okay"; }; From 4d8d49d1fa52aea7a45aa2147a79fdee58a099fa Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Aug 2015 14:43:41 +0200 Subject: [PATCH 0623/1983] mach: imx: Provide a kernel commandline option to enable SmartEEE SmartEEE for the atheros phy was deemed buggy by Freescale and commits were added to disable it for these boards. In inital testing we found that the default settings were causing disconnects but by increasing the Tw buffer time we could allow enough time for all parts of the link to come out of a low power state and function properly without causing a disconnect. This needs further testing but so far initial tests look stable. This saves between 60 and 80 mA at 5volts when the ethernet connection is idle. --- arch/arm/mach-imx/mach-imx6q.c | 63 ++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 2a3492df7bcf2a..507584cb8e6ce0 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -52,6 +52,17 @@ static int flexcan_stby_gpio; static int flexcan0_en; static int flexcan1_en; +static int ar803x_smarteee = 0; + +static int __init ar803x_smarteee_setup(char *__unused) +{ + ar803x_smarteee = 1; + return 1; +} + +__setup("ar803x_smarteee", ar803x_smarteee_setup); + + static void imx6q_fec_sleep_enable(int enabled) { struct regmap *gpr; @@ -214,15 +225,7 @@ static int ar8031_phy_fixup(struct phy_device *dev) phy_write(dev, 0xe, 0x805d); phy_write(dev, 0xd, 0x4003); val = phy_read(dev, 0xe); - val |= (0x1 << 8); - phy_write(dev, 0xe, val); - - /* Increase 1000BT tx time for SmartEEE. */ - phy_write(dev, 0xd, 0x3); - phy_write(dev, 0xe, 0x805b); - phy_write(dev, 0xd, 0x4003); - val = phy_read(dev, 0xe); - val = 0x1317; + val &= ~(0x1 << 8); phy_write(dev, 0xe, val); /* To enable AR8031 output a 125MHz clk from CLK_25M */ @@ -250,20 +253,11 @@ static int ar8035_phy_fixup(struct phy_device *dev) { u16 val; - /* Ar803x phy SmartEEE feature cause link status generates glitch, - * which cause ethernet link down/up issue, so disable SmartEEE - */ - phy_write(dev, 0xd, 0x3); - phy_write(dev, 0xe, 0x805d); - phy_write(dev, 0xd, 0x4003); - - val = phy_read(dev, 0xe); - phy_write(dev, 0xe, val & ~(1 << 8)); - /* - * Enable 125MHz clock from CLK_25M on the AR8031. This - * is fed in to the IMX6 on the ENET_REF_CLK (V22) pad. - * Also, introduce a tx clock delay. + * Disable SmartEEE and Enable 125MHz clock from + * CLK_25M on the AR8031. This is fed in to the + * IMX6 on the ENET_REF_CLK (V22) pad. Also, + * introduce a tx clock delay. * * This is the same as is the AR8031 fixup. */ @@ -274,6 +268,31 @@ static int ar8035_phy_fixup(struct phy_device *dev) if (val & BMCR_PDOWN) phy_write(dev, 0x0, val & ~BMCR_PDOWN); + if (!ar803x_smarteee) + return 0; + + /* Ar803x phy SmartEEE feature cause link status generates glitch, + * which cause ethernet link down/up issue, so disable SmartEEE + */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805d); + phy_write(dev, 0xd, 0x4003); + val = phy_read(dev, 0xe); + val |= (0x1 << 8); + phy_write(dev, 0xe, val); + + /* Increase 1000BT tw time for SmartEEE. It seems that we need + * a bit more time than standard to git up and running. Bumping + * up the Tw time allows us to enable SmartEEE without generating + * ethernet disconnects occasionally + */ + phy_write(dev, 0xd, 0x3); + phy_write(dev, 0xe, 0x805b); + phy_write(dev, 0xd, 0x4003); + val = phy_read(dev, 0xe); + val = 0x1517; + phy_write(dev, 0xe, val); + return 0; } From 67f53c3d45db6636a26df48a71fef69139edd780 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 26 Aug 2015 14:52:14 +0200 Subject: [PATCH 0624/1983] mmc: core: Don't fail UHS transition if host max_current fails. During the transition to UHS we query the max_current of the host and then attempt to negotiate that setting. If it fails then we exit the entire UHS transition. Since this is the max_current of the host and not the card try lower current settings until they are accepted and we can move forward with the transition. This should be safe since we are going from max_current down, and any problems in the signalling should be caught when the voltage switch happens. --- drivers/mmc/core/sd.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 569972f45c0d0c..b00dacb08911b6 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -562,7 +562,7 @@ static u32 sd_get_host_max_current(struct mmc_host *host) static int sd_set_current_limit(struct mmc_card *card, u8 *status) { int current_limit = SD_SET_CURRENT_NO_CHANGE; - int err; + int i, err; u32 max_current; /* @@ -598,18 +598,23 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) else if (max_current >= 200) current_limit = SD_SET_CURRENT_LIMIT_200; - if (current_limit != SD_SET_CURRENT_NO_CHANGE) { - err = mmc_sd_switch(card, 1, 3, current_limit, status); - if (err) - return err; + if (current_limit == SD_SET_CURRENT_NO_CHANGE) + return 0; - if (((status[15] >> 4) & 0x0F) != current_limit) - pr_warning("%s: Problem setting current limit!\n", - mmc_hostname(card->host)); + for (i = current_limit; i >= 0; i--) { + err = mmc_sd_switch(card, 1, 3, i, status); + if (err) + continue; + if (((status[15] >> 4) & 0x0F) != i) { + pr_warning("%s: Problem setting current limit to %d!\n", + mmc_hostname(card->host), i); + } else { + break; + } } - return 0; + return err; } /* From 19828489ef5ccbb1a4af159f0e8bbb5ecc567060 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 27 Aug 2015 15:26:05 +0200 Subject: [PATCH 0625/1983] Revert "net: fec: Make runtime pm dependant on link status" This reverts commit ef133f1176f8372c398f3f39b77a54949ec484a1. Much like the upstream tries, this is still missing some corner cases. For now we will need to wait before integrating this patch. --- drivers/net/ethernet/freescale/fec_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 668b08dfd25159..da60e94661ad13 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1722,7 +1722,6 @@ static void fec_enet_adjust_link(struct net_device *ndev) if (!fep->link) { fep->link = phy_dev->link; status_change = 1; - pm_runtime_get_sync(ndev->dev.parent); } if (fep->full_duplex != phy_dev->duplex) { @@ -1753,7 +1752,6 @@ static void fec_enet_adjust_link(struct net_device *ndev) napi_enable(&fep->napi); fep->link = phy_dev->link; status_change = 1; - pm_runtime_put_sync_suspend(ndev->dev.parent); } } @@ -2827,6 +2825,7 @@ fec_enet_open(struct net_device *ndev) phy_start(fep->phy_dev); netif_tx_start_all_queues(ndev); + pm_runtime_get_sync(ndev->dev.parent); if ((id_entry->driver_data & FEC_QUIRK_BUG_WAITMODE) && !fec_enet_irq_workaround(fep)) pm_qos_add_request(&ndev->pm_qos_req, From a15c00aca127c010a7e125a0e31c86814db31c4c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 27 Aug 2015 17:35:21 +0200 Subject: [PATCH 0626/1983] mach: imx: Change vpu clock for iMX6S/DL the iMX6S/DL generally have slower memory and are built for lower power draw. Lets clock the VPU another step down to 306Mhz, which is still faster than the 270Mhz default. --- arch/arm/mach-imx/clk-imx6q.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index 5868988a1abe56..9d48f3c1635c55 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -569,7 +569,12 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk_set_rate(clk[IMX6QDL_CLK_ENET_REF], 125000000); imx_clk_set_parent(clk[IMX6QDL_CLK_VPU_AXI_SEL], clk[IMX6QDL_CLK_PLL2_PFD0_352M]); - imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD0_352M], 327000000); + + if (cpu_is_imx6dl()) + imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD0_352M], 306000000); + else + imx_clk_set_rate(clk[IMX6QDL_CLK_PLL2_PFD0_352M], 327000000); + #ifdef CONFIG_MX6_VPU_352M /* * If VPU 352M is enabled, then PLL2_PDF2 need to be From 7fc30f8b22fa4e19dd171498e2b94abfe4d0d90f Mon Sep 17 00:00:00 2001 From: Peter Vicman Date: Tue, 1 Sep 2015 12:48:24 +0200 Subject: [PATCH 0627/1983] dts: tbs2910: Add the device-tree support --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx6q-tbs2910.dts | 643 ++++++++++++++++++++++++++++ 2 files changed, 644 insertions(+) create mode 100644 arch/arm/boot/dts/imx6q-tbs2910.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 7244693ecbc7f8..1cee09708e5937 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -186,6 +186,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6q-sbc6x.dtb \ imx6q-udoo.dtb \ imx6q-wandboard.dtb \ + imx6q-tbs2910.dtb \ imx6sl-evk.dtb \ imx6sl-evk-csi.dtb \ imx6sl-evk-ldo.dtb \ diff --git a/arch/arm/boot/dts/imx6q-tbs2910.dts b/arch/arm/boot/dts/imx6q-tbs2910.dts new file mode 100644 index 00000000000000..ba37c44d693a2c --- /dev/null +++ b/arch/arm/boot/dts/imx6q-tbs2910.dts @@ -0,0 +1,643 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; + +#include "imx6q.dtsi" +#include +#include + +/ { + model = "TBS Matrix"; + compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; + + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + mxcfb2 = &mxcfb3; + mxcfb3 = &mxcfb4; + mmc0 = &usdhc4; + }; + + chosen { + stdout-path = &uart1; + }; + + memory { + reg = <0x10000000 0x80000000>; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_2p5v: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "2P5V"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + }; + + reg_3p3v: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + reg_5p0v: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "5P0V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + }; + }; + + ir_recv: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio3 18 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ir>; + linux,rc-map-name = "rc-rc6-mce"; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_leds>; + + red { + label = "red"; + gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + fan { + compatible = "gpio-fan"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_fan>; + gpios = <&gpio3 28 GPIO_ACTIVE_HIGH>; + gpio-fan,speed-map = <0 0 + 3000 1>; + }; + + sound { + compatible = "fsl,imx-audio-sgtl5000"; + model = "imx-sgtl5000"; + cpu-dai = <&ssi2>; + audio-codec = <&codec>; + audio-routing = "MIC_IN", "Mic Jack", + "Mic Jack", "Mic Bias", + "Headphone Jack", "HP_OUT"; + mux-int-port = <2>; + mux-ext-port = <3>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <24>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb3: fb@2 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB565"; + mode_str ="CLAA-WVGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb4: fb@3 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="LDB-XGA"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; + + restart_poweroff { + compatible = "fsl,snvs-poweroff"; + }; +}; + +&sata { + status = "okay"; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio1 25 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + codec: sgtl5000@0a { + clocks = <&clks 201>; + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sgtl5000>; + VDDA-supply = <®_2p5v>; + VDDIO-supply = <®_3p3v>; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + hdmi: edid@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + rtc: ds1307@68 { + compatible = "dallas,ds1307"; + reg = <0x68>; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + imx6qdl-sabresd { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 /* HUB_GPIO_RST */ + MX6QDL_PAD_NANDF_CS1__GPIO6_IO14 0x80000000 /* eMMC_RST */ + >; + }; + + pinctrl_sgtl5000: sgtl5000grp { + fsl,pins = < + MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0 + >; + }; + + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 + MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 + MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0 + MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1 + >; + }; + + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 + MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x1b059 + >; + }; + + pinctrl_ir: sabresd-ir { + fsl,pins = < + MX6QDL_PAD_EIM_D18__GPIO3_IO18 0x17059 + >; + }; + + pinctrl_sabresd_spdif: sabresd-spdif { + fsl,pins = ; + }; + + pinctrl_hdmi_cec: hdmi_cecgrp { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__HDMI_TX_CEC_LINE 0x1f8b0 + >; + }; + + pinctrl_hdmi_hdcp: hdmi_hdcpgrp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 + MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x17059 + >; + }; + + pinctrl_pwm1: pwm1grp { + fsl,pins = < + MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x17059 + MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x17059 + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x17059 + MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x17059 + >; + }; + + pinctrl_usdhc4: usdhc4grp { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 + MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 + MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 + MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + >; + }; + }; + + gpio_leds { + pinctrl_gpio_leds: gpioledsgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x130b1 + >; + }; + }; + + gpio_fan { + pinctrl_gpio_fan: gpiofangrp { + fsl,pins = < + MX6QDL_PAD_EIM_D28__GPIO3_IO28 0x130b1 + >; + }; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; + power-on-gpio = <&gpio3 19 0>; + reset-gpio = <&gpio7 12 GPIO_ACTIVE_HIGH>; + status = "okay"; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm1>; + status = "okay"; +}; + +&ldb { + ipu_id = <1>; + disp_id = <1>; + ext_ref = <1>; + mode = "sep1"; + sec_ipu_id = <1>; + sec_disp_id = <0>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + status = "okay"; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sabresd_spdif>; + status = "okay"; +}; + +&usbh1 { + vbus-supply = <®_5p0v>; + status = "okay"; +}; + +&usbotg { + vbus-supply = <®_5p0v>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + disable-over-current; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2>; + bus-width = <4>; + cd-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>; + wp-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>; + card-external-vcc-supply = <®_3p3v>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + bus-width = <4>; + cd-gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>; + wp-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>; + card-external-vcc-supply = <®_3p3v>; + status = "okay"; +}; + +&usdhc4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4>; + bus-width = <8>; + non-removable; + no-1-8-v; + status = "okay"; +}; + +&hdmi_core { + ipu_id = <0>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + fsl,phy_reg_vlev = <0x0294>; + fsl,phy_reg_cksymtx = <0x800d>; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&hdmi_cec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_cec>; + status = "okay"; +}; + +&gpc { + fsl,cpu_pupscr_sw2iso = <0xf>; + fsl,cpu_pupscr_sw = <0xf>; + fsl,cpu_pdnscr_iso2sw = <0x1>; + fsl,cpu_pdnscr_iso = <0x1>; +}; From cb6878291e0f7571b2ea14a94ca57f4722bddd4c Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Mon, 31 Aug 2015 10:50:08 +0200 Subject: [PATCH 0628/1983] mxc-hdmi: don't unmask interrupts at start of cable_connected() session (we already changed polarity for next in/out HP - this is effectively killing jitter commit) - so run UNBLANK later. also let's put hdmi_setup() under cable_state() check what skips it's useless call in middle of reconfiguration (new mode params are not yet set). --- drivers/video/mxc/mxc_hdmi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 1ab1bafcec043a..eb26b4e1bb26f7 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -2035,6 +2035,10 @@ static void mxc_hdmi_set_mode(struct mxc_hdmi *hdmi) return; } + console_lock(); + fb_blank(hdmi->fbi, FB_BLANK_UNBLANK); + console_unlock(); + /* If video mode same as previous, init HDMI again */ if (fb_mode_is_equal(&hdmi->previous_non_vga_mode, mode)) { dev_dbg(&hdmi->pdev->dev, @@ -2061,10 +2065,6 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) hdmi->hp_state = HDMI_HOTPLUG_CONNECTED_NO_EDID; - console_lock(); - fb_blank(hdmi->fbi, FB_BLANK_UNBLANK); - console_unlock(); - /* HDMI Initialization Step C */ if (ignore_edid) { edid_status = HDMI_EDID_FAIL; @@ -2504,8 +2504,9 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, /* Unmute interrupts */ hdmi_writeb(~hdmi->plug_event, HDMI_IH_MUTE_PHY_STAT0); - mxc_hdmi_setup(hdmi, val); hdmi_set_blank_state(1); + if (check_hdmi_state()) + mxc_hdmi_setup(hdmi, val); } else if (*((int *)event->data) != hdmi->blank) { dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_BLANK - BLANK\n"); From 6dbdc8a4fac283fc054d2c6187fe2a00014f7a97 Mon Sep 17 00:00:00 2001 From: Horia Geant? Date: Tue, 11 Aug 2015 20:19:20 +0300 Subject: [PATCH 0629/1983] crypto: caam - fix memory corruption in ahash_final_ctx Upstream-commit: b310c178e6d897f82abb9da3af1cd7c02b09f592 When doing pointer operation for accessing the HW S/G table, a value representing number of entries (and not number of bytes) must be used. Cc: # 3.6+ Fixes: 045e36780f115 ("crypto: caam - ahash hmac support") Signed-off-by: Horia Geant? Signed-off-by: Herbert Xu --- drivers/crypto/caam/caamhash.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index d2a90c3c7b8bb4..387c5aa8b35431 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1144,13 +1144,14 @@ static int ahash_final_ctx(struct ahash_request *req) state->buflen_1; u32 *sh_desc = ctx->sh_desc_fin, *desc; dma_addr_t ptr = ctx->sh_desc_fin_dma; - int sec4_sg_bytes; + int sec4_sg_bytes, sec4_sg_src_index; int digestsize = crypto_ahash_digestsize(ahash); struct ahash_edesc *edesc; int ret = 0; int sh_len; - sec4_sg_bytes = (1 + (buflen ? 1 : 0)) * sizeof(struct sec4_sg_entry); + sec4_sg_src_index = 1 + (buflen ? 1 : 0); + sec4_sg_bytes = sec4_sg_src_index * sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ edesc = kzalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + @@ -1177,7 +1178,7 @@ static int ahash_final_ctx(struct ahash_request *req) state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, buf, state->buf_dma, buflen, last_buflen); - (edesc->sec4_sg + sec4_sg_bytes - 1)->len |= SEC4_SG_LEN_FIN; + (edesc->sec4_sg + sec4_sg_src_index - 1)->len |= SEC4_SG_LEN_FIN; append_seq_in_ptr(desc, edesc->sec4_sg_dma, ctx->ctx_len + buflen, LDST_SGF); From f8f6654f4c89a6fa79edb34e276e3bcf24f80f96 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 2 Sep 2015 19:32:11 +0200 Subject: [PATCH 0630/1983] gpu: galcore: Always remove from database then destroy. To make sure we don't get null references and leave garbage in the process dbs always remove from the database before destroying the object. --- drivers/gpu/galcore/gc_hal_kernel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c index d188a635343fd1..ef9d7a4473872e 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.c +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -2344,12 +2344,12 @@ gckKERNEL_Dispatch( case gcvSYNC_POINT_DESTROY: syncPoint = gcmUINT64_TO_PTR(Interface->u.SyncPoint.syncPoint); - gcmkONERROR(gckOS_DestroySyncPoint(Kernel->os, syncPoint)); - gcmkVERIFY_OK( gckKERNEL_RemoveProcessDB(Kernel, processID, gcvDB_SYNC_POINT, syncPoint)); + + gcmkONERROR(gckOS_DestroySyncPoint(Kernel->os, syncPoint)); break; default: From f42263eb87b7d6b014446a676d20c963d7e243fe Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 2 Sep 2015 19:43:58 +0200 Subject: [PATCH 0631/1983] gpu: galcore: Clean up spurious errors with gpu idling. The GPU driver will report errors in the logs if the following conditions are met, the Event submission finishes and the gpu hardware is idle, and the pm sees that we are within an interrupt context. We can avoid this and also skip a lot of needless locking by just tracking where the Event submission has come from and then skip idling the GPU. Basically if it is a command submission we know the hardware will be doing something soon so there is no reason to try to do an idle suspend. --- drivers/gpu/galcore/gc_hal_kernel.c | 2 +- drivers/gpu/galcore/gc_hal_kernel_command.c | 4 ++-- drivers/gpu/galcore/gc_hal_kernel_event.c | 17 +++++++---------- drivers/gpu/galcore/inc/aqHal.h | 1 + drivers/gpu/galcore/inc/gc_hal.h | 3 ++- 5 files changed, 13 insertions(+), 14 deletions(-) create mode 100644 drivers/gpu/galcore/inc/aqHal.h diff --git a/drivers/gpu/galcore/gc_hal_kernel.c b/drivers/gpu/galcore/gc_hal_kernel.c index ef9d7a4473872e..133729e15b56c7 100644 --- a/drivers/gpu/galcore/gc_hal_kernel.c +++ b/drivers/gpu/galcore/gc_hal_kernel.c @@ -2614,7 +2614,7 @@ gckKERNEL_AttachProcessEx( if (Kernel->vg == gcvNULL) #endif { - status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE); + status = gckEVENT_Submit(Kernel->eventObj, gcvTRUE, gcvFALSE, gcvFALSE); if (status == gcvSTATUS_INTERRUPTED && Kernel->eventObj->submitTimer) { diff --git a/drivers/gpu/galcore/gc_hal_kernel_command.c b/drivers/gpu/galcore/gc_hal_kernel_command.c index fe83bb0c13b546..9107e18946205f 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_command.c +++ b/drivers/gpu/galcore/gc_hal_kernel_command.c @@ -2129,7 +2129,7 @@ gckCOMMAND_Commit( } /* Submit events. */ - status = gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE); + status = gckEVENT_Submit(Command->kernel->eventObj, gcvTRUE, gcvFALSE, gcvTRUE); if (status == gcvSTATUS_INTERRUPTED) { gcmkTRACE( @@ -2541,7 +2541,7 @@ gckCOMMAND_Stall( gcmkONERROR(gckEVENT_Signal(eventObject, signal, gcvKERNEL_PIXEL)); /* Submit the event queue. */ - gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower)); + gcmkONERROR(gckEVENT_Submit(eventObject, gcvTRUE, FromPower, gcvFALSE)); #if gcdDUMP_COMMAND gcmkPRINT("@[kernel.stall]"); diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index 595fad9e16fbd4..d197159fb81ac1 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -429,7 +429,7 @@ _SubmitTimerFunction( ) { gckEVENT event = (gckEVENT)Data; - gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE)); + gcmkVERIFY_OK(gckEVENT_Submit(event, gcvTRUE, gcvFALSE, gcvFALSE)); } /******************************************************************************\ @@ -1533,7 +1533,8 @@ gceSTATUS gckEVENT_Submit( IN gckEVENT Event, IN gctBOOL Wait, - IN gctBOOL FromPower + IN gctBOOL FromPower, + IN gctBOOL FromCommand ) { gceSTATUS status; @@ -1684,7 +1685,8 @@ gckEVENT_Submit( gcmkONERROR(gckCOMMAND_ExitCommit(command, FromPower)); #if !gcdNULL_DRIVER - gcmkVERIFY_OK(_TryToIdleGPU(Event)); + if (!FromCommand) + gcmkVERIFY_OK(_TryToIdleGPU(Event)); #endif } @@ -1815,7 +1817,7 @@ gckEVENT_Commit( } /* Submit the event list. */ - gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); + gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE, gcvFALSE)); /* Success */ gcmkFOOTER_NO(); @@ -2494,11 +2496,6 @@ gckEVENT_Notify( "Handled interrupt 0x%x", mask); } - if (IDs == 0) - { - gcmkONERROR(_TryToIdleGPU(Event)); - } - /* Success. */ gcmkFOOTER_NO(); return gcvSTATUS_OK; @@ -2684,7 +2681,7 @@ gckEVENT_Stop( gcmkVERIFY_OBJECT(Event, gcvOBJ_EVENT); /* Submit the current event queue. */ - gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE)); + gcmkONERROR(gckEVENT_Submit(Event, gcvTRUE, gcvFALSE, gcvFALSE)); /* Allocate a record. */ gcmkONERROR(gckEVENT_AllocateRecord(Event, gcvTRUE, &record)); diff --git a/drivers/gpu/galcore/inc/aqHal.h b/drivers/gpu/galcore/inc/aqHal.h new file mode 100644 index 00000000000000..6deb4fe9212ff3 --- /dev/null +++ b/drivers/gpu/galcore/inc/aqHal.h @@ -0,0 +1 @@ +#include "HAL/gc_hal.h" diff --git a/drivers/gpu/galcore/inc/gc_hal.h b/drivers/gpu/galcore/inc/gc_hal.h index f4a0ba5c8b3d25..26f90fda57c5b1 100644 --- a/drivers/gpu/galcore/inc/gc_hal.h +++ b/drivers/gpu/galcore/inc/gc_hal.h @@ -2399,7 +2399,8 @@ gceSTATUS gckEVENT_Submit( IN gckEVENT Event, IN gctBOOL Wait, - IN gctBOOL FromPower + IN gctBOOL FromPower, + IN gctBOOL FromCommand ); gceSTATUS From a9fbd5d6a35699b4cc01b604559a9164b74eb053 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 3 Sep 2015 09:23:55 +0200 Subject: [PATCH 0632/1983] gpu: galcore: Second try at cleaning up idle pm The last commit could hit a sequence of events that led to the gpu not powering off at idle as expected. After inspecting a bit more I realized we were doing a lot of uncessary work. Rather than get the pm mutex, query the hardware for idle, notify the os, which then sets the pm_state, that gets the pm_mutex, or fails, and then does all the other checks. I move the idle query into the pm_state setting and which means we only need to get the mutex once, and if that fails we happily move along because something else is changing the state so even if we are idle we may not be soon. On top of that I made the case where we request idle and then fail to acquire the mutex an error. This is and should be a noop. Hey, Let's idle/suspend the gpu...oh we can't get the mutex, well I guess something else is doing something so we can't idle/suspend, lets bail out so we don't deadlock. Don't send errors to the logs that aren't errors. --- .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 6 +-- drivers/gpu/galcore/gc_hal_kernel_event.c | 38 +++++-------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 6ea034e5486e50..a801d06d3b78ac 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4399,7 +4399,7 @@ gckHARDWARE_SetPowerManagementState( if (broadcast) { /* Try to acquire the power mutex. */ - status = gckOS_AcquireMutex(os, Hardware->powerMutex, 1); + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0); if (status == gcvSTATUS_TIMEOUT) { @@ -4417,8 +4417,8 @@ gckHARDWARE_SetPowerManagementState( /* Called from IST, ** so waiting here will cause deadlock, ** if lock holder call gckCOMMAND_Stall() */ - status = gcvSTATUS_INVALID_REQUEST; - goto OnError; + gcmkFOOTER_NO(); + return gcvSTATUS_OK; } else { diff --git a/drivers/gpu/galcore/gc_hal_kernel_event.c b/drivers/gpu/galcore/gc_hal_kernel_event.c index d197159fb81ac1..bb6e64f09c2eb4 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_event.c +++ b/drivers/gpu/galcore/gc_hal_kernel_event.c @@ -195,8 +195,7 @@ _TryToIdleGPU( ) { gceSTATUS status; - gctBOOL empty = gcvFALSE, idle = gcvFALSE; - gctBOOL powerLocked = gcvFALSE; + gctBOOL empty = gcvFALSE; gckHARDWARE hardware; gcmkHEADER_ARG("Event=0x%x", Event); @@ -213,38 +212,16 @@ _TryToIdleGPU( if (empty) { - status = gckOS_AcquireMutex(hardware->os, hardware->powerMutex, 0); - if (status == gcvSTATUS_TIMEOUT) - { - gcmkFOOTER_NO(); - return gcvSTATUS_OK; - } - - powerLocked = gcvTRUE; - - /* Query whether the hardware is idle. */ - gcmkONERROR(gckHARDWARE_QueryIdle(Event->kernel->hardware, &idle)); - - gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex)); - powerLocked = gcvFALSE; - - if (idle) - { - /* Inform the system of idle GPU. */ - gcmkONERROR(gckOS_Broadcast(Event->os, - Event->kernel->hardware, - gcvBROADCAST_GPU_IDLE)); - } + /* Inform the system of idle GPU. */ + gcmkONERROR(gckOS_Broadcast(Event->os, + Event->kernel->hardware, + gcvBROADCAST_GPU_IDLE)); } gcmkFOOTER_NO(); return gcvSTATUS_OK; OnError: - if (powerLocked) - { - gcmkONERROR(gckOS_ReleaseMutex(hardware->os, hardware->powerMutex)); - } gcmkFOOTER(); return status; @@ -2496,6 +2473,11 @@ gckEVENT_Notify( "Handled interrupt 0x%x", mask); } + if (IDs == 0) + { + gcmkONERROR(_TryToIdleGPU(Event)); + } + /* Success. */ gcmkFOOTER_NO(); return gcvSTATUS_OK; From fdf4fd14cbcbd12afeacf697ac8886d3d0f23a43 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 3 Sep 2015 13:41:16 +0200 Subject: [PATCH 0633/1983] gpu: galcore: implement gcmkONWARNING for error handling In the previous commit I had set a value to return because it was not actually an error. However these errors need to propogate back through the logic chain so that is not an acceptible fix. Instead we will implement gcmkONWARNING which if the loglevel is changed from ERROR to WARNING will report the original error followed by the rest of the chain marked as a warning. This allows all the existing code to go unchanged, but also doesn't report errors to the logs that are not in fact errors. --- .../gpu/galcore/arch/gc_hal_kernel_hardware.c | 3 +- drivers/gpu/galcore/gc_hal_kernel_os.c | 2 +- drivers/gpu/galcore/inc/gc_hal_base.h | 67 ++++++++++++++++++- drivers/gpu/galcore/inc/gc_hal_types.h | 3 + 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index a801d06d3b78ac..a15cf1e9bac9f2 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4417,8 +4417,7 @@ gckHARDWARE_SetPowerManagementState( /* Called from IST, ** so waiting here will cause deadlock, ** if lock holder call gckCOMMAND_Stall() */ - gcmkFOOTER_NO(); - return gcvSTATUS_OK; + gcmkONWARNING(gcvSTATUS_INVALID_REQUEST); } else { diff --git a/drivers/gpu/galcore/gc_hal_kernel_os.c b/drivers/gpu/galcore/gc_hal_kernel_os.c index ab373288c59126..9d085885808cd9 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_os.c +++ b/drivers/gpu/galcore/gc_hal_kernel_os.c @@ -5861,7 +5861,7 @@ gckOS_AcquireSemaphore( /* Acquire the semaphore. */ if (down_interruptible((struct semaphore *) Semaphore)) { - gcmkONERROR(gcvSTATUS_INTERRUPTED); + gcmkONWARNING(gcvSTATUS_INTERRUPTED); } /* Success. */ diff --git a/drivers/gpu/galcore/inc/gc_hal_base.h b/drivers/gpu/galcore/inc/gc_hal_base.h index 0b070d8be8f2b6..b90b554591446e 100644 --- a/drivers/gpu/galcore/inc/gc_hal_base.h +++ b/drivers/gpu/galcore/inc/gc_hal_base.h @@ -4436,6 +4436,53 @@ gckOS_DebugStatus2Name( #define gcmERR_RETURN(func) _gcmERR_RETURN(gcm, func) #define gcmkERR_RETURN(func) _gcmkERR_RETURN(gcmk, func) +/******************************************************************************* +** +** gcmONWARNING +** +** Jump to the error handler in case there is an error, but only report at +** the WARNING debug level. This is for non fatal errors. +** +** ASSUMPTIONS: +** +** 'status' variable of gceSTATUS type must be defined. +** +** ARGUMENTS: +** +** func Function to evaluate. +*/ +#define _gcmONWARNING(prefix, func) \ + do \ + { \ + status = func; \ + if (gcmIS_ERROR(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_WARNING, \ + #prefix "ONWARNING: status=%d(%s) @ %s(%d)", \ + status, gcoOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + status = gcvSTATUS_WARNING; \ + goto OnError; \ + } \ + } \ + while (gcvFALSE) +#define _gcmkONWARNING(prefix, func) \ + do \ + { \ + status = func; \ + if (gcmIS_WARNING(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_WARNING, \ + #prefix "ONWARNING: status=%d(%s) @ %s(%d)", \ + status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + status = gcvSTATUS_WARNING; \ + goto OnError; \ + } \ + } \ + while (gcvFALSE) +#define gcmONWARNING(func) _gcmONWARNING(gcm, func) +#define gcmkONWARNING(func) _gcmkONWARNING(gcmk, func) /******************************************************************************* ** @@ -4455,7 +4502,15 @@ gckOS_DebugStatus2Name( do \ { \ status = func; \ - if (gcmIS_ERROR(status)) \ + if (gcmIS_WARNING(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_WARNING, \ + #prefix "ONWARNING: status=%d(%s) @ %s(%d)", \ + status, gcoOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + goto OnError; \ + } \ + else if (gcmIS_ERROR(status)) \ { \ prefix##PRINT_VERSION(); \ prefix##TRACE(gcvLEVEL_ERROR, \ @@ -4469,7 +4524,15 @@ gckOS_DebugStatus2Name( do \ { \ status = func; \ - if (gcmIS_ERROR(status)) \ + if (gcmIS_WARNING(status)) \ + { \ + prefix##PRINT_VERSION(); \ + prefix##TRACE(gcvLEVEL_WARNING, \ + #prefix "ONWARNING: status=%d(%s) @ %s(%d)", \ + status, gckOS_DebugStatus2Name(status), __FUNCTION__, __LINE__); \ + goto OnError; \ + } \ + else if (gcmIS_ERROR(status)) \ { \ prefix##PRINT_VERSION(); \ prefix##TRACE(gcvLEVEL_ERROR, \ diff --git a/drivers/gpu/galcore/inc/gc_hal_types.h b/drivers/gpu/galcore/inc/gc_hal_types.h index 893a3252ca373b..fbff6c2f49033f 100644 --- a/drivers/gpu/galcore/inc/gc_hal_types.h +++ b/drivers/gpu/galcore/inc/gc_hal_types.h @@ -453,6 +453,8 @@ typedef enum _gceSTATUS /* Recompilation Errors */ gcvSTATUS_RECOMPILER_CONVERT_UNIMPLEMENTED = -3000, + + gcvSTATUS_WARNING = -9999, } gceSTATUS; @@ -463,6 +465,7 @@ gceSTATUS; #define gcmIS_ERROR(status) (status < 0) #define gcmNO_ERROR(status) (status >= 0) #define gcmIS_SUCCESS(status) (status == gcvSTATUS_OK) +#define gcmIS_WARNING(status) (status == gcvSTATUS_WARNING) /******************************************************************************\ ********************************* Field Macros ********************************* From bd659583459123c5529eafbf3dbdbc9cf583dca1 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 3 Sep 2015 14:10:10 +0200 Subject: [PATCH 0634/1983] gpu: galcore: add missing part of a9fbd5d6a This patch was originally in a9fbd5d6a35699b4cc01b604559a9164b74eb053 I messed up and merged it with another patch that was stashed. This fixes the functionality to work as intended. --- drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index a15cf1e9bac9f2..d4fb1cc796b8a7 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4399,7 +4399,7 @@ gckHARDWARE_SetPowerManagementState( if (broadcast) { /* Try to acquire the power mutex. */ - status = gckOS_AcquireMutex(os, Hardware->powerMutex, 0); + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 1); if (status == gcvSTATUS_TIMEOUT) { @@ -4590,6 +4590,15 @@ gckHARDWARE_SetPowerManagementState( { if (State == gcvPOWER_OFF || State == gcvPOWER_SUSPEND || State == gcvPOWER_IDLE) { + gctBOOL idle; + /* Check for idle. */ + gcmkONERROR(gckHARDWARE_QueryIdle(Hardware, &idle)); + + if (!idle) + { + gcmkONWARNING(gcvSTATUS_CHIP_NOT_READY); + } + /* Acquire the global semaphore if it has not been acquired. */ status = gckOS_TryAcquireSemaphore(os, Hardware->globalSemaphore); if (status == gcvSTATUS_OK) From 782c57ca469fc0b55223cec76727dd5fb1e33b7c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 4 Sep 2015 17:30:51 +0200 Subject: [PATCH 0635/1983] arm: dts: bump up the operating voltage for the iMX6S/DL/D/Q Bump up the core voltage for the 396Mhz clocking. I have found that upping this voltage tends to give more stability when there is a large drain on the system, and fixes random problems with USB disconnectes and other unexplained system glitches. --- arch/arm/boot/dts/imx6dl.dtsi | 2 +- arch/arm/boot/dts/imx6q.dtsi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi index b29d02b9d86127..04c9e38597fa7a 100644 --- a/arch/arm/boot/dts/imx6dl.dtsi +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -26,7 +26,7 @@ /* kHz uV */ 996000 1275000 792000 1175000 - 396000 1150000 + 396000 1175000 >; fsl,soc-operating-points = < /* ARM kHz SOC-PU uV */ diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index 16486119cc0fdb..1faef747221a2a 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -32,7 +32,7 @@ 996000 1250000 852000 1250000 792000 1175000 - 396000 975000 + 396000 1175000 >; fsl,soc-operating-points = < /* ARM kHz SOC-PU uV */ From 0e48d9a0151d73ec17c71b0e2aa743b8898947f9 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 4 Sep 2015 17:38:21 +0200 Subject: [PATCH 0636/1983] gpu: galcore: Relax locking on the command commit atom This is supposed to be an atomic action of whether we have a commit queued that should keep power management from stopping it from getting to hardware. It makes sense to hold the PM mutex if we are adding a commit, however if we are done with a commit and decrementing then it is more efficient to change this value even if the PM mutex is held so we may be able to catch the process midway through. --- drivers/gpu/galcore/gc_hal_kernel_command.c | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/galcore/gc_hal_kernel_command.c b/drivers/gpu/galcore/gc_hal_kernel_command.c index 9107e18946205f..463aa60a39874b 100644 --- a/drivers/gpu/galcore/gc_hal_kernel_command.c +++ b/drivers/gpu/galcore/gc_hal_kernel_command.c @@ -147,18 +147,24 @@ _IncrementCommitAtom( hardware = Command->kernel->hardware; gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE); - /* Grab the power mutex. */ - gcmkONERROR(gckOS_AcquireMutex( - Command->os, hardware->powerMutex, gcvINFINITE - )); - powerAcquired = gcvTRUE; - /* Increment the commit atom. */ if (Increment) { + /* Grab the power mutex. */ + gcmkONERROR(gckOS_AcquireMutex( + Command->os, hardware->powerMutex, gcvINFINITE + )); + powerAcquired = gcvTRUE; + gcmkONERROR(gckOS_AtomIncrement( Command->os, Command->atomCommit, &atomValue )); + + /* Release the power mutex. */ + gcmkONERROR(gckOS_ReleaseMutex( + Command->os, hardware->powerMutex + )); + powerAcquired = gcvFALSE; } else { @@ -167,12 +173,6 @@ _IncrementCommitAtom( )); } - /* Release the power mutex. */ - gcmkONERROR(gckOS_ReleaseMutex( - Command->os, hardware->powerMutex - )); - powerAcquired = gcvFALSE; - /* Success. */ gcmkFOOTER(); return gcvSTATUS_OK; From e2c606a891825197cd2aed48809f917a2fb0e99b Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 4 Sep 2015 17:48:58 +0200 Subject: [PATCH 0637/1983] gpu: galcore: Some general cleanup with warnings and spacings This is just some cleanups changes errors to warnings and then cleaning up some formatting. --- drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index d4fb1cc796b8a7..5cfee89be481c0 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -3859,7 +3859,7 @@ gckHARDWARE_GetIdle( if (Wait && !_IsGPUIdle(idle)) { /* Wait a little. */ - gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HARDWARE, + gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HARDWARE, "%s: Waiting for idle: 0x%08X", __FUNCTION__, idle); @@ -4438,6 +4438,8 @@ gckHARDWARE_SetPowerManagementState( if (State == Hardware->chipPowerState) { gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); + + gcmkFOOTER_NO(); return gcvSTATUS_OK; } @@ -4467,7 +4469,7 @@ gckHARDWARE_SetPowerManagementState( /* Release the power mutex. */ gcmkONERROR(gckOS_ReleaseMutex(os, Hardware->powerMutex)); - gcmkFOOTER_NO(); + gcmkFOOTER_NO(); return gcvSTATUS_OK; #else /* Clock should be on when switch power from off to suspend */ @@ -4537,7 +4539,7 @@ gckHARDWARE_SetPowerManagementState( if (State == gcvPOWER_IDLE || State == gcvPOWER_SUSPEND) { /* Called from thread routine which should NEVER sleep.*/ - gcmkONERROR(gcvSTATUS_INVALID_REQUEST); + gcmkONWARNING(gcvSTATUS_INVALID_REQUEST); } /* Release the power mutex. */ From cd688d1c6c10f0723e31ab37ceb87f259386447c Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 4 Sep 2015 17:50:05 +0200 Subject: [PATCH 0638/1983] gpu: galcore: Try harder to get PM mutex. I have cleaned up unnecessary pm changes so now we know we are requesting a PM change only when we know we need it. We can then afford to try a bit harder here and waste some time because it will only be a rare occasion we hit here, or the device should be going into idle. --- drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c index 5cfee89be481c0..a7773f8f93360a 100644 --- a/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c +++ b/drivers/gpu/galcore/arch/gc_hal_kernel_hardware.c @@ -4399,7 +4399,7 @@ gckHARDWARE_SetPowerManagementState( if (broadcast) { /* Try to acquire the power mutex. */ - status = gckOS_AcquireMutex(os, Hardware->powerMutex, 1); + status = gckOS_AcquireMutex(os, Hardware->powerMutex, 3); if (status == gcvSTATUS_TIMEOUT) { From 5727b48b524c0c322907900ef0f5dbfdca0157ad Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 4 Sep 2015 17:52:03 +0200 Subject: [PATCH 0639/1983] gpu: galcore: don't use suspend when idle by default Now that all the locking is cleaned up and we have removed all bottlenecks and false returns in the PM it doesn't make much sense to tear down and bring up the clocks every time the GPU finishes a command and then another hasn't queued yet. We still power down once we hit idle and stay there until the timer runs out which is really all the power savings we need. --- drivers/gpu/galcore/inc/gc_hal_options.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/galcore/inc/gc_hal_options.h b/drivers/gpu/galcore/inc/gc_hal_options.h index 58162dd0d8f793..7f9060ca03be87 100644 --- a/drivers/gpu/galcore/inc/gc_hal_options.h +++ b/drivers/gpu/galcore/inc/gc_hal_options.h @@ -442,7 +442,7 @@ otherwise GPU will enter gcvPOWER_IDLE. */ #ifndef gcdPOWER_SUSPEND_WHEN_IDLE -# define gcdPOWER_SUSPEND_WHEN_IDLE 1 +# define gcdPOWER_SUSPEND_WHEN_IDLE 0 #endif #ifndef gcdFPGA_BUILD From 76c71991ca58ef66510f737d98d140d618968a7e Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sat, 5 Sep 2015 07:26:41 +0200 Subject: [PATCH 0640/1983] mach: imx: extend ar803x tw timings for gigabit again. Although much better behaved I still received a few link disconnects on my machine that has been running for the last 5 days. We will bump this again and restart the test. --- arch/arm/mach-imx/mach-imx6q.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 507584cb8e6ce0..d642c1469fd681 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -290,7 +290,7 @@ static int ar8035_phy_fixup(struct phy_device *dev) phy_write(dev, 0xe, 0x805b); phy_write(dev, 0xd, 0x4003); val = phy_read(dev, 0xe); - val = 0x1517; + val = 0x1717; phy_write(dev, 0xe, val); return 0; From 973e082ff7084c29f05f606427f9fbd49d2504db Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Sun, 6 Sep 2015 10:59:51 +0200 Subject: [PATCH 0641/1983] ARM: dts: imx: fix invalid #address-cells value The invalid value of #address-cells in the imx6 pcie host controller node causes of_irq_parse_raw() to incorrectly advance through an interrupt-map table of more than one interrupt. We also take the opportunity to drop the unused #size-cells here. This patch resolves this issue and allows proper interrupt mapping for an imx6 pcie host connected to a P2P bridge when using legacy interrupts. Signed-off-by: Tim Harvey Cc: Jason Gunthorpe Cc: Jingoo Han Cc: Lucas Stach Cc: Mark Rutland Cc: linux-samsung-soc Cc: Richard Zhu Cc: Sascha Hauer Cc: Arnd Bergmann Cc: Stephen Warren Cc: Bjorn Helgaas Cc: Simon Horman Cc: Thierry Reding Cc: Ben Dooks Cc: linux-tegra Cc: Kukjin Kim Cc: Shawn Guo Cc: Grant Likely --- arch/arm/boot/dts/imx6qdl.dtsi | 2 -- arch/arm/boot/dts/imx6sl.dtsi | 2 -- 2 files changed, 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index c775098bb143d5..52e05e6cc9a73b 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -48,8 +48,6 @@ intc: interrupt-controller@00a01000 { compatible = "arm,cortex-a9-gic"; #interrupt-cells = <3>; - #address-cells = <1>; - #size-cells = <1>; interrupt-controller; reg = <0x00a01000 0x1000>, <0x00a00100 0x100>; diff --git a/arch/arm/boot/dts/imx6sl.dtsi b/arch/arm/boot/dts/imx6sl.dtsi index cc758e77df53c9..b048b256db77f9 100644 --- a/arch/arm/boot/dts/imx6sl.dtsi +++ b/arch/arm/boot/dts/imx6sl.dtsi @@ -76,8 +76,6 @@ intc: interrupt-controller@00a01000 { compatible = "arm,cortex-a9-gic"; #interrupt-cells = <3>; - #address-cells = <1>; - #size-cells = <1>; interrupt-controller; reg = <0x00a01000 0x1000>, <0x00a00100 0x100>; From f61a01032fb0191a4147c5c4621d371671b5a946 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 15 May 2014 17:18:11 +0300 Subject: [PATCH 0642/1983] ARM: i.MX6: dts: Add initial support for cm-fx6 Add initial support for cm-fx6 module. This patch configures: 1) serial console 2) hearbeat led 3) FreeScale NIC 4) pcie 5) Intel I210 NIC 6) wif/bt 7) sata Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 368 +++++++++++++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 arch/arm/boot/dts/imx6q-cm-fx6.dts diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts new file mode 100644 index 00000000000000..974bc45f23ee7d --- /dev/null +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -0,0 +1,368 @@ +/* +* Copyright 2013 CompuLab Ltd. +* +* Author: Valentin Raevsky +* +* The code contained herein is licensed under the GNU General Public +* License. You may obtain a copy of the GNU General Public License +* Version 2 or later at the following locations: +* +* http://www.opensource.org/licenses/gpl-license.html +* http://www.gnu.org/copyleft/gpl.html +*/ + +/dts-v1/; +#include "imx6q.dtsi" + +/ { + model = "CompuLab CM-FX6"; + compatible = "compulab,cm-fx6", "fsl,imx6q"; + + memory { + reg = <0x10000000 0x80000000>; + }; + + leds { + compatible = "gpio-leds"; + + heartbeat-led { + label = "Heartbeat"; + gpios = <&gpio2 31 0>; + linux,default-trigger = "heartbeat"; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + /* regulator for mmc */ + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + /* regulator for usb otg */ + reg_usb_otg_vbus: usb_otg_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 0>; + enable-active-high; + }; + + /* regulator for usb hub1 */ + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio7 8 0>; + enable-active-high; + }; + + /* regulator1 for wifi/bt */ + awnh387_npoweron: regulator-awnh387-npoweron { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-npoweron"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio7 12 0>; + enable-active-high; + }; + + /* regulator2 for wifi/bt */ + awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-wifi-nreset"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 16 0>; + startup-delay-us = <10000>; + }; + }; +}; + +&iomuxc { + imx6q-cm-fx6 { + /* pins for eth0 */ + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 + >; + }; + + /* pins for spi */ + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 + MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 + >; + }; + + /* pins for nand */ + pinctrl_gpmi_nand: gpminandgrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 + MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 + MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 + MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 + MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 + MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 + MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 + MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 + MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 + MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 + MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 + MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 + MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 + MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 + MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 + MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 + MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 + >; + }; + + /* pins for i2c1 */ + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + /* pins for i2c2 */ + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + /* pins for i2c3 */ + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + /* pins for console */ + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + + /* pins for usb hub1 */ + pinctrl_usbh1: usbh1grp { + fsl,pins = < + MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 + >; + }; + + /* pins for usb otg */ + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + >; + }; + + /* pins for wifi/bt */ + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 + >; + }; + + /* pins for mmc */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + }; +}; + +/* spi */ +&ecspi1 { + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25px16", "st,m25p"; + spi-max-frequency = <20000000>; + reg = <0>; + + partition@0 { + label = "uboot"; + reg = <0x0 0xc0000>; + }; + + partition@c0000 { + label = "uboot environment"; + reg = <0xc0000 0x40000>; + }; + + partition@100000 { + label = "reserved"; + reg = <0x100000 0x100000>; + }; + }; +}; + +/* eth0 */ +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + status = "okay"; +}; + +/* nand */ +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + status = "okay"; +}; + +/* i2c1 */ +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + + rtc@56 { + compatible = "emmicro,em3027"; + reg = <0x56>; + }; +}; + +/* i2c2 */ +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; +}; + +/* i2c3 */ +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +/* eth1 */ +&pcie { + reset-gpio = <&gpio1 26 0>; + status = "okay"; +}; + +/* sata */ +&sata { + status = "okay"; +}; + +/* rear serial console */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_2>; + fsl,dte-mode; + fsl,uart-has-rtscts; + dma-names = "rx", "tx"; + dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; + status = "okay"; +}; + +/* console */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + +/* usb otg */ +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + dr_mode = "otg"; + status = "okay"; +}; + +/* usb hub1 */ +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1>; + status = "okay"; +}; + +/* wifi/bt */ +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>; + non-removable; + vmmc-supply = <&awnh387_npoweron>; + vmmc_aux-supply = <&awnh387_wifi_nreset>; + status = "okay"; +}; + +/* mmc */ +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + vmmc-supply = <®_3p3v>; + status = "okay"; +}; From dee270981e4514d21b3a9b3c58eada9a56ae966d Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 15 May 2014 17:25:07 +0300 Subject: [PATCH 0643/1983] ARM: i.MX6: cm-fx6: Add defconfig Add default configuration file for the cm-fx6 module. Signed-off-by: Valentin Raevsky --- arch/arm/configs/cm_fx6_defconfig | 434 ++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 arch/arm/configs/cm_fx6_defconfig diff --git a/arch/arm/configs/cm_fx6_defconfig b/arch/arm/configs/cm_fx6_defconfig new file mode 100644 index 00000000000000..92c3da103d2ba9 --- /dev/null +++ b/arch/arm/configs/cm_fx6_defconfig @@ -0,0 +1,434 @@ +CONFIG_KERNEL_LZO=y +CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_GPIO_PCA953X=y +CONFIG_ARCH_MXC=y +CONFIG_MXC_DEBUG_BOARD=y +CONFIG_MACH_IMX51_DT=y +CONFIG_MACH_EUKREA_CPUIMX51SD=y +CONFIG_SOC_IMX53=y +CONFIG_SOC_IMX6Q=y +CONFIG_SOC_IMX6SL=y +CONFIG_SOC_VF610=y +# CONFIG_SWP_EMULATE is not set +CONFIG_PCI=y +CONFIG_PCI_IMX6=y +CONFIG_SMP=y +CONFIG_VMSPLIT_2G=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_HIGHMEM=y +CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_BINFMT_MISC=m +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_PM_TEST_SUSPEND=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_DEBUG=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_CAN=y +CONFIG_CAN_FLEXCAN=y +CONFIG_CFG80211=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_STANDALONE is not set +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=320 +CONFIG_IMX_WEIM=y +CONFIG_CONNECTOR=y +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_DATAFLASH=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_SST25L=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_GPMI_NAND=y +CONFIG_MTD_NAND_MXC=y +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SCAN_ASYNC=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI_PLATFORM=y +CONFIG_AHCI_IMX=y +CONFIG_PATA_IMX=y +CONFIG_NETDEVICES=y +CONFIG_TUN=m +# CONFIG_NET_VENDOR_BROADCOM is not set +CONFIG_CS89x0=y +CONFIG_CS89x0_PLATFORM=y +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_IGB=m +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_SEEQ is not set +CONFIG_SMC91X=y +CONFIG_SMC911X=y +CONFIG_SMSC911X=y +# CONFIG_NET_VENDOR_STMICRO is not set +CONFIG_ATH_CARDS=y +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_IMX=y +CONFIG_MOUSE_PS2=m +CONFIG_MOUSE_PS2_ELANTECH=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_EGALAX=y +CONFIG_TOUCHSCREEN_ELAN=y +CONFIG_TOUCHSCREEN_MAX11801=y +CONFIG_TOUCHSCREEN_MC13783=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_MMA8450=y +CONFIG_INPUT_ISL29023=y +CONFIG_SERIO_SERPORT=m +CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_FSL_LPUART=y +CONFIG_SERIAL_FSL_LPUART_CONSOLE=y +CONFIG_FSL_OTP=y +CONFIG_MXS_VIIM=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_IMX=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_SABRESD_MAX8903=y +CONFIG_IMX6_USB_CHARGER=y +CONFIG_SENSORS_MAX17135=y +CONFIG_SENSORS_MAG3110=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y +CONFIG_IMX_THERMAL=y +CONFIG_DEVICE_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +CONFIG_MFD_DA9052_I2C=y +CONFIG_MFD_MC13XXX_SPI=y +CONFIG_MFD_MC13XXX_I2C=y +CONFIG_MFD_MAX17135=y +CONFIG_MFD_SI476X_CORE=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_MC13783=y +CONFIG_REGULATOR_MC13892=y +CONFIG_REGULATOR_MAX17135=y +CONFIG_REGULATOR_PFUZE100=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_VIDEO_V4L2_INT_DEVICE=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_MXC_OUTPUT=y +CONFIG_VIDEO_MXC_CAPTURE=m +CONFIG_VIDEO_MXC_CSI_CAMERA=m +CONFIG_MXC_CAMERA_OV5640=m +CONFIG_MXC_CAMERA_OV5642=m +CONFIG_MXC_CAMERA_OV5640_MIPI=m +CONFIG_MXC_TVIN_ADV7180=m +CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m +CONFIG_VIDEO_MXC_IPU_OUTPUT=y +CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_SOC_CAMERA=y +CONFIG_VIDEO_MX3=y +CONFIG_RADIO_SI476X=y +CONFIG_SOC_CAMERA_OV2640=y +CONFIG_DRM=y +CONFIG_DRM_VIVANTE=y +CONFIG_FB=y +CONFIG_FB_MXS=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_L4F00242T03=y +CONFIG_LCD_PLATFORM=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_FB_MXC_SYNC_PANEL=y +CONFIG_FB_MXC_LDB=y +CONFIG_FB_MXC_MIPI_DSI=y +CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL=y +CONFIG_FB_MXC_HDMI=y +CONFIG_FB_MXC_EINK_PANEL=y +CONFIG_FB_MXS_SII902X=y +CONFIG_HANNSTAR_CABC=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB_AUDIO=m +CONFIG_SND_SOC=y +CONFIG_SND_IMX_SOC=y +CONFIG_SND_SOC_EUKREA_TLV320=y +CONFIG_SND_SOC_IMX_CS42888=y +CONFIG_SND_SOC_IMX_WM8962=y +CONFIG_SND_SOC_IMX_SGTL5000=y +CONFIG_SND_SOC_IMX_SPDIF=y +CONFIG_SND_SOC_IMX_MC13783=y +CONFIG_SND_SOC_IMX_HDMI=y +CONFIG_SND_SOC_IMX_SI476X=y +CONFIG_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_CHIPIDEA=y +CONFIG_USB_CHIPIDEA_UDC=y +CONFIG_USB_CHIPIDEA_HOST=y +CONFIG_USB_PHY=y +CONFIG_NOP_USB_XCEIV=y +CONFIG_USB_MXS_PHY=y +CONFIG_USB_GADGET=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_MXC_IPU=y +CONFIG_MXC_GPU_VIV=y +CONFIG_MXC_ASRC=y +CONFIG_MXC_MIPI_CSI2=y +CONFIG_MXC_MLB150=m +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_DRV_MC13XXX=y +CONFIG_RTC_DRV_MXC=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_DMADEVICES=y +CONFIG_MXC_PXP_V2=y +CONFIG_IMX_SDMA=y +CONFIG_MXS_DMA=y +CONFIG_STAGING=y +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_PWM=y +CONFIG_PWM_IMX=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_JFFS2_FS=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_UTF8=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_FTRACE is not set +CONFIG_SECURITYFS=y +CONFIG_CRYPTO_USER=y +CONFIG_CRYPTO_TEST=m +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=y +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +CONFIG_CRYPTO_RMD128=y +CONFIG_CRYPTO_RMD160=y +CONFIG_CRYPTO_RMD256=y +CONFIG_CRYPTO_RMD320=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_TGR192=y +CONFIG_CRYPTO_WP512=y +CONFIG_CRYPTO_BLOWFISH=y +CONFIG_CRYPTO_CAMELLIA=y +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DEV_FSL_CAAM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST=y +CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO=y +CONFIG_CRC_CCITT=m +CONFIG_CRC_T10DIF=y +CONFIG_CRC7=m +CONFIG_LIBCRC32C=m From d87a4364adcb593163a4f220f2976031bd7c53f2 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 15 May 2014 17:26:30 +0300 Subject: [PATCH 0644/1983] igb: Enable random mac address Enable random mac address in order to let the driver up if eeprom values are incorrect. Signed-off-by: Valentin Raevsky --- drivers/net/ethernet/intel/igb/igb_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 206e79d29c79b7..2472835fdd41bb 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2387,6 +2387,11 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (hw->mac.ops.read_mac_addr(hw)) dev_err(&pdev->dev, "NVM Read Error\n"); + if (!is_valid_ether_addr(hw->mac.addr)) { + dev_info(&pdev->dev, "Random MAC Address\n"); + random_ether_addr(hw->mac.addr); + } + memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len); if (!is_valid_ether_addr(netdev->dev_addr)) { From 1ffd7b9dde345c9b908e404556a14c8c69fca8fd Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 22 Jun 2014 18:03:27 +0300 Subject: [PATCH 0645/1983] ARM: i.MX6: cm-fx6: update defconfig Enable EM3027 RTC Signed-off-by: Valentin Raevsky --- arch/arm/configs/cm_fx6_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/cm_fx6_defconfig b/arch/arm/configs/cm_fx6_defconfig index 92c3da103d2ba9..eb6d9cb8755f86 100644 --- a/arch/arm/configs/cm_fx6_defconfig +++ b/arch/arm/configs/cm_fx6_defconfig @@ -347,6 +347,7 @@ CONFIG_LEDS_TRIGGERS=y CONFIG_LEDS_TRIGGER_GPIO=y CONFIG_RTC_CLASS=y CONFIG_RTC_INTF_DEV_UIE_EMUL=y +CONFIG_RTC_DRV_EM3027=y CONFIG_RTC_DRV_MC13XXX=y CONFIG_RTC_DRV_MXC=y CONFIG_RTC_DRV_SNVS=y From 3f4557ed6f658091cba2c16964b0ddd4dcd87a2d Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 23 Jun 2014 13:53:35 +0300 Subject: [PATCH 0646/1983] ARM: i.MX6: dts: add HDMI and DVI support Add HDMI and DVI support on IPU1 and IPU2, define two frame buffers. Enable starting X with fbdev. --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 65 +++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 974bc45f23ee7d..be493d9810527f 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -86,6 +86,44 @@ startup-delay-us = <10000>; }; }; + + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <24>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <24>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + lcd@0 { + compatible = "fsl,lcd"; + ipu_id = <0>; + disp_id = <0>; + default_ifmt = "RGB24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_1>; + status = "okay"; + }; + }; &iomuxc { @@ -287,7 +325,7 @@ &i2c2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c2>; - status = "okay"; + /* status = "okay"; */ }; /* i2c3 */ @@ -366,3 +404,28 @@ vmmc-supply = <®_3p3v>; status = "okay"; }; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; + +&hdmi_core { + ipu_id = <1>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,hdcp; + status = "okay"; +}; + +&hdmi_audio { +/* status = "okay"; */ +}; From 2970dbeeec6f3386052b746f1c2eda6177e20aa7 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 23 Jun 2014 15:29:21 +0300 Subject: [PATCH 0647/1983] ARM: i.MX6: dts: add HDMI-Audio support Add HDMI-Audio support. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index be493d9810527f..5f7cf06531fa35 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -92,6 +92,13 @@ mxcfb1 = &mxcfb2; }; + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + mxcfb1: fb@0 { compatible = "fsl,mxc_sdc_fb"; disp_dev = "hdmi"; @@ -427,5 +434,5 @@ }; &hdmi_audio { -/* status = "okay"; */ + status = "okay"; }; From 95099a6b443aaa66bb6aac6fee7c3407c8618cc2 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 23 Jun 2014 16:30:18 +0300 Subject: [PATCH 0648/1983] ARM: i.MX6: dts: add SPDIF support Add SPDIF support. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 5f7cf06531fa35..0150990e9e14c2 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -99,6 +99,15 @@ hdmi-controller = <&hdmi_audio>; }; + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx-sabreauto-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; + }; + mxcfb1: fb@0 { compatible = "fsl,mxc_sdc_fb"; disp_dev = "hdmi"; @@ -153,7 +162,6 @@ MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 >; }; @@ -260,6 +268,15 @@ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 >; }; + + /* pins for spdif */ + pinctrl_spdif: spdifgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 + MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 + >; + }; + }; }; @@ -436,3 +453,9 @@ &hdmi_audio { status = "okay"; }; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif>; + status = "okay"; +}; From 4b0f08d9169727535148ed2274ecbcbc418deb0c Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Tue, 24 Jun 2014 15:35:52 +0300 Subject: [PATCH 0649/1983] ARM: i.MX6: dts: add "Power Button" Add "Power Button" by means of GPIO Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 0150990e9e14c2..bd20ca5fdff6c9 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -87,6 +87,16 @@ }; }; + gpio-keys { + compatible = "gpio-keys"; + power { + label = "Power Button"; + gpios = <&gpio1 29 1>; + linux,code = <116>; /* KEY_POWER */ + gpio-key,wakeup; + }; + }; + aliases { mxcfb0 = &mxcfb1; mxcfb1 = &mxcfb2; From db682ad9101176cb0b9deb3a232cee45722b2f08 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 10:42:20 +0300 Subject: [PATCH 0650/1983] ARM: i.MX6: dts: Enable uart2 as a serial console Enable ttymxc1 for use as a serial console: 1) Add the correct uart2 pinmux configuration. 2) Disable uart2 dte mode. It allows running 'getty' and 'login' on the ttymxc1. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index bd20ca5fdff6c9..ee4007d63ad711 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -287,6 +287,16 @@ >; }; + /* pins for uart2 */ + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x1b0b1 + MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x1b0b1 + MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x1b0b1 + >; + }; + }; }; @@ -389,8 +399,8 @@ /* rear serial console */ &uart2 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2_2>; - fsl,dte-mode; + pinctrl-0 = <&pinctrl_uart2>; + /* fsl,dte-mode; */ fsl,uart-has-rtscts; dma-names = "rx", "tx"; dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; From 25495bff4978b5d953b9879a35b3c9632a0a5ad6 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 10:51:44 +0300 Subject: [PATCH 0651/1983] ARM: i.MX6: dts: add pcie power/reset gpio definition Add pcie power/reset gpio definition. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index ee4007d63ad711..d5f95e7338ad23 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -297,6 +297,13 @@ >; }; + /* pins for pcie */ + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 + MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 + >; + }; }; }; @@ -387,7 +394,10 @@ /* eth1 */ &pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; reset-gpio = <&gpio1 26 0>; + power-on-gpio = <&gpio2 24 0>; status = "okay"; }; From 0a4f73efea18ee216582a716abb30e6f231c648d Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 10:53:32 +0300 Subject: [PATCH 0652/1983] ARM: i.MX6: dts: add onboard SSD pin configuration Add onboard SSD pin configuration. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index d5f95e7338ad23..4bed7820dc7fb7 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -153,6 +153,26 @@ }; &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + /* SATA PWR */ + MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 + MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x80000000 + MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 + MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 + /* SATA CTRL */ + MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 + MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 + MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x80000000 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 + >; + }; + }; + imx6q-cm-fx6 { /* pins for eth0 */ pinctrl_enet: enetgrp { From 58280942f9d62d557ed89019c5194ce4b4364c06 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 10:54:43 +0300 Subject: [PATCH 0653/1983] ARM: i.MX6: dts: add onboard SSD power up sequence Add onboard SSD power up sequence. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 4bed7820dc7fb7..e8f720998ee3f0 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -85,6 +85,72 @@ gpio = <&gpio6 16 0>; startup-delay-us = <10000>; }; + + reg_sata_phy_slp: sata_phy_slp { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_phy_slp"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 23 0>; + startup-delay-us = <100>; + enable-active-high; + }; + + reg_sata_nrstdly: sata_nrstdly { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nrstdly"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 6 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_phy_slp>; + }; + + reg_sata_pwren: sata_pwren { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_pwren"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 28 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_nrstdly>; + }; + + reg_sata_nstandby1: sata_nstandby1 { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nstandby1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 20 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_pwren>; + }; + + reg_sata_nstandby2: sata_nstandby2 { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nstandby2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio5 2 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_nstandby1>; + }; + + reg_sata_ldo_en: sata_ldo_en { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_ldo_en"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 16 0>; + startup-delay-us = <100>; + enable-active-high; + regulator-boot-on; + vin-supply = <®_sata_nstandby2>; + }; }; gpio-keys { From ef9a2e13d0a5a93712bff92b3143fc18d08e9488 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 10:57:39 +0300 Subject: [PATCH 0654/1983] ARM: i.MX6: dts: add audio mux pinmux configuration Add audio mux pinmux configuration and enable audio mux. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index e8f720998ee3f0..77e292ecf8fa10 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -373,6 +373,17 @@ >; }; + /* pins for audmux */ + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 + MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 + MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 + MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 + MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 + >; + }; + /* pins for uart2 */ pinctrl_uart2: uart2grp { fsl,pins = < @@ -575,3 +586,9 @@ pinctrl-0 = <&pinctrl_spdif>; status = "okay"; }; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; +}; From 3d3a11001fa6e6a388c35742ecda4e806eac998f Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 11:02:28 +0300 Subject: [PATCH 0655/1983] ARM: i.MX6: dts: add analog audio support 1) Add i2c analog audion device node definition. 2) Add wm8731 codec node definition. 3) Enable ssi2 in master mode. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 77e292ecf8fa10..73e6f75d96525f 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -168,6 +168,15 @@ mxcfb1 = &mxcfb2; }; + sound { + compatible = "fsl,imx6q-cm-fx6-wm8731", + "fsl,imx-audio-wm8731"; + model = "wm8731-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; + }; + sound-hdmi { compatible = "fsl,imx6q-audio-hdmi", "fsl,imx-audio-hdmi"; @@ -487,6 +496,17 @@ reg = <0x50>; pagesize = <16>; }; + + codec: wm8731@1a { + compatible = "wlf,wm8731"; + reg = <0x1a>; + clocks = <&clks 173>, <&clks 158>; + clock-names = "pll4", "imx-ssi.1"; + AVDD-supply = <&pu_dummy>; + HPVDD-supply = <&pu_dummy>; + DCVDD-supply = <&pu_dummy>; + DBVDD-supply = <&pu_dummy>; + }; }; /* eth1 */ @@ -556,6 +576,11 @@ status = "okay"; }; +&ssi2 { + fsl,mode = "i2s-master"; + status = "okay"; +}; + &mxcfb1 { status = "okay"; }; From 89baa6f80f4ddc77de45d987acc3c02d7969f7f7 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 20 Jul 2014 11:10:12 +0300 Subject: [PATCH 0656/1983] ARM: i.MX6: ASoC: add imx-wm8731 machine driver This is the initial imx-wm8731 device-tree-only machine driver working with fsl_ssi driver. Works in the slave mode. Signed-off-by: Valentin Raevsky --- sound/soc/fsl/Kconfig | 12 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/imx-wm8731.c | 505 +++++++++++++++++++++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 sound/soc/fsl/imx-wm8731.c diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 573dabbf87d251..bb229dadf47f64 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -252,6 +252,18 @@ config SND_SOC_IMX_CS42888 Say Y if you want to add support for SoC audio on an i.MX board with a cs42888 codec. +config SND_SOC_IMX_WM8731 + tristate "SoC Audio support for i.MX boards with wm8731" + depends on OF && I2C + select SND_SOC_WM8731 + select SND_SOC_IMX_PCM_DMA + select SND_SOC_IMX_AUDMUX + select SND_SOC_FSL_SSI + select SND_SOC_FSL_UTILS + help + Say Y if you want to add support for SoC audio on an i.MX board with + a wm8731 codec. + config SND_SOC_IMX_WM8962 tristate "SoC Audio support for i.MX boards with wm8962" depends on OF && I2C diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 144cd69e180b61..65f8e6f723a1dc 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -55,6 +55,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-cs42888-objs := imx-cs42888.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o +snd-soc-imx-wm8731-objs := imx-wm8731.o snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-spdif-objs := imx-spdif.o snd-soc-imx-mc13783-objs := imx-mc13783.o @@ -69,6 +70,7 @@ obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o +obj-$(CONFIG_SND_SOC_IMX_WM8731) += snd-soc-imx-wm8731.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o diff --git a/sound/soc/fsl/imx-wm8731.c b/sound/soc/fsl/imx-wm8731.c new file mode 100644 index 00000000000000..db9cb0688ba305 --- /dev/null +++ b/sound/soc/fsl/imx-wm8731.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Based on imx-sgtl5000.c + * Copyright (C) 2012 Freescale Semiconductor, Inc. + * Copyright (C) 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/wm8731.h" +#include "imx-audmux.h" +#include "imx-ssi.h" + +#define DAI_NAME_SIZE 32 + +struct imx_wm8731_data { + struct snd_soc_dai_link dai; + struct snd_soc_card card; + char codec_dai_name[DAI_NAME_SIZE]; + char platform_name[DAI_NAME_SIZE]; + struct i2c_client *codec_dev; + /* audio_clocking_data */ + struct clk *pll; + struct clk *clock_root; + long sysclk; + long current_rate; + /* platfor data */ + unsigned int ssi_num; + unsigned int src_port; + unsigned int ext_port; +}; + +static int imx_wm8731_init(struct snd_soc_pcm_runtime *rtd); +static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); +static void imx_hifi_shutdown(struct snd_pcm_substream *substream); + +struct imx_priv { + struct platform_device *pdev; + struct imx_wm8731_data *data; +}; + +static struct imx_priv card_priv; + +static struct snd_soc_ops imx_hifi_ops = { + .shutdown = imx_hifi_shutdown, + .hw_params = imx_hifi_hw_params_slv_mode, +}; + +/* imx card dapm widgets */ +static const struct snd_soc_dapm_widget imx_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_LINE("Line Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +/* imx machine connections to the codec pins */ +static const struct snd_soc_dapm_route audio_map[] = { + { "Headphone Jack", NULL, "LHPOUT" }, + { "Headphone Jack", NULL, "RHPOUT" }, + + { "Ext Spk", NULL, "LOUT" }, + { "Ext Spk", NULL, "ROUT" }, + + { "LLINEIN", NULL, "Line Jack" }, + { "RLINEIN", NULL, "Line Jack" }, + + { "MICIN", NULL, "Mic Bias" }, + { "Mic Bias", NULL, "Mic Jack"}, +}; + +static int wm8731_slv_mode_init(struct imx_wm8731_data *data) +{ + struct clk *new_parent; + struct clk *ssi_clk; + struct i2c_client *codec_dev = data->codec_dev; + + new_parent = devm_clk_get(&codec_dev->dev, "pll4"); + if (IS_ERR(new_parent)) { + pr_err("Could not get \"pll4\" clock \n"); + return PTR_ERR(new_parent); + } + + ssi_clk = devm_clk_get(&codec_dev->dev, "imx-ssi.1"); + if (IS_ERR(ssi_clk)) { + pr_err("Could not get \"imx-ssi.1\" clock \n"); + return PTR_ERR(ssi_clk); + } + + clk_set_parent(ssi_clk, new_parent); + + data->pll = new_parent; + data->clock_root = ssi_clk; + data->current_rate = 0; + + data->sysclk = 0; + + return 0; +} + +static int wm8731_slv_mode_clock_enable(int enable, struct imx_wm8731_data *data) +{ + long pll_rate; + long rate_req; + long rate_avail; + + if (!enable) + return 0; + + if (data->sysclk == data->current_rate) + return 0; + + switch (data->sysclk) { + case 11289600: + pll_rate = 632217600; + break; + + case 12288000: + pll_rate = 688128000; + break; + + default: + return -EINVAL; + } + + rate_req = pll_rate; + rate_avail = clk_round_rate(data->pll, rate_req); + clk_set_rate(data->pll, rate_avail); + + rate_req = data->sysclk; + rate_avail = clk_round_rate(data->clock_root, + rate_req); + clk_set_rate(data->clock_root, rate_avail); + + pr_info("%s: \"imx-ssi.1\" rate = %ld (= %ld)\n", + __func__, rate_avail, rate_req); + + data->current_rate = data->sysclk; + + return 0; +} + +static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = codec_dai->codec->card; + struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); + + u32 dai_format, pll_out; + snd_pcm_format_t sample_format; + unsigned int channels; + unsigned int tx_mask, rx_mask; + unsigned int sampling_rate; + unsigned int div_2, div_psr, div_pm; + int ret; + + sampling_rate = params_rate(params); + sample_format = params_format(params); + + channels = params_channels(params); + printk("%s:%s sampling rate = %u channels = %u \n", __FUNCTION__, + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"), + sampling_rate, channels); + + /* set CPU DAI configuration */ + switch (sampling_rate) { + case 8000: + case 32000: + case 48000: + case 96000: + data->sysclk = 12288000; + break; + + case 44100: + case 88200: + data->sysclk = 11289600; + break; + + default: + return -EINVAL; + } + + wm8731_slv_mode_clock_enable(1,data); + + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS; + + ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); + if (ret < 0) + return ret; + + /* set i.MX active slot mask */ + /* S[TR]CCR:DC */ + tx_mask = ~((1 << channels) - 1); + rx_mask = tx_mask; + snd_soc_dai_set_tdm_slot(cpu_dai, tx_mask, rx_mask, 2, 32); + + /* + * SSI sysclk divider: + * div_2: /1 or /2 + * div_psr: /1 or /8 + * div_pm: /1 .. /256 + */ + div_2 = 0; + div_psr = 0; + switch (sampling_rate) { + case 8000: + // 1x1x12 + div_pm = 11; + break; + case 32000: + // 1x1x3 + div_pm = 2; + break; + case 48000: + // 1x1x2 + div_pm = 1; + break; + case 96000: + // 1x1x1 + div_pm = 0; + break; + case 44100: + // 1x1x2 + div_pm = 1; + break; + case 88200: + // 1x1x1 + div_pm = 0; + break; + default: + return -EINVAL; + } + + /* sync mode: a single clock controls both playback and capture */ + snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_2, (div_2 ? SSI_STCCR_DIV2 : 0)); + snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PSR, (div_psr ? SSI_STCCR_PSR : 0)); + snd_soc_dai_set_clkdiv(cpu_dai, IMX_SSI_TX_DIV_PM, div_pm); + + /* set codec DAI configuration */ + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + ret = snd_soc_dai_set_fmt(codec_dai, dai_format); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8731_SYSCLK_MCLK, + data->sysclk, + SND_SOC_CLOCK_IN); + + if (ret < 0) { + pr_err("Failed to set codec master clock to %u: %d \n", + data->sysclk, ret); + return ret; + } + + return 0; +} + +static void imx_hifi_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = codec_dai->codec->card; + struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); + + if (!codec_dai->active) + wm8731_slv_mode_clock_enable(0,data); + + return; +} + +static int imx_wm8731_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + + /* Add imx specific widgets */ + ret = snd_soc_dapm_new_controls(&codec->dapm, imx_dapm_widgets, + ARRAY_SIZE(imx_dapm_widgets)); + if (ret) + goto out_retcode; + + /* Set up imx specific audio path audio_map */ + ret = snd_soc_dapm_add_routes(&codec->dapm, audio_map, ARRAY_SIZE(audio_map)); + if (ret) + goto out_retcode; + + ret = snd_soc_dapm_enable_pin(&codec->dapm, "Headphone Jack"); + if (ret) + goto out_retcode; + + ret = snd_soc_dapm_nc_pin(&codec->dapm, "Ext Spk"); + if (ret) + goto out_retcode; + +out_retcode: + + if (ret) + pr_err("%s: failed with error code: %d \n", __FUNCTION__, ret); + else + pr_info("%s: success \n", __FUNCTION__); + + return ret; +} + +/** + * Configure AUDMUX interconnection between + * _slave (CPU side) and _master (codec size) + * + * When SSI operates in master mode, 5-wire interconnect with + * audio codec is required: + * TXC - BCLK + * TXD - DAC data + * RXD - ADC data + * TXFS - {DAC|ADC}LRC, i.e. word clock + * RXC - MCLK, i.e. oversampling clock + * Audmux is operated in asynchronous mode to enable 6-wire + * interface (as opposed to 4-wire interface in sync mode). + */ +static int imx_audmux_config_slv_mode(int _slave, int _master) +{ + unsigned int ptcr, pdcr; + int slave = _slave - 1; + int master = _master - 1; + + ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(slave) | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_RCSEL(slave | 0x8) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(slave); + + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(slave); + imx_audmux_v2_configure_port(master, ptcr, pdcr); + ptcr = ptcr & ~IMX_AUDMUX_V2_PTCR_SYN; + imx_audmux_v2_configure_port(master, ptcr, pdcr); + + ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_RCLKDIR | + IMX_AUDMUX_V2_PTCR_RCSEL(master | 0x8) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(master); + + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(master); + imx_audmux_v2_configure_port(slave, ptcr, pdcr); + ptcr = ptcr & ~IMX_AUDMUX_V2_PTCR_SYN; + imx_audmux_v2_configure_port(slave, ptcr, pdcr); + + return 0; +} + +static int imx_wm8731_probe(struct platform_device *pdev) +{ + struct device_node *ssi_np, *codec_np; + struct platform_device *ssi_pdev; + struct imx_priv *priv = &card_priv; + struct i2c_client *codec_dev; + struct imx_wm8731_data *data; + int ret; + + priv->pdev = pdev; + + ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); + codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!ssi_np || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + ssi_pdev = of_find_device_by_node(ssi_np); + if (!ssi_pdev) { + dev_err(&pdev->dev, "failed to find SSI platform device\n"); + ret = -EINVAL; + goto fail; + } + + codec_dev = of_find_i2c_device_by_node(codec_np); + if (!codec_dev || !codec_dev->driver) { + dev_err(&pdev->dev, "failed to find codec platform device\n"); + ret = -EINVAL; + goto fail; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + card_priv.data = data; + + data->codec_dev = codec_dev; + + data->dai.name = "HiFi"; + data->dai.stream_name = "HiFi"; + data->dai.codec_dai_name = "wm8731-hifi"; + data->dai.codec_of_node = codec_np; + data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev); + data->dai.platform_of_node = ssi_np; + data->dai.ops = &imx_hifi_ops; + data->dai.init = &imx_wm8731_init; + + data->ssi_num = 2; /* 1-based */ + data->src_port = 2; + data->ext_port = 4; + + imx_audmux_config_slv_mode(data->src_port, data->ext_port); + + /* Slave Mode Init */ + wm8731_slv_mode_init(data); + + data->card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto fail; + + ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); + if (ret) + goto fail; + + data->card.num_links = 1; + data->card.dai_link = &data->dai; + + data->card.dapm_widgets = imx_dapm_widgets; + data->card.num_dapm_widgets = ARRAY_SIZE(imx_dapm_widgets); + + platform_set_drvdata(pdev, &data->card); + snd_soc_card_set_drvdata(&data->card, data); + + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto fail; + } + + return 0; + +fail: + + if (ssi_np) + of_node_put(ssi_np); + + if (codec_np) + of_node_put(codec_np); + + return ret; +} + +static int imx_wm8731_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + snd_soc_unregister_card(card); + + return 0; +} + +static const struct of_device_id imx_wm8731_dt_ids[] = { + { .compatible = "fsl,imx-audio-wm8731", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_wm8731_dt_ids); + +static struct platform_driver imx_wm8731_driver = { + .driver = { + .name = "imx-wm8731", + .owner = THIS_MODULE, + .of_match_table = imx_wm8731_dt_ids, + }, + .probe = imx_wm8731_probe, + .remove = imx_wm8731_remove, +}; +module_platform_driver(imx_wm8731_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale i.MX WM8731 ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-wm8731"); From 467215f5977515cfce498509ae3dbec91139fb43 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 21 Jul 2014 17:17:32 +0300 Subject: [PATCH 0657/1983] ARM: i.MX6: ASoC: add imx-wm8731 master mode support Add imx-wm8731 master mode support. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 8 +- sound/soc/fsl/imx-wm8731.c | 208 +++++++++++++++++++++++++++-- 2 files changed, 201 insertions(+), 15 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 73e6f75d96525f..f9aa3c24db7452 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -173,6 +173,8 @@ "fsl,imx-audio-wm8731"; model = "wm8731-audio"; ssi-controller = <&ssi2>; + src-port = <2>; + ext-port = <4>; audio-codec = <&codec>; audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; }; @@ -390,6 +392,8 @@ MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 + /* master mode pin */ + MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 >; }; @@ -500,8 +504,8 @@ codec: wm8731@1a { compatible = "wlf,wm8731"; reg = <0x1a>; - clocks = <&clks 173>, <&clks 158>; - clock-names = "pll4", "imx-ssi.1"; + clocks = <&clks 173>, <&clks 158>, <&clks 201>, <&clks 200>; + clock-names = "pll4", "imx-ssi.1", "cko", "cko2"; AVDD-supply = <&pu_dummy>; HPVDD-supply = <&pu_dummy>; DCVDD-supply = <&pu_dummy>; diff --git a/sound/soc/fsl/imx-wm8731.c b/sound/soc/fsl/imx-wm8731.c index db9cb0688ba305..9feac459bfc7a2 100644 --- a/sound/soc/fsl/imx-wm8731.c +++ b/sound/soc/fsl/imx-wm8731.c @@ -31,6 +31,7 @@ #include "imx-ssi.h" #define DAI_NAME_SIZE 32 +#define WM8731_MCLK_FREQ (24000000 / 2) struct imx_wm8731_data { struct snd_soc_dai_link dai; @@ -43,10 +44,8 @@ struct imx_wm8731_data { struct clk *clock_root; long sysclk; long current_rate; - /* platfor data */ - unsigned int ssi_num; - unsigned int src_port; - unsigned int ext_port; + /* apis */ + int (*clock_enable)(int enable,struct imx_wm8731_data *data); }; static int imx_wm8731_init(struct snd_soc_pcm_runtime *rtd); @@ -63,7 +62,6 @@ static struct imx_priv card_priv; static struct snd_soc_ops imx_hifi_ops = { .shutdown = imx_hifi_shutdown, - .hw_params = imx_hifi_hw_params_slv_mode, }; /* imx card dapm widgets */ @@ -160,6 +158,78 @@ static int wm8731_slv_mode_clock_enable(int enable, struct imx_wm8731_data *data return 0; } +static int imx_hifi_startup_slv_mode(struct snd_pcm_substream *substream) +{ + /* + * As SSI's sys clock rate depends on sampling rate, + * the clock enabling code is moved to imx_hifi_hw_params(). + */ + return 0; +} + +static int wm8731_mst_mode_init(struct imx_wm8731_data *data) +{ + long rate; + struct clk *new_parent; + struct clk *ssi_clk; + struct i2c_client *codec_dev = data->codec_dev; + + new_parent = devm_clk_get(&codec_dev->dev, "cko2"); + if (IS_ERR(new_parent)) { + pr_err("Could not get \"cko2\" clock \n"); + return PTR_ERR(new_parent); + } + + ssi_clk = devm_clk_get(&codec_dev->dev, "cko"); + if (IS_ERR(ssi_clk)) { + pr_err("Could not get \"cko\" clock \n"); + return PTR_ERR(ssi_clk); + } + + rate = clk_round_rate(new_parent, WM8731_MCLK_FREQ); + clk_set_rate(new_parent, rate); + + clk_set_parent(ssi_clk, new_parent); + + rate = clk_round_rate(ssi_clk, WM8731_MCLK_FREQ); + clk_set_rate(ssi_clk, rate); + + pr_info("%s: \"CLKO\" rate = %ld (= %d)\n", + __func__, rate, WM8731_MCLK_FREQ); + + data->pll = new_parent; + data->clock_root = ssi_clk; + data->sysclk = rate; + + return 0; +} + +static int wm8731_mst_mode_clock_enable(int enable, struct imx_wm8731_data *data) +{ + struct clk *clko = data->clock_root; + + if (enable) + clk_enable(clko); + else + clk_disable(clko); + + return 0; +} + +static int imx_hifi_startup_mst_mode(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = codec_dai->codec->card; + struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); + + if (!codec_dai->active) + data->clock_enable(1,data); + + return 0; +} + + static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -169,7 +239,7 @@ static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, struct snd_soc_card *card = codec_dai->codec->card; struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); - u32 dai_format, pll_out; + u32 dai_format; snd_pcm_format_t sample_format; unsigned int channels; unsigned int tx_mask, rx_mask; @@ -282,6 +352,63 @@ static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, return 0; } +static int imx_hifi_hw_params_mst_mode(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = codec_dai->codec->card; + struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); + u32 dai_format; + unsigned int channels; + unsigned int tx_mask, rx_mask; + unsigned int sampling_rate; + int ret; + + + sampling_rate = params_rate(params); + channels = params_channels(params); + pr_debug("%s:%s sampling rate = %u channels = %u \n", __FUNCTION__, + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"), + sampling_rate, channels); + + /* set cpu DAI configuration */ + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM; + + ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); + if (ret < 0) + return ret; + + /* set i.MX active slot mask */ + /* S[TR]CCR:DC */ + tx_mask = ~((1 << channels) - 1); + rx_mask = tx_mask; + snd_soc_dai_set_tdm_slot(cpu_dai, tx_mask, rx_mask, 2, 32); + + /* set codec DAI configuration */ + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + ret = snd_soc_dai_set_fmt(codec_dai, dai_format); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8731_SYSCLK_MCLK, + data->sysclk, + SND_SOC_CLOCK_IN); + + if (ret < 0) { + pr_err("Failed to set codec master clock to %u: %d \n", + data->sysclk, ret); + return ret; + } + + return 0; +} + static void imx_hifi_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -290,7 +417,7 @@ static void imx_hifi_shutdown(struct snd_pcm_substream *substream) struct imx_wm8731_data *data = snd_soc_card_get_drvdata(card); if (!codec_dai->active) - wm8731_slv_mode_clock_enable(0,data); + data->clock_enable(0,data); return; } @@ -376,6 +503,27 @@ static int imx_audmux_config_slv_mode(int _slave, int _master) return 0; } +static int imx_audmux_config_mst_mode(int _slave, int _master) +{ + unsigned int ptcr, pdcr; + int slave = _slave - 1; + int master = _master - 1; + + ptcr = IMX_AUDMUX_V2_PTCR_SYN; + ptcr |= IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(master) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(master); + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(master); + imx_audmux_v2_configure_port(slave, ptcr, pdcr); + + ptcr = IMX_AUDMUX_V2_PTCR_SYN; + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(slave); + imx_audmux_v2_configure_port(master, ptcr, pdcr); + + return 0; +} + static int imx_wm8731_probe(struct platform_device *pdev) { struct device_node *ssi_np, *codec_np; @@ -383,6 +531,10 @@ static int imx_wm8731_probe(struct platform_device *pdev) struct imx_priv *priv = &card_priv; struct i2c_client *codec_dev; struct imx_wm8731_data *data; + unsigned int src_port, ext_port; + unsigned int ssi_mode; + const char *ssi_mode_str; + int ret; priv->pdev = pdev; @@ -428,14 +580,44 @@ static int imx_wm8731_probe(struct platform_device *pdev) data->dai.ops = &imx_hifi_ops; data->dai.init = &imx_wm8731_init; - data->ssi_num = 2; /* 1-based */ - data->src_port = 2; - data->ext_port = 4; + ret = of_property_read_u32(pdev->dev.of_node, "src-port", &src_port); + if (ret) { + dev_err(&pdev->dev, "failed to get \"src-port\" value\n"); + ret = -EINVAL; + goto fail; + } + + ret = of_property_read_u32(pdev->dev.of_node, "ext-port", &ext_port); + if (ret) { + dev_err(&pdev->dev, "failed to get \"ext-port\" value\n"); + ret = -EINVAL; + goto fail; + } - imx_audmux_config_slv_mode(data->src_port, data->ext_port); + ret = of_property_read_string(ssi_np, "fsl,mode", &ssi_mode_str); + if (ret) { + dev_err(&pdev->dev, "failed to get \"fsl,mode\" value\n"); + ret = -EINVAL; + goto fail; + } - /* Slave Mode Init */ - wm8731_slv_mode_init(data); + ssi_mode = strcmp(ssi_mode_str, "i2s-master"); + + if (ssi_mode) { + /* Master Mode */ + imx_audmux_config_mst_mode(src_port, ext_port); + wm8731_mst_mode_init(data); + data->clock_enable = wm8731_mst_mode_clock_enable; + imx_hifi_ops.hw_params = imx_hifi_hw_params_mst_mode; + imx_hifi_ops.startup = imx_hifi_startup_mst_mode; + } else { + /* Slave Mode */ + imx_audmux_config_slv_mode(src_port, ext_port); + wm8731_slv_mode_init(data); + data->clock_enable = wm8731_slv_mode_clock_enable; + imx_hifi_ops.hw_params = imx_hifi_hw_params_slv_mode; + imx_hifi_ops.startup = imx_hifi_startup_slv_mode; + } data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); From c5d91031e73014dc0da6fbe9bcb98affe12e76ff Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 24 Jul 2014 16:11:29 +0300 Subject: [PATCH 0658/1983] ARM: i.MX6: dts: enable v4l2 output Enable v4l2 output. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index f9aa3c24db7452..9bd5b3bf97acb2 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -227,6 +227,11 @@ status = "okay"; }; + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; + }; &iomuxc { From 35f9a7af15d59589bc3fb85737a72f2a5de9bb5a Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 31 Jul 2014 16:27:38 +0300 Subject: [PATCH 0659/1983] ARM: i.MX6: dts: some small changes in the dts file 1) Fixed the color depth value for both frame buffers. 2) Added a missing OTG pinmux definition. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 9bd5b3bf97acb2..552ba2bac33ca1 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -200,7 +200,7 @@ disp_dev = "hdmi"; interface_pix_fmt = "RGB24"; mode_str ="1920x1080M@60"; - default_bpp = <24>; + default_bpp = <32>; int_clk = <0>; late_init = <0>; status = "disabled"; @@ -211,12 +211,13 @@ disp_dev = "lcd"; interface_pix_fmt = "RGB24"; mode_str ="1920x1080M@60"; - default_bpp = <24>; + default_bpp = <32>; int_clk = <0>; late_init = <0>; status = "disabled"; }; + lcd@0 { compatible = "fsl,lcd"; ipu_id = <0>; @@ -354,6 +355,7 @@ pinctrl_usbotg: usbotggrp { fsl,pins = < MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 >; }; From 2d0c250927f696958a32634c0dec7a8f8098c620 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Tue, 5 Aug 2014 15:04:44 +0300 Subject: [PATCH 0660/1983] igb: Define the device mac address in device tree 1) Define the device mac address node in the device tree. 2) Make the driver read the mac address from the device tree node. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 6 +++++ drivers/net/ethernet/intel/igb/igb_main.c | 27 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 552ba2bac33ca1..99aec80e8453a4 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -233,6 +233,12 @@ status = "okay"; }; + eth@pcie { + compatible = "intel,i211"; + local-mac-address = [00 1C 1D 1E 1F 20]; + status = "okay"; + }; + }; &iomuxc { diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 2472835fdd41bb..5f9376511ab111 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2185,6 +2185,30 @@ static s32 igb_init_i2c(struct igb_adapter *adapter) return status; } + +/** + * igb_read_mac_addr_dts - Read mac addres from the device tree + * blob + * @adapter: pointer to adapter structure + **/ +static void igb_read_mac_addr_dts(struct e1000_hw *hw) +{ + struct device_node *dn; + const uint8_t *mac; + + dn = of_find_compatible_node(NULL, NULL, "intel,i211"); + + if (!dn) + return; + + mac = of_get_property(dn, "local-mac-address", NULL); + + if (mac) + memcpy(hw->mac.addr, mac, ETH_ALEN); + + return; +} + /** * igb_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -2387,6 +2411,9 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (hw->mac.ops.read_mac_addr(hw)) dev_err(&pdev->dev, "NVM Read Error\n"); + if (!is_valid_ether_addr(hw->mac.addr)) + igb_read_mac_addr_dts(hw); + if (!is_valid_ether_addr(hw->mac.addr)) { dev_info(&pdev->dev, "Random MAC Address\n"); random_ether_addr(hw->mac.addr); From 6e1bfeb826288f81cfd20c956020cd12d3ec8a49 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Tue, 5 Aug 2014 15:39:32 +0300 Subject: [PATCH 0661/1983] ARM: i.MX6: cm-fx6: update defconfig Enable: 1) Analog audio 2) MRVL bluetooth 3) SATA AHCI 4) USB OTG 5) Board revision Signed-off-by: Valentin Raevsky --- arch/arm/configs/cm_fx6_defconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/configs/cm_fx6_defconfig b/arch/arm/configs/cm_fx6_defconfig index eb6d9cb8755f86..210062beabc933 100644 --- a/arch/arm/configs/cm_fx6_defconfig +++ b/arch/arm/configs/cm_fx6_defconfig @@ -26,6 +26,7 @@ CONFIG_SOC_IMX53=y CONFIG_SOC_IMX6Q=y CONFIG_SOC_IMX6SL=y CONFIG_SOC_VF610=y +CONFIG_MACH_CM_FX6=y # CONFIG_SWP_EMULATE is not set CONFIG_PCI=y CONFIG_PCI_IMX6=y @@ -137,6 +138,9 @@ CONFIG_VLAN_8021Q=m CONFIG_VLAN_8021Q_GVRP=y CONFIG_CAN=y CONFIG_CAN_FLEXCAN=y +CONFIG_BT=m +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m CONFIG_CFG80211=y CONFIG_CFG80211_WEXT=y CONFIG_MAC80211=y @@ -176,6 +180,7 @@ CONFIG_SCSI_LOGGING=y CONFIG_SCSI_SCAN_ASYNC=y # CONFIG_SCSI_LOWLEVEL is not set CONFIG_ATA=y +CONFIG_SATA_AHCI=y CONFIG_SATA_AHCI_PLATFORM=y CONFIG_AHCI_IMX=y CONFIG_PATA_IMX=y @@ -251,6 +256,7 @@ CONFIG_MFD_MC13XXX_I2C=y CONFIG_MFD_MAX17135=y CONFIG_MFD_SI476X_CORE=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_DUMMY=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_DA9052=y CONFIG_REGULATOR_ANATOP=y @@ -310,6 +316,7 @@ CONFIG_SND_SOC=y CONFIG_SND_IMX_SOC=y CONFIG_SND_SOC_EUKREA_TLV320=y CONFIG_SND_SOC_IMX_CS42888=y +CONFIG_SND_SOC_IMX_WM8731=y CONFIG_SND_SOC_IMX_WM8962=y CONFIG_SND_SOC_IMX_SGTL5000=y CONFIG_SND_SOC_IMX_SPDIF=y @@ -317,7 +324,10 @@ CONFIG_SND_SOC_IMX_MC13783=y CONFIG_SND_SOC_IMX_HDMI=y CONFIG_SND_SOC_IMX_SI476X=y CONFIG_USB=y +CONFIG_USB_OTG=y CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_MXC=y +CONFIG_USB_EHCI_HCD_PLATFORM=y CONFIG_USB_STORAGE=y CONFIG_USB_CHIPIDEA=y CONFIG_USB_CHIPIDEA_UDC=y @@ -326,7 +336,9 @@ CONFIG_USB_PHY=y CONFIG_NOP_USB_XCEIV=y CONFIG_USB_MXS_PHY=y CONFIG_USB_GADGET=y +CONFIG_USB_FSL_USB2=y CONFIG_USB_ZERO=m +CONFIG_USB_AUDIO=m CONFIG_USB_ETH=m CONFIG_USB_MASS_STORAGE=m CONFIG_USB_G_SERIAL=m From 09e352437d7def76f9f7ec0dcacde2045c1bc42f Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 7 Aug 2014 15:30:03 +0300 Subject: [PATCH 0662/1983] ARM: i.MX6: dts: refactoring the cm-fx6 device tree file. Separate the staff that belongs to SB-FX6 and SB-FX6m boards. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 890 +++++++++++++-------------- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 23 + arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 83 +++ 3 files changed, 521 insertions(+), 475 deletions(-) create mode 100644 arch/arm/boot/dts/imx6q-sbc-fx6.dts create mode 100644 arch/arm/boot/dts/imx6q-sbc-fx6m.dts diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 99aec80e8453a4..7ba0dd775771c2 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -15,158 +15,147 @@ #include "imx6q.dtsi" / { - model = "CompuLab CM-FX6"; - compatible = "compulab,cm-fx6", "fsl,imx6q"; - - memory { - reg = <0x10000000 0x80000000>; - }; - - leds { - compatible = "gpio-leds"; - - heartbeat-led { - label = "Heartbeat"; - gpios = <&gpio2 31 0>; - linux,default-trigger = "heartbeat"; - }; - }; - - regulators { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; - - /* regulator for mmc */ - reg_3p3v: 3p3v { - compatible = "regulator-fixed"; - regulator-name = "3P3V"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - /* regulator for usb otg */ - reg_usb_otg_vbus: usb_otg_vbus { - compatible = "regulator-fixed"; - regulator-name = "usb_otg_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&gpio3 22 0>; - enable-active-high; - }; - - /* regulator for usb hub1 */ - reg_usb_h1_vbus: usb_h1_vbus { - compatible = "regulator-fixed"; - regulator-name = "usb_h1_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&gpio7 8 0>; - enable-active-high; - }; - - /* regulator1 for wifi/bt */ - awnh387_npoweron: regulator-awnh387-npoweron { - compatible = "regulator-fixed"; - regulator-name = "regulator-awnh387-npoweron"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio7 12 0>; - enable-active-high; - }; - - /* regulator2 for wifi/bt */ - awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { - compatible = "regulator-fixed"; - regulator-name = "regulator-awnh387-wifi-nreset"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio6 16 0>; - startup-delay-us = <10000>; - }; - - reg_sata_phy_slp: sata_phy_slp { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_phy_slp"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio3 23 0>; - startup-delay-us = <100>; - enable-active-high; - }; - - reg_sata_nrstdly: sata_nrstdly { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_nrstdly"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio6 6 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_phy_slp>; - }; - - reg_sata_pwren: sata_pwren { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_pwren"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio1 28 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_nrstdly>; - }; - - reg_sata_nstandby1: sata_nstandby1 { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_nstandby1"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio3 20 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_pwren>; - }; - - reg_sata_nstandby2: sata_nstandby2 { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_nstandby2"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio5 2 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_nstandby1>; - }; - - reg_sata_ldo_en: sata_ldo_en { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_ldo_en"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio2 16 0>; - startup-delay-us = <100>; - enable-active-high; - regulator-boot-on; - vin-supply = <®_sata_nstandby2>; - }; - }; - - gpio-keys { - compatible = "gpio-keys"; - power { - label = "Power Button"; - gpios = <&gpio1 29 1>; - linux,code = <116>; /* KEY_POWER */ - gpio-key,wakeup; + model = "CompuLab CM-FX6"; + compatible = "compulab,cm-fx6", "fsl,imx6q"; + + memory { + reg = <0x10000000 0x80000000>; }; - }; - aliases { - mxcfb0 = &mxcfb1; - mxcfb1 = &mxcfb2; - }; + leds { + compatible = "gpio-leds"; + heartbeat-led { + label = "Heartbeat"; + gpios = <&gpio2 31 0>; + linux,default-trigger = "heartbeat"; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + /* regulator for mmc */ + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + /* regulator for usb otg */ + reg_usb_otg_vbus: usb_otg_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 0>; + enable-active-high; + }; + + /* regulator for usb hub1 */ + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio7 8 0>; + enable-active-high; + }; + + /* regulator1 for wifi/bt */ + awnh387_npoweron: regulator-awnh387-npoweron { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-npoweron"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio7 12 0>; + enable-active-high; + }; + + /* regulator2 for wifi/bt */ + awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-wifi-nreset"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 16 0>; + startup-delay-us = <10000>; + }; + + reg_sata_phy_slp: sata_phy_slp { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_phy_slp"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 23 0>; + startup-delay-us = <100>; + enable-active-high; + }; + + reg_sata_nrstdly: sata_nrstdly { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nrstdly"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 6 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_phy_slp>; + }; + + reg_sata_pwren: sata_pwren { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_pwren"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 28 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_nrstdly>; + }; + + reg_sata_nstandby1: sata_nstandby1 { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nstandby1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 20 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_pwren>; + }; + + reg_sata_nstandby2: sata_nstandby2 { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nstandby2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio5 2 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_nstandby1>; + }; + + reg_sata_ldo_en: sata_ldo_en { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_ldo_en"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 16 0>; + startup-delay-us = <100>; + enable-active-high; + regulator-boot-on; + vin-supply = <®_sata_nstandby2>; + }; + }; + + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + }; sound { compatible = "fsl,imx6q-cm-fx6-wm8731", @@ -179,66 +168,58 @@ audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; }; - sound-hdmi { - compatible = "fsl,imx6q-audio-hdmi", - "fsl,imx-audio-hdmi"; - model = "imx-audio-hdmi"; - hdmi-controller = <&hdmi_audio>; - }; - - sound-spdif { - compatible = "fsl,imx-audio-spdif", - "fsl,imx-sabreauto-spdif"; - model = "imx-spdif"; - spdif-controller = <&spdif>; - spdif-out; - spdif-in; - }; - - mxcfb1: fb@0 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "hdmi"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "disabled"; - }; - - mxcfb2: fb@1 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "lcd"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "disabled"; - }; - - - lcd@0 { - compatible = "fsl,lcd"; - ipu_id = <0>; - disp_id = <0>; - default_ifmt = "RGB24"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ipu1_1>; - status = "okay"; - }; + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; - v4l2_out { - compatible = "fsl,mxc_v4l2_output"; - status = "okay"; - }; + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx-sabreauto-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; + }; - eth@pcie { - compatible = "intel,i211"; - local-mac-address = [00 1C 1D 1E 1F 20]; - status = "okay"; - }; + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + lcd@0 { + compatible = "fsl,lcd"; + ipu_id = <0>; + disp_id = <0>; + default_ifmt = "RGB24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_1>; + status = "okay"; + }; + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; }; &iomuxc { @@ -258,261 +239,240 @@ MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x80000000 MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 + /* POWER_BUTTON */ + MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 >; }; }; - imx6q-cm-fx6 { - /* pins for eth0 */ - pinctrl_enet: enetgrp { - fsl,pins = < - MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 - MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - >; - }; - - /* pins for spi */ - pinctrl_ecspi1: ecspi1grp { - fsl,pins = < - MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 - MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 - MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 - MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 - MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 - >; - }; - - /* pins for nand */ - pinctrl_gpmi_nand: gpminandgrp { - fsl,pins = < - MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 - MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 - MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 - MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 - MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 - MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 - MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 - MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 - MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 - MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 - MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 - MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 - MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 - MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 - MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 - MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 - MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 - >; - }; - - /* pins for i2c1 */ - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 - MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 - >; - }; - - /* pins for i2c2 */ - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 - MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 - >; - }; - - /* pins for i2c3 */ - pinctrl_i2c3: i2c3grp { - fsl,pins = < - MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 - MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 - >; - }; - - /* pins for console */ - pinctrl_uart4: uart4grp { - fsl,pins = < - MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 - MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 - >; - }; - - /* pins for usb hub1 */ - pinctrl_usbh1: usbh1grp { - fsl,pins = < - MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 - >; - }; - - /* pins for usb otg */ - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 - MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 - >; - }; - - /* pins for wifi/bt */ - pinctrl_usdhc1: usdhc1grp { - fsl,pins = < - MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 - MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 - MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 - MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 - MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 - MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 - >; - }; - - /* pins for mmc */ - pinctrl_usdhc3: usdhc3grp { - fsl,pins = < - MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 - >; - }; - - /* pins for spdif */ - pinctrl_spdif: spdifgrp { - fsl,pins = < - MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 - MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 - >; - }; - - /* pins for audmux */ - pinctrl_audmux: audmuxgrp { - fsl,pins = < - MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 - MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 - MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 - MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 - MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 - /* master mode pin */ - MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 - >; - }; + imx6q-cm-fx6 { + /* pins for eth0 */ + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + >; + }; - /* pins for uart2 */ - pinctrl_uart2: uart2grp { - fsl,pins = < - MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1 - MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x1b0b1 - MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x1b0b1 - MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x1b0b1 - >; - }; + /* pins for spi */ + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 + MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 + >; + }; + + /* pins for nand */ + pinctrl_gpmi_nand: gpminandgrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 + MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 + MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 + MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 + MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 + MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 + MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 + MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 + MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 + MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 + MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 + MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 + MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 + MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 + MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 + MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 + MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 + >; + }; + + /* pins for i2c1 */ + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + /* pins for i2c2 */ + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + /* pins for i2c3 */ + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; - /* pins for pcie */ - pinctrl_pcie: pciegrp { - fsl,pins = < - MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 - MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 - >; - }; - }; + /* pins for console */ + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + + /* pins for usb hub1 */ + pinctrl_usbh1: usbh1grp { + fsl,pins = < + MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 + >; + }; + + /* pins for usb otg */ + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 + >; + }; + + /* pins for wifi/bt */ + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 + >; + }; + + /* pins for mmc */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + + /* pins for spdif */ + pinctrl_spdif: spdifgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 + MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 + >; + }; + + /* pins for audmux */ + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 + MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 + MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 + MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 + MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 + /* master mode pin */ + MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 + >; + }; + }; }; /* spi */ &ecspi1 { - fsl,spi-num-chipselects = <2>; - cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1>; - status = "okay"; - - flash: m25p80@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,m25px16", "st,m25p"; - spi-max-frequency = <20000000>; - reg = <0>; - - partition@0 { - label = "uboot"; - reg = <0x0 0xc0000>; - }; - - partition@c0000 { - label = "uboot environment"; - reg = <0xc0000 0x40000>; - }; - - partition@100000 { - label = "reserved"; - reg = <0x100000 0x100000>; - }; - }; + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25px16", "st,m25p"; + spi-max-frequency = <20000000>; + reg = <0>; + + partition@0 { + label = "uboot"; + reg = <0x0 0xc0000>; + }; + + partition@c0000 { + label = "uboot environment"; + reg = <0xc0000 0x40000>; + }; + + partition@100000 { + label = "reserved"; + reg = <0x100000 0x100000>; + }; + }; }; /* eth0 */ &fec { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet>; - phy-mode = "rgmii"; - status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + status = "okay"; }; /* nand */ &gpmi { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_gpmi_nand>; - status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + status = "okay"; }; /* i2c1 */ &i2c1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1>; - status = "okay"; - - eeprom@50 { - compatible = "at24,24c02"; - reg = <0x50>; - pagesize = <16>; - }; - - rtc@56 { - compatible = "emmicro,em3027"; - reg = <0x56>; - }; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; }; /* i2c2 */ -&i2c2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2>; - /* status = "okay"; */ +&i2c2 { /* to be removed */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + /* status = "okay"; */ }; /* i2c3 */ &i2c3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c3>; - status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; - eeprom@50 { - compatible = "at24,24c02"; - reg = <0x50>; - pagesize = <16>; - }; + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; codec: wm8731@1a { compatible = "wlf,wm8731"; @@ -526,71 +486,51 @@ }; }; -/* eth1 */ -&pcie { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pcie>; - reset-gpio = <&gpio1 26 0>; - power-on-gpio = <&gpio2 24 0>; - status = "okay"; -}; - /* sata */ &sata { - status = "okay"; -}; - -/* rear serial console */ -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2>; - /* fsl,dte-mode; */ - fsl,uart-has-rtscts; - dma-names = "rx", "tx"; - dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; - status = "okay"; + status = "okay"; }; /* console */ &uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4>; - status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; }; /* usb otg */ &usbotg { - vbus-supply = <®_usb_otg_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbotg>; - dr_mode = "otg"; - status = "okay"; + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + dr_mode = "otg"; + status = "okay"; }; /* usb hub1 */ &usbh1 { - vbus-supply = <®_usb_h1_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbh1>; - status = "okay"; + vbus-supply = <®_usb_h1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1>; + status = "okay"; }; /* wifi/bt */ &usdhc1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc1>; - non-removable; - vmmc-supply = <&awnh387_npoweron>; - vmmc_aux-supply = <&awnh387_wifi_nreset>; - status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>; + non-removable; + vmmc-supply = <&awnh387_npoweron>; + vmmc_aux-supply = <&awnh387_wifi_nreset>; + status = "okay"; }; /* mmc */ &usdhc3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3>; - vmmc-supply = <®_3p3v>; - status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + vmmc-supply = <®_3p3v>; + status = "okay"; }; &ssi2 { diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts new file mode 100644 index 00000000000000..5d3c7da5ea257e --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -0,0 +1,23 @@ +/* +* Copyright 2014 CompuLab Ltd. +* +* Author: Valentin Raevsky +* +* The code contained herein is licensed under the GNU General Public +* License. You may obtain a copy of the GNU General Public License +* Version 2 or later at the following locations: +* +* http://www.opensource.org/licenses/gpl-license.html +* http://www.gnu.org/copyleft/gpl.html +*/ + +#include "imx6q-cm-fx6.dts" + +/ { + model = "CompuLab CM-FX6 on SBC-FX6"; + compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6q"; +}; + +&pcie { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts new file mode 100644 index 00000000000000..d48f33735c7366 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -0,0 +1,83 @@ +/* +* Copyright 2014 CompuLab Ltd. +* +* Author: Valentin Raevsky +* +* The code contained herein is licensed under the GNU General Public +* License. You may obtain a copy of the GNU General Public License +* Version 2 or later at the following locations: +* +* http://www.opensource.org/licenses/gpl-license.html +* http://www.gnu.org/copyleft/gpl.html +*/ + +#include "imx6q-cm-fx6.dts" + +/ { + model = "CompuLab CM-FX6 on SBC-FX6m"; + compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6q"; + + eth@pcie { + compatible = "intel,i211"; + local-mac-address = [FF FF FF FF FF FF]; + status = "okay"; + }; + + gpio-keys { + compatible = "gpio-keys"; + power { + label = "Power Button"; + gpios = <&gpio1 29 1>; + linux,code = <116>; /* KEY_POWER */ + gpio-key,wakeup; + }; + }; +}; + +&iomuxc { + imx6q-sb-fx6m { + /* pins for uart2 */ + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x1b0b1 + MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x1b0b1 + MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x1b0b1 + >; + }; + + /* pins for pcie */ + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 + MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 + >; + }; + }; +}; + +&i2c1 { + rtc@56 { + compatible = "emmicro,em3027"; + reg = <0x56>; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; + reset-gpio = <&gpio1 26 0>; + power-on-gpio = <&gpio2 24 0>; + status = "okay"; +}; + +/* rear serial console */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + /* fsl,dte-mode; */ + fsl,uart-has-rtscts; + dma-names = "rx", "tx"; + dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; + status = "okay"; +}; From 86628ca78c5c136679f294e293930dea30d601ef Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Tue, 12 Aug 2014 17:46:23 +0300 Subject: [PATCH 0663/1983] ARM: i.MX6: dts: refactoring of the cm-fx6 device tree files. Refactoring device tree files: 1) Utilite: + imx6q.dtsi + imx6q-sb-fx6x.dtsi + imx6q-sb-fx6m.dtsi + imx6q-cm-fx6.dtsi = imx6q-sbc-fx6m.dts 2) CM-FX6-EVAL: + imx6q.dtsi + imx6q-sb-fx6x.dtsi + imx6q-sb-fx6.dtsi + imx6q-cm-fx6.dtsi = imx6q-sbc-fx6.dts 3) CM-FX6 Module: + imx6q.dtsi + imx6q-cm-fx6.dtsi = imx6q-cm-fx6.dts Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 582 +-------------------------- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 531 ++++++++++++++++++++++++ arch/arm/boot/dts/imx6q-sb-fx6.dtsi | 14 + arch/arm/boot/dts/imx6q-sb-fx6m.dtsi | 32 ++ arch/arm/boot/dts/imx6q-sb-fx6x.dtsi | 75 ++++ arch/arm/boot/dts/imx6q-sbc-fx6.dts | 8 +- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 38 +- 7 files changed, 677 insertions(+), 603 deletions(-) create mode 100644 arch/arm/boot/dts/imx6q-cm-fx6.dtsi create mode 100644 arch/arm/boot/dts/imx6q-sb-fx6.dtsi create mode 100644 arch/arm/boot/dts/imx6q-sb-fx6m.dtsi create mode 100644 arch/arm/boot/dts/imx6q-sb-fx6x.dtsi diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index 7ba0dd775771c2..a0e423b5f8e9ce 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -1,576 +1,20 @@ /* -* Copyright 2013 CompuLab Ltd. -* -* Author: Valentin Raevsky -* -* The code contained herein is licensed under the GNU General Public -* License. You may obtain a copy of the GNU General Public License -* Version 2 or later at the following locations: -* -* http://www.opensource.org/licenses/gpl-license.html -* http://www.gnu.org/copyleft/gpl.html -*/ + * Copyright 2014 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ /dts-v1/; -#include "imx6q.dtsi" +#include "imx6q-cm-fx6.dtsi" / { model = "CompuLab CM-FX6"; compatible = "compulab,cm-fx6", "fsl,imx6q"; - - memory { - reg = <0x10000000 0x80000000>; - }; - - leds { - compatible = "gpio-leds"; - heartbeat-led { - label = "Heartbeat"; - gpios = <&gpio2 31 0>; - linux,default-trigger = "heartbeat"; - }; - }; - - regulators { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; - - /* regulator for mmc */ - reg_3p3v: 3p3v { - compatible = "regulator-fixed"; - regulator-name = "3P3V"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - /* regulator for usb otg */ - reg_usb_otg_vbus: usb_otg_vbus { - compatible = "regulator-fixed"; - regulator-name = "usb_otg_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&gpio3 22 0>; - enable-active-high; - }; - - /* regulator for usb hub1 */ - reg_usb_h1_vbus: usb_h1_vbus { - compatible = "regulator-fixed"; - regulator-name = "usb_h1_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&gpio7 8 0>; - enable-active-high; - }; - - /* regulator1 for wifi/bt */ - awnh387_npoweron: regulator-awnh387-npoweron { - compatible = "regulator-fixed"; - regulator-name = "regulator-awnh387-npoweron"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio7 12 0>; - enable-active-high; - }; - - /* regulator2 for wifi/bt */ - awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { - compatible = "regulator-fixed"; - regulator-name = "regulator-awnh387-wifi-nreset"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio6 16 0>; - startup-delay-us = <10000>; - }; - - reg_sata_phy_slp: sata_phy_slp { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_phy_slp"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio3 23 0>; - startup-delay-us = <100>; - enable-active-high; - }; - - reg_sata_nrstdly: sata_nrstdly { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_nrstdly"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio6 6 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_phy_slp>; - }; - - reg_sata_pwren: sata_pwren { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_pwren"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio1 28 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_nrstdly>; - }; - - reg_sata_nstandby1: sata_nstandby1 { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_nstandby1"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio3 20 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_pwren>; - }; - - reg_sata_nstandby2: sata_nstandby2 { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_nstandby2"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio5 2 0>; - startup-delay-us = <100>; - enable-active-high; - vin-supply = <®_sata_nstandby1>; - }; - - reg_sata_ldo_en: sata_ldo_en { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_ldo_en"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio2 16 0>; - startup-delay-us = <100>; - enable-active-high; - regulator-boot-on; - vin-supply = <®_sata_nstandby2>; - }; - }; - - aliases { - mxcfb0 = &mxcfb1; - mxcfb1 = &mxcfb2; - }; - - sound { - compatible = "fsl,imx6q-cm-fx6-wm8731", - "fsl,imx-audio-wm8731"; - model = "wm8731-audio"; - ssi-controller = <&ssi2>; - src-port = <2>; - ext-port = <4>; - audio-codec = <&codec>; - audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; - }; - - sound-hdmi { - compatible = "fsl,imx6q-audio-hdmi", - "fsl,imx-audio-hdmi"; - model = "imx-audio-hdmi"; - hdmi-controller = <&hdmi_audio>; - }; - - sound-spdif { - compatible = "fsl,imx-audio-spdif", - "fsl,imx-sabreauto-spdif"; - model = "imx-spdif"; - spdif-controller = <&spdif>; - spdif-out; - spdif-in; - }; - - mxcfb1: fb@0 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "hdmi"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "disabled"; - }; - - mxcfb2: fb@1 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "lcd"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "disabled"; - }; - - lcd@0 { - compatible = "fsl,lcd"; - ipu_id = <0>; - disp_id = <0>; - default_ifmt = "RGB24"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ipu1_1>; - status = "okay"; - }; - - v4l2_out { - compatible = "fsl,mxc_v4l2_output"; - status = "okay"; - }; -}; - -&iomuxc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - /* SATA PWR */ - MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 - MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x80000000 - MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 - MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 - /* SATA CTRL */ - MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 - MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 - MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x80000000 - MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 - /* POWER_BUTTON */ - MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 - >; - }; - }; - - imx6q-cm-fx6 { - /* pins for eth0 */ - pinctrl_enet: enetgrp { - fsl,pins = < - MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 - MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - >; - }; - - /* pins for spi */ - pinctrl_ecspi1: ecspi1grp { - fsl,pins = < - MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 - MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 - MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 - MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 - MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 - >; - }; - - /* pins for nand */ - pinctrl_gpmi_nand: gpminandgrp { - fsl,pins = < - MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 - MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 - MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 - MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 - MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 - MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 - MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 - MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 - MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 - MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 - MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 - MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 - MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 - MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 - MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 - MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 - MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 - >; - }; - - /* pins for i2c1 */ - pinctrl_i2c1: i2c1grp { - fsl,pins = < - MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 - MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 - >; - }; - - /* pins for i2c2 */ - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 - MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 - >; - }; - - /* pins for i2c3 */ - pinctrl_i2c3: i2c3grp { - fsl,pins = < - MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 - MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 - >; - }; - - /* pins for console */ - pinctrl_uart4: uart4grp { - fsl,pins = < - MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 - MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 - >; - }; - - /* pins for usb hub1 */ - pinctrl_usbh1: usbh1grp { - fsl,pins = < - MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 - >; - }; - - /* pins for usb otg */ - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 - MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 - >; - }; - - /* pins for wifi/bt */ - pinctrl_usdhc1: usdhc1grp { - fsl,pins = < - MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 - MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 - MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 - MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 - MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 - MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 - >; - }; - - /* pins for mmc */ - pinctrl_usdhc3: usdhc3grp { - fsl,pins = < - MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 - MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 - MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 - MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 - MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 - MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 - >; - }; - - /* pins for spdif */ - pinctrl_spdif: spdifgrp { - fsl,pins = < - MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 - MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 - >; - }; - - /* pins for audmux */ - pinctrl_audmux: audmuxgrp { - fsl,pins = < - MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 - MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 - MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 - MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 - MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 - /* master mode pin */ - MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 - >; - }; - }; -}; - -/* spi */ -&ecspi1 { - fsl,spi-num-chipselects = <2>; - cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1>; - status = "okay"; - - flash: m25p80@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,m25px16", "st,m25p"; - spi-max-frequency = <20000000>; - reg = <0>; - - partition@0 { - label = "uboot"; - reg = <0x0 0xc0000>; - }; - - partition@c0000 { - label = "uboot environment"; - reg = <0xc0000 0x40000>; - }; - - partition@100000 { - label = "reserved"; - reg = <0x100000 0x100000>; - }; - }; -}; - -/* eth0 */ -&fec { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet>; - phy-mode = "rgmii"; - status = "okay"; -}; - -/* nand */ -&gpmi { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_gpmi_nand>; - status = "okay"; -}; - -/* i2c1 */ -&i2c1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1>; - status = "okay"; - - eeprom@50 { - compatible = "at24,24c02"; - reg = <0x50>; - pagesize = <16>; - }; -}; - -/* i2c2 */ -&i2c2 { /* to be removed */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2>; - /* status = "okay"; */ -}; - -/* i2c3 */ -&i2c3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c3>; - status = "okay"; - - eeprom@50 { - compatible = "at24,24c02"; - reg = <0x50>; - pagesize = <16>; - }; - - codec: wm8731@1a { - compatible = "wlf,wm8731"; - reg = <0x1a>; - clocks = <&clks 173>, <&clks 158>, <&clks 201>, <&clks 200>; - clock-names = "pll4", "imx-ssi.1", "cko", "cko2"; - AVDD-supply = <&pu_dummy>; - HPVDD-supply = <&pu_dummy>; - DCVDD-supply = <&pu_dummy>; - DBVDD-supply = <&pu_dummy>; - }; -}; - -/* sata */ -&sata { - status = "okay"; -}; - -/* console */ -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4>; - status = "okay"; -}; - -/* usb otg */ -&usbotg { - vbus-supply = <®_usb_otg_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbotg>; - dr_mode = "otg"; - status = "okay"; -}; - -/* usb hub1 */ -&usbh1 { - vbus-supply = <®_usb_h1_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbh1>; - status = "okay"; -}; - -/* wifi/bt */ -&usdhc1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc1>; - non-removable; - vmmc-supply = <&awnh387_npoweron>; - vmmc_aux-supply = <&awnh387_wifi_nreset>; - status = "okay"; -}; - -/* mmc */ -&usdhc3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3>; - vmmc-supply = <®_3p3v>; - status = "okay"; -}; - -&ssi2 { - fsl,mode = "i2s-master"; - status = "okay"; -}; - -&mxcfb1 { - status = "okay"; -}; - -&mxcfb2 { - status = "okay"; -}; - -&hdmi_core { - ipu_id = <1>; - disp_id = <0>; - status = "okay"; -}; - -&hdmi_video { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; - fsl,hdcp; - status = "okay"; -}; - -&hdmi_audio { - status = "okay"; -}; - -&spdif { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_spdif>; - status = "okay"; -}; - -&audmux { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_audmux>; - status = "okay"; -}; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi new file mode 100644 index 00000000000000..0aa446114dff7a --- /dev/null +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -0,0 +1,531 @@ +/* + * Copyright 2014 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "imx6q.dtsi" + +/ { + memory { + reg = <0x10000000 0x80000000>; + }; + + leds { + compatible = "gpio-leds"; + heartbeat-led { + label = "Heartbeat"; + gpios = <&gpio2 31 0>; + linux,default-trigger = "heartbeat"; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + /* regulator for usb otg */ + reg_usb_otg_vbus: usb_otg_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 0>; + enable-active-high; + }; + + /* regulator for usb hub1 */ + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio7 8 0>; + enable-active-high; + }; + + /* regulator1 for wifi/bt */ + awnh387_npoweron: regulator-awnh387-npoweron { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-npoweron"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio7 12 0>; + enable-active-high; + }; + + /* regulator2 for wifi/bt */ + awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-wifi-nreset"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 16 0>; + startup-delay-us = <10000>; + }; + + reg_sata_phy_slp: sata_phy_slp { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_phy_slp"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 23 0>; + startup-delay-us = <100>; + enable-active-high; + }; + + reg_sata_nrstdly: sata_nrstdly { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nrstdly"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 6 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_phy_slp>; + }; + + reg_sata_pwren: sata_pwren { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_pwren"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 28 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_nrstdly>; + }; + + reg_sata_nstandby1: sata_nstandby1 { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nstandby1"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 20 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_pwren>; + }; + + reg_sata_nstandby2: sata_nstandby2 { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_nstandby2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio5 2 0>; + startup-delay-us = <100>; + enable-active-high; + vin-supply = <®_sata_nstandby1>; + }; + + reg_sata_ldo_en: sata_ldo_en { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_ldo_en"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 16 0>; + startup-delay-us = <100>; + enable-active-high; + regulator-boot-on; + vin-supply = <®_sata_nstandby2>; + }; + }; + + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + }; + + sound { + compatible = "fsl,imx6q-cm-fx6-wm8731", + "fsl,imx-audio-wm8731"; + model = "wm8731-audio"; + ssi-controller = <&ssi2>; + src-port = <2>; + ext-port = <4>; + audio-codec = <&codec>; + audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; + }; + + sound-hdmi { + compatible = "fsl,imx6q-audio-hdmi", + "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif", + "fsl,imx-sabreauto-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + lcd@0 { + compatible = "fsl,lcd"; + ipu_id = <0>; + disp_id = <0>; + default_ifmt = "RGB24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_1>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + /* SATA PWR */ + MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 + MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x80000000 + MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 + MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 + /* SATA CTRL */ + MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 + MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 + MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x80000000 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 + /* POWER_BUTTON */ + MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 + >; + }; + }; + + imx6q-cm-fx6 { + /* pins for eth0 */ + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + >; + }; + + /* pins for spi */ + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 + MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 + >; + }; + + /* pins for nand */ + pinctrl_gpmi_nand: gpminandgrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 + MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 + MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 + MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 + MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 + MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 + MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 + MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 + MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 + MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 + MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 + MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 + MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 + MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 + MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 + MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 + MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 + >; + }; + + /* pins for i2c2 */ + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + /* pins for i2c3 */ + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + /* pins for console */ + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + + /* pins for usb hub1 */ + pinctrl_usbh1: usbh1grp { + fsl,pins = < + MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 + >; + }; + + /* pins for usb otg */ + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 + >; + }; + + /* pins for wifi/bt */ + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 + >; + }; + + /* pins for pcie */ + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 + MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 + >; + }; + + /* pins for spdif */ + pinctrl_spdif: spdifgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 + MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 + >; + }; + + /* pins for audmux */ + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 + MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 + MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 + MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 + MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 + /* master mode pin */ + MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 + >; + }; + }; +}; + +/* spi */ +&ecspi1 { + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25px16", "st,m25p"; + spi-max-frequency = <20000000>; + reg = <0>; + + partition@0 { + label = "uboot"; + reg = <0x0 0xc0000>; + }; + + partition@c0000 { + label = "uboot environment"; + reg = <0xc0000 0x40000>; + }; + + partition@100000 { + label = "reserved"; + reg = <0x100000 0x100000>; + }; + }; +}; + +/* eth0 */ +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + status = "okay"; +}; + +/* nand */ +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + status = "okay"; +}; + +/* i2c3 */ +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + + codec: wm8731@1a { + compatible = "wlf,wm8731"; + reg = <0x1a>; + clocks = <&clks 173>, <&clks 158>, <&clks 201>, <&clks 200>; + clock-names = "pll4", "imx-ssi.1", "cko", "cko2"; + AVDD-supply = <&pu_dummy>; + HPVDD-supply = <&pu_dummy>; + DCVDD-supply = <&pu_dummy>; + DBVDD-supply = <&pu_dummy>; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; + reset-gpio = <&gpio1 26 0>; + power-on-gpio = <&gpio2 24 0>; + status = "okay"; +}; + +/* sata */ +&sata { + status = "okay"; +}; + +/* console */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + +/* usb otg */ +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + dr_mode = "otg"; + status = "okay"; +}; + +/* usb hub1 */ +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1>; + status = "okay"; +}; + +/* wifi/bt */ +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>; + non-removable; + vmmc-supply = <&awnh387_npoweron>; + vmmc_aux-supply = <&awnh387_wifi_nreset>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-master"; + status = "okay"; +}; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; + +&hdmi_core { + ipu_id = <1>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,hdcp; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif>; + status = "okay"; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sb-fx6.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6.dtsi new file mode 100644 index 00000000000000..acfc57219df607 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sb-fx6.dtsi @@ -0,0 +1,14 @@ +/* + * Copyright 2014 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "imx6q-sb-fx6x.dtsi" \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi new file mode 100644 index 00000000000000..5a488f89668691 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi @@ -0,0 +1,32 @@ +/* + * Copyright 2014 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "imx6q-sb-fx6x.dtsi" + +/ { + eth@pcie { + compatible = "intel,i211"; + local-mac-address = [FF FF FF FF FF FF]; + status = "okay"; + }; + + gpio-keys { + compatible = "gpio-keys"; + power { + label = "Power Button"; + gpios = <&gpio1 29 1>; + linux,code = <116>; /* KEY_POWER */ + gpio-key,wakeup; + }; + }; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi new file mode 100644 index 00000000000000..9f67b3eb12ea6a --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi @@ -0,0 +1,75 @@ +/* + * Copyright 2014 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "imx6q.dtsi" + +/ { + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + /* regulator for mmc */ + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + +}; + +&iomuxc { + imx6q-sb-fx6x { + /* pins for i2c1 */ + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D21__I2C1_SCL 0x4001b8b1 + MX6QDL_PAD_EIM_D28__I2C1_SDA 0x4001b8b1 + >; + }; + + /* pins for mmc */ + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + }; +}; + +/* i2c1 */ +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; +}; + +/* mmc */ +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + vmmc-supply = <®_3p3v>; + status = "disabled"; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 5d3c7da5ea257e..33e4f33917a8dd 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -11,13 +11,15 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q-cm-fx6.dts" +/dts-v1/; +#include "imx6q-sb-fx6x.dtsi" +#include "imx6q-cm-fx6.dtsi" / { model = "CompuLab CM-FX6 on SBC-FX6"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6q"; }; -&pcie { +&usdhc3 { status = "okay"; -}; +}; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index d48f33735c7366..369a8ca7b423cd 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -11,31 +11,18 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q-cm-fx6.dts" +/dts-v1/; +#include "imx6q-sb-fx6m.dtsi" +#include "imx6q-cm-fx6.dtsi" / { model = "CompuLab CM-FX6 on SBC-FX6m"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6q"; - eth@pcie { - compatible = "intel,i211"; - local-mac-address = [FF FF FF FF FF FF]; - status = "okay"; - }; - - gpio-keys { - compatible = "gpio-keys"; - power { - label = "Power Button"; - gpios = <&gpio1 29 1>; - linux,code = <116>; /* KEY_POWER */ - gpio-key,wakeup; - }; - }; }; &iomuxc { - imx6q-sb-fx6m { + imx6q-sbc-fx6m { /* pins for uart2 */ pinctrl_uart2: uart2grp { fsl,pins = < @@ -45,17 +32,10 @@ MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x1b0b1 >; }; - - /* pins for pcie */ - pinctrl_pcie: pciegrp { - fsl,pins = < - MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 - MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 - >; - }; }; }; + &i2c1 { rtc@56 { compatible = "emmicro,em3027"; @@ -63,11 +43,7 @@ }; }; -&pcie { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pcie>; - reset-gpio = <&gpio1 26 0>; - power-on-gpio = <&gpio2 24 0>; +&usdhc3 { status = "okay"; }; @@ -80,4 +56,4 @@ dma-names = "rx", "tx"; dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; status = "okay"; -}; +}; \ No newline at end of file From 2d1fd8fb94d57f5b897967e1448b1b2f1e8df6c2 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 20 Aug 2014 14:48:09 +0300 Subject: [PATCH 0664/1983] ARM: i.MX6: dts: pcie power-on-gpio to a fixed regulator Define pcie power-on-gpio as a fixed regulator. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 0aa446114dff7a..27f9567d2f134f 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -42,6 +42,16 @@ enable-active-high; }; + /* regulator1 for pcie power-on-gpio */ + pcie_power_on_gpio: regulator-pcie-power-on-gpio { + compatible = "regulator-fixed"; + regulator-name = "regulator-pcie-power-on-gpio"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 24 0>; + enable-active-high; + }; + /* regulator for usb hub1 */ reg_usb_h1_vbus: usb_h1_vbus { compatible = "regulator-fixed"; @@ -445,7 +455,7 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pcie>; reset-gpio = <&gpio1 26 0>; - power-on-gpio = <&gpio2 24 0>; + vdd-supply = <&pcie_power_on_gpio>; status = "okay"; }; From c5ed157b505d1e69cf4ae170661fa5ab5ba5bb49 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 20 Aug 2014 15:15:38 +0300 Subject: [PATCH 0665/1983] ARM: i.MX6: dts: add i2c1 status okay Restore the i2c1 bus staus that has been deleted while refactoring. It was the reason why em3027 stoped working. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 4 ++++ arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 1 + 2 files changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 33e4f33917a8dd..6f6ad3366956c4 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -20,6 +20,10 @@ compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6q"; }; +&i2c1 { + status = "okay"; +}; + &usdhc3 { status = "okay"; }; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 369a8ca7b423cd..85cecc75ca9365 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -37,6 +37,7 @@ &i2c1 { + status = "okay"; rtc@56 { compatible = "emmicro,em3027"; reg = <0x56>; From 8230d9d558756d6fc4e205bc89e4590c189f98e5 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 3 Sep 2014 10:48:29 +0300 Subject: [PATCH 0666/1983] ARM: i.MX6: dts: add local-mac-address field for fec. Add local-mac-address field for fec. The board U-Boot is in charge to fill this field with a correct value. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6qdl.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index 52e05e6cc9a73b..3797681d8a28b4 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -944,6 +944,7 @@ <&clks IMX6QDL_CLK_ENET>, <&clks IMX6QDL_CLK_ENET_REF>; clock-names = "ipg", "ahb", "ptp"; + local-mac-address = [FF FF FF FF FF FF]; status = "disabled"; }; From b107a04e278d3b067756d81a9d0e1606508e321a Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 28 Dec 2014 15:05:21 +0200 Subject: [PATCH 0667/1983] ARM: mxs: change usb phy test clock gating. This change proposes to invert test clock gating. This solution has fixed usb hub suspend resume loop issue. --- drivers/usb/phy/phy-mxs-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 97556b50fced44..fffa67d4725fbf 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -239,7 +239,7 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) if (disconnect) writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, - base + HW_USBPHY_DEBUG_CLR); + base + HW_USBPHY_DEBUG_SET); if (mxs_phy->port_id == 0) { reg = disconnect ? ANADIG_USB1_LOOPBACK_SET @@ -257,7 +257,7 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) if (!disconnect) writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, - base + HW_USBPHY_DEBUG_SET); + base + HW_USBPHY_DEBUG_CLR); /* Delay some time, and let Linestate be SE0 for controller */ if (disconnect) From bab3e646bcd549536d0110e11f96b1d8745331b4 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Tue, 30 Dec 2014 13:55:58 +0200 Subject: [PATCH 0668/1983] ARM: i.MX6: dts: fix the cm-fx6 operation points. Fix the cm-fx6 operation points. Remove settings for 1.2GHz. The current ldo settings do not allow 1.2GHz cpu frequency. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 27f9567d2f134f..12eed61e7de526 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -380,6 +380,23 @@ }; }; +&cpu0 { + operating-points = < + /* kHz uV */ + 996000 1250000 + 852000 1250000 + 792000 1150000 + 396000 975000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 996000 1250000 + 852000 1250000 + 792000 1175000 + 396000 1175000 + >; +}; + /* spi */ &ecspi1 { fsl,spi-num-chipselects = <2>; @@ -538,4 +555,4 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_audmux>; status = "okay"; -}; \ No newline at end of file +}; From cea1b4d35da5826f53c18343407a845268db9d22 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 5 Jan 2015 10:34:13 +0200 Subject: [PATCH 0669/1983] ARM: i.MX6: ASoC: fix build warnings and update includes Fix build warnings and update includes. sound/soc/fsl/imx-wm8731.c: In function 'imx_hifi_hw_params_slv_mode': sound/soc/fsl/imx-wm8731.c:357:3: warning: format '%u' expects type 'unsigned int', but argument 2 has type 'long int' sound/soc/fsl/imx-wm8731.c: In function 'imx_hifi_hw_params_mst_mode': sound/soc/fsl/imx-wm8731.c:414:3: warning: format '%u' expects type 'unsigned int', but argument 2 has type 'long int' Signed-off-by: Valentin Raevsky --- sound/soc/fsl/imx-wm8731.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sound/soc/fsl/imx-wm8731.c b/sound/soc/fsl/imx-wm8731.c index 9feac459bfc7a2..898d7d13cc14aa 100644 --- a/sound/soc/fsl/imx-wm8731.c +++ b/sound/soc/fsl/imx-wm8731.c @@ -14,17 +14,12 @@ */ #include +#include #include #include -#include -#include -#include #include #include -#include #include -#include -#include #include "../codecs/wm8731.h" #include "imx-audmux.h" @@ -344,7 +339,7 @@ static int imx_hifi_hw_params_slv_mode(struct snd_pcm_substream *substream, SND_SOC_CLOCK_IN); if (ret < 0) { - pr_err("Failed to set codec master clock to %u: %d \n", + pr_err("Failed to set codec master clock to %lu: %d \n", data->sysclk, ret); return ret; } @@ -401,7 +396,7 @@ static int imx_hifi_hw_params_mst_mode(struct snd_pcm_substream *substream, SND_SOC_CLOCK_IN); if (ret < 0) { - pr_err("Failed to set codec master clock to %u: %d \n", + pr_err("Failed to set codec master clock to %lu: %d \n", data->sysclk, ret); return ret; } @@ -555,7 +550,7 @@ static int imx_wm8731_probe(struct platform_device *pdev) } codec_dev = of_find_i2c_device_by_node(codec_np); - if (!codec_dev || !codec_dev->driver) { + if (!codec_dev) { dev_err(&pdev->dev, "failed to find codec platform device\n"); ret = -EINVAL; goto fail; From d8876d2a09a587c7394b216808d0a041b75a3a19 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 15 Jan 2015 13:52:37 +0200 Subject: [PATCH 0670/1983] ARM: i.MX6: dts: change issd gpio order Change the order in which GPIOs are toggled in SATA init sequence to accomodate both SanDisk and Phison SSDs. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 12eed61e7de526..dd91190cf1f8c7 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -82,6 +82,16 @@ startup-delay-us = <10000>; }; + reg_sata_ldo_en: sata_ldo_en { + compatible = "regulator-fixed"; + regulator-name = "cm_fx6_sata_ldo_en"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 16 0>; + startup-delay-us = <100>; + enable-active-high; + }; + reg_sata_phy_slp: sata_phy_slp { compatible = "regulator-fixed"; regulator-name = "cm_fx6_sata_phy_slp"; @@ -90,6 +100,7 @@ gpio = <&gpio3 23 0>; startup-delay-us = <100>; enable-active-high; + vin-supply = <®_sata_ldo_en>; }; reg_sata_nrstdly: sata_nrstdly { @@ -133,20 +144,10 @@ gpio = <&gpio5 2 0>; startup-delay-us = <100>; enable-active-high; + regulator-boot-on; vin-supply = <®_sata_nstandby1>; }; - reg_sata_ldo_en: sata_ldo_en { - compatible = "regulator-fixed"; - regulator-name = "cm_fx6_sata_ldo_en"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio2 16 0>; - startup-delay-us = <100>; - enable-active-high; - regulator-boot-on; - vin-supply = <®_sata_nstandby2>; - }; }; aliases { From 1039d04258397ce49f919715a33875e9775405ae Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Tue, 27 Jan 2015 15:54:24 +0200 Subject: [PATCH 0671/1983] ARM: i.MX6: dts: add missing WiFi/BT pinmuxes Set a correct mux mode for both: WLAN_BT_nPD and WLAN_BT_nRESET. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index dd91190cf1f8c7..3b1a046389c2ec 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -239,6 +239,9 @@ MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 /* POWER_BUTTON */ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 + /* WIFI_PWR_RST */ + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 + MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 >; }; }; From 5380d40f82ce814e718c0619ed9ceebc65d48145 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 12 Feb 2015 13:41:56 +0200 Subject: [PATCH 0672/1983] ARM: i.MX6: cm-fx6: enable i2cmux in defconfig Enable i2cmux in defconfig. Signed-off-by: Valentin Raevsky --- arch/arm/configs/cm_fx6_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/configs/cm_fx6_defconfig b/arch/arm/configs/cm_fx6_defconfig index 210062beabc933..7d753aee6cf23a 100644 --- a/arch/arm/configs/cm_fx6_defconfig +++ b/arch/arm/configs/cm_fx6_defconfig @@ -232,6 +232,9 @@ CONFIG_FSL_OTP=y CONFIG_MXS_VIIM=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y +CONFIG_I2C_MUX_GPIO=y +CONFIG_I2C_MUX_PCA954x=y # CONFIG_I2C_HELPER_AUTO is not set CONFIG_I2C_ALGOPCF=m CONFIG_I2C_ALGOPCA=m From c9ce67031e06b8116d7f462dc2ba1aee9c3649ba Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 23 Feb 2015 15:54:13 +0200 Subject: [PATCH 0673/1983] ARM: i.MX6: sb-fx6m: Fix uart1 rts/cts flow control According to the board schematics uart1 works in DCE mode only. Remove the DCEDTE mode flag in the uart1 properties. Set a correct value in the IOMUXC_UART2_UART_RTS_B_SELECT_INPUT register. This value lets connect RTS_B pad to ipp_uart_rts_b when UART is in DCE mode. Signed-off-by: Valentin Raevsky [grinberg@compulab.co.il: removed remnant include from previous patch version] Signed-off-by: Igor Grinberg --- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 85cecc75ca9365..3a5997b5afcbd7 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -19,6 +19,14 @@ model = "CompuLab CM-FX6 on SBC-FX6m"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6q"; + iomux_uart2: pinmux@20E0924 { + compatible = "pinctrl-single"; + reg = <0x20E0000 0x924>; /* Single register */ + #address-cells = <1>; + #size-cells = <0>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x4>; + }; }; &iomuxc { @@ -52,9 +60,6 @@ &uart2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart2>; - /* fsl,dte-mode; */ fsl,uart-has-rtscts; - dma-names = "rx", "tx"; - dmas = <&sdma 27 4 0>, <&sdma 28 4 0>; status = "okay"; -}; \ No newline at end of file +}; From db537e0069933bce04833bb5d1823f36547d754b Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 18 Feb 2015 18:25:23 +0200 Subject: [PATCH 0674/1983] ARM: i.MX6: dts: add i2cmux support for SBC-FX6 boards Add i2cmux support for SBC-FX6 boards. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sb-fx6.dtsi | 50 +++++++++++++++++++++++++++- arch/arm/boot/dts/imx6q-sb-fx6m.dtsi | 36 ++++++++++++++++++++ arch/arm/boot/dts/imx6q-sb-fx6x.dtsi | 6 +--- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 2 +- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 5 --- 5 files changed, 87 insertions(+), 12 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sb-fx6.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6.dtsi index acfc57219df607..fc4f347aee2920 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6.dtsi @@ -11,4 +11,52 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q-sb-fx6x.dtsi" \ No newline at end of file +#include "imx6q-sb-fx6x.dtsi" + +/ { + i2cmux { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + mux-gpios = <&gpio1 2 0>; + i2c-parent = <&i2c1>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + pca9555@26 { + compatible = "nxp,pca9555"; + gpio-controller; + #gpio-cells = <2>; + reg = <0x26>; + }; + + hx8526@4a { + compatible = "himax,himax_ts"; + reg = <0x4a>; + gpio_intr = <&gpio1 4 0>; + }; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + dvi: edid@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; + }; + + }; +}; diff --git a/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi index 5a488f89668691..a6cc8dd2a56a4d 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi @@ -29,4 +29,40 @@ gpio-key,wakeup; }; }; + + i2cmux { + compatible = "i2c-mux-gpio"; + #address-cells = <1>; + #size-cells = <0>; + mux-gpios = <&gpio1 2 0>; + i2c-parent = <&i2c1>; + + i2c@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + + rtc@56 { + compatible = "emmicro,em3027"; + reg = <0x56>; + }; + }; + + i2c@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + dvi: edid@50 { + compatible = "fsl,imx6-hdmi-i2c"; + reg = <0x50>; + }; + }; + }; }; \ No newline at end of file diff --git a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi index 9f67b3eb12ea6a..ae70c879d0b896 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi @@ -59,11 +59,7 @@ &i2c1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1>; - eeprom@50 { - compatible = "at24,24c02"; - reg = <0x50>; - pagesize = <16>; - }; + status = "disabled"; }; /* mmc */ diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 6f6ad3366956c4..4bba196c630f14 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -12,7 +12,7 @@ */ /dts-v1/; -#include "imx6q-sb-fx6x.dtsi" +#include "imx6q-sb-fx6.dtsi" #include "imx6q-cm-fx6.dtsi" / { diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 3a5997b5afcbd7..47d76b48bba0ff 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -43,13 +43,8 @@ }; }; - &i2c1 { status = "okay"; - rtc@56 { - compatible = "emmicro,em3027"; - reg = <0x56>; - }; }; &usdhc3 { From 8791dc7f1970084bd31883563dabc666a1581855 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 12 Feb 2015 13:49:31 +0200 Subject: [PATCH 0675/1983] ARM: i.MX6: dts: add dvi edid GPIOs Add dvi edid GPIOs. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sb-fx6x.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi index ae70c879d0b896..41237c750f5254 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi @@ -32,6 +32,9 @@ }; &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>, <&pinctrl_dvi0>; + imx6q-sb-fx6x { /* pins for i2c1 */ pinctrl_i2c1: i2c1grp { @@ -52,6 +55,16 @@ MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 >; }; + + /* pins for dvi/ts */ + pinctrl_dvi0: dvi0grp { + fsl,pins = < + /* DVI_DDC_SEL */ + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 + /* SB-FX6 Himax TS PENDOWN or SB-FX6m DVI HPD */ + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 + >; + }; }; }; From 75f199026ee3ef8364779660f4d11ec39e5dbdc9 Mon Sep 17 00:00:00 2001 From: Dmitry Lifshitz Date: Thu, 12 Feb 2015 16:59:53 +0200 Subject: [PATCH 0676/1983] video: mxc: IPUv3 fb: restore sync bits Freescale framebuffer driver uses some driver-specific proprietary bits in the sync field (like pixel clock polarity). Xorg driver discards unknown sync bits in the fb_var_screeninfo structure. As the results of dropping the proprietary sync bits some displays shows various artifacts. Fix the bug by hacking mxcfb_set_par() callback. Before applying new var parameters, try to find a match in the mode list, skipping proprietary sync bits (FB_MXC_SYNC_MASK). If the entry is found, copy its FB_MXC_SYNC_MASK bits. Signed-off-by: Dmitry Lifshitz Signed-off-by: Valentin Raevsky --- drivers/video/mxc/mxc_ipuv3_fb.c | 38 ++++++++++++++++++++++++++++++++ include/linux/mxcfb.h | 11 +++++++++ 2 files changed, 49 insertions(+) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 5da874c28870a3..89b666783caa9d 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -428,6 +428,28 @@ static bool mxcfb_need_to_set_par(struct fb_info *fbi) sizeof(struct fb_var_screeninfo)); } +static struct fb_videomode *mxc_match_mode(const struct fb_var_screeninfo *var, + struct list_head *head) +{ + struct list_head *pos; + struct fb_modelist *modelist; + struct fb_videomode *m, mode; + + fb_var_to_videomode(&mode, var); + list_for_each(pos, head) { + modelist = list_entry(pos, struct fb_modelist, list); + m = &modelist->mode; + + mode.sync &= ~FB_MXC_SYNC_MASK; + mode.sync |= m->sync & FB_MXC_SYNC_MASK; + + if (fb_mode_is_equal(m, &mode)) + return m; + } + + return NULL; +} + /* * Set framebuffer parameters and change the operating mode. * @@ -583,6 +605,7 @@ static int mxcfb_set_par(struct fb_info *fbi) if (!mxc_fbi->overlay) { uint32_t out_pixel_fmt; + struct fb_videomode *sync_mode; memset(&sig_cfg, 0, sizeof(sig_cfg)); if (fbi->var.vmode & FB_VMODE_INTERLACED) @@ -596,6 +619,21 @@ static int mxcfb_set_par(struct fb_info *fbi) sig_cfg.Hsync_pol = true; if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) sig_cfg.Vsync_pol = true; + + /* + * Try to find matching all parameters, except + * FB_MXC_SYNC_MASK bits in the .sync field. + */ + sync_mode = mxc_match_mode(&fbi->var, &fbi->modelist); + /* + * If entry exists in the mode list and FB_MXC_SYNC_MASK + * bits are empty in the fbi->var.sync (most probably cleared + * by the user space application) then copy it from the found + * mode list entry. + */ + if (sync_mode && !(fbi->var.sync & FB_MXC_SYNC_MASK)) + fbi->var.sync = sync_mode->sync; + if (!(fbi->var.sync & FB_SYNC_CLK_LAT_FALL)) sig_cfg.clk_pol = true; if (fbi->var.sync & FB_SYNC_DATA_INVERT) diff --git a/include/linux/mxcfb.h b/include/linux/mxcfb.h index 67db5ee3fd119a..e63aa2c382523c 100644 --- a/include/linux/mxcfb.h +++ b/include/linux/mxcfb.h @@ -23,6 +23,17 @@ #include +#define FB_SYNC_OE_LOW_ACT 0x80000000 +#define FB_SYNC_CLK_LAT_FALL 0x40000000 +#define FB_SYNC_DATA_INVERT 0x20000000 +#define FB_SYNC_CLK_IDLE_EN 0x10000000 +#define FB_SYNC_SHARP_MODE 0x08000000 +#define FB_SYNC_SWAP_RGB 0x04000000 + +#define FB_MXC_SYNC_MASK (FB_SYNC_OE_LOW_ACT | FB_SYNC_CLK_LAT_FALL | \ + FB_SYNC_DATA_INVERT | FB_SYNC_CLK_IDLE_EN | \ + FB_SYNC_SHARP_MODE | FB_SYNC_SWAP_RGB) + extern struct fb_videomode mxcfb_modedb[]; extern int mxcfb_modedb_sz; From 41ffcd0c207a2337d8e42242607511da1dda554b Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 18 Feb 2015 18:48:26 +0200 Subject: [PATCH 0677/1983] ARM: i.MX6: dts: add backlight support for SBC-FX6 boards Add backlight support for SBC-FX6 boards. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 4bba196c630f14..5febb6990322d6 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -18,6 +18,14 @@ / { model = "CompuLab CM-FX6 on SBC-FX6"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6q"; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm3 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + }; + }; &i2c1 { @@ -26,4 +34,10 @@ &usdhc3 { status = "okay"; -}; \ No newline at end of file +}; + +&pwm3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm3_1>; + status = "okay"; +}; From 87f20a4856898550e2bf54ff10b7bfe4f70a483c Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 18 Feb 2015 18:53:01 +0200 Subject: [PATCH 0678/1983] ARM: i.MX6: dts: rearrangement of the frame buffers definitions Add a correct frame buffers' definitions with regard to the board configuration. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 8 -------- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 8 ++++++++ arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 8 ++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 3b1a046389c2ec..7d39f50a055767 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -524,14 +524,6 @@ status = "okay"; }; -&mxcfb1 { - status = "okay"; -}; - -&mxcfb2 { - status = "okay"; -}; - &hdmi_core { ipu_id = <1>; disp_id = <0>; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 5febb6990322d6..8e7432d05ef23f 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -41,3 +41,11 @@ pinctrl-0 = <&pinctrl_pwm3_1>; status = "okay"; }; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 47d76b48bba0ff..46956af4cdd819 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -58,3 +58,11 @@ fsl,uart-has-rtscts; status = "okay"; }; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; From 8e0a8828781a57db84dc6efc34e2a1af36ce1bbe Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 11 Feb 2015 18:15:42 +0200 Subject: [PATCH 0679/1983] ARM: i.MX6: iomux: raise DSE for display signals While drive strength of display signals is configured for 120 Ohm, some displays exhibit artifacts. Typical drive strength should be around 50 Ohm. To fix the the visual artifacts, we raise the drive strength to 48 Ohm. Signed-off-by: Igor Grinberg Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 36 ++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 7d39f50a055767..9a956acece9109 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -210,7 +210,7 @@ disp_id = <0>; default_ifmt = "RGB24"; pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ipu1_1>; + pinctrl-0 = <&pinctrl_ipu1_lcd>; status = "okay"; }; @@ -268,6 +268,40 @@ >; }; + pinctrl_ipu1_lcd: ipu1grp-lcd { + fsl,pins = < + MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x38 + MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x38 + MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x38 + MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x38 + MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000028 + MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x38 + MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x38 + MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x38 + MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x38 + MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x38 + MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x38 + MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x38 + MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x38 + MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x38 + MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x38 + MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x38 + MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x38 + MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x38 + MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x38 + MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x38 + MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x38 + MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x38 + MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x38 + MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x38 + MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x38 + MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x38 + MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x38 + MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x38 + MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x38 + >; + }; + /* pins for spi */ pinctrl_ecspi1: ecspi1grp { fsl,pins = < From 2a2769e69f2fda886aff6e4b85fd932863606f5d Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 12 Feb 2015 16:54:04 +0200 Subject: [PATCH 0680/1983] ARM: i.MX6: cm-fx6: add video mode for KD050C-WVGA Add video mode for KD050C-WVGA, update the lcdif_modedb with the correct parameters for KD050C-WVGA. Change mode string for second frame buffer. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 1 + drivers/video/mxc/mxc_lcdif.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 8e7432d05ef23f..63f91a69a57a3d 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -47,5 +47,6 @@ }; &mxcfb2 { + mode_str ="KD050C-WVGA"; status = "okay"; }; diff --git a/drivers/video/mxc/mxc_lcdif.c b/drivers/video/mxc/mxc_lcdif.c index d635eddbef7060..ae6ac42ebc4297 100644 --- a/drivers/video/mxc/mxc_lcdif.c +++ b/drivers/video/mxc/mxc_lcdif.c @@ -48,6 +48,12 @@ static struct fb_videomode lcdif_modedb[] = { FB_SYNC_CLK_LAT_FALL, FB_VMODE_NONINTERLACED, 0,}, + { + /* 800x480 @ 60 Hz , pixel clk @ 32MHz */ + "KD050C-WVGA", 60, 800, 480, 30000, 40, 40, 13, 29, 48, 3, + FB_SYNC_CLK_LAT_FALL, + FB_VMODE_NONINTERLACED, + 0,}, }; static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb); From cbe3e4d6cb5cc3777779bfe5707783cf7996e07b Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 19 Feb 2015 15:45:07 +0200 Subject: [PATCH 0681/1983] ARM: i.MX6: cm-fx6: refactor the cm-fx6 iomux Move Marvell Power On and Reset GPIOs into a separate group. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 9a956acece9109..412e03bfc9155a 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -239,9 +239,6 @@ MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 /* POWER_BUTTON */ MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 - /* WIFI_PWR_RST */ - MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 - MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 >; }; }; @@ -387,6 +384,15 @@ >; }; + /* pins for wifi/bt */ + pinctrl_mrvl1: mrvl1grp { + fsl,pins = < + /* WIFI_PWR_RST */ + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 + MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 + >; + }; + /* pins for pcie */ pinctrl_pcie: pciegrp { fsl,pins = < @@ -546,7 +552,7 @@ /* wifi/bt */ &usdhc1 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-0 = <&pinctrl_usdhc1>, <&pinctrl_mrvl1>; non-removable; vmmc-supply = <&awnh387_npoweron>; vmmc_aux-supply = <&awnh387_wifi_nreset>; From 5c74ba77ed5f998f2d684373b4b7f01371e3f4fd Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 15 Jan 2015 12:52:56 +0200 Subject: [PATCH 0682/1983] ARM: i.MX6: dts: gpmi: separate kernel and rootfs Make separate partitions for kernel and root filesystem on the NAND flash. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 412e03bfc9155a..5ca8993d2515d9 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -486,6 +486,16 @@ pinctrl-names = "default"; pinctrl-0 = <&pinctrl_gpmi_nand>; status = "okay"; + + partition@0 { + label = "linux"; + reg = <0x0 0x800000>; + }; + + partition@800000 { + label = "rootfs"; + reg = < 0x800000 0x0>; + }; }; /* i2c3 */ From c549751d89731fc31b5230906ebb3a5624f36bae Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 26 Feb 2015 16:45:42 +0200 Subject: [PATCH 0683/1983] ARM: dts: cm-fx6: enable can bus Enable can bus. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 63f91a69a57a3d..2432f343fd9fa0 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -50,3 +50,9 @@ mode_str ="KD050C-WVGA"; status = "okay"; }; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1_1>; + status = "okay"; +}; From 614b41ba01667affd211be767171b31ec229e272 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 25 Feb 2015 20:18:34 +0200 Subject: [PATCH 0684/1983] ARM: dts: cm-fx6: add tsc2046 touchscreen support Add tsc2046 touchscreen support. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 45 +++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 5ca8993d2515d9..704ef4b04a521f 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -148,6 +148,13 @@ vin-supply = <®_sata_nstandby1>; }; + tsc2046reg: tsc2046-reg { + compatible = "regulator-fixed"; + regulator-name = "tsc2046-reg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; aliases { @@ -393,6 +400,14 @@ >; }; + /* pins for tsc2046 pendown */ + pinctrl_tsc2046: tsc2046grp { + fsl,pins = < + /* tsc2046 PENDOWN */ + MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 + >; + }; + /* pins for pcie */ pinctrl_pcie: pciegrp { fsl,pins = < @@ -471,6 +486,36 @@ reg = <0x100000 0x100000>; }; }; + + /* touch controller */ + touch: tsc2046@1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tsc2046>; + + compatible = "ti,tsc2046"; + vcc-supply = <&tsc2046reg>; + + reg = <1>; /* CS1 */ + spi-max-frequency = <1500000>; + + interrupt-parent = <&gpio2>; + interrupts = <15 0>; + pendown-gpio = <&gpio2 15 0>; + + ti,x-min = /bits/ 16 <0x0>; + ti,x-max = /bits/ 16 <0x0fff>; + ti,y-min = /bits/ 16 <0x0>; + ti,y-max = /bits/ 16 <0x0fff>; + + ti,x-plate-ohms = /bits/ 16 <180>; + ti,pressure-max = /bits/ 16 <255>; + + ti,debounce-max = /bits/ 16 <30>; + ti,debounce-tol = /bits/ 16 <10>; + ti,debounce-rep = /bits/ 16 <1>; + + linux,wakeup; + }; }; /* eth0 */ From 1c11238503f02e204f2bda7e833ded60f18793ce Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 5 Mar 2015 09:59:32 +0200 Subject: [PATCH 0685/1983] ARM: i.MX6: sb-fx6x: refactoring of the usdhc3 definition Add uhs pinctrl state for usdhc3. This is needed for supporting ultra high speed cards. Add cd/wp definitions. Add a missing property no-1-8-v. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sb-fx6x.dtsi | 32 ++++++++++++++++++++++++++-- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 1 + 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi index 41237c750f5254..372a3c1cd5c9db 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi @@ -53,6 +53,30 @@ MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x80000000 + MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 + >; + }; + + pinctrl_usdhc3_100mhz: usdhc3grp-100mhz { /* 100Mhz */ + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170B9 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100B9 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170B9 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170B9 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170B9 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170B9 + >; + }; + + pinctrl_usdhc3_200mhz: usdhc3grp-200mhz { /* 200Mhz */ + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x170F9 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x100F9 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x170F9 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x170F9 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x170F9 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x170F9 >; }; @@ -77,8 +101,12 @@ /* mmc */ &usdhc3 { - pinctrl-names = "default"; + pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc3>; + pinctrl-1 = <&pinctrl_usdhc3_100mhz>; + pinctrl-2 = <&pinctrl_usdhc3_200mhz>; + cd-gpios = <&gpio7 1 0>; + no-1-8-v; vmmc-supply = <®_3p3v>; status = "disabled"; -}; \ No newline at end of file +}; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 2432f343fd9fa0..cd5c01159911ab 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -33,6 +33,7 @@ }; &usdhc3 { + wp-gpios = <&gpio7 0 0>; status = "okay"; }; From f0a04e6e3092da03b4a685f2cf6942092f1872ad Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 18 Mar 2015 14:30:49 +0200 Subject: [PATCH 0686/1983] ARM: i.MX6: cm-fx6: fix up incorrect compatibilities Fix up incorrect compatibilities. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 704ef4b04a521f..8175ceebf93629 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -163,8 +163,7 @@ }; sound { - compatible = "fsl,imx6q-cm-fx6-wm8731", - "fsl,imx-audio-wm8731"; + compatible = "fsl,imx-audio-wm8731"; model = "wm8731-audio"; ssi-controller = <&ssi2>; src-port = <2>; @@ -174,15 +173,13 @@ }; sound-hdmi { - compatible = "fsl,imx6q-audio-hdmi", - "fsl,imx-audio-hdmi"; + compatible = "fsl,imx-audio-hdmi"; model = "imx-audio-hdmi"; hdmi-controller = <&hdmi_audio>; }; sound-spdif { - compatible = "fsl,imx-audio-spdif", - "fsl,imx-sabreauto-spdif"; + compatible = "fsl,imx-audio-spdif"; model = "imx-spdif"; spdif-controller = <&spdif>; spdif-out; From 28d4fae54bafeea798285c264b93f7e0a403bfbf Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Sun, 22 Mar 2015 11:15:08 +0200 Subject: [PATCH 0687/1983] ARM: i.MX6: dts: fix include file order Fix include file order. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-cm-fx6.dts | 3 ++- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 2 -- arch/arm/boot/dts/imx6q-sb-fx6.dtsi | 2 -- arch/arm/boot/dts/imx6q-sb-fx6m.dtsi | 4 +--- arch/arm/boot/dts/imx6q-sb-fx6x.dtsi | 2 -- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 4 +++- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 4 +++- 7 files changed, 9 insertions(+), 12 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dts b/arch/arm/boot/dts/imx6q-cm-fx6.dts index a0e423b5f8e9ce..14c2d6a3ec1430 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dts +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dts @@ -12,9 +12,10 @@ */ /dts-v1/; +#include "imx6q.dtsi" #include "imx6q-cm-fx6.dtsi" / { model = "CompuLab CM-FX6"; compatible = "compulab,cm-fx6", "fsl,imx6q"; -}; \ No newline at end of file +}; diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index 8175ceebf93629..f53d94ece3c2c9 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -11,8 +11,6 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q.dtsi" - / { memory { reg = <0x10000000 0x80000000>; diff --git a/arch/arm/boot/dts/imx6q-sb-fx6.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6.dtsi index fc4f347aee2920..4d030f98c3ba50 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6.dtsi @@ -11,8 +11,6 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q-sb-fx6x.dtsi" - / { i2cmux { compatible = "i2c-mux-gpio"; diff --git a/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi index a6cc8dd2a56a4d..5e6c859fced5f1 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi @@ -11,8 +11,6 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q-sb-fx6x.dtsi" - / { eth@pcie { compatible = "intel,i211"; @@ -65,4 +63,4 @@ }; }; }; -}; \ No newline at end of file +}; diff --git a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi index 372a3c1cd5c9db..01f73aeabb4dd2 100644 --- a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi +++ b/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi @@ -11,8 +11,6 @@ * http://www.gnu.org/copyleft/gpl.html */ -#include "imx6q.dtsi" - / { regulators { compatible = "simple-bus"; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index cd5c01159911ab..84a6d23bd41b25 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -12,8 +12,10 @@ */ /dts-v1/; -#include "imx6q-sb-fx6.dtsi" +#include "imx6q.dtsi" #include "imx6q-cm-fx6.dtsi" +#include "imx6q-sb-fx6x.dtsi" +#include "imx6q-sb-fx6.dtsi" / { model = "CompuLab CM-FX6 on SBC-FX6"; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 46956af4cdd819..10ade0423c5ad1 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -12,8 +12,10 @@ */ /dts-v1/; -#include "imx6q-sb-fx6m.dtsi" +#include "imx6q.dtsi" #include "imx6q-cm-fx6.dtsi" +#include "imx6q-sb-fx6x.dtsi" +#include "imx6q-sb-fx6m.dtsi" / { model = "CompuLab CM-FX6 on SBC-FX6m"; From b6d710381d3967314b18630ec6645bc2be038376 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 23 Mar 2015 09:50:04 +0200 Subject: [PATCH 0688/1983] ARM: i.MX6: dts: rename the sb-fx6 board files In preparation for DL/S support addition, we rename the files to better describe the content and reuse the same DT code. Rename the sb-fx6 board files' names Signed-off-by: Valentin Raevsky [grinberg@compulab.co.il: added a bit more descriptive commit message] Signed-off-by: Igor Grinberg --- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 4 ++-- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 4 ++-- arch/arm/boot/dts/{imx6q-sb-fx6.dtsi => imx6qdl-sb-fx6.dtsi} | 0 .../arm/boot/dts/{imx6q-sb-fx6m.dtsi => imx6qdl-sb-fx6m.dtsi} | 0 .../arm/boot/dts/{imx6q-sb-fx6x.dtsi => imx6qdl-sb-fx6x.dtsi} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename arch/arm/boot/dts/{imx6q-sb-fx6.dtsi => imx6qdl-sb-fx6.dtsi} (100%) rename arch/arm/boot/dts/{imx6q-sb-fx6m.dtsi => imx6qdl-sb-fx6m.dtsi} (100%) rename arch/arm/boot/dts/{imx6q-sb-fx6x.dtsi => imx6qdl-sb-fx6x.dtsi} (100%) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 84a6d23bd41b25..9d31d1506d8d08 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -14,8 +14,8 @@ /dts-v1/; #include "imx6q.dtsi" #include "imx6q-cm-fx6.dtsi" -#include "imx6q-sb-fx6x.dtsi" -#include "imx6q-sb-fx6.dtsi" +#include "imx6qdl-sb-fx6x.dtsi" +#include "imx6qdl-sb-fx6.dtsi" / { model = "CompuLab CM-FX6 on SBC-FX6"; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 10ade0423c5ad1..9ae1e0f02c9603 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -14,8 +14,8 @@ /dts-v1/; #include "imx6q.dtsi" #include "imx6q-cm-fx6.dtsi" -#include "imx6q-sb-fx6x.dtsi" -#include "imx6q-sb-fx6m.dtsi" +#include "imx6qdl-sb-fx6x.dtsi" +#include "imx6qdl-sb-fx6m.dtsi" / { model = "CompuLab CM-FX6 on SBC-FX6m"; diff --git a/arch/arm/boot/dts/imx6q-sb-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi similarity index 100% rename from arch/arm/boot/dts/imx6q-sb-fx6.dtsi rename to arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi diff --git a/arch/arm/boot/dts/imx6q-sb-fx6m.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi similarity index 100% rename from arch/arm/boot/dts/imx6q-sb-fx6m.dtsi rename to arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi diff --git a/arch/arm/boot/dts/imx6q-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi similarity index 100% rename from arch/arm/boot/dts/imx6q-sb-fx6x.dtsi rename to arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi From e85ec8b123ffa10fb189da40d7791775bb6d201b Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 23 Mar 2015 09:59:54 +0200 Subject: [PATCH 0689/1983] ARM: i.MX6: dts: refactor the sbc-fx6 target files In preparation for DL/S support addition, we move the common code to dtsi files for better reuse of the same DT code. Refactor the sbc-fx6 target files. Signed-off-by: Valentin Raevsky [grinberg@compulab.co.il: added a bit more descriptive commit message] Signed-off-by: Igor Grinberg --- arch/arm/boot/dts/imx6q-sbc-fx6.dts | 42 ++--------------------- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 47 -------------------------- arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi | 37 ++++++++++++++++++++ arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi | 47 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 87 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6.dts b/arch/arm/boot/dts/imx6q-sbc-fx6.dts index 9d31d1506d8d08..1234fb35396ec4 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6.dts @@ -18,44 +18,6 @@ #include "imx6qdl-sb-fx6.dtsi" / { - model = "CompuLab CM-FX6 on SBC-FX6"; - compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6q"; - - backlight { - compatible = "pwm-backlight"; - pwms = <&pwm3 0 5000000>; - brightness-levels = <0 4 8 16 32 64 128 255>; - default-brightness-level = <7>; - }; - -}; - -&i2c1 { - status = "okay"; -}; - -&usdhc3 { - wp-gpios = <&gpio7 0 0>; - status = "okay"; -}; - -&pwm3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pwm3_1>; - status = "okay"; -}; - -&mxcfb1 { - status = "okay"; -}; - -&mxcfb2 { - mode_str ="KD050C-WVGA"; - status = "okay"; -}; - -&flexcan1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_flexcan1_1>; - status = "okay"; + model = "CompuLab CM-FX6 on SBC-FX6"; + compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6q"; }; diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 9ae1e0f02c9603..7b23eef6159ace 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -20,51 +20,4 @@ / { model = "CompuLab CM-FX6 on SBC-FX6m"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6q"; - - iomux_uart2: pinmux@20E0924 { - compatible = "pinctrl-single"; - reg = <0x20E0000 0x924>; /* Single register */ - #address-cells = <1>; - #size-cells = <0>; - pinctrl-single,register-width = <32>; - pinctrl-single,function-mask = <0x4>; - }; -}; - -&iomuxc { - imx6q-sbc-fx6m { - /* pins for uart2 */ - pinctrl_uart2: uart2grp { - fsl,pins = < - MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1 - MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x1b0b1 - MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x1b0b1 - MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x1b0b1 - >; - }; - }; -}; - -&i2c1 { - status = "okay"; -}; - -&usdhc3 { - status = "okay"; -}; - -/* rear serial console */ -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2>; - fsl,uart-has-rtscts; - status = "okay"; -}; - -&mxcfb1 { - status = "okay"; -}; - -&mxcfb2 { - status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi index 4d030f98c3ba50..129e88ed7e7659 100644 --- a/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi @@ -12,6 +12,13 @@ */ / { + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm3 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + }; + i2cmux { compatible = "i2c-mux-gpio"; #address-cells = <1>; @@ -58,3 +65,33 @@ }; }; + +&i2c1 { + status = "okay"; +}; + +&usdhc3 { + wp-gpios = <&gpio7 0 0>; + status = "okay"; +}; + +&pwm3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm3_1>; + status = "okay"; +}; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + mode_str ="KD050C-WVGA"; + status = "okay"; +}; + +&flexcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flexcan1_1>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi index 5e6c859fced5f1..5394364685536a 100644 --- a/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi @@ -12,6 +12,15 @@ */ / { + iomux_uart2: pinmux@20E0924 { + compatible = "pinctrl-single"; + reg = <0x20E0000 0x924>; /* Single register */ + #address-cells = <1>; + #size-cells = <0>; + pinctrl-single,register-width = <32>; + pinctrl-single,function-mask = <0x4>; + }; + eth@pcie { compatible = "intel,i211"; local-mac-address = [FF FF FF FF FF FF]; @@ -64,3 +73,41 @@ }; }; }; + +&iomuxc { + imx6q-sbc-fx6m { + /* pins for uart2 */ + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1 + MX6QDL_PAD_GPIO_8__UART2_RX_DATA 0x1b0b1 + MX6QDL_PAD_SD4_DAT5__UART2_RTS_B 0x1b0b1 + MX6QDL_PAD_SD4_DAT6__UART2_CTS_B 0x1b0b1 + >; + }; + }; +}; + +&i2c1 { + status = "okay"; +}; + +&usdhc3 { + status = "okay"; +}; + +/* rear serial console */ +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + fsl,uart-has-rtscts; + status = "okay"; +}; + +&mxcfb1 { + status = "okay"; +}; + +&mxcfb2 { + status = "okay"; +}; From bd7c0868d29ec6aee342df7218d5b0e110c3ffa1 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 23 Mar 2015 10:12:35 +0200 Subject: [PATCH 0690/1983] ARM: i.MX6: cm-fx6: separate DL and Quad stuff Break down the cm-fx6 file into two files in order to separate DL and Quad supported features. Signed-off-by: Valentin Raevsky [grinberg@compulab.co.il: fix available memory size and rename "dl" to "qdl"] Signed-off-by: Igor Grinberg --- arch/arm/boot/dts/imx6q-cm-fx6.dtsi | 553 +------------------------ arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 573 ++++++++++++++++++++++++++ 2 files changed, 575 insertions(+), 551 deletions(-) create mode 100644 arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi diff --git a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi index f53d94ece3c2c9..3a10e5ea49ec30 100644 --- a/arch/arm/boot/dts/imx6q-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6q-cm-fx6.dtsi @@ -11,75 +11,14 @@ * http://www.gnu.org/copyleft/gpl.html */ -/ { - memory { - reg = <0x10000000 0x80000000>; - }; - - leds { - compatible = "gpio-leds"; - heartbeat-led { - label = "Heartbeat"; - gpios = <&gpio2 31 0>; - linux,default-trigger = "heartbeat"; - }; - }; +#include "imx6qdl-cm-fx6.dtsi" +/ { regulators { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <0>; - /* regulator for usb otg */ - reg_usb_otg_vbus: usb_otg_vbus { - compatible = "regulator-fixed"; - regulator-name = "usb_otg_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&gpio3 22 0>; - enable-active-high; - }; - - /* regulator1 for pcie power-on-gpio */ - pcie_power_on_gpio: regulator-pcie-power-on-gpio { - compatible = "regulator-fixed"; - regulator-name = "regulator-pcie-power-on-gpio"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio2 24 0>; - enable-active-high; - }; - - /* regulator for usb hub1 */ - reg_usb_h1_vbus: usb_h1_vbus { - compatible = "regulator-fixed"; - regulator-name = "usb_h1_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - gpio = <&gpio7 8 0>; - enable-active-high; - }; - - /* regulator1 for wifi/bt */ - awnh387_npoweron: regulator-awnh387-npoweron { - compatible = "regulator-fixed"; - regulator-name = "regulator-awnh387-npoweron"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio7 12 0>; - enable-active-high; - }; - - /* regulator2 for wifi/bt */ - awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { - compatible = "regulator-fixed"; - regulator-name = "regulator-awnh387-wifi-nreset"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - gpio = <&gpio6 16 0>; - startup-delay-us = <10000>; - }; - reg_sata_ldo_en: sata_ldo_en { compatible = "regulator-fixed"; regulator-name = "cm_fx6_sata_ldo_en"; @@ -146,499 +85,11 @@ vin-supply = <®_sata_nstandby1>; }; - tsc2046reg: tsc2046-reg { - compatible = "regulator-fixed"; - regulator-name = "tsc2046-reg"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - }; - - }; - - aliases { - mxcfb0 = &mxcfb1; - mxcfb1 = &mxcfb2; - }; - - sound { - compatible = "fsl,imx-audio-wm8731"; - model = "wm8731-audio"; - ssi-controller = <&ssi2>; - src-port = <2>; - ext-port = <4>; - audio-codec = <&codec>; - audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; - }; - - sound-hdmi { - compatible = "fsl,imx-audio-hdmi"; - model = "imx-audio-hdmi"; - hdmi-controller = <&hdmi_audio>; - }; - - sound-spdif { - compatible = "fsl,imx-audio-spdif"; - model = "imx-spdif"; - spdif-controller = <&spdif>; - spdif-out; - spdif-in; - }; - - mxcfb1: fb@0 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "hdmi"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "disabled"; - }; - - mxcfb2: fb@1 { - compatible = "fsl,mxc_sdc_fb"; - disp_dev = "lcd"; - interface_pix_fmt = "RGB24"; - mode_str ="1920x1080M@60"; - default_bpp = <32>; - int_clk = <0>; - late_init = <0>; - status = "disabled"; - }; - - lcd@0 { - compatible = "fsl,lcd"; - ipu_id = <0>; - disp_id = <0>; - default_ifmt = "RGB24"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ipu1_lcd>; - status = "okay"; - }; - - v4l2_out { - compatible = "fsl,mxc_v4l2_output"; - status = "okay"; - }; -}; - -&iomuxc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - /* SATA PWR */ - MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 - MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x80000000 - MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 - MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 - /* SATA CTRL */ - MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 - MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 - MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x80000000 - MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 - /* POWER_BUTTON */ - MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 - >; - }; - }; - - imx6q-cm-fx6 { - /* pins for eth0 */ - pinctrl_enet: enetgrp { - fsl,pins = < - MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 - MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - >; - }; - - pinctrl_ipu1_lcd: ipu1grp-lcd { - fsl,pins = < - MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x38 - MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x38 - MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x38 - MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x38 - MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000028 - MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x38 - MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x38 - MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x38 - MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x38 - MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x38 - MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x38 - MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x38 - MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x38 - MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x38 - MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x38 - MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x38 - MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x38 - MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x38 - MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x38 - MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x38 - MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x38 - MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x38 - MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x38 - MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x38 - MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x38 - MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x38 - MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x38 - MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x38 - MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x38 - >; - }; - - /* pins for spi */ - pinctrl_ecspi1: ecspi1grp { - fsl,pins = < - MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 - MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 - MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 - MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 - MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 - >; - }; - - /* pins for nand */ - pinctrl_gpmi_nand: gpminandgrp { - fsl,pins = < - MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 - MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 - MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 - MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 - MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 - MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 - MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 - MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 - MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 - MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 - MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 - MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 - MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 - MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 - MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 - MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 - MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 - >; - }; - - /* pins for i2c2 */ - pinctrl_i2c2: i2c2grp { - fsl,pins = < - MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 - MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 - >; - }; - - /* pins for i2c3 */ - pinctrl_i2c3: i2c3grp { - fsl,pins = < - MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 - MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 - >; - }; - - /* pins for console */ - pinctrl_uart4: uart4grp { - fsl,pins = < - MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 - MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 - >; - }; - - /* pins for usb hub1 */ - pinctrl_usbh1: usbh1grp { - fsl,pins = < - MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 - >; - }; - - /* pins for usb otg */ - pinctrl_usbotg: usbotggrp { - fsl,pins = < - MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 - MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 - >; - }; - - /* pins for wifi/bt */ - pinctrl_usdhc1: usdhc1grp { - fsl,pins = < - MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 - MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 - MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 - MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 - MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 - MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 - >; - }; - - /* pins for wifi/bt */ - pinctrl_mrvl1: mrvl1grp { - fsl,pins = < - /* WIFI_PWR_RST */ - MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 - MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 - >; - }; - - /* pins for tsc2046 pendown */ - pinctrl_tsc2046: tsc2046grp { - fsl,pins = < - /* tsc2046 PENDOWN */ - MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 - >; - }; - - /* pins for pcie */ - pinctrl_pcie: pciegrp { - fsl,pins = < - MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 - MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 - >; - }; - - /* pins for spdif */ - pinctrl_spdif: spdifgrp { - fsl,pins = < - MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 - MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 - >; - }; - - /* pins for audmux */ - pinctrl_audmux: audmuxgrp { - fsl,pins = < - MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 - MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 - MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 - MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 - MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 - /* master mode pin */ - MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 - >; - }; - }; -}; - -&cpu0 { - operating-points = < - /* kHz uV */ - 996000 1250000 - 852000 1250000 - 792000 1150000 - 396000 975000 - >; - fsl,soc-operating-points = < - /* ARM kHz SOC-PU uV */ - 996000 1250000 - 852000 1250000 - 792000 1175000 - 396000 1175000 - >; -}; - -/* spi */ -&ecspi1 { - fsl,spi-num-chipselects = <2>; - cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1>; - status = "okay"; - - flash: m25p80@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,m25px16", "st,m25p"; - spi-max-frequency = <20000000>; - reg = <0>; - - partition@0 { - label = "uboot"; - reg = <0x0 0xc0000>; - }; - - partition@c0000 { - label = "uboot environment"; - reg = <0xc0000 0x40000>; - }; - - partition@100000 { - label = "reserved"; - reg = <0x100000 0x100000>; - }; - }; - - /* touch controller */ - touch: tsc2046@1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_tsc2046>; - - compatible = "ti,tsc2046"; - vcc-supply = <&tsc2046reg>; - - reg = <1>; /* CS1 */ - spi-max-frequency = <1500000>; - - interrupt-parent = <&gpio2>; - interrupts = <15 0>; - pendown-gpio = <&gpio2 15 0>; - - ti,x-min = /bits/ 16 <0x0>; - ti,x-max = /bits/ 16 <0x0fff>; - ti,y-min = /bits/ 16 <0x0>; - ti,y-max = /bits/ 16 <0x0fff>; - - ti,x-plate-ohms = /bits/ 16 <180>; - ti,pressure-max = /bits/ 16 <255>; - - ti,debounce-max = /bits/ 16 <30>; - ti,debounce-tol = /bits/ 16 <10>; - ti,debounce-rep = /bits/ 16 <1>; - - linux,wakeup; - }; -}; - -/* eth0 */ -&fec { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet>; - phy-mode = "rgmii"; - status = "okay"; -}; - -/* nand */ -&gpmi { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_gpmi_nand>; - status = "okay"; - - partition@0 { - label = "linux"; - reg = <0x0 0x800000>; - }; - - partition@800000 { - label = "rootfs"; - reg = < 0x800000 0x0>; - }; -}; - -/* i2c3 */ -&i2c3 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c3>; - status = "okay"; - - eeprom@50 { - compatible = "at24,24c02"; - reg = <0x50>; - pagesize = <16>; }; - codec: wm8731@1a { - compatible = "wlf,wm8731"; - reg = <0x1a>; - clocks = <&clks 173>, <&clks 158>, <&clks 201>, <&clks 200>; - clock-names = "pll4", "imx-ssi.1", "cko", "cko2"; - AVDD-supply = <&pu_dummy>; - HPVDD-supply = <&pu_dummy>; - DCVDD-supply = <&pu_dummy>; - DBVDD-supply = <&pu_dummy>; - }; -}; - -&pcie { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pcie>; - reset-gpio = <&gpio1 26 0>; - vdd-supply = <&pcie_power_on_gpio>; - status = "okay"; }; /* sata */ &sata { status = "okay"; }; - -/* console */ -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4>; - status = "okay"; -}; - -/* usb otg */ -&usbotg { - vbus-supply = <®_usb_otg_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbotg>; - dr_mode = "otg"; - status = "okay"; -}; - -/* usb hub1 */ -&usbh1 { - vbus-supply = <®_usb_h1_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbh1>; - status = "okay"; -}; - -/* wifi/bt */ -&usdhc1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc1>, <&pinctrl_mrvl1>; - non-removable; - vmmc-supply = <&awnh387_npoweron>; - vmmc_aux-supply = <&awnh387_wifi_nreset>; - status = "okay"; -}; - -&ssi2 { - fsl,mode = "i2s-master"; - status = "okay"; -}; - -&hdmi_core { - ipu_id = <1>; - disp_id = <0>; - status = "okay"; -}; - -&hdmi_video { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; - fsl,hdcp; - status = "okay"; -}; - -&hdmi_audio { - status = "okay"; -}; - -&spdif { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_spdif>; - status = "okay"; -}; - -&audmux { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_audmux>; - status = "okay"; -}; diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi new file mode 100644 index 00000000000000..31086b77d25b12 --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -0,0 +1,573 @@ +/* + * Copyright 2014 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/ { + memory { + reg = <0x10000000 0x20000000>; + }; + + leds { + compatible = "gpio-leds"; + heartbeat-led { + label = "Heartbeat"; + gpios = <&gpio2 31 0>; + linux,default-trigger = "heartbeat"; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + /* regulator for usb otg */ + reg_usb_otg_vbus: usb_otg_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 0>; + enable-active-high; + }; + + /* regulator1 for pcie power-on-gpio */ + pcie_power_on_gpio: regulator-pcie-power-on-gpio { + compatible = "regulator-fixed"; + regulator-name = "regulator-pcie-power-on-gpio"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 24 0>; + enable-active-high; + }; + + /* regulator for usb hub1 */ + reg_usb_h1_vbus: usb_h1_vbus { + compatible = "regulator-fixed"; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio7 8 0>; + enable-active-high; + }; + + /* regulator1 for wifi/bt */ + awnh387_npoweron: regulator-awnh387-npoweron { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-npoweron"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio7 12 0>; + enable-active-high; + }; + + /* regulator2 for wifi/bt */ + awnh387_wifi_nreset: regulator-awnh387-wifi-nreset { + compatible = "regulator-fixed"; + regulator-name = "regulator-awnh387-wifi-nreset"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio6 16 0>; + startup-delay-us = <10000>; + }; + + tsc2046reg: tsc2046-reg { + compatible = "regulator-fixed"; + regulator-name = "tsc2046-reg"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + }; + + aliases { + mxcfb0 = &mxcfb1; + mxcfb1 = &mxcfb2; + }; + + sound { + compatible = "fsl,imx-audio-wm8731"; + model = "wm8731-audio"; + ssi-controller = <&ssi2>; + src-port = <2>; + ext-port = <4>; + audio-codec = <&codec>; + audio-routing = "LOUT", "ROUT", "LLINEIN", "RLINEIN"; + }; + + sound-hdmi { + compatible = "fsl,imx-audio-hdmi"; + model = "imx-audio-hdmi"; + hdmi-controller = <&hdmi_audio>; + }; + + sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; + }; + + mxcfb1: fb@0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb2: fb@1 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "lcd"; + interface_pix_fmt = "RGB24"; + mode_str ="1920x1080M@60"; + default_bpp = <32>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + lcd@0 { + compatible = "fsl,lcd"; + ipu_id = <0>; + disp_id = <0>; + default_ifmt = "RGB24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu1_lcd>; + status = "okay"; + }; + + v4l2_out { + compatible = "fsl,mxc_v4l2_output"; + status = "okay"; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + /* SATA PWR */ + MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 + MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x80000000 + MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x80000000 + MX6QDL_PAD_EIM_A25__GPIO5_IO02 0x80000000 + /* SATA CTRL */ + MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 + MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 + MX6QDL_PAD_EIM_A23__GPIO6_IO06 0x80000000 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x80000000 + /* POWER_BUTTON */ + MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x80000000 + >; + }; + }; + + imx6q-cm-fx6 { + /* pins for eth0 */ + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + >; + }; + + pinctrl_ipu1_lcd: ipu1grp-lcd { + fsl,pins = < + MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x38 + MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15 0x38 + MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02 0x38 + MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03 0x38 + MX6QDL_PAD_DI0_PIN4__IPU1_DI0_PIN04 0x80000028 + MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00 0x38 + MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01 0x38 + MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02 0x38 + MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03 0x38 + MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04 0x38 + MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05 0x38 + MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06 0x38 + MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07 0x38 + MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08 0x38 + MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09 0x38 + MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10 0x38 + MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11 0x38 + MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12 0x38 + MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13 0x38 + MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14 0x38 + MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15 0x38 + MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16 0x38 + MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17 0x38 + MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18 0x38 + MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19 0x38 + MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20 0x38 + MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21 0x38 + MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22 0x38 + MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23 0x38 + >; + }; + + /* pins for spi */ + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1 + MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_EIM_EB2__GPIO2_IO30 0x100b1 + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x100b1 + >; + }; + + /* pins for nand */ + pinctrl_gpmi_nand: gpminandgrp { + fsl,pins = < + MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 + MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 + MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 + MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 + MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 + MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 + MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 + MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 + MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 + MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 + MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 + MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 + MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 + MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 + MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 + MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 + MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 + >; + }; + + /* pins for i2c2 */ + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + /* pins for i2c3 */ + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + /* pins for console */ + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + + /* pins for usb hub1 */ + pinctrl_usbh1: usbh1grp { + fsl,pins = < + MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x80000000 + >; + }; + + /* pins for usb otg */ + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x80000000 + >; + }; + + /* pins for wifi/bt */ + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17071 + MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10071 + MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17071 + MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17071 + MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17071 + MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17071 + >; + }; + + /* pins for wifi/bt */ + pinctrl_mrvl1: mrvl1grp { + fsl,pins = < + /* WIFI_PWR_RST */ + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 + MX6QDL_PAD_NANDF_CS3__GPIO6_IO16 0x80000000 + >; + }; + + /* pins for tsc2046 pendown */ + pinctrl_tsc2046: tsc2046grp { + fsl,pins = < + /* tsc2046 PENDOWN */ + MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 + >; + }; + + /* pins for pcie */ + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_ENET_RXD1__GPIO1_IO26 0x80000000 + MX6QDL_PAD_EIM_CS1__GPIO2_IO24 0x80000000 + >; + }; + + /* pins for spdif */ + pinctrl_spdif: spdifgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_16__SPDIF_IN 0x1b0b0 + MX6QDL_PAD_GPIO_19__SPDIF_OUT 0x1b0b0 + >; + }; + + /* pins for audmux */ + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__AUD4_RXC 0x17059 + MX6QDL_PAD_SD2_DAT0__AUD4_RXD 0x17059 + MX6QDL_PAD_SD2_DAT3__AUD4_TXC 0x17059 + MX6QDL_PAD_SD2_DAT2__AUD4_TXD 0x17059 + MX6QDL_PAD_SD2_DAT1__AUD4_TXFS 0x17059 + /* master mode pin */ + MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 + >; + }; + }; +}; + +&cpu0 { + operating-points = < + /* kHz uV */ + 996000 1250000 + 852000 1250000 + 792000 1150000 + 396000 975000 + >; + fsl,soc-operating-points = < + /* ARM kHz SOC-PU uV */ + 996000 1250000 + 852000 1250000 + 792000 1175000 + 396000 1175000 + >; +}; + +/* spi */ +&ecspi1 { + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25px16", "st,m25p"; + spi-max-frequency = <20000000>; + reg = <0>; + + partition@0 { + label = "uboot"; + reg = <0x0 0xc0000>; + }; + + partition@c0000 { + label = "uboot environment"; + reg = <0xc0000 0x40000>; + }; + + partition@100000 { + label = "reserved"; + reg = <0x100000 0x100000>; + }; + }; + + /* touch controller */ + touch: tsc2046@1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tsc2046>; + + compatible = "ti,tsc2046"; + vcc-supply = <&tsc2046reg>; + + reg = <1>; /* CS1 */ + spi-max-frequency = <1500000>; + + interrupt-parent = <&gpio2>; + interrupts = <15 0>; + pendown-gpio = <&gpio2 15 0>; + + ti,x-min = /bits/ 16 <0x0>; + ti,x-max = /bits/ 16 <0x0fff>; + ti,y-min = /bits/ 16 <0x0>; + ti,y-max = /bits/ 16 <0x0fff>; + + ti,x-plate-ohms = /bits/ 16 <180>; + ti,pressure-max = /bits/ 16 <255>; + + ti,debounce-max = /bits/ 16 <30>; + ti,debounce-tol = /bits/ 16 <10>; + ti,debounce-rep = /bits/ 16 <1>; + + linux,wakeup; + }; +}; + +/* eth0 */ +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + status = "okay"; +}; + +/* nand */ +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand>; + status = "okay"; + + partition@0 { + label = "linux"; + reg = <0x0 0x800000>; + }; + + partition@800000 { + label = "rootfs"; + reg = < 0x800000 0x0>; + }; +}; + +/* i2c3 */ +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + eeprom@50 { + compatible = "at24,24c02"; + reg = <0x50>; + pagesize = <16>; + }; + + codec: wm8731@1a { + compatible = "wlf,wm8731"; + reg = <0x1a>; + clocks = <&clks 173>, <&clks 158>, <&clks 201>, <&clks 200>; + clock-names = "pll4", "imx-ssi.1", "cko", "cko2"; + AVDD-supply = <&pu_dummy>; + HPVDD-supply = <&pu_dummy>; + DCVDD-supply = <&pu_dummy>; + DBVDD-supply = <&pu_dummy>; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; + reset-gpio = <&gpio1 26 0>; + vdd-supply = <&pcie_power_on_gpio>; + status = "okay"; +}; + +/* console */ +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + +/* usb otg */ +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + dr_mode = "otg"; + status = "okay"; +}; + +/* usb hub1 */ +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1>; + status = "okay"; +}; + +/* wifi/bt */ +&usdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc1>, <&pinctrl_mrvl1>; + non-removable; + vmmc-supply = <&awnh387_npoweron>; + vmmc_aux-supply = <&awnh387_wifi_nreset>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-master"; + status = "okay"; +}; + +&hdmi_core { + ipu_id = <1>; + disp_id = <0>; + status = "okay"; +}; + +&hdmi_video { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + fsl,hdcp; + status = "okay"; +}; + +&hdmi_audio { + status = "okay"; +}; + +&spdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spdif>; + status = "okay"; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; +}; From 1341c725a455de80b910fa4c8d4c59a9032efe84 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 23 Mar 2015 10:15:50 +0200 Subject: [PATCH 0691/1983] ARM: i.MX6: dts: add initial support for cm-fx6 DL/S Add initial support for cm-fx6 DL/S modules. This patch configures: 1) serial console 2) hearbeat led 3) FreeScale NIC 4) pcie 5) Intel I210 NIC 6) Analog audio wm8731-audio Signed-off-by: Valentin Raevsky [grinberg@compulab.co.il: fix dtsi file name as per previous patch] Signed-off-by: Igor Grinberg --- arch/arm/boot/dts/imx6dl-cm-fx6.dts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 arch/arm/boot/dts/imx6dl-cm-fx6.dts diff --git a/arch/arm/boot/dts/imx6dl-cm-fx6.dts b/arch/arm/boot/dts/imx6dl-cm-fx6.dts new file mode 100644 index 00000000000000..d33d14c6139fa2 --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-cm-fx6.dts @@ -0,0 +1,21 @@ +/* + * Copyright 2015 CompuLab Ltd. + * + * Author: Valentin Raevsky + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; +#include "imx6dl.dtsi" +#include "imx6qdl-cm-fx6.dtsi" + +/ { + model = "CompuLab CM-FX6"; + compatible = "compulab,cm-fx6", "fsl,imx6dl"; +}; From d72574fd09d0a5e1711fe91010576220159e36d4 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Mon, 23 Mar 2015 10:21:26 +0200 Subject: [PATCH 0692/1983] ARM: i.MX6: dts: add board files for sbc-fx6 DL/S Add board files for sbc-fx6 DL/S modules and Utilite Value. Signed-off-by: Valentin Raevsky [grinberg@compulab.co.il: fix dtsi files names as per previous patches] Signed-off-by: Igor Grinberg --- arch/arm/boot/dts/imx6dl-sbc-fx6.dts | 23 +++++++++++++++++++++++ arch/arm/boot/dts/imx6dl-sbc-fx6m.dts | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 arch/arm/boot/dts/imx6dl-sbc-fx6.dts create mode 100644 arch/arm/boot/dts/imx6dl-sbc-fx6m.dts diff --git a/arch/arm/boot/dts/imx6dl-sbc-fx6.dts b/arch/arm/boot/dts/imx6dl-sbc-fx6.dts new file mode 100644 index 00000000000000..723b65463b111b --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-sbc-fx6.dts @@ -0,0 +1,23 @@ +/* +* Copyright 2015 CompuLab Ltd. +* +* Author: Valentin Raevsky +* +* The code contained herein is licensed under the GNU General Public +* License. You may obtain a copy of the GNU General Public License +* Version 2 or later at the following locations: +* +* http://www.opensource.org/licenses/gpl-license.html +* http://www.gnu.org/copyleft/gpl.html +*/ + +/dts-v1/; +#include "imx6dl.dtsi" +#include "imx6qdl-cm-fx6.dtsi" +#include "imx6qdl-sb-fx6x.dtsi" +#include "imx6qdl-sb-fx6.dtsi" + +/ { + model = "CompuLab CM-FX6 on SBC-FX6"; + compatible = "compulab,cm-fx6", "compulab,sbc-fx6", "fsl,imx6dl"; +}; diff --git a/arch/arm/boot/dts/imx6dl-sbc-fx6m.dts b/arch/arm/boot/dts/imx6dl-sbc-fx6m.dts new file mode 100644 index 00000000000000..52937e3115ab6c --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-sbc-fx6m.dts @@ -0,0 +1,23 @@ +/* +* Copyright 2015 CompuLab Ltd. +* +* Author: Valentin Raevsky +* +* The code contained herein is licensed under the GNU General Public +* License. You may obtain a copy of the GNU General Public License +* Version 2 or later at the following locations: +* +* http://www.opensource.org/licenses/gpl-license.html +* http://www.gnu.org/copyleft/gpl.html +*/ + +/dts-v1/; +#include "imx6dl.dtsi" +#include "imx6qdl-cm-fx6.dtsi" +#include "imx6qdl-sb-fx6x.dtsi" +#include "imx6qdl-sb-fx6m.dtsi" + +/ { + model = "CompuLab CM-FX6 on SBC-FX6m"; + compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6dl"; +}; From 1a0c54f9dd245e4482c819b8acc337ea5aff6425 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 23 Mar 2015 11:47:54 +0200 Subject: [PATCH 0693/1983] ARM: dts: cm-fx6: add build targets for cm-fx6 flavour boards. Add build targets for cm-fx6 flavour boards. Signed-off-by: Valentin Raevsky Conflicts: arch/arm/boot/dts/Makefile Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 1cee09708e5937..68ab97eb563424 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -153,6 +153,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx53-mba53.dtb \ imx53-qsb.dtb \ imx53-smd.dtb \ + imx6dl-cm-fx6.dtb \ imx6dl-cubox-i.dtb \ imx6dl-hummingboard.dtb \ imx6dl-hummingboard2.dtb \ @@ -166,8 +167,11 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6dl-sabresd-pf200.dtb \ imx6dl-sabresd-hdcp.dtb \ imx6dl-udoo.dtb \ + imx6dl-sbc-fx6.dtb \ + imx6dl-sbc-fx6m.dtb \ imx6dl-wandboard.dtb \ imx6q-arm2.dtb \ + imx6q-cm-fx6.dtb \ imx6q-cubox-i.dtb \ imx6q-hummingboard.dtb \ imx6q-hummingboard2.dtb \ @@ -183,6 +187,8 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx6q-sabresd-uart.dtb \ imx6q-sabresd-hdcp.dtb \ imx6q-sabresd-ldo.dtb \ + imx6q-sbc-fx6.dtb \ + imx6q-sbc-fx6m.dtb \ imx6q-sbc6x.dtb \ imx6q-udoo.dtb \ imx6q-wandboard.dtb \ From af4d2c6d0986dd3f930773d1ba612995f8a05d21 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 25 Mar 2015 17:39:07 +0200 Subject: [PATCH 0694/1983] ARM: dts: cm-fx6: change the hdmi_core ipu connection Change the hdmi_core ipu connection in order to allow using the second IPU on quad SBC-FX6 boards with ldb devices. SBC-FX6m boards still use an IPU per port. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 6 ++++++ arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index 7b23eef6159ace..ddb550f4718c94 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -21,3 +21,9 @@ model = "CompuLab CM-FX6 on SBC-FX6m"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6q"; }; + +&hdmi_core { + ipu_id = <1>; + disp_id = <0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index 31086b77d25b12..4f02e30404627a 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -544,8 +544,8 @@ }; &hdmi_core { - ipu_id = <1>; - disp_id = <0>; + ipu_id = <0>; + disp_id = <1>; status = "okay"; }; From 66cf80464a5b244893affb8379aacce3bd3a9591 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Wed, 25 Mar 2015 17:55:09 +0200 Subject: [PATCH 0695/1983] ARM: i.MX6: dts: add ldb support for SBC-FX6 boards Add ldb support for SBC-FX6 boards. LVDS1/0 ports of the SBC-FX6 are configured. LVDS0 - IPU1:DISP0 - fb3 LVDS1 - IPU1:DISP1 - fb5 Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 24 ++++++++++++++++++++++++ arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi | 18 ++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index 4f02e30404627a..d17a4d14a1508d 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -92,6 +92,8 @@ aliases { mxcfb0 = &mxcfb1; mxcfb1 = &mxcfb2; + mxcfb2 = &mxcfb3; + mxcfb3 = &mxcfb4; }; sound { @@ -140,6 +142,28 @@ status = "disabled"; }; + mxcfb3: fb@2 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="1366x768M-18@60"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + + mxcfb4: fb@3 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str ="1280x800M-18@60"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "disabled"; + }; + lcd@0 { compatible = "fsl,lcd"; ipu_id = <0>; diff --git a/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi index 129e88ed7e7659..85836d7b912540 100644 --- a/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi @@ -90,6 +90,24 @@ status = "okay"; }; +&mxcfb3 { + status = "okay"; +}; + +&mxcfb4 { + status = "okay"; +}; + +&ldb { + ipu_id = <1>; + disp_id = <0>; + ext_ref = <1>; + mode = "sep0"; + sec_ipu_id = <1>; + sec_disp_id = <1>; + status = "okay"; +}; + &flexcan1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_flexcan1_1>; From e82c0426bf2eb1c5770792b6b07c256db2835c85 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Mon, 30 Mar 2015 11:29:07 +0300 Subject: [PATCH 0696/1983] ARM: dts: cm-fx6: IOMUXC_GPR1/6/7 to set correct values Add IOMUXC_GPR1/6/7 registers to the iomux default pinctrl group. The IOMUXC_GPR1 register must have default value in order to let the SoC boot up after a warm reboot. IOMUXC_GPR6/7 registers must have a correct value for the ipu QoS priority. Otherwise the SoC reports on: 1) the interrupt that is a result of a time out error during a read access via DIx. 2) a new frame starts before the previous end-of-frame event. Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index d17a4d14a1508d..cff8d4e39e5cbf 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -11,6 +11,10 @@ * http://www.gnu.org/copyleft/gpl.html */ +#define MX6QDL_GPR1 0x04 0x04 0x000 0x0 0x0 +#define MX6QDL_GPR6 0x18 0x18 0x000 0x0 0x0 +#define MX6QDL_GPR7 0x1c 0x1c 0x000 0x0 0x0 + / { memory { reg = <0x10000000 0x20000000>; @@ -187,6 +191,10 @@ hog { pinctrl_hog: hoggrp { fsl,pins = < + MX6QDL_GPR1 0x48400005 + /* ipu3 QoS */ + MX6QDL_GPR6 0x007f007f + MX6QDL_GPR7 0x007f007f /* SATA PWR */ MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 MX6QDL_PAD_EIM_A22__GPIO2_IO16 0x80000000 From 506774247186d0fdb680e9504ff2b782c1ddaa90 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 9 Apr 2015 13:27:01 +0300 Subject: [PATCH 0697/1983] i2c: fix i2c_of include The OF helpers have been moved to the core. As a result the i2c_of.h does not exist anymore. Fix i2c_of include with respect to the latest core implementation. Signed-off-by: Valentin Raevsky --- sound/soc/fsl/imx-wm8731.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-wm8731.c b/sound/soc/fsl/imx-wm8731.c index 898d7d13cc14aa..c485484d030d7d 100644 --- a/sound/soc/fsl/imx-wm8731.c +++ b/sound/soc/fsl/imx-wm8731.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include From d5a56cc71ca2d708efaa2e7f814b7422a91157d0 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 9 Apr 2015 13:30:42 +0300 Subject: [PATCH 0698/1983] ARM: dts: cm-fx6: fix missing defines Fix missing defines that have been changed since 3.10.17 Signed-off-by: Valentin Raevsky --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index cff8d4e39e5cbf..e8f80bca2d671e 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -182,6 +182,10 @@ compatible = "fsl,mxc_v4l2_output"; status = "okay"; }; + + pu_dummy: pudummy_reg { + compatible = "fsl,imx6-dummy-pureg"; /* only used in ldo-bypass */ + }; }; &iomuxc { @@ -397,6 +401,26 @@ MX6QDL_PAD_GPIO_5__CCM_CLKO1 0x17059 >; }; + + pinctrl_hdmi_hdcp: hdmihdcpgrp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__HDMI_TX_DDC_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__HDMI_TX_DDC_SDA 0x4001b8b1 + >; + }; + + pinctrl_pwm3_1: pwm3grp-1 { + fsl,pins = < + MX6QDL_PAD_SD4_DAT1__PWM3_OUT 0x1b0b1 + >; + }; + + pinctrl_flexcan1_1: flexcan1grp-1 { + fsl,pins = < + MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x80000000 + MX6QDL_PAD_KEY_COL2__FLEXCAN1_TX 0x80000000 + >; + }; }; }; @@ -583,7 +607,7 @@ &hdmi_video { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hdmi_hdcp_1>; + pinctrl-0 = <&pinctrl_hdmi_hdcp>; fsl,hdcp; status = "okay"; }; From 2a5f97f424d85d752b673aae37b98996a6b73b88 Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 9 Apr 2015 14:01:38 +0300 Subject: [PATCH 0699/1983] imx6: hdmi: add missing definition IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT Signed-off-by: Valentin Raevsky --- include/linux/mfd/syscon/imx6q-iomuxc-gpr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h index 2e15a7b44fed76..e89c5711af5f24 100644 --- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h +++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h @@ -215,6 +215,7 @@ #define IMX6Q_GPR3_MIPI_MUX_CTL_IPU1_DI1 (0x1 << 4) #define IMX6Q_GPR3_MIPI_MUX_CTL_IPU2_DI0 (0x2 << 4) #define IMX6Q_GPR3_MIPI_MUX_CTL_IPU2_DI1 (0x3 << 4) +#define IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT 2 #define IMX6Q_GPR3_HDMI_MUX_CTL_MASK (0x3 << 2) #define IMX6Q_GPR3_HDMI_MUX_CTL_IPU1_DI0 (0x0 << 2) #define IMX6Q_GPR3_HDMI_MUX_CTL_IPU1_DI1 (0x1 << 2) From fa54f8b338a7ff62be7d3cbe8d8811b030db8b6d Mon Sep 17 00:00:00 2001 From: Valentin Raevsky Date: Thu, 16 Apr 2015 14:55:42 +0300 Subject: [PATCH 0700/1983] ARM: i.MX6: cm-fx6: update defconfig Update the cm_fx6_defconfig with respect to the kernel release after applying the 3.10.17 patches. Signed-off-by: Valentin Raevsky --- arch/arm/configs/cm_fx6_defconfig | 55 ++++++++++++++----------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/arch/arm/configs/cm_fx6_defconfig b/arch/arm/configs/cm_fx6_defconfig index 7d753aee6cf23a..a247438e7499e9 100644 --- a/arch/arm/configs/cm_fx6_defconfig +++ b/arch/arm/configs/cm_fx6_defconfig @@ -1,3 +1,4 @@ +CONFIG_LOCALVERSION="-cm-fx6" CONFIG_KERNEL_LZO=y CONFIG_SYSVIPC=y CONFIG_NO_HZ=y @@ -25,8 +26,8 @@ CONFIG_MACH_EUKREA_CPUIMX51SD=y CONFIG_SOC_IMX53=y CONFIG_SOC_IMX6Q=y CONFIG_SOC_IMX6SL=y +CONFIG_SOC_IMX6SX=y CONFIG_SOC_VF610=y -CONFIG_MACH_CM_FX6=y # CONFIG_SWP_EMULATE is not set CONFIG_PCI=y CONFIG_PCI_IMX6=y @@ -34,16 +35,16 @@ CONFIG_SMP=y CONFIG_VMSPLIT_2G=y CONFIG_PREEMPT=y CONFIG_AEABI=y -# CONFIG_OABI_COMPAT is not set CONFIG_HIGHMEM=y -CONFIG_CMDLINE="noinitrd console=ttymxc0,115200" +CONFIG_CMA=y +CONFIG_CMDLINE="console=ttymxc3,115200 root=/dev/mmcblk0p1 rootwait" CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -CONFIG_ARM_IMX6_CPUFREQ=y +CONFIG_ARM_IMX6Q_CPUFREQ=y CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y @@ -147,8 +148,6 @@ CONFIG_MAC80211=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y # CONFIG_STANDALONE is not set -CONFIG_CMA=y -CONFIG_CMA_SIZE_MBYTES=320 CONFIG_IMX_WEIM=y CONFIG_CONNECTOR=y CONFIG_MTD=y @@ -161,7 +160,6 @@ CONFIG_MTD_CFI_AMDSTD=y CONFIG_MTD_CFI_STAA=y CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_DATAFLASH=y -CONFIG_MTD_M25P80=y CONFIG_MTD_SST25L=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_GPMI_NAND=y @@ -221,7 +219,6 @@ CONFIG_INPUT_MISC=y CONFIG_INPUT_MMA8450=y CONFIG_INPUT_ISL29023=y CONFIG_SERIO_SERPORT=m -CONFIG_VT_HW_CONSOLE_BINDING=y # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVKMEM is not set CONFIG_SERIAL_IMX=y @@ -229,10 +226,8 @@ CONFIG_SERIAL_IMX_CONSOLE=y CONFIG_SERIAL_FSL_LPUART=y CONFIG_SERIAL_FSL_LPUART_CONSOLE=y CONFIG_FSL_OTP=y -CONFIG_MXS_VIIM=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y CONFIG_I2C_MUX_GPIO=y CONFIG_I2C_MUX_PCA954x=y # CONFIG_I2C_HELPER_AUTO is not set @@ -259,24 +254,21 @@ CONFIG_MFD_MC13XXX_I2C=y CONFIG_MFD_MAX17135=y CONFIG_MFD_SI476X_CORE=y CONFIG_REGULATOR=y -CONFIG_REGULATOR_DUMMY=y CONFIG_REGULATOR_FIXED_VOLTAGE=y -CONFIG_REGULATOR_DA9052=y CONFIG_REGULATOR_ANATOP=y +CONFIG_REGULATOR_DA9052=y +CONFIG_REGULATOR_MAX17135=y CONFIG_REGULATOR_MC13783=y CONFIG_REGULATOR_MC13892=y -CONFIG_REGULATOR_MAX17135=y CONFIG_REGULATOR_PFUZE100=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_MEDIA_RADIO_SUPPORT=y -CONFIG_VIDEO_V4L2_INT_DEVICE=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_MXC_OUTPUT=y CONFIG_VIDEO_MXC_CAPTURE=m -CONFIG_VIDEO_MXC_CSI_CAMERA=m CONFIG_MXC_CAMERA_OV5640=m CONFIG_MXC_CAMERA_OV5642=m CONFIG_MXC_CAMERA_OV5640_MIPI=m @@ -284,13 +276,13 @@ CONFIG_MXC_TVIN_ADV7180=m CONFIG_MXC_IPU_DEVICE_QUEUE_SDC=m CONFIG_VIDEO_MXC_IPU_OUTPUT=y CONFIG_VIDEO_MXC_PXP_V4L2=y +CONFIG_VIDEO_MXC_CSI_CAMERA=m CONFIG_SOC_CAMERA=y CONFIG_VIDEO_MX3=y CONFIG_RADIO_SI476X=y CONFIG_SOC_CAMERA_OV2640=y CONFIG_DRM=y CONFIG_DRM_VIVANTE=y -CONFIG_FB=y CONFIG_FB_MXS=y CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y @@ -307,25 +299,20 @@ CONFIG_FB_MXC_EINK_PANEL=y CONFIG_FB_MXS_SII902X=y CONFIG_HANNSTAR_CABC=y CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y -CONFIG_FONTS=y -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_USB_AUDIO=m CONFIG_SND_SOC=y +CONFIG_SND_SOC_FSL_ASRC=y +CONFIG_SND_SOC_FSL_SAI=y +CONFIG_SND_SOC_FSL_SSI=y +CONFIG_SND_SOC_FSL_ESAI=y +CONFIG_SND_SOC_IMX_AUDMUX=y CONFIG_SND_IMX_SOC=y -CONFIG_SND_SOC_EUKREA_TLV320=y -CONFIG_SND_SOC_IMX_CS42888=y -CONFIG_SND_SOC_IMX_WM8731=y -CONFIG_SND_SOC_IMX_WM8962=y -CONFIG_SND_SOC_IMX_SGTL5000=y CONFIG_SND_SOC_IMX_SPDIF=y -CONFIG_SND_SOC_IMX_MC13783=y CONFIG_SND_SOC_IMX_HDMI=y -CONFIG_SND_SOC_IMX_SI476X=y +CONFIG_SND_SOC_CS42XX8_I2C=y CONFIG_USB=y CONFIG_USB_OTG=y CONFIG_USB_EHCI_HCD=y @@ -335,7 +322,6 @@ CONFIG_USB_STORAGE=y CONFIG_USB_CHIPIDEA=y CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y -CONFIG_USB_PHY=y CONFIG_NOP_USB_XCEIV=y CONFIG_USB_MXS_PHY=y CONFIG_USB_GADGET=y @@ -352,7 +338,6 @@ CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_ESDHC_IMX=y CONFIG_MXC_IPU=y CONFIG_MXC_GPU_VIV=y -CONFIG_MXC_ASRC=y CONFIG_MXC_MIPI_CSI2=y CONFIG_MXC_MLB150=m CONFIG_NEW_LEDS=y @@ -371,7 +356,13 @@ CONFIG_MXC_PXP_V2=y CONFIG_IMX_SDMA=y CONFIG_MXS_DMA=y CONFIG_STAGING=y -CONFIG_COMMON_CLK_DEBUG=y +CONFIG_DRM_IMX=y +CONFIG_DRM_IMX_FB_HELPER=y +CONFIG_DRM_IMX_PARALLEL_DISPLAY=y +CONFIG_DRM_IMX_LDB=y +CONFIG_DRM_IMX_IPUV3_CORE=y +CONFIG_DRM_IMX_IPUV3=y +CONFIG_DRM_IMX_HDMI=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_PWM=y CONFIG_PWM_IMX=y @@ -416,7 +407,6 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_SECURITYFS=y CONFIG_CRYPTO_USER=y CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CTS=y @@ -448,3 +438,6 @@ CONFIG_CRC_CCITT=m CONFIG_CRC_T10DIF=y CONFIG_CRC7=m CONFIG_LIBCRC32C=m +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y From aa955d96ef55c8b850e3815f5b123eea1fcd7d3d Mon Sep 17 00:00:00 2001 From: Thomas Genty Date: Sat, 5 Sep 2015 19:35:34 +0200 Subject: [PATCH 0701/1983] ARM: i.MX6: cm-fx6: Fix usdhc3 controller's property original patch Valentin Raevsky --- arch/arm/boot/dts/imx6q-sbc-fx6m.dts | 4 ++++ arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi | 1 + arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi | 1 + arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts index ddb550f4718c94..6874f89f7751e4 100644 --- a/arch/arm/boot/dts/imx6q-sbc-fx6m.dts +++ b/arch/arm/boot/dts/imx6q-sbc-fx6m.dts @@ -20,6 +20,10 @@ / { model = "CompuLab CM-FX6 on SBC-FX6m"; compatible = "compulab,cm-fx6", "compulab,sbc-fx6m", "fsl,imx6q"; + + aliases { + mmc0 = &usdhc3; + }; }; &hdmi_core { diff --git a/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi index 85836d7b912540..fb389fbd2e296b 100644 --- a/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sb-fx6.dtsi @@ -72,6 +72,7 @@ &usdhc3 { wp-gpios = <&gpio7 0 0>; + cd-gpios = <&gpio7 1 0>; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi index 5394364685536a..dc6449d10df7da 100644 --- a/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sb-fx6m.dtsi @@ -93,6 +93,7 @@ }; &usdhc3 { + non-removable; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi b/arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi index 01f73aeabb4dd2..f98a87d5911800 100644 --- a/arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sb-fx6x.dtsi @@ -103,8 +103,8 @@ pinctrl-0 = <&pinctrl_usdhc3>; pinctrl-1 = <&pinctrl_usdhc3_100mhz>; pinctrl-2 = <&pinctrl_usdhc3_200mhz>; - cd-gpios = <&gpio7 1 0>; no-1-8-v; + keep-power-in-suspend; vmmc-supply = <®_3p3v>; status = "disabled"; }; From 17dbb708c3dd03a53f5b71d10fb2f512a646ceb1 Mon Sep 17 00:00:00 2001 From: Thomas Genty Date: Sun, 6 Sep 2015 10:01:09 +0200 Subject: [PATCH 0702/1983] imx6qdl-cm-fx6 : remove pu_dummy, use reg_3p3v instead Now wm8731-audio is working. --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index e8f80bca2d671e..a317c56012ef1d 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -183,9 +183,6 @@ status = "okay"; }; - pu_dummy: pudummy_reg { - compatible = "fsl,imx6-dummy-pureg"; /* only used in ldo-bypass */ - }; }; &iomuxc { @@ -545,10 +542,10 @@ reg = <0x1a>; clocks = <&clks 173>, <&clks 158>, <&clks 201>, <&clks 200>; clock-names = "pll4", "imx-ssi.1", "cko", "cko2"; - AVDD-supply = <&pu_dummy>; - HPVDD-supply = <&pu_dummy>; - DCVDD-supply = <&pu_dummy>; - DBVDD-supply = <&pu_dummy>; + AVDD-supply = <®_3p3v>; + HPVDD-supply = <®_3p3v>; + DCVDD-supply = <®_3p3v>; + DBVDD-supply = <®_3p3v>; }; }; From f82ffdb72208f785461ba26939f06e4a38abf32a Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 30 Apr 2014 14:04:54 -0700 Subject: [PATCH 0703/1983] add tc358743_h2c driver --- drivers/media/platform/mxc/capture/Kconfig | 9 + drivers/media/platform/mxc/capture/Makefile | 3 + .../media/platform/mxc/capture/tc358743_h2c.c | 3338 +++++++++++++++++ 3 files changed, 3350 insertions(+) create mode 100644 drivers/media/platform/mxc/capture/tc358743_h2c.c diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig index a3e47d16b47222..0110f30cebc9aa 100644 --- a/drivers/media/platform/mxc/capture/Kconfig +++ b/drivers/media/platform/mxc/capture/Kconfig @@ -38,6 +38,15 @@ config MXC_CAMERA_OV5647_MIPI ---help--- If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here. +config MXC_HDMI_CSI2_TC358743 + tristate "Toshiba tc358743 Hdmi to CSI 2 bridge" + depends on !VIDEO_MXC_EMMA_CAMERA + depends on ARCH_MX6Q + select MXC_MIPI_CSI2 if ARCH_MX6Q + select MXC_CAMERA_SENSOR_CLK + ---help--- + Toshina HDMI to MIPI-CSI2 bridge + config MXC_TVIN_ADV7180 tristate "Analog Device adv7180 TV Decoder Input support" depends on !VIDEO_MXC_EMMA_CAMERA && I2C diff --git a/drivers/media/platform/mxc/capture/Makefile b/drivers/media/platform/mxc/capture/Makefile index 48fef0fc5bd603..70adf562402231 100644 --- a/drivers/media/platform/mxc/capture/Makefile +++ b/drivers/media/platform/mxc/capture/Makefile @@ -18,6 +18,9 @@ obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi.o ov5647_camera_mipi-objs := ov5647_mipi.o obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.o +tc358743_h2c_bridge-objs := tc358743_h2c.o +obj-$(CONFIG_MXC_HDMI_CSI2_TC358743) += tc358743_h2c_bridge.o + adv7180_tvin-objs := adv7180.o obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c new file mode 100644 index 00000000000000..6a6e8c3ae0de7f --- /dev/null +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -0,0 +1,3338 @@ +/* + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * Modifyed by: Edison Fernández + * Added support to use it with Nitrogen6x + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_v4l2_capture.h" + +#define CODEC_CLOCK 16500000 +/* SSI clock sources */ +#define IMX_SSP_SYS_CLK 0 + + +#define TC358743_VOLTAGE_ANALOG 2800000 +#define TC358743_VOLTAGE_DIGITAL_CORE 1500000 +#define TC358743_VOLTAGE_DIGITAL_IO 1800000 + +#define MIN_FPS 30 +#define MAX_FPS 60 +#define DEFAULT_FPS 60 + +#define TC358743_XCLK_MIN 27000000 +#define TC358743_XCLK_MAX 42000000 + +#define TC358743_CHIP_ID_HIGH_BYTE 0x0 +#define TC358743_CHIP_ID_LOW_BYTE 0x0 +#define TC3587430_HDMI_DETECT 0x0f //0x10 + +enum tc358743_mode { + tc358743_mode_INIT, /*only for sensor init*/ + tc358743_mode_INIT1, /*only for sensor init*/ + tc358743_mode_480P_720_480, + tc358743_mode_720P_60_1280_720, + tc358743_mode_480P_640_480, + tc358743_mode_1080P_1920_1080, + tc358743_mode_INIT2, /*only for sensor init*/ + tc358743_mode_INIT3, /*only for sensor init*/ + tc358743_mode_INIT4, /*only for sensor init*/ + tc358743_mode_INIT5, /*only for sensor init*/ + tc358743_mode_INIT6, /*only for sensor init*/ + tc358743_mode_720P_1280_720, + tc358743_mode_MAX , +}; + +enum tc358743_frame_rate { + tc358743_60_fps, + tc358743_30_fps, + tc358743_max_fps +}; + +struct reg_value { + u16 u16RegAddr; + u32 u32Val; + u32 u32Mask; + u8 u8Length; + u32 u32Delay_ms; +}; + +struct tc358743_mode_info { + enum tc358743_mode mode; + u32 width; + u32 height; + u32 vformat; + u32 fps; + u32 lanes; + u32 freq; + struct reg_value *init_data_ptr; + u32 init_data_size; + __u32 flags; +}; + +static struct delayed_work det_work; + +static u16 hpd_active = 1; + +#define DET_WORK_TIMEOUT_DEFAULT 100 +#define DET_WORK_TIMEOUT_DEFERRED 2000 +#define MAX_BOUNCE 5 + +static DEFINE_MUTEX(access_lock); +static int det_work_disable = 0; +static int det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; +static u32 hdmi_mode = 0, lock = 0, bounce = 0, fps = 0, audio = 2; + +static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, + enum tc358743_mode mode); + +static int tc358743_toggle_hpd(int active); + +static void det_work_enable(int i) +{ + mutex_lock(&access_lock); + if(i) + { + det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); + det_work_disable = 0; + } + else + { + det_work_disable = 1; + det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + } + mutex_unlock(&access_lock); + printk(KERN_DEBUG "%s: %d %d\n", __FUNCTION__, det_work_disable, det_work_timeout); +} + +static u8 cHDMIEDID[256]; + +/*! + * Maintains the information on the current state of the sesor. + */ +static struct sensor_data tc358743_data; + +static struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000004, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000040, 0x00000000, 2, 0}, + {0x0014, 0x00000000, 0x00000000, 2, 0}, + {0x0016, 0x000005ff, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000402d, 0x00000000, 2, 0}, + {0x0022, 0x00000213, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000e00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000801, 0x00000000, 4, 0}, + {0x021c, 0x00000001, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004800, 0x00000000, 4, 0}, + {0x0228, 0x00000005, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa300be82, 0x00000000, 4, 0}, +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, + {0x8512, 0x000000fe, 0x00000000, 1, 0}, + {0x8514, 0x00000000, 0x00000000, 1, 0}, + {0x8515, 0x00000000, 0x00000000, 1, 0}, + {0x8516, 0x00000000, 0x00000000, 1, 0}, +// HDMI Audio RefClk (26 MHz) + {0x8531, 0x00000001, 0x00000000, 1, 0}, + {0x8540, 0x0000008c, 0x00000000, 1, 0}, + {0x8541, 0x0000000a, 0x00000000, 1, 0}, + {0x8630, 0x000000b0, 0x00000000, 1, 0}, + {0x8631, 0x0000001e, 0x00000000, 1, 0}, + {0x8632, 0x00000004, 0x00000000, 1, 0}, + {0x8670, 0x00000001, 0x00000000, 1, 0}, +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, + {0x8536, 0x00000040, 0x00000000, 1, 0}, + {0x853f, 0x0000000a, 0x00000000, 1, 0}, +// EDID + {0x85c7, 0x00000001, 0x00000000, 1, 0}, + {0x85cb, 0x00000001, 0x00000000, 1, 0}, +// HDMI System + {0x8543, 0x00000032, 0x00000000, 1, 0}, +// {0x8544, 0x00000000, 0x00000000, 1, 1000}, +// {0x8544, 0x00000001, 0x00000000, 1, 100}, + {0x8545, 0x00000031, 0x00000000, 1, 0}, + {0x8546, 0x0000002d, 0x00000000, 1, 0}, +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, + {0x8560, 0x00000024, 0x00000000, 1, 0}, + {0x8563, 0x00000011, 0x00000000, 1, 0}, + {0x8564, 0x0000000f, 0x00000000, 1, 0}, +// Video settings + {0x8573, 0x00000081, 0x00000000, 1, 0}, + {0x8571, 0x00000002, 0x00000000, 1, 0}, +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, +// {0x8651, 0x00000003, 0x00000000, 1, 0}, // Inverted LRCK polarity - (Sony) format + {0x8652, 0x00000002, 0x00000000, 1, 0}, // Left-justified I2S (Phillips) format +// {0x8652, 0x00000000, 0x00000000, 1, 0}, // Right-justified (Sony) format + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, + {0x870b, 0x0000002c, 0x00000000, 1, 0}, + {0x870c, 0x00000053, 0x00000000, 1, 0}, + {0x870d, 0x00000001, 0x00000000, 1, 0}, + {0x870e, 0x00000030, 0x00000000, 1, 0}, + {0x9007, 0x00000010, 0x00000000, 1, 0}, + {0x854a, 0x00000001, 0x00000000, 1, 0}, +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, + }; + +static struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000004, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0014, 0x0000ffff, 0x00000000, 2, 0}, + {0x0016, 0x000005ff, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x00004062, 0x00000000, 2, 0}, + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000d00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000701, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000005, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa300be86, 0x00000000, 4, 0}, +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, + {0x8512, 0x000000fe, 0x00000000, 1, 0}, + {0x8514, 0x00000000, 0x00000000, 1, 0}, + {0x8515, 0x00000000, 0x00000000, 1, 0}, + {0x8516, 0x00000000, 0x00000000, 1, 0}, +// HDMI Audio RefClk (26 MHz) + {0x8531, 0x00000001, 0x00000000, 1, 0}, + {0x8540, 0x00000a8c, 0x00000000, 1, 0}, + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, + {0x8670, 0x00000001, 0x00000000, 1, 0}, +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, + {0x8536, 0x00000040, 0x00000000, 1, 0}, + {0x853f, 0x0000000a, 0x00000000, 1, 0}, +// HDMI System + {0x8543, 0x00000032, 0x00000000, 1, 0}, + {0x8544, 0x00000000, 0x00000000, 1, 0}, + {0x8545, 0x00000031, 0x00000000, 1, 0}, + {0x8546, 0x0000002d, 0x00000000, 1, 0}, +// EDID + {0x85c7, 0x00000001, 0x00000000, 1, 0}, + {0x85cb, 0x00000001, 0x00000000, 1, 0}, +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, + {0x8560, 0x00000024, 0x00000000, 1, 0}, + {0x8563, 0x00000011, 0x00000000, 1, 0}, + {0x8564, 0x0000000f, 0x00000000, 1, 0}, +// RGB --> YUV Conversion +// {0x8574, 0x00000000, 0x00000000, 1, 0}, + {0x8573, 0x00000081, 0x00000000, 1, 0}, + {0x8571, 0x00000002, 0x00000000, 1, 0}, +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, + {0x8652, 0x00000002, 0x00000000, 1, 0}, + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, + {0x870b, 0x0000002c, 0x00000000, 1, 0}, + {0x870c, 0x00000053, 0x00000000, 1, 0}, + {0x870d, 0x00000001, 0x00000000, 1, 0}, + {0x870e, 0x00000030, 0x00000000, 1, 0}, + {0x9007, 0x00000010, 0x00000000, 1, 0}, + {0x854a, 0x00000001, 0x00000000, 1, 0}, +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000405c, 0x00000000, 2, 0}, + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000e00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000801, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000006, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x00000007, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa30080a2, 0x00000000, 4, 0}, +// 1280x720 colorbar + {0x000a, 0x00000a00, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 128 pixel black - repeat 128 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)}, +// 128 pixel blue - repeat 64 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel red - repeat 64 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel pink - repeat 64 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel green - repeat 64 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel light blue - repeat 64 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel yellow - repeat 64 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel white - repeat 64 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)}, +// 720 lines + {0x7090, 0x000002cf, 0x00000000, 2, 0}, + {0x7092, 0x00000580, 0x00000000, 2, 0}, + {0x7094, 0x00000010, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000405c, 0x00000000, 2, 0}, + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000e00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000801, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000006, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x0000001F, 0x00000000, 4, 0}, //{0x0234, 0x00000007, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, //{0x0500, 0xa30080a2, 0x00000000, 4, 0}, +// 1280x720 colorbar + {0x000a, 0x00000a00, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 128 pixel black - repeat 128 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)}, +// 128 pixel blue - repeat 64 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel red - repeat 64 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel pink - repeat 64 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel green - repeat 64 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel light blue - repeat 64 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel yellow - repeat 64 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel white - repeat 64 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)}, +// 720 lines + {0x7090, 0x000002cf, 0x00000000, 2, 0}, + {0x7092, 0x00000300, 0x00000000, 2, 0}, //{0x7092, 0x00000580, 0x00000000, 2, 0}, + {0x7094, 0x00000010, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + + +static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x00004050, 0x00000000, 2, 0}, + {0x0022, 0x00000213, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001800, 0x00000000, 4, 0}, + {0x0214, 0x00000002, 0x00000000, 4, 0}, + {0x0218, 0x00001102, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000003, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000007, 0x00000000, 4, 0}, + {0x022c, 0x00000001, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, +// 1280x720 colorbar + {0x000a, 0x00000800, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 128 pixel black - repeat 128 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)}, +// 128 pixel blue - repeat 64 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel red - repeat 64 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel pink - repeat 64 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel green - repeat 64 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel light blue - repeat 64 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel yellow - repeat 64 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel white - repeat 64 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)}, +// 720 lines + {0x0020, 0x0000406f, 0x00000000, 2, 100}, + {0x7090, 0x000002cf, 0x00000000, 2, 0}, + {0x7092, 0x00000540, 0x00000000, 2, 0}, + {0x7094, 0x00000010, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x000080c7, 0x00000000, 2, 0}, + {0x0022, 0x00000213, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001e00, 0x00000000, 4, 0}, + {0x0214, 0x00000003, 0x00000000, 4, 0}, + {0x0218, 0x00001402, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000003, 0x00000000, 4, 0}, + {0x0224, 0x00004a00, 0x00000000, 4, 0}, + {0x0228, 0x00000008, 0x00000000, 4, 0}, + {0x022c, 0x00000002, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, +// 1280x720 colorbar + {0x000a, 0x00000a00, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 128 pixel black - repeat 128 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)}, +// 128 pixel blue - repeat 64 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel red - repeat 64 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel pink - repeat 64 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel green - repeat 64 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel light blue - repeat 64 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel yellow - repeat 64 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel white - repeat 64 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)}, +// 720 lines + {0x7090, 0x000002cf, 0x00000000, 2, 0}, + {0x7092, 0x000006b8, 0x00000000, 2, 0}, + {0x7094, 0x00000010, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x000080c7, 0x00000000, 2, 0}, + {0x0022, 0x00000213, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001e00, 0x00000000, 4, 0}, + {0x0214, 0x00000003, 0x00000000, 4, 0}, + {0x0218, 0x00001402, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000003, 0x00000000, 4, 0}, + {0x0224, 0x00004a00, 0x00000000, 4, 0}, + {0x0228, 0x00000008, 0x00000000, 4, 0}, + {0x022c, 0x00000002, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, //non-continuous clock + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, +// 1920x1023 colorbar + {0x000a, 0x00000f00, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 128 pixel black - repeat 128 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(128<<16)}, +// 128 pixel blue - repeat 64 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel red - repeat 64 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel pink - repeat 64 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel green - repeat 64 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel light blue - repeat 64 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel yellow - repeat 64 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(64<<16)}, +// 128 pixel white - repeat 64 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(64<<16)}, +// 1023 lines + {0x7090, 0x000003fe, 0x00000000, 2, 0}, + {0x7092, 0x000004d8, 0x00000000, 2, 0}, + {0x7094, 0x0000002d, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x00008073, 0x00000000, 2, 0}, + {0x0022, 0x00000213, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, +// {0x014c, 0x00000000, 0x00000000, 4, 0}, +// {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001200, 0x00000000, 4, 0}, + {0x0214, 0x00000002, 0x00000000, 4, 0}, + {0x0218, 0x00000b02, 0x00000000, 4, 0}, + {0x021c, 0x00000001, 0x00000000, 4, 0}, + {0x0220, 0x00000103, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000008, 0x00000000, 4, 0}, + {0x022c, 0x00000002, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000000, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xA3008082, 0x00000000, 4, 0}, +// 640x480 colorbar + {0x000a, 0x00000500, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 80 pixel black - repeate 80 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(80<<16)}, +// 80 pixel blue - repeate 40 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel red - repeate 40 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel pink - repeate 40 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel green - repeate 40 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel light blue - repeate 40 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel yellow - repeate 40 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel white - repeate 40 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(40<<16)}, +// 480 lines + {0x7090, 0x000001df, 0x00000000, 2, 0}, + {0x7092, 0x00000898, 0x00000000, 2, 0}, + {0x7094, 0x00000285, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0010, 0x0000001e, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000404F, 0x00000000, 2, 0}, + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001800, 0x00000000, 4, 0}, + {0x0214, 0x00000002, 0x00000000, 4, 0}, + {0x0218, 0x00001102, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000003, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000007, 0x00000000, 4, 0}, + {0x022c, 0x00000001, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xA30080A2, 0x00000000, 4, 0}, +// 640x480 colorbar + {0x000a, 0x00000500, 0x00000000, 2, 0}, + {0x7080, 0x00000082, 0x00000000, 2, 0}, +// 80 pixel black - repeate 80 times + {0x7000, 0x0000007f, 0x00000000, 2, (1<<24)|(80<<16)}, +// 80 pixel blue - repeate 40 times + {0x7000, 0x000000ff, 0x00000000, 2, 0}, + {0x7000, 0x00000000, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel red - repeate 40 times + {0x7000, 0x00000000, 0x00000000, 2, 0}, + {0x7000, 0x000000ff, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel pink - repeate 40 times + {0x7000, 0x00007fff, 0x00000000, 2, 0}, + {0x7000, 0x00007fff, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel green - repeate 40 times + {0x7000, 0x00007f00, 0x00000000, 2, 0}, + {0x7000, 0x00007f00, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel light blue - repeate 40 times + {0x7000, 0x0000c0ff, 0x00000000, 2, 0}, + {0x7000, 0x0000c000, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel yellow - repeate 40 times + {0x7000, 0x0000ff00, 0x00000000, 2, 0}, + {0x7000, 0x0000ffff, 0x00000000, 2, (2<<24)|(40<<16)}, +// 80 pixel white - repeate 40 times + {0x7000, 0x0000ff7f, 0x00000000, 2, 0}, + {0x7000, 0x0000ff7f, 0x00000000, 2, (2<<24)|(40<<16)}, +// 480 lines + {0x7090, 0x000001df, 0x00000000, 2, 0}, + {0x7092, 0x00000700, 0x00000000, 2, 0}, + {0x7094, 0x00000010, 0x00000000, 2, 0}, + {0x7080, 0x00000083, 0x00000000, 2, 0}, +}; + +//480p RGB2YUV442 +static struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000004, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000040, 0x00000000, 2, 0}, +// {0x000a, 0x000005a0, 0x00000000, 2, 0}, +// {0x0010, 0x0000001e, 0x00000000, 2, 0}, + {0x0014, 0x00000000, 0x00000000, 2, 0}, + {0x0016, 0x000005ff, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000405c, 0x00000000, 2, 0}, + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000d00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000701, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000005, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xA30080A2, 0x00000000, 4, 0}, +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, + {0x8512, 0x000000fe, 0x00000000, 1, 0}, + {0x8514, 0x00000000, 0x00000000, 1, 0}, + {0x8515, 0x00000000, 0x00000000, 1, 0}, + {0x8516, 0x00000000, 0x00000000, 1, 0}, +// HDMI Audio RefClk (26 MHz) + {0x8531, 0x00000001, 0x00000000, 1, 0}, + {0x8540, 0x00000a8c, 0x00000000, 1, 0}, + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, + {0x8670, 0x00000001, 0x00000000, 1, 0}, +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, + {0x8536, 0x00000040, 0x00000000, 1, 0}, + {0x853f, 0x0000000a, 0x00000000, 1, 0}, +// HDMI System + {0x8543, 0x00000032, 0x00000000, 1, 0}, + {0x8544, 0x00000000, 0x00000000, 1, 100}, +// {0x8544, 0x00000001, 0x00000000, 1, 100}, + {0x8545, 0x00000031, 0x00000000, 1, 0}, + {0x8546, 0x0000002d, 0x00000000, 1, 0}, +// EDID + {0x85c7, 0x00000001, 0x00000000, 1, 0}, + {0x85cb, 0x00000001, 0x00000000, 1, 0}, +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, + {0x8560, 0x00000024, 0x00000000, 1, 0}, + {0x8563, 0x00000011, 0x00000000, 1, 0}, + {0x8564, 0x0000000f, 0x00000000, 1, 0}, +// RGB --> YUV Conversion + {0x8573, 0x00000081, 0x00000000, 1, 0}, + {0x8571, 0x00000002, 0x00000000, 1, 0}, +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, + {0x8652, 0x00000002, 0x00000000, 1, 0}, + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, + {0x870b, 0x0000002c, 0x00000000, 1, 0}, + {0x870c, 0x00000053, 0x00000000, 1, 0}, + {0x870d, 0x00000001, 0x00000000, 1, 0}, + {0x870e, 0x00000030, 0x00000000, 1, 0}, + {0x9007, 0x00000010, 0x00000000, 1, 0}, + {0x854a, 0x00000001, 0x00000000, 1, 0}, +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, + }; + +//480p RGB2YUV442 +static struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000004, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100}, + {0x0002, 0x00000000, 0x00000000, 2, 1000}, + {0x0006, 0x00000040, 0x00000000, 2, 0}, + {0x000a, 0x000005a0, 0x00000000, 2, 0}, +// {0x0010, 0x0000001e, 0x00000000, 2, 0}, + {0x0014, 0x00000000, 0x00000000, 2, 0}, + {0x0016, 0x000005ff, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000405b, 0x00000000, 2, 0}, + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000d00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000701, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000005, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xA30080A2, 0x00000000, 4, 0}, +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, + {0x8512, 0x000000fe, 0x00000000, 1, 0}, + {0x8514, 0x00000000, 0x00000000, 1, 0}, + {0x8515, 0x00000000, 0x00000000, 1, 0}, + {0x8516, 0x00000000, 0x00000000, 1, 0}, +// HDMI Audio RefClk (27 MHz) + {0x8531, 0x00000001, 0x00000000, 1, 0}, + {0x8540, 0x00000a8c, 0x00000000, 1, 0}, + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, + {0x8670, 0x00000001, 0x00000000, 1, 0}, +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, + {0x8536, 0x00000040, 0x00000000, 1, 0}, + {0x853f, 0x0000000a, 0x00000000, 1, 0}, +// HDMI System + {0x8543, 0x00000032, 0x00000000, 1, 0}, + {0x8544, 0x00000000, 0x00000000, 1, 100}, +// {0x8544, 0x00000001, 0x00000000, 1, 100}, + {0x8545, 0x00000031, 0x00000000, 1, 0}, + {0x8546, 0x0000002d, 0x00000000, 1, 0}, +// EDID + {0x85c7, 0x00000001, 0x00000000, 1, 0}, + {0x85cb, 0x00000001, 0x00000000, 1, 0}, +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, + {0x8560, 0x00000024, 0x00000000, 1, 0}, + {0x8563, 0x00000011, 0x00000000, 1, 0}, + {0x8564, 0x0000000f, 0x00000000, 1, 0}, +// RGB --> YUV Conversion + {0x8573, 0x00000081, 0x00000000, 1, 0}, + {0x8571, 0x00000002, 0x00000000, 1, 0}, +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, + {0x8652, 0x00000002, 0x00000000, 1, 0}, + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, + {0x870b, 0x0000002c, 0x00000000, 1, 0}, + {0x870c, 0x00000053, 0x00000000, 1, 0}, + {0x870d, 0x00000001, 0x00000000, 1, 0}, + {0x870e, 0x00000030, 0x00000000, 1, 0}, + {0x9007, 0x00000010, 0x00000000, 1, 0}, + {0x854a, 0x00000001, 0x00000000, 1, 0}, +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, + }; + +static struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, + {0x0004, 0x00000084, 0x00000000, 2, 0}, + {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, + {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0014, 0x00000000, 0x00000000, 2, 0}, + {0x0016, 0x000005ff, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x000080c7, 0x00000000, 2, 0}, + {0x0022, 0x00000213, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001e00, 0x00000000, 4, 0}, + {0x0214, 0x00000003, 0x00000000, 4, 0}, + {0x0218, 0x00001402, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000003, 0x00000000, 4, 0}, + {0x0224, 0x00004a00, 0x00000000, 4, 0}, + {0x0228, 0x00000008, 0x00000000, 4, 0}, + {0x022c, 0x00000002, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, + {0x8512, 0x000000fe, 0x00000000, 1, 0}, + {0x8514, 0x00000000, 0x00000000, 1, 0}, + {0x8515, 0x00000000, 0x00000000, 1, 0}, + {0x8516, 0x00000000, 0x00000000, 1, 0}, +// HDMI Audio RefClk (27 MHz) + {0x8531, 0x00000001, 0x00000000, 1, 0}, + {0x8540, 0x00000a8c, 0x00000000, 1, 0}, + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, + {0x8670, 0x00000001, 0x00000000, 1, 0}, +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, + {0x8536, 0x00000040, 0x00000000, 1, 0}, + {0x853f, 0x0000000a, 0x00000000, 1, 0}, +// HDMI System + {0x8543, 0x00000032, 0x00000000, 1, 0}, + {0x8544, 0x00000010, 0x00000000, 1, 100}, + {0x8545, 0x00000031, 0x00000000, 1, 0}, + {0x8546, 0x0000002d, 0x00000000, 1, 0}, +// EDID + {0x85c7, 0x00000001, 0x00000000, 1, 0}, + {0x85cb, 0x00000001, 0x00000000, 1, 0}, +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, + {0x8560, 0x00000024, 0x00000000, 1, 0}, + {0x8563, 0x00000011, 0x00000000, 1, 0}, + {0x8564, 0x0000000f, 0x00000000, 1, 0}, +// RGB --> YUV Conversion + {0x8571, 0x00000002, 0x00000000, 1, 0}, + {0x8573, 0x00000081, 0x00000000, 1, 0}, + {0x8576, 0x00000060, 0x00000000, 1, 0}, +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, + {0x8652, 0x00000002, 0x00000000, 1, 0}, + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, + {0x870b, 0x0000002c, 0x00000000, 1, 0}, + {0x870c, 0x00000053, 0x00000000, 1, 0}, + {0x870d, 0x00000001, 0x00000000, 1, 0}, + {0x870e, 0x00000030, 0x00000000, 1, 0}, + {0x9007, 0x00000010, 0x00000000, 1, 0}, + {0x854a, 0x00000001, 0x00000000, 1, 0}, +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, +}; + +static struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz[] = { + {0x7080, 0x00000000, 0x00000000, 2, 0}, // IR control resister + {0x0004, 0x00000084, 0x00000000, 2, 0}, // Internal Generated output pattern,Do not send InfoFrame data out to CSI2,Audio output to CSI2-TX i/f,I2C address index increments on every data byte transfer, disable audio and video TX buffers + {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, // Reset devices and set normal operatio (not sleep) + {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, // Clear reset bits + {0x0006, 0x000001f8, 0x00000000, 2, 0}, // FIFO level = 1f8 = 504 + {0x0014, 0x00000000, 0x00000000, 2, 0}, // Clear interrupt status bits + {0x0016, 0x000005ff, 0x00000000, 2, 0}, // Mask audio mute, CSI-TX, and the other interrups +// Program CSI Tx PLL + //{0x0020, 0x000080c7, 0x00000000, 2, 0}, // Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200 + {0x0020, 0x000080c7, 0x00000000, 2, 0}, // Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200 + {0x0022, 0x00000213, 0x00000000, 2, 0}, // HSCK frequency = 500MHz – 1GHz HSCK frequency, Loop bandwidth setting = 50% of maximum loop bandwidth (default), REFCLK toggling –> normal operation, REFCLK stops -> no oscillation, Bypass clock = normal operation, clocks switched off (output LOW), PLL Reset normal operation, PLL Enable = PLL on +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, // Clock Lane DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0144, 0x00000000, 0x00000000, 4, 0}, // Data Lane 0 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0148, 0x00000000, 0x00000000, 4, 0}, // Data Lane 1 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x014c, 0x00000000, 0x00000000, 4, 0}, // Data Lane 2 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0150, 0x00000000, 0x00000000, 4, 0}, // Data Lane 3 DPHY Control: Bypass Lane Enable from PPI Layer enable. +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00001e00, 0x00000000, 4, 0}, // LINEINITCNT: Line Initialization Wait Counter = 0x1e00 = 7680 + {0x0214, 0x00000003, 0x00000000, 4, 0}, // LPTXTIMECNT: SYSLPTX Timing Generation Counter = 3 + {0x0218, 0x00001402, 0x00000000, 4, 0}, // TCLK_HEADERCNT: TCLK_ZERO Counter = 0x14 = 20, TCLK_PREPARE Counter = 0x02 = 2 + {0x021c, 0x00000000, 0x00000000, 4, 0}, // TCLK_TRAILCNT: TCLK_TRAIL Counter = 0 + {0x0220, 0x00000003, 0x00000000, 4, 0}, // THS_HEADERCNT: THS_ZERO Counter = 0, THS_PREPARE Counter = 3 + {0x0224, 0x00004a00, 0x00000000, 4, 0}, // TWAKEUP: TWAKEUP Counter = 0x4a00 = 18944 + {0x0228, 0x00000008, 0x00000000, 4, 0}, // TCLK_POSTCNT: TCLK_POST Counter = 8 + {0x022c, 0x00000002, 0x00000000, 4, 0}, // THS_TRAILCNT: THS_TRAIL Counter = 2 + {0x0234, 0x0000001f, 0x00000000, 4, 0}, // HSTXVREGEN: Enable voltage regulators for lanes and clk + {0x0238, 0x00000001, 0x00000000, 4, 0}, // TXOPTIONCNTRL: Set Continuous Clock Mode + {0x0204, 0x00000001, 0x00000000, 4, 0}, // PPI STARTCNTRL: start PPI function + {0x0518, 0x00000001, 0x00000000, 4, 0}, // CSI_START: start + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, // CSI Configuration Register: set register 0x040C with data 0x80a6 (CSI MOde, Disables the HTX_TO timer, High-Speed data transfer is performed to Tx, DSCClk Stays in HS mode when Data Lane goes to LP, 4 Data Lanes,The EOT packet is automatically granted at the end of HS transfer then is transmitted) +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, // SYSTEM INTERRUPT: clear DDC power change detection interrupt + {0x8512, 0x000000fe, 0x00000000, 1, 0}, // SYS INTERRUPT MASK: DDC power change detection interrupt not masked + {0x8514, 0x00000000, 0x00000000, 1, 0}, // PACKET INTERRUPT MASK: unmask all + {0x8515, 0x00000000, 0x00000000, 1, 0}, // CBIT INTERRUPT MASK: unmask all + {0x8516, 0x00000000, 0x00000000, 1, 0}, // AUDIO INTERRUPT MASK: unmask all +// HDMI Audio RefClk (27 MHz) + {0x8531, 0x00000001, 0x00000000, 1, 0}, // PHY CONTROL0: 27MHz, DDC5V detection operation. + {0x8540, 0x00000a8c, 0x00000000, 1, 0}, // SYS FREQ0 Register: 27MHz + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, // Audio FS Lock Detect Control: for 27MHz + {0x8670, 0x00000001, 0x00000000, 1, 0}, // AUDIO PLL Setting: For REFCLK = 27MHz +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, // + {0x8536, 0x00000040, 0x00000000, 1, 0}, // + {0x853f, 0x0000000a, 0x00000000, 1, 0}, // +// HDMI System + {0x8543, 0x00000032, 0x00000000, 1, 0}, // DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms + {0x8544, 0x00000010, 0x00000000, 1, 100}, // HPD Control Register: HOTPLUG output ON/OFF control mode = DDC5V detection interlock + {0x8545, 0x00000031, 0x00000000, 1, 0}, // ANA CONTROL: PLL charge pump setting for Audio = normal, DAC/PLL power ON/OFF setting for Audio = ON + {0x8546, 0x0000002d, 0x00000000, 1, 0}, // AVMUTE CONTROL: AVM_CTL = 0x2d +// EDID + {0x85c7, 0x00000001, 0x00000000, 1, 0}, // EDID MODE REGISTER: nternal EDID-RAM & DDC2B mode + {0x85cb, 0x00000001, 0x00000000, 1, 0}, // EDID Length REGISTER 2: EDID data size stored in RAM (upper address bits) = 0x1 (Size = 0x100 = 256) +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, // + {0x8560, 0x00000024, 0x00000000, 1, 0}, // HDCP MODE: HDCP automatic reset when DVI⇔HDMI switched = on, HDCP Line Rekey timing switch = 7clk mode (Data island delay ON), Bcaps[5] KSVINFO_READY(0x8840[5]) auto clear mode = Auto clear using AKSV write + {0x8563, 0x00000011, 0x00000000, 1, 0}, // + {0x8564, 0x0000000f, 0x00000000, 1, 0}, // +// RGB --> YUV Conversion + {0x8571, 0x00000002, 0x00000000, 1, 0}, // + {0x8573, 0x000000c1, 0x00000000, 1, 0}, // VOUT SET2 REGISTER: 422 fixed output, Video Output 422 conversion mode selection 000: During 444 input, 3tap filter; during 422 input, simple decimation, Enable RGB888 to YUV422 Conversion (Fixed Color output) + {0x8574, 0x00000008, 0x00000000, 1, 0}, // VOUT SET3 REGISTER (VOUT_SET3): Follow register bit 0x8573[7] setting + {0x8576, 0x00000060, 0x00000000, 1, 0}, // VOUT_COLOR: Output Color = 601 YCbCr Limited, Input Pixel Repetition judgment = automatic, Input Pixel Repetition HOST setting = no repetition +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, + {0x8652, 0x00000002, 0x00000000, 1, 0}, + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, // PACKET INTERRUPT MODE: all enable + {0x870b, 0x0000002c, 0x00000000, 1, 0}, // NO PACKET LIMIT: NO_ACP_LIMIT = 0x2, NO_AVI_LIMIT = 0xC + {0x870c, 0x00000053, 0x00000000, 1, 0}, // When VS receive interrupt is detected, VS storage register automatic clear, When ACP receive interrupt is detected, ACP storage register automatic clear, When AVI receive interrupt occurs, judge input video signal with RGB and no Repetition, When AVI receive interrupt is detected, AVI storage register automatic clear. + {0x870d, 0x00000001, 0x00000000, 1, 0}, // ERROR PACKET LIMIT: Packet continuing receive error occurrence detection threshold = 1 + {0x870e, 0x00000030, 0x00000000, 1, 0}, // NO PACKET LIMIT: + {0x9007, 0x00000010, 0x00000000, 1, 0}, // + {0x854a, 0x00000001, 0x00000000, 1, 0}, // Initialization completed flag +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, // Configuration Control Register: Power Island Normal, I2S/TDM clock are free running, Enable 2 Audio channels, Audio channel number Auto detect by HW, I2S/TDM Data no delay, Select YCbCr422 8-bit (HDMI YCbCr422 12-bit data format), Send InfoFrame data out to CSI2, Audio output to I2S i/f (valid for 2 channel only), I2C address index increments on every data byte transfer, Audio and Video tx buffres enable. +}; + +/* list of image formats supported by TCM825X sensor */ +static const struct v4l2_fmtdesc tc358743_formats[] = { + { + .description = "RGB888 (RGB24)", + .pixelformat = V4L2_PIX_FMT_RGB24, /* 24 RGB-8-8-8 */ + .flags = MIPI_DT_RGB888 // 0x24 + }, + { + .description = "RAW12 (Y/CbCr 4:2:0)", + .pixelformat = V4L2_PIX_FMT_UYVY, /* 12 Y/CbCr 4:2:0 */ + .flags = MIPI_DT_RAW12 // 0x2c + }, + { + .description = "YUV 4:2:2 8-bit", + .pixelformat = V4L2_PIX_FMT_YUYV, /* 8 8-bit color */ + .flags = MIPI_DT_YUV422 // 0x1e /* UYVY... */ + }, +}; + + +static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = { + [0][tc358743_mode_720P_60_1280_720] = + {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, + tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_1080P_1920_1080] = + {tc358743_mode_1080P_1920_1080, 1920, 1080, 15, 0x0b, 4, 300, + tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT1] = + {tc358743_mode_INIT1, 1280, 720, 12, 0, 2, 125, + tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT2] = + {tc358743_mode_INIT2, 1280, 720, 12, 0, 4, 125, + tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT] = + {tc358743_mode_INIT, 640, 480, 6, 1, 2, 108, + tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT4] = + {tc358743_mode_INIT4, 640, 480, 6, 1, 2, 174, + tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT3] = + {tc358743_mode_INIT3, 1024, 720, 6, 1, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_720P_1280_720] = + {tc358743_mode_720P_1280_720, 1280, 720, 12, (0x3e)<<8|(0x3c), 2, 125, + tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz), + MIPI_DT_YUV422, + }, + [0][tc358743_mode_480P_720_480] = + {tc358743_mode_480P_720_480, 720, 480, 6, (0x02)<<8|(0x00), 2, 125, + tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), + MIPI_DT_YUV422, + }, + [0][tc358743_mode_480P_640_480] = + {tc358743_mode_480P_640_480, 640, 480, 6, (0x02)<<8|(0x00), 2, 125, + tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), + MIPI_DT_YUV422, + }, + [0][tc358743_mode_INIT5] = + {tc358743_mode_INIT5, 1280, 720, 12, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT6] = + {tc358743_mode_INIT6, 1920, 1023, 15, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_720P_60_1280_720] = + {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, + tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_1080P_1920_1080] = + {tc358743_mode_1080P_1920_1080, 1920, 1080, 15, 0xa, 4, 300, + tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_INIT1] = + {tc358743_mode_INIT1, 1280, 720, 12, 0, 2, 125, + tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_INIT2] = + {tc358743_mode_INIT2, 1280, 720, 12, 0, 4, 125, + tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), + MIPI_DT_YUV422 + }, + + [1][tc358743_mode_INIT] = + {tc358743_mode_INIT, 640, 480, 6, 1, 2, 108, + tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_INIT4] = + {tc358743_mode_INIT4, 640, 480, 6, 1, 2, 174, + tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_INIT3] = + {tc358743_mode_INIT3, 1024, 720, 6, 1, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_720P_1280_720] = + {tc358743_mode_720P_1280_720, 1280, 720, 12, (0x3e)<<8|(0x3c), 2, 125, + tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz), + MIPI_DT_YUV422, + }, + [1][tc358743_mode_480P_720_480] = + {tc358743_mode_480P_720_480, 720, 480, 6, (0x02)<<8|(0x00), 2, 125, + tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), + MIPI_DT_YUV422, + }, + [0][tc358743_mode_480P_640_480] = + {tc358743_mode_480P_640_480, 640, 480, 1, (0x02)<<8|(0x00), 2, 125, + tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), + MIPI_DT_YUV422, + }, + [1][tc358743_mode_INIT5] = + {tc358743_mode_INIT5, 1280, 720, 12, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_INIT6] = + {tc358743_mode_INIT6, 1920, 1023, 15, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), + MIPI_DT_YUV422 + }, +}; + +static struct regulator *io_regulator; +static struct regulator *core_regulator; +static struct regulator *analog_regulator; +static struct regulator *gpo_regulator; +static struct fsl_mxc_camera_platform_data *camera_plat; + +static int tc358743_probe(struct i2c_client *adapter, + const struct i2c_device_id *device_id); +static int tc358743_remove(struct i2c_client *client); + +static s32 tc358743_read_reg(u16 reg, u32 *val); +static s32 tc358743_write_reg(u16 reg, u32 val, int len); + +static const struct i2c_device_id tc358743_id[] = { + {"tc358743_mipi", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tc358743_id); + +static struct i2c_driver tc358743_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tc358743_mipi", + }, + .probe = tc358743_probe, + .remove = tc358743_remove, + .id_table = tc358743_id, +}; + +struct _reg_size +{ + u16 startaddr, endaddr; + int size; +} +tc358743_read_reg_size [] = +{ + {0x0000, 0x005a, 2}, + {0x0140, 0x0150, 4}, + {0x0204, 0x0238, 4}, + {0x040c, 0x0418, 4}, + {0x044c, 0x0454, 4}, + {0x0500, 0x0518, 4}, + {0x0600, 0x06cc, 4}, + {0x7000, 0x7100, 2}, + {0x8500, 0x8bff, 1}, + {0x8c00, 0x8fff, 4}, + {0x9000, 0x90ff, 1}, + {0x9100, 0x92ff, 1}, + {0, 0, 0}, +}; + +static s32 tc358743_write_reg(u16 reg, u32 val, int len) +{ + int i = 0; + u32 data = val; + u8 au8Buf[6] = {0}; + int size = 0; + + while(0 != tc358743_read_reg_size[i].startaddr || + 0 != tc358743_read_reg_size[i].endaddr || + 0 != tc358743_read_reg_size[i].size) + { + if(tc358743_read_reg_size[i].startaddr <= reg && tc358743_read_reg_size[i].endaddr >= reg) + { + size = tc358743_read_reg_size[i].size; + break; + } + i++; + } + if(!size) + { + pr_err("%s:write reg error:reg=%x is not found\n",__func__, reg); + return -1; + } + if(size == 3) + { + size = 2; + } + else + if(size != len) + { + pr_err("%s:write reg len error:reg=%x %d instead of %d\n", + __func__, reg, len, size); + return 0; + } + + while(len > 0) + { + i = 0; + au8Buf[i++] = (reg >> 8) & 0xff; + au8Buf[i++] = reg & 0xff; + while(size-- > 0) + { + au8Buf[i++] = (u8)data; + data >>= 8; + } + + if (i2c_master_send(tc358743_data.i2c_client, au8Buf, i) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, val); + return -1; + } + len -= (u8)size; + reg += (u16)size; + } + + return 0; +} + +static s32 tc358743_read_reg(u16 reg, u32 *val) +{ + u8 au8RegBuf[2] = {0}; + u32 u32RdVal = 0; + int i=0; + int size = 0; + + while(0 != tc358743_read_reg_size[i].startaddr || + 0 != tc358743_read_reg_size[i].endaddr || + 0 != tc358743_read_reg_size[i].size) + { + if(tc358743_read_reg_size[i].startaddr <= reg && tc358743_read_reg_size[i].endaddr >= reg) + { + size = tc358743_read_reg_size[i].size; + break; + } + i++; + } + if(!size) + { + return -1; + } + + au8RegBuf[0] = reg >> 8; + au8RegBuf[1] = reg & 0xff; + + if (2 != i2c_master_send(tc358743_data.i2c_client, au8RegBuf, 2)) { + pr_err("%s:read reg error:reg=%x\n", + __func__, reg); + return -1; + } + + if (size /*of(u32RdVal)*/ != i2c_master_recv(tc358743_data.i2c_client, (char *)&u32RdVal, size /*of(u32RdVal)*/)) { + pr_err("%s:read reg error:reg=%x,val=%x\n", + __func__, reg, u32RdVal); + return -1; + } + *val = u32RdVal; + return size; +} + +static int tc358743_write_edid(u8 *edid, int len) +{ + int i = 0, off = 0; + u8 au8Buf[8+2] = {0}; + int size = 0; + u16 reg; + + reg = 0x8C00; + off = 0; + size = ARRAY_SIZE(au8Buf)-2; + printk(KERN_DEBUG "Write EDID: %d (%d)\n", len, size); + while(len > 0) + { + i = 0; + au8Buf[i++] = (reg >> 8) & 0xff; + au8Buf[i++] = reg & 0xff; + while(i < ARRAY_SIZE(au8Buf)) + { + au8Buf[i++] = edid[off++]; + } + + if (i2c_master_send(tc358743_data.i2c_client, au8Buf, i) < 0) { + pr_err("%s:write reg error:reg=%x,val=%x\n", + __func__, reg, off); + return -1; + } + len -= (u8)size; + reg += (u16)size; + } + printk(KERN_DEBUG "Activate EDID\n"); + tc358743_write_reg(0x85c7, 0x01, 1); + tc358743_write_reg(0x85ca, 0x00, 1); + tc358743_write_reg(0x85cb, 0x01, 1); + return 0; +} + +static int tc358743_reset(struct sensor_data *sensor) +{ + u32 tgt_fps; /* target frames per secound */ + enum tc358743_frame_rate frame_rate = tc358743_60_fps; + int ret = -1; + + det_work_enable(0); + while(ret) + { + if (camera_plat->pwdn) + { + printk(KERN_DEBUG "%s: RESET\n", __FUNCTION__); + camera_plat->pwdn(1); + mdelay(100); + camera_plat->pwdn(0); + mdelay(1000); + } + + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 60) + frame_rate = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate = tc358743_30_fps; + + printk(KERN_DEBUG "%s: capture mode: %d extended mode: %d fps: %d\n", __FUNCTION__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode, tgt_fps); + + ret = tc358743_init_mode(frame_rate, + sensor->streamcap.capturemode); + if(ret) + printk(KERN_ERR "%s: Fail to init tc35874! - retry\n", __FUNCTION__); + } + det_work_enable(1); + return ret; +} + +void mipi_csi2_swreset(struct mipi_csi2_info *info); +#include "../../../../mxc/mipi/mxc_mipi_csi2.h" +static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, + enum tc358743_mode mode) +{ + + struct reg_value *pModeSetting = NULL; + s32 i = 0; + s32 iModeSettingArySize = 0; + register u32 RepeateLines = 0; + register int RepeateTimes = 0; + register u32 Delay_ms = 0; + register u16 RegAddr = 0; + register u32 Mask = 0; + register u32 Val = 0; + u8 Length; + u32 RegVal = 0; + int retval = 0; + void *mipi_csi2_info; + u32 mipi_reg; + u32 mipi_reg_test[10]; + + printk(KERN_DEBUG "%s rate: %d mode: %d\n", __FUNCTION__, frame_rate, mode); + if ((mode > tc358743_mode_MAX || mode < 0) + && (mode != tc358743_mode_INIT)) { + printk(KERN_DEBUG "%s Wrong tc358743 mode detected! %d. Set mode 0\n", __FUNCTION__, mode); + mode = 0; + } + + mipi_csi2_info = mipi_csi2_get_info(); + printk(KERN_DEBUG "%s rate: %d mode: %d, info %p\n", __FUNCTION__, frame_rate, mode, mipi_csi2_info); + + /* initial mipi dphy */ + tc358743_toggle_hpd(!hpd_active); + if (mipi_csi2_info) { + printk(KERN_DEBUG "%s: mipi_csi2_info:\n" + "mipi_en: %d\n" + "ipu_id: %d\n" + "csi_id: %d\n" + "v_channel: %d\n" + "lanes: %d\n" + "datatype: %d\n" + "dphy_clk: %p\n" + "pixel_clk: %p\n" + "mipi_csi2_base:%p\n" + "pdev: %p\n" + , __FUNCTION__, + ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en, + ((struct mipi_csi2_info *)mipi_csi2_info)->ipu_id, + ((struct mipi_csi2_info *)mipi_csi2_info)->csi_id, + ((struct mipi_csi2_info *)mipi_csi2_info)->v_channel, + ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, + ((struct mipi_csi2_info *)mipi_csi2_info)->datatype, + ((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk, + ((struct mipi_csi2_info *)mipi_csi2_info)->pixel_clk, + ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_csi2_base, + ((struct mipi_csi2_info *)mipi_csi2_info)->pdev + ); + if (!mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_enable(mipi_csi2_info); + + if (mipi_csi2_get_status(mipi_csi2_info)) { + int ifmt; + if(tc358743_mode_info_data[frame_rate][mode].lanes != 0) + { + printk(KERN_DEBUG "%s Change lanes: from %d to %d\n", __FUNCTION__, ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, tc358743_mode_info_data[frame_rate][mode].lanes); + ((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes; + ((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes; + } + printk(KERN_DEBUG "Now Using %d lanes\n",mipi_csi2_set_lanes(mipi_csi2_info)); + + /*Only reset MIPI CSI2 HW at sensor initialize*/ + if(!hdmi_mode) // is this during reset + mipi_csi2_reset(mipi_csi2_info); + + + printk(KERN_DEBUG "%s format: %x\n", __FUNCTION__, tc358743_data.pix.pixelformat); + for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) + if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) + { + tc358743_data.pix.pixelformat = tc358743_formats[ifmt].pixelformat; + printk(KERN_DEBUG "%s: %s (%x, %x)\n", __FUNCTION__, tc358743_formats[ifmt].description, tc358743_data.pix.pixelformat, tc358743_formats[ifmt].flags); + mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags); + break; + } + if(ifmt >= ARRAY_SIZE(tc358743_formats)) + { + printk(KERN_ERR "currently this sensor format (0x%x) can not be supported!\n", tc358743_data.pix.pixelformat); + return -1; + } + } else { + printk(KERN_ERR "Can not enable mipi csi2 driver!\n"); + return -1; + } + } else { + printk(KERN_ERR "Fail to get mipi_csi2_info!\n"); + return -1; + } + + { + pModeSetting = + tc358743_mode_info_data[frame_rate][mode].init_data_ptr; + iModeSettingArySize = + tc358743_mode_info_data[frame_rate][mode].init_data_size; + + tc358743_data.pix.width = + tc358743_mode_info_data[frame_rate][mode].width; + tc358743_data.pix.height = + tc358743_mode_info_data[frame_rate][mode].height; + printk(KERN_DEBUG "%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __FUNCTION__, + iModeSettingArySize, + pModeSetting, + frame_rate, + mode, + tc358743_data.pix.width, + tc358743_data.pix.height); + for (i = 0; i < iModeSettingArySize; ++i) { + pModeSetting = tc358743_mode_info_data[frame_rate][mode].init_data_ptr + i; + + Delay_ms = pModeSetting->u32Delay_ms & (0xffff); + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u32Val; + Mask = pModeSetting->u32Mask; + Length = pModeSetting->u8Length; + if (Mask) { + retval = tc358743_read_reg(RegAddr, &RegVal); + if (retval < 0) + break; + + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } + + retval = tc358743_write_reg(RegAddr, Val, Length); + if (retval < 0) + break; + + if (Delay_ms) + msleep(Delay_ms); + + if(0 != ((pModeSetting->u32Delay_ms>>16) & (0xff))) + { + if(!RepeateTimes) + { + RepeateTimes = (pModeSetting->u32Delay_ms>>16) & (0xff); + RepeateLines = (pModeSetting->u32Delay_ms>>24) & (0xff); + } + if(--RepeateTimes > 0) + { + i -= RepeateLines; + } + } + } + if(retval < 0) + { + printk(KERN_ERR "%s: Fail to write REGS to tc35874!\n", __FUNCTION__); + goto err; + } + } + if(!hdmi_mode) // is this during reset + if((retval = tc358743_write_edid(cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) + printk(KERN_ERR "%s: Fail to write EDID to tc35874!\n", __FUNCTION__); + + tc358743_toggle_hpd(hpd_active); + if (mipi_csi2_info) { + unsigned int i; + + i = 0; + + /* wait for mipi sensor ready */ + mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); + while ((mipi_reg == 0x200) && (i < 10)) { + mipi_reg_test[i] = mipi_reg; + mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); + i++; + msleep(10); + } + + if (i >= 10) { + pr_err("mipi csi2 can not receive sensor clk!\n"); + return -1; + } + + { + int j; + for (j = 0; j < i; j++) + { + printk(KERN_DEBUG "%d mipi csi2 dphy status %x\n", j, mipi_reg_test[j]); + } + } + + i = 0; + + /* wait for mipi stable */ + mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); + while ((mipi_reg != 0x0) && (i < 10)) { + mipi_reg_test[i] = mipi_reg; + mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); + i++; + msleep(10); + } + + if (i >= 10) { + pr_err("mipi csi2 can not reveive data correctly!\n"); + return -1; + } + + { + int j; + for (j = 0; j < i; j++) + { + printk(KERN_DEBUG "%d mipi csi2 err1 %x\n", j, mipi_reg_test[j]); + } + } + } +err: + return (retval>0)?0:retval; +} + +/* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ + +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + printk(KERN_DEBUG "%s\n", __FUNCTION__); + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -1; + } + + memset(p, 0, sizeof(*p)); + p->u.bt656.clock_curr = TC358743_XCLK_MIN; //tc358743_data.mclk; + printk(KERN_DEBUG "%s: clock_curr=mclk=%d\n", __FUNCTION__, tc358743_data.mclk); + p->if_type = V4L2_IF_TYPE_BT656; + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.clock_min = TC358743_XCLK_MIN; + p->u.bt656.clock_max = TC358743_XCLK_MAX; + p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ + + return 0; +} + +/*! + * ioctl_s_power - V4L2 sensor interface handler for VIDIOC_S_POWER ioctl + * @s: pointer to standard V4L2 device structure + * @on: indicates power mode (on or off) + * + * Turns the power on or off, depending on the value of on and returns the + * appropriate error code. + */ +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor_data *sensor = s->priv; + + printk(KERN_DEBUG "%s: %d\n", __FUNCTION__, on); + if (on && !sensor->on) { + if (io_regulator) + if (regulator_enable(io_regulator) != 0) + return -EIO; + if (core_regulator) + if (regulator_enable(core_regulator) != 0) + return -EIO; + if (gpo_regulator) + if (regulator_enable(gpo_regulator) != 0) + return -EIO; + if (analog_regulator) + if (regulator_enable(analog_regulator) != 0) + return -EIO; + /* Make sure power on */ + if (camera_plat->pwdn) + camera_plat->pwdn(0); + + } else if (!on && sensor->on) { + if (analog_regulator) + regulator_disable(analog_regulator); + if (core_regulator) + regulator_disable(core_regulator); + if (io_regulator) + regulator_disable(io_regulator); + if (gpo_regulator) + regulator_disable(gpo_regulator); + if(!hdmi_mode) + tc358743_reset(sensor); + } + + sensor->on = on; + + return 0; +} + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + int ret = 0; + + printk(KERN_DEBUG "%s type: %x\n", __FUNCTION__, a->type); + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->streamcap.capability; + cparm->timeperframe = sensor->streamcap.timeperframe; + cparm->capturemode = sensor->streamcap.capturemode; + cparm->extendedmode = sensor->streamcap.extendedmode; + ret = 0; + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + det_work_enable(1); + printk(KERN_DEBUG "%s done %d\n", __FUNCTION__, ret); + return ret; +} + +static int tc358743_toggle_hpd(int active) +{ + int ret = 0; + if(active) + { + ret += tc358743_write_reg(0x8544, 0x00, 1); + mdelay(500); + ret += tc358743_write_reg(0x8544, 0x10, 1); + } + else + { + ret += tc358743_write_reg(0x8544, 0x10, 1); + mdelay(500); + ret += tc358743_write_reg(0x8544, 0x00, 1); + } + return ret; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor_data *sensor = s->priv; + struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; + u32 tgt_fps; /* target frames per secound */ + enum tc358743_frame_rate frame_rate = tc358743_60_fps, frame_rate_now = tc358743_60_fps; + int ret = 0; + + printk(KERN_DEBUG "%s\n", __FUNCTION__); + det_work_enable(0); + /* Make sure power on */ + if (camera_plat->pwdn) + camera_plat->pwdn(0); + + switch (a->type) { + /* This is the only case currently handled. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + /* Check that the new frame rate is allowed. */ + if ((timeperframe->numerator == 0) || + (timeperframe->denominator == 0)) { + timeperframe->denominator = DEFAULT_FPS; + timeperframe->numerator = 1; + } + + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps > MAX_FPS) { + timeperframe->denominator = MAX_FPS; + timeperframe->numerator = 1; + } else if (tgt_fps < MIN_FPS) { + timeperframe->denominator = MIN_FPS; + timeperframe->numerator = 1; + } + + /* Actual frame rate we use */ + tgt_fps = timeperframe->denominator / + timeperframe->numerator; + + if (tgt_fps == 60) + frame_rate = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate = tc358743_30_fps; + else { + pr_err(" The camera frame rate is not supported!\n"); + ret = -EINVAL; + break; + } + + if((u32)a->parm.capture.capturemode > tc358743_mode_MAX) + { + a->parm.capture.capturemode = 0; + printk(KERN_DEBUG "%s: Forse extended mode: %d \n", __FUNCTION__,(u32)a->parm.capture.capturemode); + } + + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 60) + frame_rate_now = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate_now = tc358743_30_fps; + + if(frame_rate_now != frame_rate || + sensor->streamcap.capturemode != (u32)a->parm.capture.capturemode || + sensor->streamcap.extendedmode != (u32)a->parm.capture.extendedmode) + { + sensor->streamcap.timeperframe = *timeperframe; + sensor->streamcap.capturemode = + (u32)a->parm.capture.capturemode; + sensor->streamcap.extendedmode = + (u32)a->parm.capture.extendedmode; + + printk(KERN_DEBUG "%s: capture mode: %d extended mode: %d \n", __FUNCTION__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode); + + ret = tc358743_init_mode(frame_rate, + sensor->streamcap.capturemode); + } + else + printk(KERN_DEBUG "%s: Keep current settings\n", __FUNCTION__); + break; + + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + pr_debug(" type is not " \ + "V4L2_BUF_TYPE_VIDEO_CAPTURE but %d\n", + a->type); + ret = -EINVAL; + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + ret = -EINVAL; + break; + } + + if(ret) + det_work_enable(1); + return ret; +} + +/*! + * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure + * + * If the requested control is supported, returns the control's current + * value from the video_control[] array. Otherwise, returns -EINVAL + * if the control is not supported. + */ +static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int ret = 0; + + printk(KERN_DEBUG "%s\n", __FUNCTION__); + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + vc->value = tc358743_data.brightness; + break; + case V4L2_CID_HUE: + vc->value = tc358743_data.hue; + break; + case V4L2_CID_CONTRAST: + vc->value = tc358743_data.contrast; + break; + case V4L2_CID_SATURATION: + vc->value = tc358743_data.saturation; + break; + case V4L2_CID_RED_BALANCE: + vc->value = tc358743_data.red; + break; + case V4L2_CID_BLUE_BALANCE: + vc->value = tc358743_data.blue; + break; + case V4L2_CID_EXPOSURE: + vc->value = tc358743_data.ae_mode; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/*! + * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl + * @s: pointer to standard V4L2 device structure + * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure + * + * If the requested control is supported, sets the control's current + * value in HW (and updates the video_control[] array). Otherwise, + * returns -EINVAL if the control is not supported. + */ +static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) +{ + int retval = 0; + + pr_debug("In tc358743:ioctl_s_ctrl %d\n", + vc->id); + + switch (vc->id) { + case V4L2_CID_BRIGHTNESS: + break; + case V4L2_CID_CONTRAST: + break; + case V4L2_CID_SATURATION: + break; + case V4L2_CID_HUE: + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + break; + case V4L2_CID_DO_WHITE_BALANCE: + break; + case V4L2_CID_RED_BALANCE: + break; + case V4L2_CID_BLUE_BALANCE: + break; + case V4L2_CID_GAMMA: + break; + case V4L2_CID_EXPOSURE: + break; + case V4L2_CID_AUTOGAIN: + break; + case V4L2_CID_GAIN: + break; + case V4L2_CID_HFLIP: + break; + case V4L2_CID_VFLIP: + break; + default: + retval = -EPERM; + break; + } + + return retval; +} + +int get_pixelformat(int index) +{ +int ifmt; + for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) + if (tc358743_mode_info_data[0][index].flags == tc358743_formats[ifmt].flags) + { + break; + } + + if (ifmt == ARRAY_SIZE(tc358743_formats)) + ifmt = 0; /* Default = RBG888 */ + return ifmt; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + printk(KERN_DEBUG "%s, INDEX: %d\n", __FUNCTION__,fsize->index); + if (fsize->index > tc358743_mode_MAX) + return -EINVAL; + + fsize->pixel_format = tc358743_formats[get_pixelformat(fsize->index)].pixelformat; + fsize->discrete.width = + tc358743_mode_info_data[0][fsize->index].width; + fsize->discrete.height = + tc358743_mode_info_data[0][fsize->index].height; + printk(KERN_DEBUG "%s %d:%d format: %x\n", __FUNCTION__, fsize->discrete.width, fsize->discrete.height, fsize->pixel_format); + return 0; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, + "tc358743_mipi"); + + return 0; +} + +/*! + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT + * @s: pointer to standard V4L2 device structure + */ +static int ioctl_init(struct v4l2_int_device *s) +{ + printk(KERN_DEBUG "%s\n", __FUNCTION__); + return 0; +} + +/*! + * ioctl_enum_fmt_cap - V4L2 sensor interface handler for VIDIOC_ENUM_FMT + * @s: pointer to standard V4L2 device structure + * @fmt: pointer to standard V4L2 fmt description structure + * + * Return 0. + */ +static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, + struct v4l2_fmtdesc *fmt) +{ + printk(KERN_DEBUG "%s\n", __FUNCTION__); + if (fmt->index > tc358743_mode_MAX) + return -EINVAL; + + fmt->pixelformat = tc358743_formats[get_pixelformat(fmt->index)].pixelformat; + + printk(KERN_DEBUG "%s: format: %x\n", __FUNCTION__, fmt->pixelformat); + return 0; +} + +static int ioctl_try_fmt_cap(struct v4l2_int_device *s, + struct v4l2_format *f) +{ + struct sensor_data *sensor = s->priv; + u32 tgt_fps; /* target frames per secound */ + enum tc358743_frame_rate frame_rate; +// enum image_size isize; + int ifmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + printk(KERN_DEBUG "%s\n", __FUNCTION__); + + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 60) + frame_rate = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate = tc358743_30_fps; + else + { + printk(KERN_DEBUG "%s: %d fps (%d,%d) is not supported\n", __FUNCTION__, tgt_fps, sensor->streamcap.timeperframe.denominator,sensor->streamcap.timeperframe.numerator); + return -EINVAL; + } + + tc358743_data.pix.width = pix->width = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].width; + tc358743_data.pix.height = pix->height = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].height; + + for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) + if (tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].flags == tc358743_formats[ifmt].flags) + { + break; + } + + if (ifmt == ARRAY_SIZE(tc358743_formats)) + ifmt = 0; /* Default = RBG888 */ + + tc358743_data.pix.pixelformat = pix->pixelformat = tc358743_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width * 4; + pix->sizeimage = pix->bytesperline * pix->height; + pix->priv = 0; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + + u32 u32val; + int ret = tc358743_read_reg(0x8520,&u32val); + printk(KERN_DEBUG "SYS_STATUS: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8521,&u32val); + printk(KERN_DEBUG "VI_STATUS0: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8522,&u32val); + printk(KERN_DEBUG "VI_STATUS1: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8525,&u32val); + printk(KERN_DEBUG "VI_STATUS2: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8528,&u32val); + printk(KERN_DEBUG "VI_STATUS3: 0x%x, ret val: %d \n",u32val,ret); + printk(KERN_DEBUG "%s %d:%d format: %x\n", __FUNCTION__, pix->width, pix->height, pix->pixelformat); + return 0; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + + printk(KERN_DEBUG "%s\n", __FUNCTION__); + return ioctl_try_fmt_cap(s, f); +} + +/*! + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num + * @s: pointer to standard V4L2 device structure + * + * Initialise the device when slave attaches to the master. + */ +static int ioctl_dev_init(struct v4l2_int_device *s) +{ + struct sensor_data *sensor = s->priv; + u32 tgt_xclk; /* target xclk */ + u32 tgt_fps; /* target frames per secound */ + int ret = 0; + enum tc358743_frame_rate frame_rate; + void *mipi_csi2_info; + + printk(KERN_DEBUG "%s\n", __FUNCTION__); + tc358743_data.on = true; + + /* mclk */ + tgt_xclk = tc358743_data.mclk; + tgt_xclk = min(tgt_xclk, (u32)TC358743_XCLK_MAX); + tgt_xclk = max(tgt_xclk, (u32)TC358743_XCLK_MIN); + tc358743_data.mclk = tgt_xclk; + + printk(KERN_DEBUG "%s: Setting mclk to %d MHz\n", __FUNCTION__, tc358743_data.mclk / 1000000); + set_mclk_rate(&tc358743_data.mclk, tc358743_data.mclk_source); + printk(KERN_DEBUG "%s: After mclk to %d MHz\n", __FUNCTION__, tc358743_data.mclk / 1000000); + + /* Default camera frame rate is set in probe */ + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 60) + frame_rate = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate = tc358743_30_fps; + else + return -EINVAL; + + mipi_csi2_info = mipi_csi2_get_info(); + + /* enable mipi csi2 */ + if (mipi_csi2_info) + mipi_csi2_enable(mipi_csi2_info); + else { + printk(KERN_ERR "Fail to get mipi_csi2_info!\n"); + return -EPERM; + } + + printk(KERN_DEBUG "%s done\n", __FUNCTION__); + return ret; +} + +/*! + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num + * @s: pointer to standard V4L2 device structure + * + * Delinitialise the device when slave detaches to the master. + */ +static int ioctl_dev_exit(struct v4l2_int_device *s) +{ + void *mipi_csi2_info; + + mipi_csi2_info = mipi_csi2_get_info(); + + /* disable mipi csi2 */ + if (mipi_csi2_info) + if (mipi_csi2_get_status(mipi_csi2_info)) + mipi_csi2_disable(mipi_csi2_info); + + return 0; +} + +/*! + * This structure defines all the ioctls for this module and links them to the + * enumeration. + */ +static struct v4l2_int_ioctl_desc tc358743_ioctl_desc[] = { + {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, + {vidioc_int_dev_exit_num, ioctl_dev_exit}, + {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, + {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, + {vidioc_int_enum_fmt_cap_num, + (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, + {vidioc_int_try_fmt_cap_num, + (v4l2_int_ioctl_func *)ioctl_try_fmt_cap}, + {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, + {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, + {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, + {vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, + {vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *) ioctl_g_chip_ident}, +}; + +static struct v4l2_int_slave tc358743_slave = { + .ioctls = tc358743_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tc358743_ioctl_desc), +}; + +static struct v4l2_int_device tc358743_int_device = { + .module = THIS_MODULE, + .name = "tc358743", + .type = v4l2_int_type_slave, + .u = { + .slave = &tc358743_slave, + }, +}; + + +#ifdef AUDIO_ENABLE +/* Audio setup */ + +static int imxpac_tc358743_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret) { + pr_err("%s: failed set cpu dai format\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret) { + pr_err("%s: failed set codec dai format\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + CODEC_CLOCK, SND_SOC_CLOCK_OUT); + if (ret) { + pr_err("%s: failed setting codec sysclk\n", __func__); + return ret; + } + snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); + + ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + return ret; + } +#if 1 +// clear SSI_SRCR_RXBIT0 and SSI_SRCR_RSHFD in order to push Right-justified MSB data fro + { +struct imx_ssi { + struct platform_device *ac97_dev; + + struct snd_soc_dai *imx_ac97; + struct clk *clk; + void __iomem *base; + int irq; + int fiq_enable; + unsigned int offset; + + unsigned int flags; + + void (*ac97_reset) (struct snd_ac97 *ac97); + void (*ac97_warm_reset)(struct snd_ac97 *ac97); + + struct imx_pcm_dma_params dma_params_rx; + struct imx_pcm_dma_params dma_params_tx; + + int enabled; + + struct platform_device *soc_platform_pdev; + struct platform_device *soc_platform_pdev_fiq; +}; + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + u32 scr = 0, srcr = 0, stccr = 0, srccr = 0; +#define SSI_SCR 0x10 +#define SSI_SRCR 0x20 +#define SSI_STCCR 0x24 +#define SSI_SRCCR 0x28 +#define SSI_SCR_I2S_MODE_NORM (0 << 5) +#define SSI_SCR_I2S_MODE_MSTR (1 << 5) +#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) +#define SSI_I2S_MODE_MASK (3 << 5) +#define SSI_SCR_SYN (1 << 4) +#define SSI_SRCR_RSHFD (1 << 4) +#define SSI_SRCR_RSCKP (1 << 3) +#define SSI_SRCR_RFSI (1 << 2) +#define SSI_SRCR_REFS (1 << 0) +#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_STCCR_WL_MASK (0xf << 13) +#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_SRCCR_WL_MASK (0xf << 13) + + printk(KERN_DEBUG "%s: base %p\n", __FUNCTION__, (void *)ssi->base); + scr = readl(ssi->base + SSI_SCR); + printk(KERN_DEBUG "%s: SSI_SCR before: %p\n", __FUNCTION__, (void *)scr); + writel(scr, ssi->base + SSI_SCR); + printk(KERN_DEBUG "%s: SSI_SCR after: %p\n", __FUNCTION__, (void *)scr); + + srcr = readl(ssi->base + SSI_SRCR); + printk(KERN_DEBUG "%s: SSI_SRCR before: %p\n", __FUNCTION__, (void *)srcr); + writel(srcr, ssi->base + SSI_SRCR); + printk(KERN_DEBUG "%s: SSI_SRCR after: %p\n", __FUNCTION__, (void *)srcr); + + stccr = readl(ssi->base + SSI_STCCR); + printk(KERN_DEBUG "%s: SSI_STCCR before: %p\n", __FUNCTION__, (void *)stccr); + stccr &= ~SSI_STCCR_WL_MASK; + stccr |= SSI_STCCR_WL(16); + writel(stccr, ssi->base + SSI_STCCR); + printk(KERN_DEBUG "%s: SSI_STCCR after: %p\n", __FUNCTION__, (void *)stccr); + + srccr = readl(ssi->base + SSI_SRCCR); + printk(KERN_DEBUG "%s: SSI_SRCCR before: %p\n", __FUNCTION__, (void *)srccr); + srccr &= ~SSI_SRCCR_WL_MASK; + srccr |= SSI_SRCCR_WL(16); + writel(srccr, ssi->base + SSI_SRCCR); + printk(KERN_DEBUG "%s: SSI_SRCCR after: %p\n", __FUNCTION__, (void *)srccr); + + } +#endif + return 0; +} + + + +/* Headphones jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins_a[] = { +}; + +/* imx_3stack card dapm widgets */ +static struct snd_soc_dapm_widget imx_3stack_dapm_widgets_a[] = { +}; + + + +static struct snd_kcontrol_new tc358743_machine_controls_a[] = { +}; + +/* imx_3stack machine connections to the codec pins */ +static struct snd_soc_dapm_route audio_map_a[] = { +}; + +static int imx_3stack_tc358743_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + int ret; + struct snd_soc_jack *hs_jack; + + struct snd_soc_jack_pin *hs_jack_pins; + int hs_jack_pins_size; + struct snd_soc_dapm_widget *imx_3stack_dapm_widgets; + int imx_3stack_dapm_widgets_size; + struct snd_kcontrol_new *tc358743_machine_controls; + int tc358743_machine_controls_size; + struct snd_soc_dapm_route *audio_map; + int audio_map_size; + int gpio_num = -1; + char *gpio_name; + + printk(KERN_DEBUG "%s started\n", __func__); + + { + hs_jack_pins = hs_jack_pins_a; + hs_jack_pins_size = ARRAY_SIZE(hs_jack_pins_a); + imx_3stack_dapm_widgets = imx_3stack_dapm_widgets_a; + imx_3stack_dapm_widgets_size = ARRAY_SIZE(imx_3stack_dapm_widgets_a); + tc358743_machine_controls = tc358743_machine_controls_a; + tc358743_machine_controls_size = ARRAY_SIZE(tc358743_machine_controls_a); + audio_map = audio_map_a; + audio_map_size = ARRAY_SIZE(audio_map_a); + gpio_num = -1; //card_a_gpio_num; + gpio_name = NULL; + } + + ret = snd_soc_add_controls(codec, tc358743_machine_controls, + tc358743_machine_controls_size); + if (ret) + { + printk(KERN_ERR "%s: snd_soc_add_controls failed. err = %d\n", __func__, ret); + return ret; + } + /* Add imx_3stack specific widgets */ + snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets, + imx_3stack_dapm_widgets_size); + + /* Set up imx_3stack specific audio path audio_map */ + snd_soc_dapm_add_routes(&codec->dapm, audio_map, audio_map_size); + + snd_soc_dapm_enable_pin(&codec->dapm, hs_jack_pins->pin); + snd_soc_dapm_sync(&codec->dapm); + + hs_jack = kzalloc(sizeof(struct snd_soc_jack), GFP_KERNEL); + + + ret = snd_soc_jack_new(codec, hs_jack_pins->pin, + SND_JACK_HEADPHONE, hs_jack); + if (ret) + { + printk(KERN_ERR "%s: snd_soc_jack_new failed. err = %d\n", __func__, ret); + return ret; + } + + ret = snd_soc_jack_add_pins(hs_jack,hs_jack_pins_size, + hs_jack_pins); + if (ret) { + printk(KERN_ERR "%s: snd_soc_jack_add_pinsfailed. err = %d\n", __func__, ret); + return ret; + } + + return 0; +} + + +static struct snd_soc_ops imxpac_tc358743_snd_ops = { + .hw_params = imxpac_tc358743_hw_params, +}; + +static struct snd_soc_dai_link imxpac_tc358743_dai = { + .name = "tc358743", + .stream_name = "TC358743", + .codec_dai_name = "tc358743-hifi", + .platform_name = "imx-pcm-audio.2", + .codec_name = "tc358743_mipi.1-000f", + .cpu_dai_name = "imx-ssi.2", + .init = imx_3stack_tc358743_init, + .ops = &imxpac_tc358743_snd_ops, +}; + +static struct snd_soc_card imxpac_tc358743 = { + .name = "cpuimx-audio_hdmi_in", + .dai_link = &imxpac_tc358743_dai, + .num_links = 1, +}; + +static struct platform_device *imxpac_tc358743_snd_device; +static struct platform_device *imxpac_tc358743_snd_device; + +static int imx_audmux_config(int slave, int master) +{ + unsigned int ptcr, pdcr; + slave = slave - 1; + master = master - 1; + + /* SSI0 mastered by port 5 */ + ptcr = MXC_AUDMUX_V2_PTCR_SYN | + MXC_AUDMUX_V2_PTCR_TFSDIR | + MXC_AUDMUX_V2_PTCR_TFSEL(master | 0x8) | + MXC_AUDMUX_V2_PTCR_TCLKDIR | + MXC_AUDMUX_V2_PTCR_RFSDIR | + MXC_AUDMUX_V2_PTCR_RFSEL(master | 0x8) | + MXC_AUDMUX_V2_PTCR_RCLKDIR | + MXC_AUDMUX_V2_PTCR_RCSEL(master | 0x8) | + MXC_AUDMUX_V2_PTCR_TCSEL(master | 0x8); + pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master); + mxc_audmux_v2_configure_port(slave, ptcr, pdcr); + + ptcr = MXC_AUDMUX_V2_PTCR_SYN; + pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master); + mxc_audmux_v2_configure_port(master, ptcr, pdcr); + + return 0; +} + +static int __devinit imx_tc358743_probe(struct platform_device *pdev) +{ + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + int ret = 0; + + + imx_audmux_config(plat->src_port, plat->ext_port); + + ret = -EINVAL; + if (plat->init && plat->init()) + return ret; + + printk("%s %d %s\n",__FUNCTION__,__LINE__,pdev->name); + + return 0; +} + +static int imx_tc358743_remove(struct platform_device *pdev) +{ + struct mxc_audio_platform_data *plat = pdev->dev.platform_data; + + if (plat->finit) + plat->finit(); + + return 0; +} + +static struct platform_driver imx_tc358743_audio1_driver = { + .probe = imx_tc358743_probe, + .remove = imx_tc358743_remove, + .driver = { + .name = "imx-tc358743", + }, +}; + + +/* Codec setup */ +static int tc358743_codec_probe(struct snd_soc_codec *codec) +{ + return 0; +} + +static int tc358743_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static int tc358743_codec_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ +// tc358743_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int tc358743_codec_resume(struct snd_soc_codec *codec) +{ +// tc358743_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int tc358743_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + return 0; +} + +static const u8 tc358743_reg[0] = { +}; + +static struct snd_soc_codec_driver soc_codec_dev_tc358743 = { + .set_bias_level = tc358743_set_bias_level, + .reg_cache_size = ARRAY_SIZE(tc358743_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = tc358743_reg, + .probe = tc358743_codec_probe, + .remove = tc358743_codec_remove, + .suspend = tc358743_codec_suspend, + .resume = tc358743_codec_resume, +}; + +#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static int tc358743_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int tc358743_mute(struct snd_soc_dai *dai, int mute) +{ + return 0; +} + +static int tc358743_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + return 0; +} + +static int tc358743_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + return 0; +} + +static struct snd_soc_dai_ops tc358743_dai_ops = { + .hw_params = tc358743_hw_params, + .digital_mute = tc358743_mute, + .set_sysclk = tc358743_set_dai_sysclk, + .set_fmt = tc358743_set_dai_fmt, +}; + +static struct snd_soc_dai_driver tc358743_dai = { + .name = "tc358743-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3X_RATES, + .formats = AIC3X_FORMATS,}, + .ops = &tc358743_dai_ops, + .symmetric_rates = 1, +}; + +#endif + +static char tc358743_mode_list[16][12] = +{ + "None", + "VGA", + "240p/480i", + "288p/576i", + "W240p/480i", + "W288p/576i", + "480p", + "576p", + "W480p", + "W576p", + "WW480p", + "WW576p", + "720p", + "1035i", + "1080i", + "1080p" +}; + +static char tc358743_fps_list[tc358743_max_fps+1] = +{ +[tc358743_60_fps] = 60, +[tc358743_30_fps] = 30, +[tc358743_max_fps] = 0 +}; + +static int tc358743_audio_list[16] = +{ + 44100, + 0, + 48000, + 32000, + 22050, + 384000, + 24000, + 352800, + 88200, + 768000, + 96000, + 705600, + 176400, + 0, + 192000, + 0 +}; + +static char str_on[80]; +static report_netlink(void) +{ + char *envp[2]; + envp[0] = &str_on[0]; + envp[1] = NULL; + sprintf(envp[0], "HDMI RX: %d (%s) %d %d", (unsigned char)hdmi_mode & 0xf, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], tc358743_fps_list[fps], tc358743_audio_list[audio]); + kobject_uevent_env(&(tc358743_data.i2c_client->dev.kobj), KOBJ_CHANGE, envp); + det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + printk(KERN_DEBUG "%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", __FUNCTION__, (unsigned char)hdmi_mode, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout, tc358743_audio_list[audio]); +} + +static void det_worker(struct work_struct *work) +{ + u32 u32val; + u16 reg; + int ret; + + mutex_lock(&access_lock); + if(!det_work_disable) + { + reg = 0x8621; + ret = tc358743_read_reg(reg, &u32val); + if(ret > 0) + { + if(audio != ((unsigned char)u32val) & 0x0f) + { + audio = ((unsigned char)u32val) & 0x0f; + report_netlink(); + } + } + reg = 0x852f; + ret = tc358743_read_reg(reg, &u32val); + if(ret > 0) + { + while(1) + { + if(u32val & TC3587430_HDMI_DETECT) + { + lock = u32val & TC3587430_HDMI_DETECT; + reg = 0x8521; + ret = tc358743_read_reg(reg, &u32val); + if(ret < 0) + { + printk(KERN_ERR "%s: Error reading mode\n", __FUNCTION__); + } + } + else + { + if(lock) // check if it is realy un-plug + { + lock = 0; + u32val = 0x0; + hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. + } + } + if((unsigned char)hdmi_mode != (unsigned char)u32val) + { + if(u32val) + det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + else + det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + bounce = MAX_BOUNCE; + printk(KERN_DEBUG "%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", __FUNCTION__, (unsigned char)hdmi_mode, (unsigned char)u32val, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout); + hdmi_mode = u32val; + } + else + if(bounce) + { + bounce--; + det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + } + + if(1 == bounce) + { + if(hdmi_mode >= 0xe) + { + reg = 0x852f; + ret = tc358743_read_reg(reg, &u32val); + if(ret > 0) + fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; + } + reg = 0x8621; + ret = tc358743_read_reg(reg, &u32val); + if(ret > 0) + audio = ((unsigned char)u32val) & 0x0f; + report_netlink(); + } + break; + } + } + else + printk(KERN_ERR "%s: Error reading lock\n", __FUNCTION__); + } + else + det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + mutex_unlock(&access_lock); + schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); +} + +static irqreturn_t tc358743_detect_handler(int irq, void *data) +{ + + printk(KERN_DEBUG "%s: IRQ %d\n", __FUNCTION__, tc358743_data.i2c_client->irq); + schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); + return IRQ_HANDLED; +} + + +/*! + * tc358743 I2C probe function + * + * @param adapter struct i2c_adapter * + * @return Error code indicating success or failure + */ +#include +#include + +#define DUMP_LENGTH 256 +static u16 regoffs = 0; + +static ssize_t tc358743_show_regdump(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len = 0; + int retval; + u32 u32val; + + mutex_lock(&access_lock); + for(i=0; i 0) + { + if(0 == (i & 0xf)) + len += sprintf(buf+len, "\n%04X:", regoffs+i); + len += sprintf(buf+len, " %02X", u32val&0xff); + u32val >>= 8; + i++; + } + } + mutex_unlock(&access_lock); + len += sprintf(buf+len, "\n"); + return len; +} + +static DEVICE_ATTR(regdump, S_IRUGO, tc358743_show_regdump, NULL); + +static ssize_t tc358743_store_regoffs(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 val; + int retval; + retval = sscanf(buf, "%x", &val); + if(1 == retval) + regoffs = (u16)val; + return count; +} + +static ssize_t tc358743_show_regoffs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len += sprintf(buf+len, "0x%04X\n", regoffs); + return len; +} + +static DEVICE_ATTR(regoffs, S_IRUGO|S_IWUSR, tc358743_show_regoffs, tc358743_store_regoffs); + +static ssize_t tc358743_store_hpd(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 val; + int retval; + retval = sscanf(buf, "%d", &val); + if(1 == retval) + hpd_active = (u16)val; + return count; +} + +static ssize_t tc358743_show_hpd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len += sprintf(buf+len, "%d\n", hpd_active); + return len; +} + +static DEVICE_ATTR(hpd, S_IRUGO|S_IWUSR, tc358743_show_hpd, tc358743_store_hpd); + +static ssize_t tc358743_show_hdmirx(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len += sprintf(buf+len, "%d\n", hdmi_mode); + return len; +} + +static DEVICE_ATTR(hdmirx, S_IRUGO, tc358743_show_hdmirx, NULL); + +static ssize_t tc358743_show_fps(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len += sprintf(buf+len, "%d\n", tc358743_fps_list[fps]); + return len; +} + +static DEVICE_ATTR(fps, S_IRUGO, tc358743_show_fps, NULL); + +#ifdef AUDIO_ENABLE +static ssize_t tc358743_show_audio(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len += sprintf(buf+len, "%d\n", tc358743_audio_list[audio]); + return len; +} + +static DEVICE_ATTR(audio, S_IRUGO, tc358743_show_audio, NULL); +#endif + +static int tc358743_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int retval = -1; + struct fsl_mxc_camera_platform_data *plat_data = client->dev.platform_data; + u8 chip_id_high; + u32 u32val; + + printk(KERN_DEBUG "%s: started, error=%d\n", + __func__, retval); + + /* Set initial values for the sensor struct. */ + memset(&tc358743_data, 0, sizeof(tc358743_data)); + tc358743_data.mclk = 27000000; /* 6 - 54 MHz, typical 24MHz */ + tc358743_data.mclk_source = plat_data->mclk_source; + tc358743_data.csi = plat_data->csi; + tc358743_data.io_init = plat_data->io_init; + + tc358743_data.i2c_client = client; + tc358743_data.pix.pixelformat = tc358743_formats[0].pixelformat; + tc358743_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | + V4L2_CAP_TIMEPERFRAME; + tc358743_data.streamcap.capturemode = 0; + tc358743_data.streamcap.extendedmode = tc358743_mode_1080P_1920_1080; + tc358743_data.streamcap.timeperframe.denominator = DEFAULT_FPS; + tc358743_data.streamcap.timeperframe.numerator = 1; + + tc358743_data.pix.width = tc358743_mode_info_data[0][tc358743_data.streamcap.capturemode].width; + tc358743_data.pix.height = tc358743_mode_info_data[0][tc358743_data.streamcap.capturemode].height; + printk(KERN_DEBUG "%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__, + tc358743_data.pix.pixelformat, + tc358743_data.streamcap.capturemode, tc358743_data.streamcap.extendedmode, + tc358743_data.streamcap.timeperframe.denominator * + tc358743_data.streamcap.timeperframe.numerator, + tc358743_data.pix.width, + tc358743_data.pix.height); + + if (plat_data->io_regulator) { + io_regulator = regulator_get(&client->dev, + plat_data->io_regulator); + if (!IS_ERR(io_regulator)) { + regulator_set_voltage(io_regulator, + TC358743_VOLTAGE_DIGITAL_IO, + TC358743_VOLTAGE_DIGITAL_IO); + retval = regulator_enable(io_regulator); + if (retval) { + pr_err("%s:io set voltage error\n", __func__); + goto err1; + } else { + dev_dbg(&client->dev, + "%s:io set voltage ok\n", __func__); + } + } else + io_regulator = NULL; + } + + if (plat_data->core_regulator) { + core_regulator = regulator_get(&client->dev, + plat_data->core_regulator); + if (!IS_ERR(core_regulator)) { + regulator_set_voltage(core_regulator, + TC358743_VOLTAGE_DIGITAL_CORE, + TC358743_VOLTAGE_DIGITAL_CORE); + retval = regulator_enable(core_regulator); + if (retval) { + pr_err("%s:core set voltage error\n", __func__); + goto err2; + } else { + dev_dbg(&client->dev, + "%s:core set voltage ok\n", __func__); + } + } else + core_regulator = NULL; + } + + if (plat_data->analog_regulator) { + analog_regulator = regulator_get(&client->dev, + plat_data->analog_regulator); + if (!IS_ERR(analog_regulator)) { + regulator_set_voltage(analog_regulator, + TC358743_VOLTAGE_ANALOG, + TC358743_VOLTAGE_ANALOG); + retval = regulator_enable(analog_regulator); + if (retval) { + pr_err("%s:analog set voltage error\n", + __func__); + goto err3; + } else { + dev_dbg(&client->dev, + "%s:analog set voltage ok\n", __func__); + } + } else + analog_regulator = NULL; + } + + if (plat_data->io_init) + plat_data->io_init(); + + if (plat_data->pwdn) + plat_data->pwdn(0); + + retval = tc358743_read_reg(TC358743_CHIP_ID_HIGH_BYTE, &u32val); + chip_id_high = (u8)u32val; + if (retval < 0 /* || chip_id_high != 0x00*/) { + pr_err("%s:cannot find camera\n", __func__); + retval = -ENODEV; + goto err4; + } + + camera_plat = plat_data; + + tc358743_int_device.priv = &tc358743_data; + retval = v4l2_int_device_register(&tc358743_int_device); + if (retval) + { + printk(KERN_ERR "%s: v4l2_int_device_register failed, error=%d\n", + __func__, retval); + goto err4; + } + + //retval = device_create_file(&client->dev, &dev_attr_audio); + retval = device_create_file(&client->dev, &dev_attr_fps); + retval = device_create_file(&client->dev, &dev_attr_hdmirx); + retval = device_create_file(&client->dev, &dev_attr_hpd); + retval = device_create_file(&client->dev, &dev_attr_regoffs); + retval = device_create_file(&client->dev, &dev_attr_regdump); + + if (retval) + { + printk(KERN_ERR "%s: create bin file failed, error=%d\n", + __func__, retval); + goto err4; + } + +#ifdef AUDIO_ENABLE +/* Audio setup */ + retval = snd_soc_register_codec(&client->dev, + &soc_codec_dev_tc358743, &tc358743_dai, 1); + if (retval) + { + printk(KERN_ERR "%s: register failed, error=%d\n", + __func__, retval); + goto err4; + } + + retval = platform_driver_register(&imx_tc358743_audio1_driver); + if (retval) + { + printk(KERN_ERR "%s: Platform driver register failed, error=%d\n", + __func__, retval); + goto err4; + } + + imxpac_tc358743_snd_device = platform_device_alloc("soc-audio", 5); + if (!imxpac_tc358743_snd_device) + { + printk(KERN_ERR "%s: Platform device allocation failed, error=%d\n", + __func__, retval); + goto err4; + } + + platform_set_drvdata(imxpac_tc358743_snd_device, &imxpac_tc358743); + retval = platform_device_add(imxpac_tc358743_snd_device); + + if (retval) { + printk(KERN_ERR "%s: Platform device add failed, error=%d\n", + __func__, retval); + platform_device_put(imxpac_tc358743_snd_device); + goto err4; + } +#endif + +#if 1 + INIT_DELAYED_WORK(&(det_work), det_worker); + if (tc358743_data.i2c_client->irq) { + retval = request_irq(tc358743_data.i2c_client->irq, tc358743_detect_handler, + IRQF_SHARED | IRQF_TRIGGER_FALLING, + "tc358743_det", &tc358743_data); + if (retval < 0) + dev_warn(&tc358743_data.i2c_client->dev, + "cound not request det irq %d\n", + tc358743_data.i2c_client->irq); + } + + schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); +#endif + retval = tc358743_reset(&tc358743_data); + + printk(KERN_DEBUG "%s: finished, error=%d\n", + __func__, retval); + return retval; + +err4: + if (analog_regulator) { + regulator_disable(analog_regulator); + regulator_put(analog_regulator); + } +err3: + if (core_regulator) { + regulator_disable(core_regulator); + regulator_put(core_regulator); + } +err2: + if (io_regulator) { + regulator_disable(io_regulator); + regulator_put(io_regulator); + } +err1: + printk(KERN_ERR "%s: failed, error=%d\n", + __func__, retval); + return retval; +} + +/*! + * tc358743 I2C detach function + * + * @param client struct i2c_client * + * @return Error code indicating success or failure + */ +static int tc358743_remove(struct i2c_client *client) +{ + // Stop delayed work + cancel_delayed_work_sync(&(det_work)); + + // Remove IRQ + if (tc358743_data.i2c_client->irq) { + free_irq(tc358743_data.i2c_client->irq, &tc358743_data); + } + + /*Remove sysfs entries*/ + device_remove_file(&client->dev, &dev_attr_fps); + device_remove_file(&client->dev, &dev_attr_hdmirx); + device_remove_file(&client->dev, &dev_attr_hpd); + device_remove_file(&client->dev, &dev_attr_regoffs); + device_remove_file(&client->dev, &dev_attr_regdump); + + v4l2_int_device_unregister(&tc358743_int_device); + + if (gpo_regulator) { + regulator_disable(gpo_regulator); + regulator_put(gpo_regulator); + } + + if (analog_regulator) { + regulator_disable(analog_regulator); + regulator_put(analog_regulator); + } + + if (core_regulator) { + regulator_disable(core_regulator); + regulator_put(core_regulator); + } + + if (io_regulator) { + regulator_disable(io_regulator); + regulator_put(io_regulator); + } + + return 0; +} + +/*! + * tc358743 init function + * Called by insmod tc358743_camera.ko. + * + * @return Error code indicating success or failure + */ +static __init int tc358743_init(void) +{ + int err; + + err = i2c_add_driver(&tc358743_i2c_driver); + if (err != 0) + pr_err("%s:driver registration failed, error=%d\n", + __func__, err); + + return err; +} + +/*! + * tc358743 cleanup function + * Called on rmmod tc358743_camera.ko + * + * @return Error code indicating success or failure + */ +static void __exit tc358743_clean(void) +{ + i2c_del_driver(&tc358743_i2c_driver); +} + +module_init(tc358743_init); +module_exit(tc358743_clean); + +MODULE_AUTHOR("Panasonic Avionics Corp."); +MODULE_DESCRIPTION("Toshiba TC358743 HDMI-to-CSI2 Bridge MIPI Input Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("CSI"); From ac7c5bde4d47702bcaa14c58b4ceb272a15ca862 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 30 Apr 2014 15:11:47 -0700 Subject: [PATCH 0704/1983] tc358743_h2c: cleanup spacing --- .../media/platform/mxc/capture/tc358743_h2c.c | 1040 ++++++++--------- 1 file changed, 486 insertions(+), 554 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index 6a6e8c3ae0de7f..f1de64dcc3d62f 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -5,7 +5,7 @@ /* * Modifyed by: Edison Fernández * Added support to use it with Nitrogen6x - * + * * 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 @@ -19,7 +19,7 @@ * 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. - * + * */ #include #include @@ -50,9 +50,9 @@ #define IMX_SSP_SYS_CLK 0 -#define TC358743_VOLTAGE_ANALOG 2800000 -#define TC358743_VOLTAGE_DIGITAL_CORE 1500000 -#define TC358743_VOLTAGE_DIGITAL_IO 1800000 +#define TC358743_VOLTAGE_ANALOG 2800000 +#define TC358743_VOLTAGE_DIGITAL_CORE 1500000 +#define TC358743_VOLTAGE_DIGITAL_IO 1800000 #define MIN_FPS 30 #define MAX_FPS 60 @@ -68,9 +68,9 @@ enum tc358743_mode { tc358743_mode_INIT, /*only for sensor init*/ tc358743_mode_INIT1, /*only for sensor init*/ - tc358743_mode_480P_720_480, + tc358743_mode_480P_720_480, tc358743_mode_720P_60_1280_720, - tc358743_mode_480P_640_480, + tc358743_mode_480P_640_480, tc358743_mode_1080P_1920_1080, tc358743_mode_INIT2, /*only for sensor init*/ tc358743_mode_INIT3, /*only for sensor init*/ @@ -129,19 +129,16 @@ static int tc358743_toggle_hpd(int active); static void det_work_enable(int i) { mutex_lock(&access_lock); - if(i) - { + if (i) { det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); det_work_disable = 0; - } - else - { + } else { det_work_disable = 1; det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; } mutex_unlock(&access_lock); - printk(KERN_DEBUG "%s: %d %d\n", __FUNCTION__, det_work_disable, det_work_timeout); + pr_debug("%s: %d %d\n", __func__, det_work_disable, det_work_timeout); } static u8 cHDMIEDID[256]; @@ -159,7 +156,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz {0x0006, 0x00000040, 0x00000000, 2, 0}, {0x0014, 0x00000000, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x0000402d, 0x00000000, 2, 0}, {0x0022, 0x00000213, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -252,7 +249,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0014, 0x0000ffff, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x00004062, 0x00000000, 2, 0}, {0x0022, 0x00000613, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -339,7 +336,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[ {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x0000405c, 0x00000000, 2, 0}, {0x0022, 0x00000613, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -402,7 +399,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[ {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x0000405c, 0x00000000, 2, 0}, {0x0022, 0x00000613, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -466,7 +463,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[ {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x00004050, 0x00000000, 2, 0}, {0x0022, 0x00000213, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -530,7 +527,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[ {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x000080c7, 0x00000000, 2, 0}, {0x0022, 0x00000213, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -593,7 +590,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x000080c7, 0x00000000, 2, 0}, {0x0022, 0x00000213, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -656,7 +653,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x00008073, 0x00000000, 2, 0}, {0x0022, 0x00000213, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -719,7 +716,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_c {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x0000404F, 0x00000000, 2, 0}, {0x0022, 0x00000613, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -786,7 +783,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = { // {0x0010, 0x0000001e, 0x00000000, 2, 0}, {0x0014, 0x00000000, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x0000405c, 0x00000000, 2, 0}, {0x0022, 0x00000613, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -877,7 +874,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = { // {0x0010, 0x0000001e, 0x00000000, 2, 0}, {0x0014, 0x00000000, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x0000405b, 0x00000000, 2, 0}, {0x0022, 0x00000613, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -965,7 +962,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300M {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0014, 0x00000000, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, -// Program CSI Tx PLL +// Program CSI Tx PLL {0x0020, 0x000080c7, 0x00000000, 2, 0}, {0x0022, 0x00000213, 0x00000000, 2, 0}, // CSI Tx PHY (32-bit Registers) @@ -1046,70 +1043,70 @@ static struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300M }; static struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, // IR control resister - {0x0004, 0x00000084, 0x00000000, 2, 0}, // Internal Generated output pattern,Do not send InfoFrame data out to CSI2,Audio output to CSI2-TX i/f,I2C address index increments on every data byte transfer, disable audio and video TX buffers - {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, // Reset devices and set normal operatio (not sleep) - {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, // Clear reset bits - {0x0006, 0x000001f8, 0x00000000, 2, 0}, // FIFO level = 1f8 = 504 - {0x0014, 0x00000000, 0x00000000, 2, 0}, // Clear interrupt status bits - {0x0016, 0x000005ff, 0x00000000, 2, 0}, // Mask audio mute, CSI-TX, and the other interrups -// Program CSI Tx PLL - //{0x0020, 0x000080c7, 0x00000000, 2, 0}, // Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200 - {0x0020, 0x000080c7, 0x00000000, 2, 0}, // Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200 - {0x0022, 0x00000213, 0x00000000, 2, 0}, // HSCK frequency = 500MHz – 1GHz HSCK frequency, Loop bandwidth setting = 50% of maximum loop bandwidth (default), REFCLK toggling –> normal operation, REFCLK stops -> no oscillation, Bypass clock = normal operation, clocks switched off (output LOW), PLL Reset normal operation, PLL Enable = PLL on + {0x7080, 0x00000000, 0x00000000, 2, 0}, // IR control resister + {0x0004, 0x00000084, 0x00000000, 2, 0}, // Internal Generated output pattern,Do not send InfoFrame data out to CSI2,Audio output to CSI2-TX i/f,I2C address index increments on every data byte transfer, disable audio and video TX buffers + {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, // Reset devices and set normal operatio (not sleep) + {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, // Clear reset bits + {0x0006, 0x000001f8, 0x00000000, 2, 0}, // FIFO level = 1f8 = 504 + {0x0014, 0x00000000, 0x00000000, 2, 0}, // Clear interrupt status bits + {0x0016, 0x000005ff, 0x00000000, 2, 0}, // Mask audio mute, CSI-TX, and the other interrups +// Program CSI Tx PLL + //{0x0020, 0x000080c7, 0x00000000, 2, 0}, // Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200 + {0x0020, 0x000080c7, 0x00000000, 2, 0}, // Input divider setting = 0x8 -> Division ratio = (PRD3..0) + 1 = 9, Feedback divider setting = 0xc7 -> Division ratio = (FBD8...0) + 1 = 200 + {0x0022, 0x00000213, 0x00000000, 2, 0}, // HSCK frequency = 500MHz – 1GHz HSCK frequency, Loop bandwidth setting = 50% of maximum loop bandwidth (default), REFCLK toggling –> normal operation, REFCLK stops -> no oscillation, Bypass clock = normal operation, clocks switched off (output LOW), PLL Reset normal operation, PLL Enable = PLL on // CSI Tx PHY (32-bit Registers) - {0x0140, 0x00000000, 0x00000000, 4, 0}, // Clock Lane DPHY Control: Bypass Lane Enable from PPI Layer enable. - {0x0144, 0x00000000, 0x00000000, 4, 0}, // Data Lane 0 DPHY Control: Bypass Lane Enable from PPI Layer enable. - {0x0148, 0x00000000, 0x00000000, 4, 0}, // Data Lane 1 DPHY Control: Bypass Lane Enable from PPI Layer enable. - {0x014c, 0x00000000, 0x00000000, 4, 0}, // Data Lane 2 DPHY Control: Bypass Lane Enable from PPI Layer enable. - {0x0150, 0x00000000, 0x00000000, 4, 0}, // Data Lane 3 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0140, 0x00000000, 0x00000000, 4, 0}, // Clock Lane DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0144, 0x00000000, 0x00000000, 4, 0}, // Data Lane 0 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0148, 0x00000000, 0x00000000, 4, 0}, // Data Lane 1 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x014c, 0x00000000, 0x00000000, 4, 0}, // Data Lane 2 DPHY Control: Bypass Lane Enable from PPI Layer enable. + {0x0150, 0x00000000, 0x00000000, 4, 0}, // Data Lane 3 DPHY Control: Bypass Lane Enable from PPI Layer enable. // CSI Tx PPI (32-bit Registers) - {0x0210, 0x00001e00, 0x00000000, 4, 0}, // LINEINITCNT: Line Initialization Wait Counter = 0x1e00 = 7680 - {0x0214, 0x00000003, 0x00000000, 4, 0}, // LPTXTIMECNT: SYSLPTX Timing Generation Counter = 3 + {0x0210, 0x00001e00, 0x00000000, 4, 0}, // LINEINITCNT: Line Initialization Wait Counter = 0x1e00 = 7680 + {0x0214, 0x00000003, 0x00000000, 4, 0}, // LPTXTIMECNT: SYSLPTX Timing Generation Counter = 3 {0x0218, 0x00001402, 0x00000000, 4, 0}, // TCLK_HEADERCNT: TCLK_ZERO Counter = 0x14 = 20, TCLK_PREPARE Counter = 0x02 = 2 - {0x021c, 0x00000000, 0x00000000, 4, 0}, // TCLK_TRAILCNT: TCLK_TRAIL Counter = 0 - {0x0220, 0x00000003, 0x00000000, 4, 0}, // THS_HEADERCNT: THS_ZERO Counter = 0, THS_PREPARE Counter = 3 - {0x0224, 0x00004a00, 0x00000000, 4, 0}, // TWAKEUP: TWAKEUP Counter = 0x4a00 = 18944 - {0x0228, 0x00000008, 0x00000000, 4, 0}, // TCLK_POSTCNT: TCLK_POST Counter = 8 - {0x022c, 0x00000002, 0x00000000, 4, 0}, // THS_TRAILCNT: THS_TRAIL Counter = 2 - {0x0234, 0x0000001f, 0x00000000, 4, 0}, // HSTXVREGEN: Enable voltage regulators for lanes and clk - {0x0238, 0x00000001, 0x00000000, 4, 0}, // TXOPTIONCNTRL: Set Continuous Clock Mode - {0x0204, 0x00000001, 0x00000000, 4, 0}, // PPI STARTCNTRL: start PPI function - {0x0518, 0x00000001, 0x00000000, 4, 0}, // CSI_START: start - {0x0500, 0xa30080a6, 0x00000000, 4, 0}, // CSI Configuration Register: set register 0x040C with data 0x80a6 (CSI MOde, Disables the HTX_TO timer, High-Speed data transfer is performed to Tx, DSCClk Stays in HS mode when Data Lane goes to LP, 4 Data Lanes,The EOT packet is automatically granted at the end of HS transfer then is transmitted) + {0x021c, 0x00000000, 0x00000000, 4, 0}, // TCLK_TRAILCNT: TCLK_TRAIL Counter = 0 + {0x0220, 0x00000003, 0x00000000, 4, 0}, // THS_HEADERCNT: THS_ZERO Counter = 0, THS_PREPARE Counter = 3 + {0x0224, 0x00004a00, 0x00000000, 4, 0}, // TWAKEUP: TWAKEUP Counter = 0x4a00 = 18944 + {0x0228, 0x00000008, 0x00000000, 4, 0}, // TCLK_POSTCNT: TCLK_POST Counter = 8 + {0x022c, 0x00000002, 0x00000000, 4, 0}, // THS_TRAILCNT: THS_TRAIL Counter = 2 + {0x0234, 0x0000001f, 0x00000000, 4, 0}, // HSTXVREGEN: Enable voltage regulators for lanes and clk + {0x0238, 0x00000001, 0x00000000, 4, 0}, // TXOPTIONCNTRL: Set Continuous Clock Mode + {0x0204, 0x00000001, 0x00000000, 4, 0}, // PPI STARTCNTRL: start PPI function + {0x0518, 0x00000001, 0x00000000, 4, 0}, // CSI_START: start + {0x0500, 0xa30080a6, 0x00000000, 4, 0}, // CSI Configuration Register: set register 0x040C with data 0x80a6 (CSI MOde, Disables the HTX_TO timer, High-Speed data transfer is performed to Tx, DSCClk Stays in HS mode when Data Lane goes to LP, 4 Data Lanes,The EOT packet is automatically granted at the end of HS transfer then is transmitted) // HDMI Interrupt Mask - {0x8502, 0x00000001, 0x00000000, 1, 0}, // SYSTEM INTERRUPT: clear DDC power change detection interrupt - {0x8512, 0x000000fe, 0x00000000, 1, 0}, // SYS INTERRUPT MASK: DDC power change detection interrupt not masked - {0x8514, 0x00000000, 0x00000000, 1, 0}, // PACKET INTERRUPT MASK: unmask all - {0x8515, 0x00000000, 0x00000000, 1, 0}, // CBIT INTERRUPT MASK: unmask all - {0x8516, 0x00000000, 0x00000000, 1, 0}, // AUDIO INTERRUPT MASK: unmask all + {0x8502, 0x00000001, 0x00000000, 1, 0}, // SYSTEM INTERRUPT: clear DDC power change detection interrupt + {0x8512, 0x000000fe, 0x00000000, 1, 0}, // SYS INTERRUPT MASK: DDC power change detection interrupt not masked + {0x8514, 0x00000000, 0x00000000, 1, 0}, // PACKET INTERRUPT MASK: unmask all + {0x8515, 0x00000000, 0x00000000, 1, 0}, // CBIT INTERRUPT MASK: unmask all + {0x8516, 0x00000000, 0x00000000, 1, 0}, // AUDIO INTERRUPT MASK: unmask all // HDMI Audio RefClk (27 MHz) - {0x8531, 0x00000001, 0x00000000, 1, 0}, // PHY CONTROL0: 27MHz, DDC5V detection operation. - {0x8540, 0x00000a8c, 0x00000000, 1, 0}, // SYS FREQ0 Register: 27MHz - {0x8630, 0x00041eb0, 0x00000000, 1, 0}, // Audio FS Lock Detect Control: for 27MHz - {0x8670, 0x00000001, 0x00000000, 1, 0}, // AUDIO PLL Setting: For REFCLK = 27MHz + {0x8531, 0x00000001, 0x00000000, 1, 0}, // PHY CONTROL0: 27MHz, DDC5V detection operation. + {0x8540, 0x00000a8c, 0x00000000, 1, 0}, // SYS FREQ0 Register: 27MHz + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, // Audio FS Lock Detect Control: for 27MHz + {0x8670, 0x00000001, 0x00000000, 1, 0}, // AUDIO PLL Setting: For REFCLK = 27MHz // HDMI PHY - {0x8532, 0x00000080, 0x00000000, 1, 0}, // - {0x8536, 0x00000040, 0x00000000, 1, 0}, // - {0x853f, 0x0000000a, 0x00000000, 1, 0}, // + {0x8532, 0x00000080, 0x00000000, 1, 0}, // + {0x8536, 0x00000040, 0x00000000, 1, 0}, // + {0x853f, 0x0000000a, 0x00000000, 1, 0}, // // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, // DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms - {0x8544, 0x00000010, 0x00000000, 1, 100}, // HPD Control Register: HOTPLUG output ON/OFF control mode = DDC5V detection interlock - {0x8545, 0x00000031, 0x00000000, 1, 0}, // ANA CONTROL: PLL charge pump setting for Audio = normal, DAC/PLL power ON/OFF setting for Audio = ON - {0x8546, 0x0000002d, 0x00000000, 1, 0}, // AVMUTE CONTROL: AVM_CTL = 0x2d + {0x8543, 0x00000032, 0x00000000, 1, 0}, // DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms + {0x8544, 0x00000010, 0x00000000, 1, 100}, // HPD Control Register: HOTPLUG output ON/OFF control mode = DDC5V detection interlock + {0x8545, 0x00000031, 0x00000000, 1, 0}, // ANA CONTROL: PLL charge pump setting for Audio = normal, DAC/PLL power ON/OFF setting for Audio = ON + {0x8546, 0x0000002d, 0x00000000, 1, 0}, // AVMUTE CONTROL: AVM_CTL = 0x2d // EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, // EDID MODE REGISTER: nternal EDID-RAM & DDC2B mode - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // EDID Length REGISTER 2: EDID data size stored in RAM (upper address bits) = 0x1 (Size = 0x100 = 256) + {0x85c7, 0x00000001, 0x00000000, 1, 0}, // EDID MODE REGISTER: nternal EDID-RAM & DDC2B mode + {0x85cb, 0x00000001, 0x00000000, 1, 0}, // EDID Length REGISTER 2: EDID data size stored in RAM (upper address bits) = 0x1 (Size = 0x100 = 256) // HDCP Setting - {0x85d1, 0x00000001, 0x00000000, 1, 0}, // - {0x8560, 0x00000024, 0x00000000, 1, 0}, // HDCP MODE: HDCP automatic reset when DVI⇔HDMI switched = on, HDCP Line Rekey timing switch = 7clk mode (Data island delay ON), Bcaps[5] KSVINFO_READY(0x8840[5]) auto clear mode = Auto clear using AKSV write - {0x8563, 0x00000011, 0x00000000, 1, 0}, // - {0x8564, 0x0000000f, 0x00000000, 1, 0}, // + {0x85d1, 0x00000001, 0x00000000, 1, 0}, // + {0x8560, 0x00000024, 0x00000000, 1, 0}, // HDCP MODE: HDCP automatic reset when DVI⇔HDMI switched = on, HDCP Line Rekey timing switch = 7clk mode (Data island delay ON), Bcaps[5] KSVINFO_READY(0x8840[5]) auto clear mode = Auto clear using AKSV write + {0x8563, 0x00000011, 0x00000000, 1, 0}, // + {0x8564, 0x0000000f, 0x00000000, 1, 0}, // // RGB --> YUV Conversion - {0x8571, 0x00000002, 0x00000000, 1, 0}, // - {0x8573, 0x000000c1, 0x00000000, 1, 0}, // VOUT SET2 REGISTER: 422 fixed output, Video Output 422 conversion mode selection 000: During 444 input, 3tap filter; during 422 input, simple decimation, Enable RGB888 to YUV422 Conversion (Fixed Color output) + {0x8571, 0x00000002, 0x00000000, 1, 0}, // + {0x8573, 0x000000c1, 0x00000000, 1, 0}, // VOUT SET2 REGISTER: 422 fixed output, Video Output 422 conversion mode selection 000: During 444 input, 3tap filter; during 422 input, simple decimation, Enable RGB888 to YUV422 Conversion (Fixed Color output) {0x8574, 0x00000008, 0x00000000, 1, 0}, // VOUT SET3 REGISTER (VOUT_SET3): Follow register bit 0x8573[7] setting - {0x8576, 0x00000060, 0x00000000, 1, 0}, // VOUT_COLOR: Output Color = 601 YCbCr Limited, Input Pixel Repetition judgment = automatic, Input Pixel Repetition HOST setting = no repetition + {0x8576, 0x00000060, 0x00000000, 1, 0}, // VOUT_COLOR: Output Color = 601 YCbCr Limited, Input Pixel Repetition judgment = automatic, Input Pixel Repetition HOST setting = no repetition // HDMI Audio In Setting {0x8600, 0x00000000, 0x00000000, 1, 0}, {0x8602, 0x000000f3, 0x00000000, 1, 0}, @@ -1124,15 +1121,15 @@ static struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300M {0x8652, 0x00000002, 0x00000000, 1, 0}, {0x8665, 0x00000010, 0x00000000, 1, 0}, // InfoFrame Extraction - {0x8709, 0x000000ff, 0x00000000, 1, 0}, // PACKET INTERRUPT MODE: all enable - {0x870b, 0x0000002c, 0x00000000, 1, 0}, // NO PACKET LIMIT: NO_ACP_LIMIT = 0x2, NO_AVI_LIMIT = 0xC - {0x870c, 0x00000053, 0x00000000, 1, 0}, // When VS receive interrupt is detected, VS storage register automatic clear, When ACP receive interrupt is detected, ACP storage register automatic clear, When AVI receive interrupt occurs, judge input video signal with RGB and no Repetition, When AVI receive interrupt is detected, AVI storage register automatic clear. - {0x870d, 0x00000001, 0x00000000, 1, 0}, // ERROR PACKET LIMIT: Packet continuing receive error occurrence detection threshold = 1 - {0x870e, 0x00000030, 0x00000000, 1, 0}, // NO PACKET LIMIT: - {0x9007, 0x00000010, 0x00000000, 1, 0}, // - {0x854a, 0x00000001, 0x00000000, 1, 0}, // Initialization completed flag + {0x8709, 0x000000ff, 0x00000000, 1, 0}, // PACKET INTERRUPT MODE: all enable + {0x870b, 0x0000002c, 0x00000000, 1, 0}, // NO PACKET LIMIT: NO_ACP_LIMIT = 0x2, NO_AVI_LIMIT = 0xC + {0x870c, 0x00000053, 0x00000000, 1, 0}, // When VS receive interrupt is detected, VS storage register automatic clear, When ACP receive interrupt is detected, ACP storage register automatic clear, When AVI receive interrupt occurs, judge input video signal with RGB and no Repetition, When AVI receive interrupt is detected, AVI storage register automatic clear. + {0x870d, 0x00000001, 0x00000000, 1, 0}, // ERROR PACKET LIMIT: Packet continuing receive error occurrence detection threshold = 1 + {0x870e, 0x00000030, 0x00000000, 1, 0}, // NO PACKET LIMIT: + {0x9007, 0x00000010, 0x00000000, 1, 0}, // + {0x854a, 0x00000001, 0x00000000, 1, 0}, // Initialization completed flag // Output Control - {0x0004, 0x00000cf7, 0x00000000, 2, 0}, // Configuration Control Register: Power Island Normal, I2S/TDM clock are free running, Enable 2 Audio channels, Audio channel number Auto detect by HW, I2S/TDM Data no delay, Select YCbCr422 8-bit (HDMI YCbCr422 12-bit data format), Send InfoFrame data out to CSI2, Audio output to I2S i/f (valid for 2 channel only), I2C address index increments on every data byte transfer, Audio and Video tx buffres enable. + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, // Configuration Control Register: Power Island Normal, I2S/TDM clock are free running, Enable 2 Audio channels, Audio channel number Auto detect by HW, I2S/TDM Data no delay, Select YCbCr422 8-bit (HDMI YCbCr422 12-bit data format), Send InfoFrame data out to CSI2, Audio output to I2S i/f (valid for 2 channel only), I2C address index increments on every data byte transfer, Audio and Video tx buffres enable. }; /* list of image formats supported by TCM825X sensor */ @@ -1156,43 +1153,43 @@ static const struct v4l2_fmtdesc tc358743_formats[] = { static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = { - [0][tc358743_mode_720P_60_1280_720] = + [0][tc358743_mode_720P_60_1280_720] = {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), MIPI_DT_YUV422 - }, - [0][tc358743_mode_1080P_1920_1080] = + }, + [0][tc358743_mode_1080P_1920_1080] = {tc358743_mode_1080P_1920_1080, 1920, 1080, 15, 0x0b, 4, 300, tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz), MIPI_DT_YUV422 }, - [0][tc358743_mode_INIT1] = + [0][tc358743_mode_INIT1] = {tc358743_mode_INIT1, 1280, 720, 12, 0, 2, 125, tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - [0][tc358743_mode_INIT2] = + [0][tc358743_mode_INIT2] = {tc358743_mode_INIT2, 1280, 720, 12, 0, 4, 125, tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - [0][tc358743_mode_INIT] = + [0][tc358743_mode_INIT] = {tc358743_mode_INIT, 640, 480, 6, 1, 2, 108, tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), MIPI_DT_YUV422 }, - [0][tc358743_mode_INIT4] = + [0][tc358743_mode_INIT4] = {tc358743_mode_INIT4, 640, 480, 6, 1, 2, 174, tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), MIPI_DT_YUV422 - }, - [0][tc358743_mode_INIT3] = + }, + [0][tc358743_mode_INIT3] = {tc358743_mode_INIT3, 1024, 720, 6, 1, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), @@ -1209,63 +1206,63 @@ static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), MIPI_DT_YUV422, - }, + }, [0][tc358743_mode_480P_640_480] = {tc358743_mode_480P_640_480, 640, 480, 6, (0x02)<<8|(0x00), 2, 125, tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), MIPI_DT_YUV422, - }, - [0][tc358743_mode_INIT5] = + }, + [0][tc358743_mode_INIT5] = {tc358743_mode_INIT5, 1280, 720, 12, 0, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), MIPI_DT_YUV422 - }, - [0][tc358743_mode_INIT6] = + }, + [0][tc358743_mode_INIT6] = {tc358743_mode_INIT6, 1920, 1023, 15, 0, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), MIPI_DT_YUV422 - }, - [1][tc358743_mode_720P_60_1280_720] = + }, + [1][tc358743_mode_720P_60_1280_720] = {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), MIPI_DT_YUV422 - }, - [1][tc358743_mode_1080P_1920_1080] = + }, + [1][tc358743_mode_1080P_1920_1080] = {tc358743_mode_1080P_1920_1080, 1920, 1080, 15, 0xa, 4, 300, tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT1] = + [1][tc358743_mode_INIT1] = {tc358743_mode_INIT1, 1280, 720, 12, 0, 2, 125, tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT2] = + [1][tc358743_mode_INIT2] = {tc358743_mode_INIT2, 1280, 720, 12, 0, 4, 125, tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - - [1][tc358743_mode_INIT] = + + [1][tc358743_mode_INIT] = {tc358743_mode_INIT, 640, 480, 6, 1, 2, 108, tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT4] = + [1][tc358743_mode_INIT4] = {tc358743_mode_INIT4, 640, 480, 6, 1, 2, 174, tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), MIPI_DT_YUV422 - }, - [1][tc358743_mode_INIT3] = + }, + [1][tc358743_mode_INIT3] = {tc358743_mode_INIT3, 1024, 720, 6, 1, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), @@ -1282,20 +1279,20 @@ static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), MIPI_DT_YUV422, - }, + }, [0][tc358743_mode_480P_640_480] = {tc358743_mode_480P_640_480, 640, 480, 1, (0x02)<<8|(0x00), 2, 125, tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), MIPI_DT_YUV422, - }, - [1][tc358743_mode_INIT5] = + }, + [1][tc358743_mode_INIT5] = {tc358743_mode_INIT5, 1280, 720, 12, 0, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), MIPI_DT_YUV422 - }, - [1][tc358743_mode_INIT6] = + }, + [1][tc358743_mode_INIT6] = {tc358743_mode_INIT6, 1920, 1023, 15, 0, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), @@ -1352,7 +1349,7 @@ tc358743_read_reg_size [] = {0x8c00, 0x8fff, 4}, {0x9000, 0x90ff, 1}, {0x9100, 0x92ff, 1}, - {0, 0, 0}, + {0, 0, 0}, }; static s32 tc358743_write_reg(u16 reg, u32 val, int len) @@ -1362,40 +1359,33 @@ static s32 tc358743_write_reg(u16 reg, u32 val, int len) u8 au8Buf[6] = {0}; int size = 0; - while(0 != tc358743_read_reg_size[i].startaddr || + while (0 != tc358743_read_reg_size[i].startaddr || 0 != tc358743_read_reg_size[i].endaddr || - 0 != tc358743_read_reg_size[i].size) - { - if(tc358743_read_reg_size[i].startaddr <= reg && tc358743_read_reg_size[i].endaddr >= reg) - { + 0 != tc358743_read_reg_size[i].size) { + if (tc358743_read_reg_size[i].startaddr <= reg + && tc358743_read_reg_size[i].endaddr >= reg) { size = tc358743_read_reg_size[i].size; break; } i++; - } - if(!size) - { + } + if (!size) { pr_err("%s:write reg error:reg=%x is not found\n",__func__, reg); return -1; } - if(size == 3) - { + if (size == 3) { size = 2; - } - else - if(size != len) - { - pr_err("%s:write reg len error:reg=%x %d instead of %d\n", + } else if (size != len) { + pr_err("%s:write reg len error:reg=%x %d instead of %d\n", __func__, reg, len, size); - return 0; - } - - while(len > 0) - { + return 0; + } + + while (len > 0) { i = 0; au8Buf[i++] = (reg >> 8) & 0xff; au8Buf[i++] = reg & 0xff; - while(size-- > 0) + while (size-- > 0) { au8Buf[i++] = (u8)data; data >>= 8; @@ -1409,7 +1399,7 @@ static s32 tc358743_write_reg(u16 reg, u32 val, int len) len -= (u8)size; reg += (u16)size; } - + return 0; } @@ -1420,22 +1410,19 @@ static s32 tc358743_read_reg(u16 reg, u32 *val) int i=0; int size = 0; - while(0 != tc358743_read_reg_size[i].startaddr || + while (0 != tc358743_read_reg_size[i].startaddr || 0 != tc358743_read_reg_size[i].endaddr || - 0 != tc358743_read_reg_size[i].size) - { - if(tc358743_read_reg_size[i].startaddr <= reg && tc358743_read_reg_size[i].endaddr >= reg) - { - size = tc358743_read_reg_size[i].size; - break; + 0 != tc358743_read_reg_size[i].size) { + if (tc358743_read_reg_size[i].startaddr <= reg && + tc358743_read_reg_size[i].endaddr >= reg) { + size = tc358743_read_reg_size[i].size; + break; + } + i++; } - i++; - } - if(!size) - { + if (!size) return -1; - } - + au8RegBuf[0] = reg >> 8; au8RegBuf[1] = reg & 0xff; @@ -1464,14 +1451,12 @@ static int tc358743_write_edid(u8 *edid, int len) reg = 0x8C00; off = 0; size = ARRAY_SIZE(au8Buf)-2; - printk(KERN_DEBUG "Write EDID: %d (%d)\n", len, size); - while(len > 0) - { + pr_debug("Write EDID: %d (%d)\n", len, size); + while (len > 0) { i = 0; au8Buf[i++] = (reg >> 8) & 0xff; au8Buf[i++] = reg & 0xff; - while(i < ARRAY_SIZE(au8Buf)) - { + while (i < ARRAY_SIZE(au8Buf)) { au8Buf[i++] = edid[off++]; } @@ -1481,9 +1466,9 @@ static int tc358743_write_edid(u8 *edid, int len) return -1; } len -= (u8)size; - reg += (u16)size; + reg += (u16)size; } - printk(KERN_DEBUG "Activate EDID\n"); + pr_debug("Activate EDID\n"); tc358743_write_reg(0x85c7, 0x01, 1); tc358743_write_reg(0x85ca, 0x00, 1); tc358743_write_reg(0x85cb, 0x01, 1); @@ -1495,33 +1480,31 @@ static int tc358743_reset(struct sensor_data *sensor) u32 tgt_fps; /* target frames per secound */ enum tc358743_frame_rate frame_rate = tc358743_60_fps; int ret = -1; - + det_work_enable(0); - while(ret) - { - if (camera_plat->pwdn) - { - printk(KERN_DEBUG "%s: RESET\n", __FUNCTION__); - camera_plat->pwdn(1); - mdelay(100); - camera_plat->pwdn(0); - mdelay(1000); - } - - tgt_fps = sensor->streamcap.timeperframe.denominator / - sensor->streamcap.timeperframe.numerator; + while (ret) { + if (camera_plat->pwdn) { + pr_debug("%s: RESET\n", __func__); + camera_plat->pwdn(1); + mdelay(100); + camera_plat->pwdn(0); + mdelay(1000); + } - if (tgt_fps == 60) - frame_rate = tc358743_60_fps; - else if (tgt_fps == 30) - frame_rate = tc358743_30_fps; - - printk(KERN_DEBUG "%s: capture mode: %d extended mode: %d fps: %d\n", __FUNCTION__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode, tgt_fps); - - ret = tc358743_init_mode(frame_rate, - sensor->streamcap.capturemode); - if(ret) - printk(KERN_ERR "%s: Fail to init tc35874! - retry\n", __FUNCTION__); + tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 60) + frame_rate = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate = tc358743_30_fps; + + pr_debug("%s: capture mode: %d extended mode: %d fps: %d\n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode, tgt_fps); + + ret = tc358743_init_mode(frame_rate, + sensor->streamcap.capturemode); + if (ret) + pr_err("%s: Fail to init tc35874! - retry\n", __func__); } det_work_enable(1); return ret; @@ -1532,9 +1515,9 @@ void mipi_csi2_swreset(struct mipi_csi2_info *info); static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, enum tc358743_mode mode) { - + struct reg_value *pModeSetting = NULL; - s32 i = 0; + s32 i = 0; s32 iModeSettingArySize = 0; register u32 RepeateLines = 0; register int RepeateTimes = 0; @@ -1547,22 +1530,22 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, int retval = 0; void *mipi_csi2_info; u32 mipi_reg; - u32 mipi_reg_test[10]; + u32 mipi_reg_test[10]; - printk(KERN_DEBUG "%s rate: %d mode: %d\n", __FUNCTION__, frame_rate, mode); + pr_debug("%s rate: %d mode: %d\n", __func__, frame_rate, mode); if ((mode > tc358743_mode_MAX || mode < 0) && (mode != tc358743_mode_INIT)) { - printk(KERN_DEBUG "%s Wrong tc358743 mode detected! %d. Set mode 0\n", __FUNCTION__, mode); + pr_debug("%s Wrong tc358743 mode detected! %d. Set mode 0\n", __func__, mode); mode = 0; } mipi_csi2_info = mipi_csi2_get_info(); - printk(KERN_DEBUG "%s rate: %d mode: %d, info %p\n", __FUNCTION__, frame_rate, mode, mipi_csi2_info); - + pr_debug("%s rate: %d mode: %d, info %p\n", __func__, frame_rate, mode, mipi_csi2_info); + /* initial mipi dphy */ tc358743_toggle_hpd(!hpd_active); if (mipi_csi2_info) { - printk(KERN_DEBUG "%s: mipi_csi2_info:\n" + pr_debug("%s: mipi_csi2_info:\n" "mipi_en: %d\n" "ipu_id: %d\n" "csi_id: %d\n" @@ -1573,7 +1556,7 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, "pixel_clk: %p\n" "mipi_csi2_base:%p\n" "pdev: %p\n" - , __FUNCTION__, + , __func__, ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en, ((struct mipi_csi2_info *)mipi_csi2_info)->ipu_id, ((struct mipi_csi2_info *)mipi_csi2_info)->csi_id, @@ -1583,46 +1566,43 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, ((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk, ((struct mipi_csi2_info *)mipi_csi2_info)->pixel_clk, ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_csi2_base, - ((struct mipi_csi2_info *)mipi_csi2_info)->pdev + ((struct mipi_csi2_info *)mipi_csi2_info)->pdev ); if (!mipi_csi2_get_status(mipi_csi2_info)) mipi_csi2_enable(mipi_csi2_info); if (mipi_csi2_get_status(mipi_csi2_info)) { int ifmt; - if(tc358743_mode_info_data[frame_rate][mode].lanes != 0) - { - printk(KERN_DEBUG "%s Change lanes: from %d to %d\n", __FUNCTION__, ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, tc358743_mode_info_data[frame_rate][mode].lanes); + if (tc358743_mode_info_data[frame_rate][mode].lanes != 0) { + pr_debug("%s Change lanes: from %d to %d\n", __func__, ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, tc358743_mode_info_data[frame_rate][mode].lanes); ((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes; ((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes; } - printk(KERN_DEBUG "Now Using %d lanes\n",mipi_csi2_set_lanes(mipi_csi2_info)); + pr_debug("Now Using %d lanes\n",mipi_csi2_set_lanes(mipi_csi2_info)); /*Only reset MIPI CSI2 HW at sensor initialize*/ - if(!hdmi_mode) // is this during reset + if (!hdmi_mode) // is this during reset mipi_csi2_reset(mipi_csi2_info); - printk(KERN_DEBUG "%s format: %x\n", __FUNCTION__, tc358743_data.pix.pixelformat); + pr_debug("%s format: %x\n", __func__, tc358743_data.pix.pixelformat); for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) - if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) - { - tc358743_data.pix.pixelformat = tc358743_formats[ifmt].pixelformat; - printk(KERN_DEBUG "%s: %s (%x, %x)\n", __FUNCTION__, tc358743_formats[ifmt].description, tc358743_data.pix.pixelformat, tc358743_formats[ifmt].flags); - mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags); - break; - } - if(ifmt >= ARRAY_SIZE(tc358743_formats)) - { - printk(KERN_ERR "currently this sensor format (0x%x) can not be supported!\n", tc358743_data.pix.pixelformat); - return -1; + if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) { + tc358743_data.pix.pixelformat = tc358743_formats[ifmt].pixelformat; + pr_debug("%s: %s (%x, %x)\n", __func__, tc358743_formats[ifmt].description, tc358743_data.pix.pixelformat, tc358743_formats[ifmt].flags); + mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags); + break; + } + if (ifmt >= ARRAY_SIZE(tc358743_formats)) { + pr_err("currently this sensor format (0x%x) can not be supported!\n", tc358743_data.pix.pixelformat); + return -1; } } else { - printk(KERN_ERR "Can not enable mipi csi2 driver!\n"); + pr_err("Can not enable mipi csi2 driver!\n"); return -1; } } else { - printk(KERN_ERR "Fail to get mipi_csi2_info!\n"); + pr_err("Fail to get mipi_csi2_info!\n"); return -1; } @@ -1636,7 +1616,7 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, tc358743_mode_info_data[frame_rate][mode].width; tc358743_data.pix.height = tc358743_mode_info_data[frame_rate][mode].height; - printk(KERN_DEBUG "%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __FUNCTION__, + pr_debug("%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __func__, iModeSettingArySize, pModeSetting, frame_rate, @@ -1667,40 +1647,34 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, if (Delay_ms) msleep(Delay_ms); - - if(0 != ((pModeSetting->u32Delay_ms>>16) & (0xff))) - { - if(!RepeateTimes) - { + + if (0 != ((pModeSetting->u32Delay_ms>>16) & (0xff))) { + if (!RepeateTimes) { RepeateTimes = (pModeSetting->u32Delay_ms>>16) & (0xff); RepeateLines = (pModeSetting->u32Delay_ms>>24) & (0xff); } - if(--RepeateTimes > 0) - { + if (--RepeateTimes > 0) { i -= RepeateLines; } } } - if(retval < 0) - { - printk(KERN_ERR "%s: Fail to write REGS to tc35874!\n", __FUNCTION__); + if (retval < 0) { + pr_err("%s: Fail to write REGS to tc35874!\n", __func__); goto err; } - } - if(!hdmi_mode) // is this during reset - if((retval = tc358743_write_edid(cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) - printk(KERN_ERR "%s: Fail to write EDID to tc35874!\n", __FUNCTION__); - + } + if (!hdmi_mode) // is this during reset + if ((retval = tc358743_write_edid(cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) + pr_err("%s: Fail to write EDID to tc35874!\n", __func__); + tc358743_toggle_hpd(hpd_active); if (mipi_csi2_info) { - unsigned int i; - - i = 0; + unsigned int i = 0; /* wait for mipi sensor ready */ mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); while ((mipi_reg == 0x200) && (i < 10)) { - mipi_reg_test[i] = mipi_reg; + mipi_reg_test[i] = mipi_reg; mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); i++; msleep(10); @@ -1711,20 +1685,20 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, return -1; } - { - int j; - for (j = 0; j < i; j++) - { - printk(KERN_DEBUG "%d mipi csi2 dphy status %x\n", j, mipi_reg_test[j]); - } - } + { + int j; + for (j = 0; j < i; j++) + { + pr_debug("%d mipi csi2 dphy status %x\n", j, mipi_reg_test[j]); + } + } i = 0; /* wait for mipi stable */ mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); while ((mipi_reg != 0x0) && (i < 10)) { - mipi_reg_test[i] = mipi_reg; + mipi_reg_test[i] = mipi_reg; mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); i++; msleep(10); @@ -1735,13 +1709,12 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, return -1; } - { - int j; - for (j = 0; j < i; j++) - { - printk(KERN_DEBUG "%d mipi csi2 err1 %x\n", j, mipi_reg_test[j]); - } - } + { + int j; + for (j = 0; j < i; j++) { + pr_debug("%d mipi csi2 err1 %x\n", j, mipi_reg_test[j]); + } + } } err: return (retval>0)?0:retval; @@ -1751,7 +1724,7 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) { - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); if (s == NULL) { pr_err(" ERROR!! no slave device set!\n"); return -1; @@ -1759,7 +1732,7 @@ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) memset(p, 0, sizeof(*p)); p->u.bt656.clock_curr = TC358743_XCLK_MIN; //tc358743_data.mclk; - printk(KERN_DEBUG "%s: clock_curr=mclk=%d\n", __FUNCTION__, tc358743_data.mclk); + pr_debug("%s: clock_curr=mclk=%d\n", __func__, tc358743_data.mclk); p->if_type = V4L2_IF_TYPE_BT656; p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; p->u.bt656.clock_min = TC358743_XCLK_MIN; @@ -1781,7 +1754,7 @@ static int ioctl_s_power(struct v4l2_int_device *s, int on) { struct sensor_data *sensor = s->priv; - printk(KERN_DEBUG "%s: %d\n", __FUNCTION__, on); + pr_debug("%s: %d\n", __func__, on); if (on && !sensor->on) { if (io_regulator) if (regulator_enable(io_regulator) != 0) @@ -1808,7 +1781,7 @@ static int ioctl_s_power(struct v4l2_int_device *s, int on) regulator_disable(io_regulator); if (gpo_regulator) regulator_disable(gpo_regulator); - if(!hdmi_mode) + if (!hdmi_mode) tc358743_reset(sensor); } @@ -1830,7 +1803,7 @@ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) struct v4l2_captureparm *cparm = &a->parm.capture; int ret = 0; - printk(KERN_DEBUG "%s type: %x\n", __FUNCTION__, a->type); + pr_debug("%s type: %x\n", __func__, a->type); switch (a->type) { /* This is the only case currently handled. */ case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -1860,21 +1833,18 @@ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } det_work_enable(1); - printk(KERN_DEBUG "%s done %d\n", __FUNCTION__, ret); + pr_debug("%s done %d\n", __func__, ret); return ret; } static int tc358743_toggle_hpd(int active) { int ret = 0; - if(active) - { + if (active) { ret += tc358743_write_reg(0x8544, 0x00, 1); mdelay(500); ret += tc358743_write_reg(0x8544, 0x10, 1); - } - else - { + } else { ret += tc358743_write_reg(0x8544, 0x10, 1); mdelay(500); ret += tc358743_write_reg(0x8544, 0x00, 1); @@ -1899,7 +1869,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) enum tc358743_frame_rate frame_rate = tc358743_60_fps, frame_rate_now = tc358743_60_fps; int ret = 0; - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); det_work_enable(0); /* Make sure power on */ if (camera_plat->pwdn) @@ -1940,12 +1910,11 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) break; } - if((u32)a->parm.capture.capturemode > tc358743_mode_MAX) - { - a->parm.capture.capturemode = 0; - printk(KERN_DEBUG "%s: Forse extended mode: %d \n", __FUNCTION__,(u32)a->parm.capture.capturemode); + if ((u32)a->parm.capture.capturemode > tc358743_mode_MAX) { + a->parm.capture.capturemode = 0; + pr_debug("%s: Forse extended mode: %d \n", __func__,(u32)a->parm.capture.capturemode); } - + tgt_fps = sensor->streamcap.timeperframe.denominator / sensor->streamcap.timeperframe.numerator; @@ -1953,24 +1922,23 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) frame_rate_now = tc358743_60_fps; else if (tgt_fps == 30) frame_rate_now = tc358743_30_fps; - - if(frame_rate_now != frame_rate || + + if (frame_rate_now != frame_rate || sensor->streamcap.capturemode != (u32)a->parm.capture.capturemode || - sensor->streamcap.extendedmode != (u32)a->parm.capture.extendedmode) - { + sensor->streamcap.extendedmode != (u32)a->parm.capture.extendedmode) { sensor->streamcap.timeperframe = *timeperframe; sensor->streamcap.capturemode = (u32)a->parm.capture.capturemode; sensor->streamcap.extendedmode = (u32)a->parm.capture.extendedmode; - printk(KERN_DEBUG "%s: capture mode: %d extended mode: %d \n", __FUNCTION__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode); - - ret = tc358743_init_mode(frame_rate, - sensor->streamcap.capturemode); + pr_debug("%s: capture mode: %d extended mode: %d \n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode); + + ret = tc358743_init_mode(frame_rate, + sensor->streamcap.capturemode); + } else { + pr_debug("%s: Keep current settings\n", __func__); } - else - printk(KERN_DEBUG "%s: Keep current settings\n", __FUNCTION__); break; /* These are all the possible cases. */ @@ -1992,7 +1960,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) break; } - if(ret) + if (ret) det_work_enable(1); return ret; } @@ -2010,7 +1978,7 @@ static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) { int ret = 0; - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); switch (vc->id) { case V4L2_CID_BRIGHTNESS: vc->value = tc358743_data.brightness; @@ -2095,12 +2063,11 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) int get_pixelformat(int index) { -int ifmt; + int ifmt; + for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) if (tc358743_mode_info_data[0][index].flags == tc358743_formats[ifmt].flags) - { break; - } if (ifmt == ARRAY_SIZE(tc358743_formats)) ifmt = 0; /* Default = RBG888 */ @@ -2118,16 +2085,16 @@ int ifmt; static int ioctl_enum_framesizes(struct v4l2_int_device *s, struct v4l2_frmsizeenum *fsize) { - printk(KERN_DEBUG "%s, INDEX: %d\n", __FUNCTION__,fsize->index); + pr_debug("%s, INDEX: %d\n", __func__,fsize->index); if (fsize->index > tc358743_mode_MAX) return -EINVAL; fsize->pixel_format = tc358743_formats[get_pixelformat(fsize->index)].pixelformat; - fsize->discrete.width = + fsize->discrete.width = tc358743_mode_info_data[0][fsize->index].width; - fsize->discrete.height = + fsize->discrete.height = tc358743_mode_info_data[0][fsize->index].height; - printk(KERN_DEBUG "%s %d:%d format: %x\n", __FUNCTION__, fsize->discrete.width, fsize->discrete.height, fsize->pixel_format); + pr_debug("%s %d:%d format: %x\n", __func__, fsize->discrete.width, fsize->discrete.height, fsize->pixel_format); return 0; } @@ -2155,7 +2122,7 @@ static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) */ static int ioctl_init(struct v4l2_int_device *s) { - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); return 0; } @@ -2169,13 +2136,13 @@ static int ioctl_init(struct v4l2_int_device *s) static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) { - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); if (fmt->index > tc358743_mode_MAX) return -EINVAL; fmt->pixelformat = tc358743_formats[get_pixelformat(fmt->index)].pixelformat; - printk(KERN_DEBUG "%s: format: %x\n", __FUNCTION__, fmt->pixelformat); + pr_debug("%s: format: %x\n", __func__, fmt->pixelformat); return 0; } @@ -2188,33 +2155,30 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, // enum image_size isize; int ifmt; struct v4l2_pix_format *pix = &f->fmt.pix; - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); tgt_fps = sensor->streamcap.timeperframe.denominator / sensor->streamcap.timeperframe.numerator; - - if (tgt_fps == 60) + + if (tgt_fps == 60) { frame_rate = tc358743_60_fps; - else if (tgt_fps == 30) + } else if (tgt_fps == 30) { frame_rate = tc358743_30_fps; - else - { - printk(KERN_DEBUG "%s: %d fps (%d,%d) is not supported\n", __FUNCTION__, tgt_fps, sensor->streamcap.timeperframe.denominator,sensor->streamcap.timeperframe.numerator); - return -EINVAL; + } else { + pr_debug("%s: %d fps (%d,%d) is not supported\n", __func__, tgt_fps, sensor->streamcap.timeperframe.denominator,sensor->streamcap.timeperframe.numerator); + return -EINVAL; } - + tc358743_data.pix.width = pix->width = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].width; tc358743_data.pix.height = pix->height = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].height; for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) if (tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].flags == tc358743_formats[ifmt].flags) - { break; - } if (ifmt == ARRAY_SIZE(tc358743_formats)) ifmt = 0; /* Default = RBG888 */ - + tc358743_data.pix.pixelformat = pix->pixelformat = tc358743_formats[ifmt].pixelformat; pix->field = V4L2_FIELD_NONE; pix->bytesperline = pix->width * 4; @@ -2228,18 +2192,20 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, break; } - u32 u32val; - int ret = tc358743_read_reg(0x8520,&u32val); - printk(KERN_DEBUG "SYS_STATUS: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8521,&u32val); - printk(KERN_DEBUG "VI_STATUS0: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8522,&u32val); - printk(KERN_DEBUG "VI_STATUS1: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8525,&u32val); - printk(KERN_DEBUG "VI_STATUS2: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8528,&u32val); - printk(KERN_DEBUG "VI_STATUS3: 0x%x, ret val: %d \n",u32val,ret); - printk(KERN_DEBUG "%s %d:%d format: %x\n", __FUNCTION__, pix->width, pix->height, pix->pixelformat); + { + u32 u32val; + int ret = tc358743_read_reg(0x8520,&u32val); + pr_debug("SYS_STATUS: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8521,&u32val); + pr_debug("VI_STATUS0: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8522,&u32val); + pr_debug("VI_STATUS1: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8525,&u32val); + pr_debug("VI_STATUS2: 0x%x, ret val: %d \n",u32val,ret); + ret = tc358743_read_reg(0x8528,&u32val); + pr_debug("VI_STATUS3: 0x%x, ret val: %d \n",u32val,ret); + pr_debug("%s %d:%d format: %x\n", __func__, pix->width, pix->height, pix->pixelformat); + } return 0; } @@ -2254,7 +2220,7 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); return ioctl_try_fmt_cap(s, f); } @@ -2273,7 +2239,7 @@ static int ioctl_dev_init(struct v4l2_int_device *s) enum tc358743_frame_rate frame_rate; void *mipi_csi2_info; - printk(KERN_DEBUG "%s\n", __FUNCTION__); + pr_debug("%s\n", __func__); tc358743_data.on = true; /* mclk */ @@ -2282,9 +2248,9 @@ static int ioctl_dev_init(struct v4l2_int_device *s) tgt_xclk = max(tgt_xclk, (u32)TC358743_XCLK_MIN); tc358743_data.mclk = tgt_xclk; - printk(KERN_DEBUG "%s: Setting mclk to %d MHz\n", __FUNCTION__, tc358743_data.mclk / 1000000); + pr_debug("%s: Setting mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); set_mclk_rate(&tc358743_data.mclk, tc358743_data.mclk_source); - printk(KERN_DEBUG "%s: After mclk to %d MHz\n", __FUNCTION__, tc358743_data.mclk / 1000000); + pr_debug("%s: After mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); /* Default camera frame rate is set in probe */ tgt_fps = sensor->streamcap.timeperframe.denominator / @@ -2295,19 +2261,19 @@ static int ioctl_dev_init(struct v4l2_int_device *s) else if (tgt_fps == 30) frame_rate = tc358743_30_fps; else - return -EINVAL; + return -EINVAL; mipi_csi2_info = mipi_csi2_get_info(); /* enable mipi csi2 */ - if (mipi_csi2_info) + if (mipi_csi2_info) { mipi_csi2_enable(mipi_csi2_info); - else { - printk(KERN_ERR "Fail to get mipi_csi2_info!\n"); + } else { + pr_err("Fail to get mipi_csi2_info!\n"); return -EPERM; } - - printk(KERN_DEBUG "%s done\n", __FUNCTION__); + + pr_debug("%s done\n", __func__); return ret; } @@ -2372,6 +2338,46 @@ static struct v4l2_int_device tc358743_int_device = { #ifdef AUDIO_ENABLE +struct imx_ssi { + struct platform_device *ac97_dev; + + struct snd_soc_dai *imx_ac97; + struct clk *clk; + void __iomem *base; + int irq; + int fiq_enable; + unsigned int offset; + + unsigned int flags; + + void (*ac97_reset) (struct snd_ac97 *ac97); + void (*ac97_warm_reset)(struct snd_ac97 *ac97); + + struct imx_pcm_dma_params dma_params_rx; + struct imx_pcm_dma_params dma_params_tx; + + int enabled; + + struct platform_device *soc_platform_pdev; + struct platform_device *soc_platform_pdev_fiq; +}; +#define SSI_SCR 0x10 +#define SSI_SRCR 0x20 +#define SSI_STCCR 0x24 +#define SSI_SRCCR 0x28 +#define SSI_SCR_I2S_MODE_NORM (0 << 5) +#define SSI_SCR_I2S_MODE_MSTR (1 << 5) +#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) +#define SSI_I2S_MODE_MASK (3 << 5) +#define SSI_SCR_SYN (1 << 4) +#define SSI_SRCR_RSHFD (1 << 4) +#define SSI_SRCR_RSCKP (1 << 3) +#define SSI_SRCR_RFSI (1 << 2) +#define SSI_SRCR_REFS (1 << 0) +#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_STCCR_WL_MASK (0xf << 13) +#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_SRCCR_WL_MASK (0xf << 13) /* Audio setup */ static int imxpac_tc358743_hw_params(struct snd_pcm_substream *substream, @@ -2383,16 +2389,16 @@ static int imxpac_tc358743_hw_params(struct snd_pcm_substream *substream, int ret; ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_CBM_CFM); + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM); if (ret) { pr_err("%s: failed set cpu dai format\n", __func__); return ret; } ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); if (ret) { pr_err("%s: failed set codec dai format\n", __func__); return ret; @@ -2415,74 +2421,33 @@ static int imxpac_tc358743_hw_params(struct snd_pcm_substream *substream, #if 1 // clear SSI_SRCR_RXBIT0 and SSI_SRCR_RSHFD in order to push Right-justified MSB data fro { -struct imx_ssi { - struct platform_device *ac97_dev; - - struct snd_soc_dai *imx_ac97; - struct clk *clk; - void __iomem *base; - int irq; - int fiq_enable; - unsigned int offset; - - unsigned int flags; - - void (*ac97_reset) (struct snd_ac97 *ac97); - void (*ac97_warm_reset)(struct snd_ac97 *ac97); - - struct imx_pcm_dma_params dma_params_rx; - struct imx_pcm_dma_params dma_params_tx; - - int enabled; - - struct platform_device *soc_platform_pdev; - struct platform_device *soc_platform_pdev_fiq; -}; struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); u32 scr = 0, srcr = 0, stccr = 0, srccr = 0; -#define SSI_SCR 0x10 -#define SSI_SRCR 0x20 -#define SSI_STCCR 0x24 -#define SSI_SRCCR 0x28 -#define SSI_SCR_I2S_MODE_NORM (0 << 5) -#define SSI_SCR_I2S_MODE_MSTR (1 << 5) -#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) -#define SSI_I2S_MODE_MASK (3 << 5) -#define SSI_SCR_SYN (1 << 4) -#define SSI_SRCR_RSHFD (1 << 4) -#define SSI_SRCR_RSCKP (1 << 3) -#define SSI_SRCR_RFSI (1 << 2) -#define SSI_SRCR_REFS (1 << 0) -#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) -#define SSI_STCCR_WL_MASK (0xf << 13) -#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) -#define SSI_SRCCR_WL_MASK (0xf << 13) - printk(KERN_DEBUG "%s: base %p\n", __FUNCTION__, (void *)ssi->base); + pr_debug("%s: base %p\n", __func__, (void *)ssi->base); scr = readl(ssi->base + SSI_SCR); - printk(KERN_DEBUG "%s: SSI_SCR before: %p\n", __FUNCTION__, (void *)scr); - writel(scr, ssi->base + SSI_SCR); - printk(KERN_DEBUG "%s: SSI_SCR after: %p\n", __FUNCTION__, (void *)scr); - - srcr = readl(ssi->base + SSI_SRCR); - printk(KERN_DEBUG "%s: SSI_SRCR before: %p\n", __FUNCTION__, (void *)srcr); - writel(srcr, ssi->base + SSI_SRCR); - printk(KERN_DEBUG "%s: SSI_SRCR after: %p\n", __FUNCTION__, (void *)srcr); - - stccr = readl(ssi->base + SSI_STCCR); - printk(KERN_DEBUG "%s: SSI_STCCR before: %p\n", __FUNCTION__, (void *)stccr); - stccr &= ~SSI_STCCR_WL_MASK; - stccr |= SSI_STCCR_WL(16); - writel(stccr, ssi->base + SSI_STCCR); - printk(KERN_DEBUG "%s: SSI_STCCR after: %p\n", __FUNCTION__, (void *)stccr); + pr_debug("%s: SSI_SCR before: %p\n", __func__, (void *)scr); + writel(scr, ssi->base + SSI_SCR); + pr_debug("%s: SSI_SCR after: %p\n", __func__, (void *)scr); + + srcr = readl(ssi->base + SSI_SRCR); + pr_debug("%s: SSI_SRCR before: %p\n", __func__, (void *)srcr); + writel(srcr, ssi->base + SSI_SRCR); + pr_debug("%s: SSI_SRCR after: %p\n", __func__, (void *)srcr); + + stccr = readl(ssi->base + SSI_STCCR); + pr_debug("%s: SSI_STCCR before: %p\n", __func__, (void *)stccr); + stccr &= ~SSI_STCCR_WL_MASK; + stccr |= SSI_STCCR_WL(16); + writel(stccr, ssi->base + SSI_STCCR); + pr_debug("%s: SSI_STCCR after: %p\n", __func__, (void *)stccr); srccr = readl(ssi->base + SSI_SRCCR); - printk(KERN_DEBUG "%s: SSI_SRCCR before: %p\n", __FUNCTION__, (void *)srccr); - srccr &= ~SSI_SRCCR_WL_MASK; - srccr |= SSI_SRCCR_WL(16); - writel(srccr, ssi->base + SSI_SRCCR); - printk(KERN_DEBUG "%s: SSI_SRCCR after: %p\n", __FUNCTION__, (void *)srccr); - + pr_debug("%s: SSI_SRCCR before: %p\n", __func__, (void *)srccr); + srccr &= ~SSI_SRCCR_WL_MASK; + srccr |= SSI_SRCCR_WL(16); + writel(srccr, ssi->base + SSI_SRCCR); + pr_debug("%s: SSI_SRCCR after: %p\n", __func__, (void *)srccr); } #endif return 0; @@ -2509,72 +2474,67 @@ static struct snd_soc_dapm_route audio_map_a[] = { static int imx_3stack_tc358743_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_codec *codec = rtd->codec; int ret; struct snd_soc_jack *hs_jack; - struct snd_soc_jack_pin *hs_jack_pins; - int hs_jack_pins_size; - struct snd_soc_dapm_widget *imx_3stack_dapm_widgets; - int imx_3stack_dapm_widgets_size; - struct snd_kcontrol_new *tc358743_machine_controls; - int tc358743_machine_controls_size; - struct snd_soc_dapm_route *audio_map; - int audio_map_size; - int gpio_num = -1; - char *gpio_name; - - printk(KERN_DEBUG "%s started\n", __func__); - - { - hs_jack_pins = hs_jack_pins_a; - hs_jack_pins_size = ARRAY_SIZE(hs_jack_pins_a); - imx_3stack_dapm_widgets = imx_3stack_dapm_widgets_a; - imx_3stack_dapm_widgets_size = ARRAY_SIZE(imx_3stack_dapm_widgets_a); - tc358743_machine_controls = tc358743_machine_controls_a; - tc358743_machine_controls_size = ARRAY_SIZE(tc358743_machine_controls_a); - audio_map = audio_map_a; - audio_map_size = ARRAY_SIZE(audio_map_a); - gpio_num = -1; //card_a_gpio_num; - gpio_name = NULL; - } - - ret = snd_soc_add_controls(codec, tc358743_machine_controls, - tc358743_machine_controls_size); - if (ret) - { - printk(KERN_ERR "%s: snd_soc_add_controls failed. err = %d\n", __func__, ret); - return ret; + struct snd_soc_jack_pin *hs_jack_pins; + int hs_jack_pins_size; + struct snd_soc_dapm_widget *imx_3stack_dapm_widgets; + int imx_3stack_dapm_widgets_size; + struct snd_kcontrol_new *tc358743_machine_controls; + int tc358743_machine_controls_size; + struct snd_soc_dapm_route *audio_map; + int audio_map_size; + int gpio_num = -1; + char *gpio_name; + + pr_debug("%s started\n", __func__); + + hs_jack_pins = hs_jack_pins_a; + hs_jack_pins_size = ARRAY_SIZE(hs_jack_pins_a); + imx_3stack_dapm_widgets = imx_3stack_dapm_widgets_a; + imx_3stack_dapm_widgets_size = ARRAY_SIZE(imx_3stack_dapm_widgets_a); + tc358743_machine_controls = tc358743_machine_controls_a; + tc358743_machine_controls_size = ARRAY_SIZE(tc358743_machine_controls_a); + audio_map = audio_map_a; + audio_map_size = ARRAY_SIZE(audio_map_a); + gpio_num = -1; //card_a_gpio_num; + gpio_name = NULL; + + ret = snd_soc_add_controls(codec, tc358743_machine_controls, + tc358743_machine_controls_size); + if (ret) { + pr_err("%s: snd_soc_add_controls failed. err = %d\n", __func__, ret); + return ret; } - /* Add imx_3stack specific widgets */ - snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets, - imx_3stack_dapm_widgets_size); + /* Add imx_3stack specific widgets */ + snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets, + imx_3stack_dapm_widgets_size); - /* Set up imx_3stack specific audio path audio_map */ - snd_soc_dapm_add_routes(&codec->dapm, audio_map, audio_map_size); + /* Set up imx_3stack specific audio path audio_map */ + snd_soc_dapm_add_routes(&codec->dapm, audio_map, audio_map_size); - snd_soc_dapm_enable_pin(&codec->dapm, hs_jack_pins->pin); - snd_soc_dapm_sync(&codec->dapm); + snd_soc_dapm_enable_pin(&codec->dapm, hs_jack_pins->pin); + snd_soc_dapm_sync(&codec->dapm); - hs_jack = kzalloc(sizeof(struct snd_soc_jack), GFP_KERNEL); + hs_jack = kzalloc(sizeof(struct snd_soc_jack), GFP_KERNEL); ret = snd_soc_jack_new(codec, hs_jack_pins->pin, - SND_JACK_HEADPHONE, hs_jack); - if (ret) - { - printk(KERN_ERR "%s: snd_soc_jack_new failed. err = %d\n", __func__, ret); + SND_JACK_HEADPHONE, hs_jack); + if (ret) { + pr_err("%s: snd_soc_jack_new failed. err = %d\n", __func__, ret); return ret; } ret = snd_soc_jack_add_pins(hs_jack,hs_jack_pins_size, - hs_jack_pins); + hs_jack_pins); if (ret) { - printk(KERN_ERR "%s: snd_soc_jack_add_pinsfailed. err = %d\n", __func__, ret); + pr_err("%s: snd_soc_jack_add_pinsfailed. err = %d\n", __func__, ret); return ret; } - - return 0; + return 0; } @@ -2613,10 +2573,10 @@ static int imx_audmux_config(int slave, int master) MXC_AUDMUX_V2_PTCR_TFSDIR | MXC_AUDMUX_V2_PTCR_TFSEL(master | 0x8) | MXC_AUDMUX_V2_PTCR_TCLKDIR | - MXC_AUDMUX_V2_PTCR_RFSDIR | - MXC_AUDMUX_V2_PTCR_RFSEL(master | 0x8) | - MXC_AUDMUX_V2_PTCR_RCLKDIR | - MXC_AUDMUX_V2_PTCR_RCSEL(master | 0x8) | + MXC_AUDMUX_V2_PTCR_RFSDIR | + MXC_AUDMUX_V2_PTCR_RFSEL(master | 0x8) | + MXC_AUDMUX_V2_PTCR_RCLKDIR | + MXC_AUDMUX_V2_PTCR_RCSEL(master | 0x8) | MXC_AUDMUX_V2_PTCR_TCSEL(master | 0x8); pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master); mxc_audmux_v2_configure_port(slave, ptcr, pdcr); @@ -2624,14 +2584,12 @@ static int imx_audmux_config(int slave, int master) ptcr = MXC_AUDMUX_V2_PTCR_SYN; pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(master); mxc_audmux_v2_configure_port(master, ptcr, pdcr); - return 0; } static int __devinit imx_tc358743_probe(struct platform_device *pdev) { struct mxc_audio_platform_data *plat = pdev->dev.platform_data; - int ret = 0; @@ -2641,8 +2599,7 @@ static int __devinit imx_tc358743_probe(struct platform_device *pdev) if (plat->init && plat->init()) return ret; - printk("%s %d %s\n",__FUNCTION__,__LINE__,pdev->name); - + printk("%s %d %s\n",__func__,__LINE__,pdev->name); return 0; } @@ -2670,12 +2627,12 @@ static int tc358743_codec_probe(struct snd_soc_codec *codec) { return 0; } - + static int tc358743_codec_remove(struct snd_soc_codec *codec) { return 0; } - + static int tc358743_codec_suspend(struct snd_soc_codec *codec, pm_message_t state) { // tc358743_set_bias_level(codec, SND_SOC_BIAS_OFF); @@ -2696,7 +2653,7 @@ static int tc358743_set_bias_level(struct snd_soc_codec *codec, static const u8 tc358743_reg[0] = { }; - + static struct snd_soc_codec_driver soc_codec_dev_tc358743 = { .set_bias_level = tc358743_set_bias_level, .reg_cache_size = ARRAY_SIZE(tc358743_reg), @@ -2708,7 +2665,7 @@ static struct snd_soc_codec_driver soc_codec_dev_tc358743 = { .resume = tc358743_codec_resume, }; -#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 #define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE) @@ -2731,7 +2688,7 @@ static int tc358743_set_dai_sysclk(struct snd_soc_dai *codec_dai, } static int tc358743_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) + unsigned int fmt) { return 0; } @@ -2744,7 +2701,7 @@ static struct snd_soc_dai_ops tc358743_dai_ops = { }; static struct snd_soc_dai_driver tc358743_dai = { - .name = "tc358743-hifi", + .name = "tc358743-hifi", .capture = { .stream_name = "Capture", .channels_min = 1, @@ -2809,11 +2766,11 @@ static report_netlink(void) { char *envp[2]; envp[0] = &str_on[0]; - envp[1] = NULL; + envp[1] = NULL; sprintf(envp[0], "HDMI RX: %d (%s) %d %d", (unsigned char)hdmi_mode & 0xf, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], tc358743_fps_list[fps], tc358743_audio_list[audio]); - kobject_uevent_env(&(tc358743_data.i2c_client->dev.kobj), KOBJ_CHANGE, envp); + kobject_uevent_env(&(tc358743_data.i2c_client->dev.kobj), KOBJ_CHANGE, envp); det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; - printk(KERN_DEBUG "%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", __FUNCTION__, (unsigned char)hdmi_mode, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout, tc358743_audio_list[audio]); + pr_debug("%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", __func__, (unsigned char)hdmi_mode, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout, tc358743_audio_list[audio]); } static void det_worker(struct work_struct *work) @@ -2823,91 +2780,75 @@ static void det_worker(struct work_struct *work) int ret; mutex_lock(&access_lock); - if(!det_work_disable) - { + if (!det_work_disable) { reg = 0x8621; ret = tc358743_read_reg(reg, &u32val); - if(ret > 0) - { - if(audio != ((unsigned char)u32val) & 0x0f) - { + if (ret > 0) { + if (audio != ((unsigned char)u32val) & 0x0f) { audio = ((unsigned char)u32val) & 0x0f; report_netlink(); } } reg = 0x852f; ret = tc358743_read_reg(reg, &u32val); - if(ret > 0) - { - while(1) - { - if(u32val & TC3587430_HDMI_DETECT) - { + if (ret > 0) { + while (1) { + if (u32val & TC3587430_HDMI_DETECT) { lock = u32val & TC3587430_HDMI_DETECT; reg = 0x8521; ret = tc358743_read_reg(reg, &u32val); - if(ret < 0) - { - printk(KERN_ERR "%s: Error reading mode\n", __FUNCTION__); + if (ret < 0) { + pr_err("%s: Error reading mode\n", __func__); } - } - else - { - if(lock) // check if it is realy un-plug - { + } else { + if (lock) { // check if it is realy un-plug lock = 0; u32val = 0x0; hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. } } - if((unsigned char)hdmi_mode != (unsigned char)u32val) - { - if(u32val) + if ((unsigned char)hdmi_mode != (unsigned char)u32val) { + if (u32val) det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; else det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; bounce = MAX_BOUNCE; - printk(KERN_DEBUG "%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", __FUNCTION__, (unsigned char)hdmi_mode, (unsigned char)u32val, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout); + pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", __func__, (unsigned char)hdmi_mode, (unsigned char)u32val, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout); hdmi_mode = u32val; + } else if (bounce) { + bounce--; + det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; } - else - if(bounce) - { - bounce--; - det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; - } - - if(1 == bounce) - { - if(hdmi_mode >= 0xe) - { + + if (1 == bounce) { + if (hdmi_mode >= 0xe) { reg = 0x852f; ret = tc358743_read_reg(reg, &u32val); - if(ret > 0) + if (ret > 0) fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; } reg = 0x8621; ret = tc358743_read_reg(reg, &u32val); - if(ret > 0) + if (ret > 0) audio = ((unsigned char)u32val) & 0x0f; - report_netlink(); + report_netlink(); } break; } + } else { + pr_err("%s: Error reading lock\n", __func__); } - else - printk(KERN_ERR "%s: Error reading lock\n", __FUNCTION__); + } else { + det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; } - else - det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; mutex_unlock(&access_lock); schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); } static irqreturn_t tc358743_detect_handler(int irq, void *data) { - - printk(KERN_DEBUG "%s: IRQ %d\n", __FUNCTION__, tc358743_data.i2c_client->irq); + + pr_debug("%s: IRQ %d\n", __func__, tc358743_data.i2c_client->irq); schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); return IRQ_HANDLED; } @@ -2916,7 +2857,7 @@ static irqreturn_t tc358743_detect_handler(int irq, void *data) /*! * tc358743 I2C probe function * - * @param adapter struct i2c_adapter * + * @param adapter struct i2c_adapter * * @return Error code indicating success or failure */ #include @@ -2933,17 +2874,14 @@ static ssize_t tc358743_show_regdump(struct device *dev, u32 u32val; mutex_lock(&access_lock); - for(i=0; i 0) - { - if(0 == (i & 0xf)) + while (retval-- > 0) { + if (0 == (i & 0xf)) len += sprintf(buf+len, "\n%04X:", regoffs+i); len += sprintf(buf+len, " %02X", u32val&0xff); u32val >>= 8; @@ -2958,13 +2896,13 @@ static ssize_t tc358743_show_regdump(struct device *dev, static DEVICE_ATTR(regdump, S_IRUGO, tc358743_show_regdump, NULL); static ssize_t tc358743_store_regoffs(struct device *device, - struct device_attribute *attr, - const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, size_t count) { u32 val; int retval; retval = sscanf(buf, "%x", &val); - if(1 == retval) + if (1 == retval) regoffs = (u16)val; return count; } @@ -2973,7 +2911,7 @@ static ssize_t tc358743_show_regoffs(struct device *dev, struct device_attribute *attr, char *buf) { int len = 0; - + len += sprintf(buf+len, "0x%04X\n", regoffs); return len; } @@ -2981,13 +2919,13 @@ static ssize_t tc358743_show_regoffs(struct device *dev, static DEVICE_ATTR(regoffs, S_IRUGO|S_IWUSR, tc358743_show_regoffs, tc358743_store_regoffs); static ssize_t tc358743_store_hpd(struct device *device, - struct device_attribute *attr, - const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, size_t count) { u32 val; int retval; retval = sscanf(buf, "%d", &val); - if(1 == retval) + if (1 == retval) hpd_active = (u16)val; return count; } @@ -2996,7 +2934,7 @@ static ssize_t tc358743_show_hpd(struct device *dev, struct device_attribute *attr, char *buf) { int len = 0; - + len += sprintf(buf+len, "%d\n", hpd_active); return len; } @@ -3007,7 +2945,7 @@ static ssize_t tc358743_show_hdmirx(struct device *dev, struct device_attribute *attr, char *buf) { int len = 0; - + len += sprintf(buf+len, "%d\n", hdmi_mode); return len; } @@ -3018,7 +2956,7 @@ static ssize_t tc358743_show_fps(struct device *dev, struct device_attribute *attr, char *buf) { int len = 0; - + len += sprintf(buf+len, "%d\n", tc358743_fps_list[fps]); return len; } @@ -3030,7 +2968,7 @@ static ssize_t tc358743_show_audio(struct device *dev, struct device_attribute *attr, char *buf) { int len = 0; - + len += sprintf(buf+len, "%d\n", tc358743_audio_list[audio]); return len; } @@ -3043,12 +2981,11 @@ static int tc358743_probe(struct i2c_client *client, { int retval = -1; struct fsl_mxc_camera_platform_data *plat_data = client->dev.platform_data; - u8 chip_id_high; + u8 chip_id_high; u32 u32val; - printk(KERN_DEBUG "%s: started, error=%d\n", - __func__, retval); - + pr_debug("%s: started, error=%d\n", __func__, retval); + /* Set initial values for the sensor struct. */ memset(&tc358743_data, 0, sizeof(tc358743_data)); tc358743_data.mclk = 27000000; /* 6 - 54 MHz, typical 24MHz */ @@ -3067,14 +3004,14 @@ static int tc358743_probe(struct i2c_client *client, tc358743_data.pix.width = tc358743_mode_info_data[0][tc358743_data.streamcap.capturemode].width; tc358743_data.pix.height = tc358743_mode_info_data[0][tc358743_data.streamcap.capturemode].height; - printk(KERN_DEBUG "%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__, - tc358743_data.pix.pixelformat, + pr_debug("%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__, + tc358743_data.pix.pixelformat, tc358743_data.streamcap.capturemode, tc358743_data.streamcap.extendedmode, tc358743_data.streamcap.timeperframe.denominator * tc358743_data.streamcap.timeperframe.numerator, tc358743_data.pix.width, tc358743_data.pix.height); - + if (plat_data->io_regulator) { io_regulator = regulator_get(&client->dev, plat_data->io_regulator); @@ -3096,7 +3033,7 @@ static int tc358743_probe(struct i2c_client *client, if (plat_data->core_regulator) { core_regulator = regulator_get(&client->dev, - plat_data->core_regulator); + plat_data->core_regulator); if (!IS_ERR(core_regulator)) { regulator_set_voltage(core_regulator, TC358743_VOLTAGE_DIGITAL_CORE, @@ -3146,55 +3083,50 @@ static int tc358743_probe(struct i2c_client *client, retval = -ENODEV; goto err4; } - + camera_plat = plat_data; tc358743_int_device.priv = &tc358743_data; retval = v4l2_int_device_register(&tc358743_int_device); - if (retval) - { - printk(KERN_ERR "%s: v4l2_int_device_register failed, error=%d\n", + if (retval) { + pr_err("%s: v4l2_int_device_register failed, error=%d\n", __func__, retval); goto err4; } - + //retval = device_create_file(&client->dev, &dev_attr_audio); retval = device_create_file(&client->dev, &dev_attr_fps); retval = device_create_file(&client->dev, &dev_attr_hdmirx); retval = device_create_file(&client->dev, &dev_attr_hpd); retval = device_create_file(&client->dev, &dev_attr_regoffs); retval = device_create_file(&client->dev, &dev_attr_regdump); - - if (retval) - { - printk(KERN_ERR "%s: create bin file failed, error=%d\n", + + if (retval) { + pr_err("%s: create bin file failed, error=%d\n", __func__, retval); goto err4; } - -#ifdef AUDIO_ENABLE + +#ifdef AUDIO_ENABLE /* Audio setup */ retval = snd_soc_register_codec(&client->dev, &soc_codec_dev_tc358743, &tc358743_dai, 1); - if (retval) - { - printk(KERN_ERR "%s: register failed, error=%d\n", + if (retval) { + pr_err("%s: register failed, error=%d\n", __func__, retval); goto err4; } retval = platform_driver_register(&imx_tc358743_audio1_driver); - if (retval) - { - printk(KERN_ERR "%s: Platform driver register failed, error=%d\n", + if (retval) { + pr_err("%s: Platform driver register failed, error=%d\n", __func__, retval); goto err4; } - + imxpac_tc358743_snd_device = platform_device_alloc("soc-audio", 5); - if (!imxpac_tc358743_snd_device) - { - printk(KERN_ERR "%s: Platform device allocation failed, error=%d\n", + if (!imxpac_tc358743_snd_device) { + pr_err("%s: Platform device allocation failed, error=%d\n", __func__, retval); goto err4; } @@ -3203,7 +3135,7 @@ static int tc358743_probe(struct i2c_client *client, retval = platform_device_add(imxpac_tc358743_snd_device); if (retval) { - printk(KERN_ERR "%s: Platform device add failed, error=%d\n", + pr_err("%s: Platform device add failed, error=%d\n", __func__, retval); platform_device_put(imxpac_tc358743_snd_device); goto err4; @@ -3221,12 +3153,12 @@ static int tc358743_probe(struct i2c_client *client, "cound not request det irq %d\n", tc358743_data.i2c_client->irq); } - + schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); #endif retval = tc358743_reset(&tc358743_data); - - printk(KERN_DEBUG "%s: finished, error=%d\n", + + pr_debug("%s: finished, error=%d\n", __func__, retval); return retval; @@ -3246,7 +3178,7 @@ static int tc358743_probe(struct i2c_client *client, regulator_put(io_regulator); } err1: - printk(KERN_ERR "%s: failed, error=%d\n", + pr_err("%s: failed, error=%d\n", __func__, retval); return retval; } @@ -3254,26 +3186,26 @@ static int tc358743_probe(struct i2c_client *client, /*! * tc358743 I2C detach function * - * @param client struct i2c_client * + * @param client struct i2c_client * * @return Error code indicating success or failure */ static int tc358743_remove(struct i2c_client *client) { // Stop delayed work cancel_delayed_work_sync(&(det_work)); - + // Remove IRQ if (tc358743_data.i2c_client->irq) { free_irq(tc358743_data.i2c_client->irq, &tc358743_data); } - + /*Remove sysfs entries*/ device_remove_file(&client->dev, &dev_attr_fps); device_remove_file(&client->dev, &dev_attr_hdmirx); device_remove_file(&client->dev, &dev_attr_hpd); device_remove_file(&client->dev, &dev_attr_regoffs); device_remove_file(&client->dev, &dev_attr_regdump); - + v4l2_int_device_unregister(&tc358743_int_device); if (gpo_regulator) { From e64a9887f99d4f5e4db07f1fcaf85b48d434868f Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 30 Apr 2014 15:21:09 -0700 Subject: [PATCH 0705/1983] tc358743_h2c: fix compiler warnings --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index f1de64dcc3d62f..daf8b436b337b9 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -2762,7 +2762,7 @@ static int tc358743_audio_list[16] = }; static char str_on[80]; -static report_netlink(void) +static void report_netlink(void) { char *envp[2]; envp[0] = &str_on[0]; @@ -2784,7 +2784,7 @@ static void det_worker(struct work_struct *work) reg = 0x8621; ret = tc358743_read_reg(reg, &u32val); if (ret > 0) { - if (audio != ((unsigned char)u32val) & 0x0f) { + if (audio != (((unsigned char)u32val) & 0x0f)) { audio = ((unsigned char)u32val) & 0x0f; report_netlink(); } @@ -2829,9 +2829,10 @@ static void det_worker(struct work_struct *work) } reg = 0x8621; ret = tc358743_read_reg(reg, &u32val); - if (ret > 0) + if (ret > 0) { audio = ((unsigned char)u32val) & 0x0f; - report_netlink(); + report_netlink(); + } } break; } From dbb3db0296d7845844726244c4ffa6767a839b19 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 2 May 2014 12:16:13 -0700 Subject: [PATCH 0706/1983] tc358743_h2c: fill in sample edid data from Asus monitor --- .../media/platform/mxc/capture/tc358743_h2c.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index daf8b436b337b9..ffb551d5c824a7 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -141,7 +141,25 @@ static void det_work_enable(int i) pr_debug("%s: %d %d\n", __func__, det_work_disable, det_work_timeout); } -static u8 cHDMIEDID[256]; +static u8 cHDMIEDID[256] = { + /* FIXME! This is the edid that my ASUS HDMI monitor returns */ + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x69, 0xf3, 0x24, 0xd6, 0x12, 0x00, 0x00, + 0x16, 0x16, 0x01, 0x03, 0x80, 0x34, 0x1d, 0x78, 0x2a, 0xc7, 0x20, 0xa4, 0x55, 0x49, 0x99, 0x27, + 0x13, 0x50, 0x54, 0xbf, 0xef, 0x00, 0x71, 0x4f, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00, + 0xd1, 0xc0, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xff, 0x00, 0x43, 0x36, 0x4c, + 0x4d, 0x54, 0x46, 0x30, 0x30, 0x34, 0x38, 0x32, 0x32, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x37, + 0x4b, 0x1e, 0x55, 0x10, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x48, 0x32, 0x34, 0x32, 0x48, 0x0a, 0x20, 0x01, 0x78, + 0x02, 0x03, 0x22, 0x71, 0x4f, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f, + 0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, 0x07, 0x01, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00, + 0x10, 0x00, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0x09, 0x25, + 0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, + 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28, + 0x55, 0x40, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, + 0x0c, 0x40, 0x55, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, +}; /*! * Maintains the information on the current state of the sesor. From 2f5c9f1583786537f5a886fce0fa844869f4660c Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 2 May 2014 19:45:25 -0700 Subject: [PATCH 0707/1983] TC358743 Kconfig dependency update --- drivers/media/platform/mxc/capture/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig index 0110f30cebc9aa..5338220bd658a5 100644 --- a/drivers/media/platform/mxc/capture/Kconfig +++ b/drivers/media/platform/mxc/capture/Kconfig @@ -40,8 +40,7 @@ config MXC_CAMERA_OV5647_MIPI config MXC_HDMI_CSI2_TC358743 tristate "Toshiba tc358743 Hdmi to CSI 2 bridge" - depends on !VIDEO_MXC_EMMA_CAMERA - depends on ARCH_MX6Q + depends on !VIDEO_MXC_EMMA_CAMERA && I2C select MXC_MIPI_CSI2 if ARCH_MX6Q select MXC_CAMERA_SENSOR_CLK ---help--- From 39ef974c65f787f4e6a0eb0f1d194c5e6ac34461 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 2 May 2014 19:46:08 -0700 Subject: [PATCH 0708/1983] tc358743_h2c: add devicetree support --- .../media/platform/mxc/capture/tc358743_h2c.c | 390 +++++++++++------- 1 file changed, 251 insertions(+), 139 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index ffb551d5c824a7..8d2e8f7f1ab8ff 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -28,11 +28,16 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include -#include +#include +#include #include #include #include @@ -41,7 +46,7 @@ #include #include #include -#include +//#include #include #include "mxc_v4l2_capture.h" @@ -65,6 +70,10 @@ #define TC358743_CHIP_ID_LOW_BYTE 0x0 #define TC3587430_HDMI_DETECT 0x0f //0x10 +#define TC_VOLTAGE_ANALOG 2800000 +#define TC_VOLTAGE_DIGITAL_CORE 1500000 +#define TC_VOLTAGE_DIGITAL_IO 1800000 + enum tc358743_mode { tc358743_mode_INIT, /*only for sensor init*/ tc358743_mode_INIT1, /*only for sensor init*/ @@ -109,6 +118,12 @@ struct tc358743_mode_info { }; static struct delayed_work det_work; +static struct sensor_data tc358743_data; +static int pwn_gpio, rst_gpio; +static struct regulator *io_regulator; +static struct regulator *core_regulator; +static struct regulator *analog_regulator; +static struct regulator *gpo_regulator; static u16 hpd_active = 1; @@ -126,6 +141,100 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, static int tc358743_toggle_hpd(int active); +static void tc_standby(s32 enable) +{ + if (gpio_is_valid(pwn_gpio)) + gpio_set_value(pwn_gpio, enable ? 1 : 0); + pr_debug("tc_standby: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio); + msleep(2); +} + +static void tc_reset(void) +{ + /* camera reset */ + gpio_set_value(rst_gpio, 1); + + /* camera power dowmn */ + if (gpio_is_valid(pwn_gpio)) { + gpio_set_value(pwn_gpio, 1); + msleep(5); + + gpio_set_value(pwn_gpio, 0); + } + msleep(5); + + gpio_set_value(rst_gpio, 0); + msleep(1); + + gpio_set_value(rst_gpio, 1); + msleep(20); + + if (gpio_is_valid(pwn_gpio)) + gpio_set_value(pwn_gpio, 1); +} + +static int tc_power_on(struct device *dev) +{ + int ret = 0; + + io_regulator = devm_regulator_get(dev, "DOVDD"); + if (!IS_ERR(io_regulator)) { + regulator_set_voltage(io_regulator, + TC_VOLTAGE_DIGITAL_IO, + TC_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(io_regulator); + if (ret) { + pr_err("%s:io set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:io set voltage ok\n", __func__); + } + } else { + pr_err("%s: cannot get io voltage error\n", __func__); + io_regulator = NULL; + } + + core_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(core_regulator)) { + regulator_set_voltage(core_regulator, + TC_VOLTAGE_DIGITAL_CORE, + TC_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(core_regulator); + if (ret) { + pr_err("%s:core set voltage error\n", __func__); + return ret; + } else { + dev_dbg(dev, + "%s:core set voltage ok\n", __func__); + } + } else { + core_regulator = NULL; + pr_err("%s: cannot get core voltage error\n", __func__); + } + + analog_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(analog_regulator)) { + regulator_set_voltage(analog_regulator, + TC_VOLTAGE_ANALOG, + TC_VOLTAGE_ANALOG); + ret = regulator_enable(analog_regulator); + if (ret) { + pr_err("%s:analog set voltage error\n", + __func__); + return ret; + } else { + dev_dbg(dev, + "%s:analog set voltage ok\n", __func__); + } + } else { + analog_regulator = NULL; + pr_err("%s: cannot get analog voltage error\n", __func__); + } + + return ret; +} + static void det_work_enable(int i) { mutex_lock(&access_lock); @@ -164,7 +273,6 @@ static u8 cHDMIEDID[256] = { /*! * Maintains the information on the current state of the sesor. */ -static struct sensor_data tc358743_data; static struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, @@ -1318,12 +1426,6 @@ static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = }, }; -static struct regulator *io_regulator; -static struct regulator *core_regulator; -static struct regulator *analog_regulator; -static struct regulator *gpo_regulator; -static struct fsl_mxc_camera_platform_data *camera_plat; - static int tc358743_probe(struct i2c_client *adapter, const struct i2c_device_id *device_id); static int tc358743_remove(struct i2c_client *client); @@ -1501,13 +1603,10 @@ static int tc358743_reset(struct sensor_data *sensor) det_work_enable(0); while (ret) { - if (camera_plat->pwdn) { - pr_debug("%s: RESET\n", __func__); - camera_plat->pwdn(1); - mdelay(100); - camera_plat->pwdn(0); - mdelay(1000); - } + tc_standby(1); + mdelay(100); + tc_standby(0); + mdelay(1000); tgt_fps = sensor->streamcap.timeperframe.denominator / sensor->streamcap.timeperframe.numerator; @@ -1787,8 +1886,7 @@ static int ioctl_s_power(struct v4l2_int_device *s, int on) if (regulator_enable(analog_regulator) != 0) return -EIO; /* Make sure power on */ - if (camera_plat->pwdn) - camera_plat->pwdn(0); + tc_standby(0); } else if (!on && sensor->on) { if (analog_regulator) @@ -1890,8 +1988,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) pr_debug("%s\n", __func__); det_work_enable(0); /* Make sure power on */ - if (camera_plat->pwdn) - camera_plat->pwdn(0); + tc_standby(0); switch (a->type) { /* This is the only case currently handled. */ @@ -2267,8 +2364,8 @@ static int ioctl_dev_init(struct v4l2_int_device *s) tc358743_data.mclk = tgt_xclk; pr_debug("%s: Setting mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); - set_mclk_rate(&tc358743_data.mclk, tc358743_data.mclk_source); - pr_debug("%s: After mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); +// set_mclk_rate(&tc358743_data.mclk, tc358743_data.mclk_source); +// pr_debug("%s: After mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); /* Default camera frame rate is set in probe */ tgt_fps = sensor->streamcap.timeperframe.denominator / @@ -2879,9 +2976,6 @@ static irqreturn_t tc358743_detect_handler(int irq, void *data) * @param adapter struct i2c_adapter * * @return Error code indicating success or failure */ -#include -#include - #define DUMP_LENGTH 256 static u16 regoffs = 0; @@ -2998,121 +3092,147 @@ static DEVICE_ATTR(audio, S_IRUGO, tc358743_show_audio, NULL); static int tc358743_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int retval = -1; - struct fsl_mxc_camera_platform_data *plat_data = client->dev.platform_data; - u8 chip_id_high; + struct pwm_device *pwm; + struct device *dev = &client->dev; + int retval; + struct regmap *gpr; + struct sensor_data *sensor = &tc358743_data; u32 u32val; - pr_debug("%s: started, error=%d\n", __func__, retval); + + /* request power down pin */ + pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); + if (!gpio_is_valid(pwn_gpio)) { + dev_warn(dev, "no sensor pwdn pin available"); + } else { + retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, + "tc_mipi_pwdn"); + if (retval < 0) { + dev_warn(dev, "request of pwn_gpio failed"); + return retval; + } + } + /* request reset pin */ + rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); + if (!gpio_is_valid(rst_gpio)) { + dev_warn(dev, "no sensor reset pin available"); + return -EINVAL; + } + retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, + "tc_mipi_reset"); + if (retval < 0) { + dev_warn(dev, "request of tc_mipi_reset failed"); + return retval; + } /* Set initial values for the sensor struct. */ - memset(&tc358743_data, 0, sizeof(tc358743_data)); - tc358743_data.mclk = 27000000; /* 6 - 54 MHz, typical 24MHz */ - tc358743_data.mclk_source = plat_data->mclk_source; - tc358743_data.csi = plat_data->csi; - tc358743_data.io_init = plat_data->io_init; - - tc358743_data.i2c_client = client; - tc358743_data.pix.pixelformat = tc358743_formats[0].pixelformat; - tc358743_data.streamcap.capability = V4L2_MODE_HIGHQUALITY | - V4L2_CAP_TIMEPERFRAME; - tc358743_data.streamcap.capturemode = 0; - tc358743_data.streamcap.extendedmode = tc358743_mode_1080P_1920_1080; - tc358743_data.streamcap.timeperframe.denominator = DEFAULT_FPS; - tc358743_data.streamcap.timeperframe.numerator = 1; + memset(sensor, 0, sizeof(*sensor)); + + sensor->sensor_clk = devm_clk_get(dev, "csi_mclk"); + if (IS_ERR(sensor->sensor_clk)) { + /* assuming clock enabled by default */ + sensor->sensor_clk = NULL; + dev_err(dev, "clock-frequency missing or invalid\n"); + return PTR_ERR(sensor->sensor_clk); + } - tc358743_data.pix.width = tc358743_mode_info_data[0][tc358743_data.streamcap.capturemode].width; - tc358743_data.pix.height = tc358743_mode_info_data[0][tc358743_data.streamcap.capturemode].height; - pr_debug("%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__, - tc358743_data.pix.pixelformat, - tc358743_data.streamcap.capturemode, tc358743_data.streamcap.extendedmode, - tc358743_data.streamcap.timeperframe.denominator * - tc358743_data.streamcap.timeperframe.numerator, - tc358743_data.pix.width, - tc358743_data.pix.height); - - if (plat_data->io_regulator) { - io_regulator = regulator_get(&client->dev, - plat_data->io_regulator); - if (!IS_ERR(io_regulator)) { - regulator_set_voltage(io_regulator, - TC358743_VOLTAGE_DIGITAL_IO, - TC358743_VOLTAGE_DIGITAL_IO); - retval = regulator_enable(io_regulator); - if (retval) { - pr_err("%s:io set voltage error\n", __func__); - goto err1; - } else { - dev_dbg(&client->dev, - "%s:io set voltage ok\n", __func__); - } - } else - io_regulator = NULL; + retval = of_property_read_u32(dev->of_node, "mclk", + &(sensor->mclk)); + if (retval) { + dev_err(dev, "mclk missing or invalid\n"); + return retval; } - if (plat_data->core_regulator) { - core_regulator = regulator_get(&client->dev, - plat_data->core_regulator); - if (!IS_ERR(core_regulator)) { - regulator_set_voltage(core_regulator, - TC358743_VOLTAGE_DIGITAL_CORE, - TC358743_VOLTAGE_DIGITAL_CORE); - retval = regulator_enable(core_regulator); - if (retval) { - pr_err("%s:core set voltage error\n", __func__); - goto err2; - } else { - dev_dbg(&client->dev, - "%s:core set voltage ok\n", __func__); - } - } else - core_regulator = NULL; + retval = of_property_read_u32(dev->of_node, "mclk_source", + (u32 *) &(sensor->mclk_source)); + if (retval) { + dev_err(dev, "mclk_source missing or invalid\n"); + return retval; } - if (plat_data->analog_regulator) { - analog_regulator = regulator_get(&client->dev, - plat_data->analog_regulator); - if (!IS_ERR(analog_regulator)) { - regulator_set_voltage(analog_regulator, - TC358743_VOLTAGE_ANALOG, - TC358743_VOLTAGE_ANALOG); - retval = regulator_enable(analog_regulator); - if (retval) { - pr_err("%s:analog set voltage error\n", - __func__); - goto err3; - } else { - dev_dbg(&client->dev, - "%s:analog set voltage ok\n", __func__); - } - } else - analog_regulator = NULL; + retval = of_property_read_u32(dev->of_node, "ipu_id", + &sensor->ipu_id); + if (retval) { + dev_err(dev, "ipu_id missing or invalid\n"); + return retval; + } + + retval = of_property_read_u32(dev->of_node, "csi_id", + &(sensor->csi)); + if (retval) { + dev_err(dev, "csi id missing or invalid\n"); + return retval; + } + if (((unsigned)sensor->ipu_id > 1) || ((unsigned)sensor->csi > 1)) { + dev_err(dev, "invalid ipu/csi\n"); + return -EINVAL; } - if (plat_data->io_init) - plat_data->io_init(); + clk_prepare_enable(sensor->sensor_clk); - if (plat_data->pwdn) - plat_data->pwdn(0); + sensor->io_init = tc_reset; + sensor->i2c_client = client; + sensor->pix.pixelformat = tc358743_formats[0].pixelformat; + sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY | + V4L2_CAP_TIMEPERFRAME; + sensor->streamcap.capturemode = 0; + sensor->streamcap.extendedmode = tc358743_mode_1080P_1920_1080; + sensor->streamcap.timeperframe.denominator = DEFAULT_FPS; + sensor->streamcap.timeperframe.numerator = 1; + + sensor->pix.width = tc358743_mode_info_data[0][sensor->streamcap.capturemode].width; + sensor->pix.height = tc358743_mode_info_data[0][sensor->streamcap.capturemode].height; + pr_debug("%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__, + sensor->pix.pixelformat, + sensor->streamcap.capturemode, sensor->streamcap.extendedmode, + sensor->streamcap.timeperframe.denominator * + sensor->streamcap.timeperframe.numerator, + sensor->pix.width, + sensor->pix.height); + + pwm = pwm_get(dev, NULL); + if (!IS_ERR(pwm)) { + dev_info(dev, "found pwm%d, period=%d\n", pwm->pwm, pwm->period); + pwm_config(pwm, pwm->period >> 1, pwm->period); + pwm_enable(pwm); + } + + tc_power_on(dev); + tc_reset(); + tc_standby(0); retval = tc358743_read_reg(TC358743_CHIP_ID_HIGH_BYTE, &u32val); - chip_id_high = (u8)u32val; - if (retval < 0 /* || chip_id_high != 0x00*/) { + if (retval < 0) { pr_err("%s:cannot find camera\n", __func__); retval = -ENODEV; goto err4; } - camera_plat = plat_data; + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (of_machine_is_compatible("fsl,imx6q")) { + if (sensor->csi == sensor->ipu_id) { + int mask = sensor->csi ? (1 << 20) : (1 << 19); - tc358743_int_device.priv = &tc358743_data; - retval = v4l2_int_device_register(&tc358743_int_device); - if (retval) { - pr_err("%s: v4l2_int_device_register failed, error=%d\n", - __func__, retval); - goto err4; + regmap_update_bits(gpr, IOMUXC_GPR1, mask, 0); + } + } else if (of_machine_is_compatible("fsl,imx6dl")) { + int mask = sensor->csi ? (7 << 3) : (7 << 0); + int val = sensor->csi ? (3 << 3) : (0 << 0); + + if (sensor->ipu_id) { + dev_err(dev, "invalid ipu\n"); + return -EINVAL; + } + regmap_update_bits(gpr, IOMUXC_GPR13, mask, val); + } + } else { + pr_err("%s: failed to find fsl,imx6q-iomux-gpr regmap\n", + __func__); } + tc358743_int_device.priv = sensor; + //retval = device_create_file(&client->dev, &dev_attr_audio); retval = device_create_file(&client->dev, &dev_attr_fps); retval = device_create_file(&client->dev, &dev_attr_hdmirx); @@ -3163,40 +3283,32 @@ static int tc358743_probe(struct i2c_client *client, #if 1 INIT_DELAYED_WORK(&(det_work), det_worker); - if (tc358743_data.i2c_client->irq) { - retval = request_irq(tc358743_data.i2c_client->irq, tc358743_detect_handler, + if (sensor->i2c_client->irq) { + retval = request_irq(sensor->i2c_client->irq, tc358743_detect_handler, IRQF_SHARED | IRQF_TRIGGER_FALLING, - "tc358743_det", &tc358743_data); + "tc358743_det", sensor); if (retval < 0) - dev_warn(&tc358743_data.i2c_client->dev, + dev_warn(&sensor->i2c_client->dev, "cound not request det irq %d\n", - tc358743_data.i2c_client->irq); + sensor->i2c_client->irq); } schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); #endif - retval = tc358743_reset(&tc358743_data); + retval = tc358743_reset(sensor); + tc_standby(1); + retval = v4l2_int_device_register(&tc358743_int_device); + if (retval) { + pr_err("%s: v4l2_int_device_register failed, error=%d\n", + __func__, retval); + goto err4; + } pr_debug("%s: finished, error=%d\n", __func__, retval); return retval; err4: - if (analog_regulator) { - regulator_disable(analog_regulator); - regulator_put(analog_regulator); - } -err3: - if (core_regulator) { - regulator_disable(core_regulator); - regulator_put(core_regulator); - } -err2: - if (io_regulator) { - regulator_disable(io_regulator); - regulator_put(io_regulator); - } -err1: pr_err("%s: failed, error=%d\n", __func__, retval); return retval; From d5cb90bca32e1be0143e1f01bad0e1f1fd53f87a Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 6 May 2014 19:11:23 -0700 Subject: [PATCH 0709/1983] tc358743_h2c: remove ipu/csi/v_channel, mipi camera will select this --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index 8d2e8f7f1ab8ff..62c79714a0d0d4 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -1664,9 +1664,6 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, if (mipi_csi2_info) { pr_debug("%s: mipi_csi2_info:\n" "mipi_en: %d\n" - "ipu_id: %d\n" - "csi_id: %d\n" - "v_channel: %d\n" "lanes: %d\n" "datatype: %d\n" "dphy_clk: %p\n" @@ -1675,9 +1672,6 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, "pdev: %p\n" , __func__, ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en, - ((struct mipi_csi2_info *)mipi_csi2_info)->ipu_id, - ((struct mipi_csi2_info *)mipi_csi2_info)->csi_id, - ((struct mipi_csi2_info *)mipi_csi2_info)->v_channel, ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, ((struct mipi_csi2_info *)mipi_csi2_info)->datatype, ((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk, @@ -3128,6 +3122,8 @@ static int tc358743_probe(struct i2c_client *client, /* Set initial values for the sensor struct. */ memset(sensor, 0, sizeof(*sensor)); + sensor->mipi_camera = 1; + sensor->virtual_channel = 0; sensor->sensor_clk = devm_clk_get(dev, "csi_mclk"); if (IS_ERR(sensor->sensor_clk)) { /* assuming clock enabled by default */ @@ -3163,7 +3159,7 @@ static int tc358743_probe(struct i2c_client *client, dev_err(dev, "csi id missing or invalid\n"); return retval; } - if (((unsigned)sensor->ipu_id > 1) || ((unsigned)sensor->csi > 1)) { + if ((unsigned)sensor->ipu_id || (unsigned)sensor->csi) { dev_err(dev, "invalid ipu/csi\n"); return -EINVAL; } From dde31e399e392affddb1676c9d32c90815afe2a1 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 28 May 2014 16:41:09 -0700 Subject: [PATCH 0710/1983] tc358743_h2c.c: have mipi drivers specify lane in mipi_csi2_set_lanes --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index 62c79714a0d0d4..ee92d2ee2bee18 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -1664,7 +1664,6 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, if (mipi_csi2_info) { pr_debug("%s: mipi_csi2_info:\n" "mipi_en: %d\n" - "lanes: %d\n" "datatype: %d\n" "dphy_clk: %p\n" "pixel_clk: %p\n" @@ -1672,7 +1671,6 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, "pdev: %p\n" , __func__, ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en, - ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, ((struct mipi_csi2_info *)mipi_csi2_info)->datatype, ((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk, ((struct mipi_csi2_info *)mipi_csi2_info)->pixel_clk, @@ -1684,12 +1682,11 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, if (mipi_csi2_get_status(mipi_csi2_info)) { int ifmt; - if (tc358743_mode_info_data[frame_rate][mode].lanes != 0) { - pr_debug("%s Change lanes: from %d to %d\n", __func__, ((struct mipi_csi2_info *)mipi_csi2_info)->lanes, tc358743_mode_info_data[frame_rate][mode].lanes); - ((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes; - ((struct mipi_csi2_info *)mipi_csi2_info)->lanes = tc358743_mode_info_data[frame_rate][mode].lanes; - } - pr_debug("Now Using %d lanes\n",mipi_csi2_set_lanes(mipi_csi2_info)); + int lanes = tc358743_mode_info_data[frame_rate][mode].lanes; + if (!lanes) + lanes = 4; + lanes = mipi_csi2_set_lanes(mipi_csi2_info, lanes); + pr_debug("Now Using %d lanes\n", lanes); /*Only reset MIPI CSI2 HW at sensor initialize*/ if (!hdmi_mode) // is this during reset From d60e41eda08c40bce4aa0dfb89db3b8800fc2a8b Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 17 Jun 2014 12:14:54 -0700 Subject: [PATCH 0711/1983] tc358743_h2c: update Signed-off-by: Troy Kisky --- .../media/platform/mxc/capture/tc358743_h2c.c | 878 +++++++++--------- 1 file changed, 454 insertions(+), 424 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index ee92d2ee2bee18..a3fe221dfa61c3 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2014 Boundary Devices */ /* @@ -54,11 +55,6 @@ /* SSI clock sources */ #define IMX_SSP_SYS_CLK 0 - -#define TC358743_VOLTAGE_ANALOG 2800000 -#define TC358743_VOLTAGE_DIGITAL_CORE 1500000 -#define TC358743_VOLTAGE_DIGITAL_IO 1800000 - #define MIN_FPS 30 #define MAX_FPS 60 #define DEFAULT_FPS 60 @@ -70,9 +66,10 @@ #define TC358743_CHIP_ID_LOW_BYTE 0x0 #define TC3587430_HDMI_DETECT 0x0f //0x10 -#define TC_VOLTAGE_ANALOG 2800000 -#define TC_VOLTAGE_DIGITAL_CORE 1500000 #define TC_VOLTAGE_DIGITAL_IO 1800000 +#define TC_VOLTAGE_DIGITAL_CORE 1500000 +#define TC_VOLTAGE_DIGITAL_GPO 1500000 +#define TC_VOLTAGE_ANALOG 2800000 enum tc358743_mode { tc358743_mode_INIT, /*only for sensor init*/ @@ -112,145 +109,132 @@ struct tc358743_mode_info { u32 fps; u32 lanes; u32 freq; - struct reg_value *init_data_ptr; + const struct reg_value *init_data_ptr; u32 init_data_size; __u32 flags; }; -static struct delayed_work det_work; -static struct sensor_data tc358743_data; -static int pwn_gpio, rst_gpio; -static struct regulator *io_regulator; -static struct regulator *core_regulator; -static struct regulator *analog_regulator; -static struct regulator *gpo_regulator; +/*! + * Maintains the information on the current state of the sensor. + */ +struct tc_data { + struct sensor_data sensor; + struct delayed_work det_work; + struct mutex access_lock; + int det_work_disable; + int det_work_timeout; + int det_changed; +#define REGULATOR_IO 0 +#define REGULATOR_CORE 1 +#define REGULATOR_GPO 2 +#define REGULATOR_ANALOG 3 +#define REGULATOR_CNT 4 + struct regulator *regulator[REGULATOR_CNT]; +#ifdef CONFIG_TC358743_AUDIO + struct platform_device *snd_device; +#endif + u32 lock; + u32 bounce; + u32 hdmi_mode; + u32 fps; + u32 audio; + int pwn_gpio; + int rst_gpio; + u16 hpd_active; +}; + +static struct tc_data *g_td; -static u16 hpd_active = 1; #define DET_WORK_TIMEOUT_DEFAULT 100 #define DET_WORK_TIMEOUT_DEFERRED 2000 #define MAX_BOUNCE 5 -static DEFINE_MUTEX(access_lock); -static int det_work_disable = 0; -static int det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; -static u32 hdmi_mode = 0, lock = 0, bounce = 0, fps = 0, audio = 2; - -static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, - enum tc358743_mode mode); - -static int tc358743_toggle_hpd(int active); - -static void tc_standby(s32 enable) +static void tc_standby(struct tc_data *td, s32 standby) { - if (gpio_is_valid(pwn_gpio)) - gpio_set_value(pwn_gpio, enable ? 1 : 0); - pr_debug("tc_standby: powerdown=%x, power_gp=0x%x\n", enable, pwn_gpio); + if (gpio_is_valid(td->pwn_gpio)) + gpio_set_value(td->pwn_gpio, standby ? 1 : 0); + pr_debug("tc_standby: powerdown=%x, power_gp=0x%x\n", standby, td->pwn_gpio); msleep(2); } -static void tc_reset(void) +static void tc_reset(struct tc_data *td) { /* camera reset */ - gpio_set_value(rst_gpio, 1); + gpio_set_value(td->rst_gpio, 1); /* camera power dowmn */ - if (gpio_is_valid(pwn_gpio)) { - gpio_set_value(pwn_gpio, 1); + if (gpio_is_valid(td->pwn_gpio)) { + gpio_set_value(td->pwn_gpio, 1); msleep(5); - gpio_set_value(pwn_gpio, 0); + gpio_set_value(td->pwn_gpio, 0); } msleep(5); - gpio_set_value(rst_gpio, 0); + gpio_set_value(td->rst_gpio, 0); msleep(1); - gpio_set_value(rst_gpio, 1); + gpio_set_value(td->rst_gpio, 1); msleep(20); - if (gpio_is_valid(pwn_gpio)) - gpio_set_value(pwn_gpio, 1); + if (gpio_is_valid(td->pwn_gpio)) + gpio_set_value(td->pwn_gpio, 1); } -static int tc_power_on(struct device *dev) +static void tc_io_init(void) { - int ret = 0; + return tc_reset(g_td); +} - io_regulator = devm_regulator_get(dev, "DOVDD"); - if (!IS_ERR(io_regulator)) { - regulator_set_voltage(io_regulator, - TC_VOLTAGE_DIGITAL_IO, - TC_VOLTAGE_DIGITAL_IO); - ret = regulator_enable(io_regulator); - if (ret) { - pr_err("%s:io set voltage error\n", __func__); - return ret; - } else { - dev_dbg(dev, - "%s:io set voltage ok\n", __func__); - } - } else { - pr_err("%s: cannot get io voltage error\n", __func__); - io_regulator = NULL; - } +const char * const sregulator[REGULATOR_CNT] = { + [REGULATOR_IO] = "DOVDD", + [REGULATOR_CORE] = "DVDD", + [REGULATOR_GPO] = "DGPO", + [REGULATOR_ANALOG] = "AVDD", +}; - core_regulator = devm_regulator_get(dev, "DVDD"); - if (!IS_ERR(core_regulator)) { - regulator_set_voltage(core_regulator, - TC_VOLTAGE_DIGITAL_CORE, - TC_VOLTAGE_DIGITAL_CORE); - ret = regulator_enable(core_regulator); - if (ret) { - pr_err("%s:core set voltage error\n", __func__); - return ret; - } else { - dev_dbg(dev, - "%s:core set voltage ok\n", __func__); - } - } else { - core_regulator = NULL; - pr_err("%s: cannot get core voltage error\n", __func__); - } +static const int voltages[REGULATOR_CNT] = { + [REGULATOR_IO] = TC_VOLTAGE_DIGITAL_IO, + [REGULATOR_CORE] = TC_VOLTAGE_DIGITAL_CORE, + [REGULATOR_GPO] = TC_VOLTAGE_DIGITAL_GPO, + [REGULATOR_ANALOG] = TC_VOLTAGE_ANALOG, +}; - analog_regulator = devm_regulator_get(dev, "AVDD"); - if (!IS_ERR(analog_regulator)) { - regulator_set_voltage(analog_regulator, - TC_VOLTAGE_ANALOG, - TC_VOLTAGE_ANALOG); - ret = regulator_enable(analog_regulator); - if (ret) { - pr_err("%s:analog set voltage error\n", - __func__); - return ret; +static int tc_regulator_init(struct tc_data *td, struct device *dev) +{ + int i; + int ret = 0; + + for (i = 0; i < REGULATOR_CNT; i++) { + td->regulator[i] = devm_regulator_get(dev, sregulator[i]); + if (!IS_ERR(td->regulator[i])) { + regulator_set_voltage(td->regulator[i], + voltages[i], voltages[i]); } else { - dev_dbg(dev, - "%s:analog set voltage ok\n", __func__); + pr_err("%s:%s devm_regulator_get failed\n", __func__, sregulator[i]); + td->regulator[i] = NULL; } - } else { - analog_regulator = NULL; - pr_err("%s: cannot get analog voltage error\n", __func__); } - return ret; } -static void det_work_enable(int i) +static void det_work_enable(struct tc_data *td, int i) { - mutex_lock(&access_lock); + mutex_lock(&td->access_lock); if (i) { - det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; - schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); - det_work_disable = 0; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + schedule_delayed_work(&(td->det_work), msecs_to_jiffies(td->det_work_timeout)); + td->det_work_disable = 0; } else { - det_work_disable = 1; - det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + td->det_work_disable = 1; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; } - mutex_unlock(&access_lock); - pr_debug("%s: %d %d\n", __func__, det_work_disable, det_work_timeout); + mutex_unlock(&td->access_lock); + pr_debug("%s: %d %d\n", __func__, td->det_work_disable, td->det_work_timeout); } -static u8 cHDMIEDID[256] = { +static const u8 cHDMIEDID[256] = { /* FIXME! This is the edid that my ASUS HDMI monitor returns */ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x69, 0xf3, 0x24, 0xd6, 0x12, 0x00, 0x00, 0x16, 0x16, 0x01, 0x03, 0x80, 0x34, 0x1d, 0x78, 0x2a, 0xc7, 0x20, 0xa4, 0x55, 0x49, 0x99, 0x27, @@ -270,11 +254,7 @@ static u8 cHDMIEDID[256] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, }; -/*! - * Maintains the information on the current state of the sesor. - */ - -static struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = { +static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000004, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, @@ -367,7 +347,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz {0x0004, 0x00000cf7, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000004, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, @@ -455,7 +435,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz {0x0004, 0x00000cf7, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[] = { +static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -518,7 +498,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[ {0x7080, 0x00000083, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -582,7 +562,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[ }; -static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -646,7 +626,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[ {0x7080, 0x00000083, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -709,7 +689,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[ {0x7080, 0x00000083, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -772,7 +752,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz {0x7080, 0x00000083, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] = { +static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -835,7 +815,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] {0x7080, 0x00000083, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont[] = { +static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, {0x0002, 0x00000000, 0x00000000, 2, 1000}, @@ -899,7 +879,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_c }; //480p RGB2YUV442 -static struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = { +static const struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000004, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, @@ -990,7 +970,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = { }; //480p RGB2YUV442 -static struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = { +static const struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000004, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100}, @@ -1080,7 +1060,7 @@ static struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = { {0x0004, 0x00000cf7, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, @@ -1168,7 +1148,7 @@ static struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300M {0x0004, 0x00000cf7, 0x00000000, 2, 0}, }; -static struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz[] = { +static const struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz[] = { {0x7080, 0x00000000, 0x00000000, 2, 0}, // IR control resister {0x0004, 0x00000084, 0x00000000, 2, 0}, // Internal Generated output pattern,Do not send InfoFrame data out to CSI2,Audio output to CSI2-TX i/f,I2C address index increments on every data byte transfer, disable audio and video TX buffers {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, // Reset devices and set normal operatio (not sleep) @@ -1278,7 +1258,7 @@ static const struct v4l2_fmtdesc tc358743_formats[] = { }; -static struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = { +static const struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = { [0][tc358743_mode_720P_60_1280_720] = {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, @@ -1430,9 +1410,6 @@ static int tc358743_probe(struct i2c_client *adapter, const struct i2c_device_id *device_id); static int tc358743_remove(struct i2c_client *client); -static s32 tc358743_read_reg(u16 reg, u32 *val); -static s32 tc358743_write_reg(u16 reg, u32 val, int len); - static const struct i2c_device_id tc358743_id[] = { {"tc358743_mipi", 0}, {}, @@ -1454,8 +1431,9 @@ struct _reg_size { u16 startaddr, endaddr; int size; -} -tc358743_read_reg_size [] = +}; + +static const struct _reg_size tc358743_read_reg_size[] = { {0x0000, 0x005a, 2}, {0x0140, 0x0150, 4}, @@ -1472,7 +1450,7 @@ tc358743_read_reg_size [] = {0, 0, 0}, }; -static s32 tc358743_write_reg(u16 reg, u32 val, int len) +static s32 tc358743_write_reg(struct sensor_data *sensor, u16 reg, u32 val, int len) { int i = 0; u32 data = val; @@ -1511,7 +1489,7 @@ static s32 tc358743_write_reg(u16 reg, u32 val, int len) data >>= 8; } - if (i2c_master_send(tc358743_data.i2c_client, au8Buf, i) < 0) { + if (i2c_master_send(sensor->i2c_client, au8Buf, i) < 0) { pr_err("%s:write reg error:reg=%x,val=%x\n", __func__, reg, val); return -1; @@ -1523,7 +1501,7 @@ static s32 tc358743_write_reg(u16 reg, u32 val, int len) return 0; } -static s32 tc358743_read_reg(u16 reg, u32 *val) +static s32 tc358743_read_reg(struct sensor_data *sensor, u16 reg, u32 *val) { u8 au8RegBuf[2] = {0}; u32 u32RdVal = 0; @@ -1546,13 +1524,13 @@ static s32 tc358743_read_reg(u16 reg, u32 *val) au8RegBuf[0] = reg >> 8; au8RegBuf[1] = reg & 0xff; - if (2 != i2c_master_send(tc358743_data.i2c_client, au8RegBuf, 2)) { + if (2 != i2c_master_send(sensor->i2c_client, au8RegBuf, 2)) { pr_err("%s:read reg error:reg=%x\n", __func__, reg); return -1; } - if (size /*of(u32RdVal)*/ != i2c_master_recv(tc358743_data.i2c_client, (char *)&u32RdVal, size /*of(u32RdVal)*/)) { + if (size /*of(u32RdVal)*/ != i2c_master_recv(sensor->i2c_client, (char *)&u32RdVal, size /*of(u32RdVal)*/)) { pr_err("%s:read reg error:reg=%x,val=%x\n", __func__, reg, u32RdVal); return -1; @@ -1561,26 +1539,28 @@ static s32 tc358743_read_reg(u16 reg, u32 *val) return size; } -static int tc358743_write_edid(u8 *edid, int len) +static int tc358743_write_edid(struct sensor_data *sensor, const u8 *edid, int len) { int i = 0, off = 0; - u8 au8Buf[8+2] = {0}; + u8 au8Buf[16+2] = {0}; int size = 0; u16 reg; reg = 0x8C00; off = 0; - size = ARRAY_SIZE(au8Buf)-2; + size = ARRAY_SIZE(au8Buf) - 2; pr_debug("Write EDID: %d (%d)\n", len, size); while (len > 0) { i = 0; au8Buf[i++] = (reg >> 8) & 0xff; au8Buf[i++] = reg & 0xff; - while (i < ARRAY_SIZE(au8Buf)) { + if (size > len) + size = len; + while (i < size + 2) { au8Buf[i++] = edid[off++]; } - if (i2c_master_send(tc358743_data.i2c_client, au8Buf, i) < 0) { + if (i2c_master_send(sensor->i2c_client, au8Buf, i) < 0) { pr_err("%s:write reg error:reg=%x,val=%x\n", __func__, reg, off); return -1; @@ -1589,51 +1569,70 @@ static int tc358743_write_edid(u8 *edid, int len) reg += (u16)size; } pr_debug("Activate EDID\n"); - tc358743_write_reg(0x85c7, 0x01, 1); - tc358743_write_reg(0x85ca, 0x00, 1); - tc358743_write_reg(0x85cb, 0x01, 1); + tc358743_write_reg(sensor, 0x85c7, 0x01, 1); + tc358743_write_reg(sensor, 0x85ca, 0x00, 1); + tc358743_write_reg(sensor, 0x85cb, 0x01, 1); return 0; } -static int tc358743_reset(struct sensor_data *sensor) -{ - u32 tgt_fps; /* target frames per secound */ - enum tc358743_frame_rate frame_rate = tc358743_60_fps; - int ret = -1; - - det_work_enable(0); - while (ret) { - tc_standby(1); - mdelay(100); - tc_standby(0); - mdelay(1000); - - tgt_fps = sensor->streamcap.timeperframe.denominator / - sensor->streamcap.timeperframe.numerator; - - if (tgt_fps == 60) - frame_rate = tc358743_60_fps; - else if (tgt_fps == 30) - frame_rate = tc358743_30_fps; +static s32 power_control(struct tc_data *td, int on) + { + struct sensor_data *sensor = &td->sensor; + int i; + int ret = 0; - pr_debug("%s: capture mode: %d extended mode: %d fps: %d\n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode, tgt_fps); + pr_debug("%s: %d\n", __func__, on); + if (sensor->on != on) { + mutex_lock(&td->access_lock); + if (on) { + for (i = 0; i < REGULATOR_CNT; i++) { + if (td->regulator[i]) { + ret = regulator_enable(td->regulator[i]); + if (ret) { + pr_err("%s:regulator_enable failed(%d)\n", + __func__, ret); + on = 0; /* power all off */ + break; + } + } + } + } + tc_standby(td, on ? 0 : 1); + sensor->on = on; + if (!on) { + for (i = REGULATOR_CNT - 1; i >= 0; i--) { + if (td->regulator[i]) + regulator_disable(td->regulator[i]); + } + } + mutex_unlock(&td->access_lock); + } + return ret; +} - ret = tc358743_init_mode(frame_rate, - sensor->streamcap.capturemode); - if (ret) - pr_err("%s: Fail to init tc35874! - retry\n", __func__); +static int tc358743_toggle_hpd(struct sensor_data *sensor, int active) +{ + int ret = 0; + if (active) { + ret += tc358743_write_reg(sensor, 0x8544, 0x00, 1); + mdelay(500); + ret += tc358743_write_reg(sensor, 0x8544, 0x10, 1); + } else { + ret += tc358743_write_reg(sensor, 0x8544, 0x10, 1); + mdelay(500); + ret += tc358743_write_reg(sensor, 0x8544, 0x00, 1); } - det_work_enable(1); return ret; } void mipi_csi2_swreset(struct mipi_csi2_info *info); #include "../../../../mxc/mipi/mxc_mipi_csi2.h" -static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, - enum tc358743_mode mode) +static int tc358743_init_mode(struct tc_data *td, + enum tc358743_frame_rate frame_rate, + enum tc358743_mode mode) { - - struct reg_value *pModeSetting = NULL; + struct sensor_data *sensor = &td->sensor; + const struct reg_value *pModeSetting = NULL; s32 i = 0; s32 iModeSettingArySize = 0; register u32 RepeateLines = 0; @@ -1660,7 +1659,7 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, pr_debug("%s rate: %d mode: %d, info %p\n", __func__, frame_rate, mode, mipi_csi2_info); /* initial mipi dphy */ - tc358743_toggle_hpd(!hpd_active); + tc358743_toggle_hpd(sensor, !td->hpd_active); if (mipi_csi2_info) { pr_debug("%s: mipi_csi2_info:\n" "mipi_en: %d\n" @@ -1689,20 +1688,20 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, pr_debug("Now Using %d lanes\n", lanes); /*Only reset MIPI CSI2 HW at sensor initialize*/ - if (!hdmi_mode) // is this during reset + if (!td->hdmi_mode) // is this during reset mipi_csi2_reset(mipi_csi2_info); - pr_debug("%s format: %x\n", __func__, tc358743_data.pix.pixelformat); + pr_debug("%s format: %x\n", __func__, sensor->pix.pixelformat); for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) { - tc358743_data.pix.pixelformat = tc358743_formats[ifmt].pixelformat; - pr_debug("%s: %s (%x, %x)\n", __func__, tc358743_formats[ifmt].description, tc358743_data.pix.pixelformat, tc358743_formats[ifmt].flags); + sensor->pix.pixelformat = tc358743_formats[ifmt].pixelformat; + pr_debug("%s: %s (%x, %x)\n", __func__, tc358743_formats[ifmt].description, sensor->pix.pixelformat, tc358743_formats[ifmt].flags); mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags); break; } if (ifmt >= ARRAY_SIZE(tc358743_formats)) { - pr_err("currently this sensor format (0x%x) can not be supported!\n", tc358743_data.pix.pixelformat); + pr_err("currently this sensor format (0x%x) can not be supported!\n", sensor->pix.pixelformat); return -1; } } else { @@ -1720,17 +1719,17 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, iModeSettingArySize = tc358743_mode_info_data[frame_rate][mode].init_data_size; - tc358743_data.pix.width = + sensor->pix.width = tc358743_mode_info_data[frame_rate][mode].width; - tc358743_data.pix.height = + sensor->pix.height = tc358743_mode_info_data[frame_rate][mode].height; pr_debug("%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __func__, iModeSettingArySize, pModeSetting, frame_rate, mode, - tc358743_data.pix.width, - tc358743_data.pix.height); + sensor->pix.width, + sensor->pix.height); for (i = 0; i < iModeSettingArySize; ++i) { pModeSetting = tc358743_mode_info_data[frame_rate][mode].init_data_ptr + i; @@ -1740,7 +1739,7 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, Mask = pModeSetting->u32Mask; Length = pModeSetting->u8Length; if (Mask) { - retval = tc358743_read_reg(RegAddr, &RegVal); + retval = tc358743_read_reg(sensor, RegAddr, &RegVal); if (retval < 0) break; @@ -1749,7 +1748,7 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, Val |= RegVal; } - retval = tc358743_write_reg(RegAddr, Val, Length); + retval = tc358743_write_reg(sensor, RegAddr, Val, Length); if (retval < 0) break; @@ -1771,11 +1770,11 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, goto err; } } - if (!hdmi_mode) // is this during reset - if ((retval = tc358743_write_edid(cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) + if (!td->hdmi_mode) // is this during reset + if ((retval = tc358743_write_edid(sensor, cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) pr_err("%s: Fail to write EDID to tc35874!\n", __func__); - tc358743_toggle_hpd(hpd_active); + tc358743_toggle_hpd(sensor, td->hpd_active); if (mipi_csi2_info) { unsigned int i = 0; @@ -1828,19 +1827,66 @@ static int tc358743_init_mode(enum tc358743_frame_rate frame_rate, return (retval>0)?0:retval; } +static int tc358743_minit(struct tc_data *td) +{ + struct sensor_data *sensor = &td->sensor; + int ret; + enum tc358743_frame_rate frame_rate = tc358743_60_fps; + u32 tgt_fps = sensor->streamcap.timeperframe.denominator / + sensor->streamcap.timeperframe.numerator; + + if (tgt_fps == 60) + frame_rate = tc358743_60_fps; + else if (tgt_fps == 30) + frame_rate = tc358743_30_fps; + + pr_debug("%s: capture mode: %d extended mode: %d fps: %d\n", __func__, + sensor->streamcap.capturemode, + sensor->streamcap.extendedmode, tgt_fps); + + ret = tc358743_init_mode(td, frame_rate, sensor->streamcap.capturemode); + if (ret) + pr_err("%s: Fail to init tc35874!\n", __func__); + return ret; +} + +static int tc358743_reset(struct tc_data *td) +{ + int loop = 0; + int ret; + + det_work_enable(td, 0); + for (;;) { + pr_debug("%s: RESET\n", __func__); + power_control(td, 0); + mdelay(100); + power_control(td, 1); + mdelay(1000); + + ret = tc358743_minit(td); + if (!ret) + break; + if (loop++ >= 3) { + pr_err("%s:failed(%d)\n", __func__, ret); + break; + } + } + det_work_enable(td, 1); + return ret; +} + /* --------------- IOCTL functions from v4l2_int_ioctl_desc --------------- */ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) { + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; + pr_debug("%s\n", __func__); - if (s == NULL) { - pr_err(" ERROR!! no slave device set!\n"); - return -1; - } memset(p, 0, sizeof(*p)); - p->u.bt656.clock_curr = TC358743_XCLK_MIN; //tc358743_data.mclk; - pr_debug("%s: clock_curr=mclk=%d\n", __func__, tc358743_data.mclk); + p->u.bt656.clock_curr = TC358743_XCLK_MIN; //sensor->mclk; + pr_debug("%s: clock_curr=mclk=%d\n", __func__, sensor->mclk); p->if_type = V4L2_IF_TYPE_BT656; p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; p->u.bt656.clock_min = TC358743_XCLK_MIN; @@ -1860,41 +1906,11 @@ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) */ static int ioctl_s_power(struct v4l2_int_device *s, int on) { - struct sensor_data *sensor = s->priv; - - pr_debug("%s: %d\n", __func__, on); - if (on && !sensor->on) { - if (io_regulator) - if (regulator_enable(io_regulator) != 0) - return -EIO; - if (core_regulator) - if (regulator_enable(core_regulator) != 0) - return -EIO; - if (gpo_regulator) - if (regulator_enable(gpo_regulator) != 0) - return -EIO; - if (analog_regulator) - if (regulator_enable(analog_regulator) != 0) - return -EIO; - /* Make sure power on */ - tc_standby(0); - - } else if (!on && sensor->on) { - if (analog_regulator) - regulator_disable(analog_regulator); - if (core_regulator) - regulator_disable(core_regulator); - if (io_regulator) - regulator_disable(io_regulator); - if (gpo_regulator) - regulator_disable(gpo_regulator); - if (!hdmi_mode) - tc358743_reset(sensor); - } - - sensor->on = on; - - return 0; + struct tc_data *td = s->priv; + if (on) + if (!td->hdmi_mode) + return tc358743_reset(td); + return power_control(td, on); } /*! @@ -1906,7 +1922,8 @@ static int ioctl_s_power(struct v4l2_int_device *s, int on) */ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) { - struct sensor_data *sensor = s->priv; + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; struct v4l2_captureparm *cparm = &a->parm.capture; int ret = 0; @@ -1939,26 +1956,11 @@ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) break; } - det_work_enable(1); + det_work_enable(td, 1); pr_debug("%s done %d\n", __func__, ret); return ret; } -static int tc358743_toggle_hpd(int active) -{ - int ret = 0; - if (active) { - ret += tc358743_write_reg(0x8544, 0x00, 1); - mdelay(500); - ret += tc358743_write_reg(0x8544, 0x10, 1); - } else { - ret += tc358743_write_reg(0x8544, 0x10, 1); - mdelay(500); - ret += tc358743_write_reg(0x8544, 0x00, 1); - } - return ret; -} - /*! * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl * @s: pointer to standard V4L2 device structure @@ -1970,16 +1972,17 @@ static int tc358743_toggle_hpd(int active) */ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) { - struct sensor_data *sensor = s->priv; + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; u32 tgt_fps; /* target frames per secound */ enum tc358743_frame_rate frame_rate = tc358743_60_fps, frame_rate_now = tc358743_60_fps; int ret = 0; pr_debug("%s\n", __func__); - det_work_enable(0); + det_work_enable(td, 0); /* Make sure power on */ - tc_standby(0); + power_control(td, 1); switch (a->type) { /* This is the only case currently handled. */ @@ -2040,7 +2043,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) pr_debug("%s: capture mode: %d extended mode: %d \n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode); - ret = tc358743_init_mode(frame_rate, + ret = tc358743_init_mode(td, frame_rate, sensor->streamcap.capturemode); } else { pr_debug("%s: Keep current settings\n", __func__); @@ -2067,7 +2070,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } if (ret) - det_work_enable(1); + det_work_enable(td, 1); return ret; } @@ -2082,30 +2085,32 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) */ static int ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) { + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; int ret = 0; pr_debug("%s\n", __func__); switch (vc->id) { case V4L2_CID_BRIGHTNESS: - vc->value = tc358743_data.brightness; + vc->value = sensor->brightness; break; case V4L2_CID_HUE: - vc->value = tc358743_data.hue; + vc->value = sensor->hue; break; case V4L2_CID_CONTRAST: - vc->value = tc358743_data.contrast; + vc->value = sensor->contrast; break; case V4L2_CID_SATURATION: - vc->value = tc358743_data.saturation; + vc->value = sensor->saturation; break; case V4L2_CID_RED_BALANCE: - vc->value = tc358743_data.red; + vc->value = sensor->red; break; case V4L2_CID_BLUE_BALANCE: - vc->value = tc358743_data.blue; + vc->value = sensor->blue; break; case V4L2_CID_EXPOSURE: - vc->value = tc358743_data.ae_mode; + vc->value = sensor->ae_mode; break; default: ret = -EINVAL; @@ -2167,7 +2172,7 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) return retval; } -int get_pixelformat(int index) +static int get_pixelformat(int index) { int ifmt; @@ -2255,7 +2260,8 @@ static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, static int ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { - struct sensor_data *sensor = s->priv; + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; u32 tgt_fps; /* target frames per secound */ enum tc358743_frame_rate frame_rate; // enum image_size isize; @@ -2275,8 +2281,8 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, return -EINVAL; } - tc358743_data.pix.width = pix->width = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].width; - tc358743_data.pix.height = pix->height = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].height; + sensor->pix.width = pix->width = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].width; + sensor->pix.height = pix->height = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].height; for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) if (tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].flags == tc358743_formats[ifmt].flags) @@ -2285,7 +2291,7 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, if (ifmt == ARRAY_SIZE(tc358743_formats)) ifmt = 0; /* Default = RBG888 */ - tc358743_data.pix.pixelformat = pix->pixelformat = tc358743_formats[ifmt].pixelformat; + sensor->pix.pixelformat = pix->pixelformat = tc358743_formats[ifmt].pixelformat; pix->field = V4L2_FIELD_NONE; pix->bytesperline = pix->width * 4; pix->sizeimage = pix->bytesperline * pix->height; @@ -2300,15 +2306,15 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, { u32 u32val; - int ret = tc358743_read_reg(0x8520,&u32val); + int ret = tc358743_read_reg(sensor, 0x8520,&u32val); pr_debug("SYS_STATUS: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8521,&u32val); + ret = tc358743_read_reg(sensor, 0x8521,&u32val); pr_debug("VI_STATUS0: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8522,&u32val); + ret = tc358743_read_reg(sensor, 0x8522,&u32val); pr_debug("VI_STATUS1: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8525,&u32val); + ret = tc358743_read_reg(sensor, 0x8525,&u32val); pr_debug("VI_STATUS2: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(0x8528,&u32val); + ret = tc358743_read_reg(sensor, 0x8528,&u32val); pr_debug("VI_STATUS3: 0x%x, ret val: %d \n",u32val,ret); pr_debug("%s %d:%d format: %x\n", __func__, pix->width, pix->height, pix->pixelformat); } @@ -2325,8 +2331,13 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, */ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { + struct tc_data *td = s->priv; pr_debug("%s\n", __func__); + if (td->det_changed) { + td->det_changed = 0; + tc358743_minit(td); + } return ioctl_try_fmt_cap(s, f); } @@ -2338,7 +2349,8 @@ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) */ static int ioctl_dev_init(struct v4l2_int_device *s) { - struct sensor_data *sensor = s->priv; + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; u32 tgt_xclk; /* target xclk */ u32 tgt_fps; /* target frames per secound */ int ret = 0; @@ -2346,17 +2358,16 @@ static int ioctl_dev_init(struct v4l2_int_device *s) void *mipi_csi2_info; pr_debug("%s\n", __func__); - tc358743_data.on = true; /* mclk */ - tgt_xclk = tc358743_data.mclk; + tgt_xclk = sensor->mclk; tgt_xclk = min(tgt_xclk, (u32)TC358743_XCLK_MAX); tgt_xclk = max(tgt_xclk, (u32)TC358743_XCLK_MIN); - tc358743_data.mclk = tgt_xclk; +// sensor->mclk = tgt_xclk; - pr_debug("%s: Setting mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); -// set_mclk_rate(&tc358743_data.mclk, tc358743_data.mclk_source); -// pr_debug("%s: After mclk to %d MHz\n", __func__, tc358743_data.mclk / 1000000); + pr_debug("%s: Setting mclk to %d MHz\n", __func__, sensor->mclk / 1000000); +// set_mclk_rate(&sensor->mclk, sensor->mclk_source); +// pr_debug("%s: After mclk to %d MHz\n", __func__, sensor->mclk / 1000000); /* Default camera frame rate is set in probe */ tgt_fps = sensor->streamcap.timeperframe.denominator / @@ -2443,7 +2454,7 @@ static struct v4l2_int_device tc358743_int_device = { }; -#ifdef AUDIO_ENABLE +#ifdef CONFIG_TC358743_AUDIO struct imx_ssi { struct platform_device *ac97_dev; @@ -2561,44 +2572,35 @@ static int imxpac_tc358743_hw_params(struct snd_pcm_substream *substream, -/* Headphones jack detection DAPM pins */ -static struct snd_soc_jack_pin hs_jack_pins_a[] = { -}; - /* imx_3stack card dapm widgets */ -static struct snd_soc_dapm_widget imx_3stack_dapm_widgets_a[] = { +static const struct snd_soc_dapm_widget imx_3stack_dapm_widgets_a[] = { }; -static struct snd_kcontrol_new tc358743_machine_controls_a[] = { +static const struct snd_kcontrol_new tc358743_machine_controls_a[] = { }; /* imx_3stack machine connections to the codec pins */ -static struct snd_soc_dapm_route audio_map_a[] = { +static const struct snd_soc_dapm_route audio_map_a[] = { }; static int imx_3stack_tc358743_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; int ret; - struct snd_soc_jack *hs_jack; - struct snd_soc_jack_pin *hs_jack_pins; - int hs_jack_pins_size; - struct snd_soc_dapm_widget *imx_3stack_dapm_widgets; + const struct snd_soc_dapm_widget *imx_3stack_dapm_widgets; int imx_3stack_dapm_widgets_size; - struct snd_kcontrol_new *tc358743_machine_controls; + const struct snd_kcontrol_new *tc358743_machine_controls; int tc358743_machine_controls_size; - struct snd_soc_dapm_route *audio_map; + const struct snd_soc_dapm_route *audio_map; int audio_map_size; int gpio_num = -1; char *gpio_name; pr_debug("%s started\n", __func__); - hs_jack_pins = hs_jack_pins_a; - hs_jack_pins_size = ARRAY_SIZE(hs_jack_pins_a); imx_3stack_dapm_widgets = imx_3stack_dapm_widgets_a; imx_3stack_dapm_widgets_size = ARRAY_SIZE(imx_3stack_dapm_widgets_a); tc358743_machine_controls = tc358743_machine_controls_a; @@ -2620,26 +2622,7 @@ static int imx_3stack_tc358743_init(struct snd_soc_pcm_runtime *rtd) /* Set up imx_3stack specific audio path audio_map */ snd_soc_dapm_add_routes(&codec->dapm, audio_map, audio_map_size); - - snd_soc_dapm_enable_pin(&codec->dapm, hs_jack_pins->pin); snd_soc_dapm_sync(&codec->dapm); - - hs_jack = kzalloc(sizeof(struct snd_soc_jack), GFP_KERNEL); - - - ret = snd_soc_jack_new(codec, hs_jack_pins->pin, - SND_JACK_HEADPHONE, hs_jack); - if (ret) { - pr_err("%s: snd_soc_jack_new failed. err = %d\n", __func__, ret); - return ret; - } - - ret = snd_soc_jack_add_pins(hs_jack,hs_jack_pins_size, - hs_jack_pins); - if (ret) { - pr_err("%s: snd_soc_jack_add_pinsfailed. err = %d\n", __func__, ret); - return ret; - } return 0; } @@ -2665,9 +2648,6 @@ static struct snd_soc_card imxpac_tc358743 = { .num_links = 1, }; -static struct platform_device *imxpac_tc358743_snd_device; -static struct platform_device *imxpac_tc358743_snd_device; - static int imx_audmux_config(int slave, int master) { unsigned int ptcr, pdcr; @@ -2820,26 +2800,32 @@ static struct snd_soc_dai_driver tc358743_dai = { #endif -static char tc358743_mode_list[16][12] = +struct tc_mode_list { + const char *name; + enum tc358743_mode mode; +}; + +static const struct tc_mode_list tc358743_mode_list[16] = { - "None", - "VGA", - "240p/480i", - "288p/576i", - "W240p/480i", - "W288p/576i", - "480p", - "576p", - "W480p", - "W576p", - "WW480p", - "WW576p", - "720p", - "1035i", - "1080i", - "1080p" + {"None", 0}, /* 0 */ + {"VGA", tc358743_mode_480P_640_480}, /* 1 */ + {"240p/480i", 0}, /* 2 */ + {"288p/576i", 0}, /* 3 */ + {"W240p/480i", 0}, /* 4 */ + {"W288p/576i", 0}, /* 5 */ + {"480p", 0}, /* 6 */ + {"576p", 0}, /* 7 */ + {"W480p", tc358743_mode_480P_720_480}, /* 8 */ + {"W576p", 0}, /* 9 */ + {"WW480p", 0}, /* 10 */ + {"WW576p", 0}, /* 11 */ + {"720p", tc358743_mode_720P_60_1280_720}, /* 12 */ + {"1035i", 0}, /* 13 */ + {"1080i", 0}, /* 14 */ + {"1080p", tc358743_mode_1080P_1920_1080}, /* 15 */ }; + static char tc358743_fps_list[tc358743_max_fps+1] = { [tc358743_60_fps] = 60, @@ -2868,76 +2854,91 @@ static int tc358743_audio_list[16] = }; static char str_on[80]; -static void report_netlink(void) +static void report_netlink(struct tc_data *td) { + struct sensor_data *sensor = &td->sensor; char *envp[2]; envp[0] = &str_on[0]; envp[1] = NULL; - sprintf(envp[0], "HDMI RX: %d (%s) %d %d", (unsigned char)hdmi_mode & 0xf, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], tc358743_fps_list[fps], tc358743_audio_list[audio]); - kobject_uevent_env(&(tc358743_data.i2c_client->dev.kobj), KOBJ_CHANGE, envp); - det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; - pr_debug("%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", __func__, (unsigned char)hdmi_mode, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout, tc358743_audio_list[audio]); + sprintf(envp[0], "HDMI RX: %d (%s) %d %d", + td->hdmi_mode & 0xf, + tc358743_mode_list[td->hdmi_mode & 0xf].name, + tc358743_fps_list[td->fps], tc358743_audio_list[td->audio]); + kobject_uevent_env(&(sensor->i2c_client->dev.kobj), KOBJ_CHANGE, envp); + td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + pr_debug("%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", + __func__, td->hdmi_mode, + tc358743_mode_list[td->hdmi_mode & 0xf].name, td->fps, td->bounce, + td->det_work_timeout, tc358743_audio_list[td->audio]); } static void det_worker(struct work_struct *work) { + struct tc_data *td = container_of(work, struct tc_data, det_work.work); + struct sensor_data *sensor = &td->sensor; u32 u32val; u16 reg; int ret; - mutex_lock(&access_lock); - if (!det_work_disable) { + mutex_lock(&td->access_lock); + if (!td->det_work_disable) { reg = 0x8621; - ret = tc358743_read_reg(reg, &u32val); + ret = tc358743_read_reg(sensor, reg, &u32val); if (ret > 0) { - if (audio != (((unsigned char)u32val) & 0x0f)) { - audio = ((unsigned char)u32val) & 0x0f; - report_netlink(); + if (td->audio != (((unsigned char)u32val) & 0x0f)) { + td->audio = ((unsigned char)u32val) & 0x0f; + report_netlink(td); } } reg = 0x852f; - ret = tc358743_read_reg(reg, &u32val); + ret = tc358743_read_reg(sensor, reg, &u32val); if (ret > 0) { while (1) { if (u32val & TC3587430_HDMI_DETECT) { - lock = u32val & TC3587430_HDMI_DETECT; + td->lock = u32val & TC3587430_HDMI_DETECT; reg = 0x8521; - ret = tc358743_read_reg(reg, &u32val); + ret = tc358743_read_reg(sensor, reg, &u32val); if (ret < 0) { pr_err("%s: Error reading mode\n", __func__); } } else { - if (lock) { // check if it is realy un-plug - lock = 0; + if (td->lock) { // check if it is realy un-plug + td->lock = 0; u32val = 0x0; - hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. + td->hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. } } - if ((unsigned char)hdmi_mode != (unsigned char)u32val) { + if ((unsigned char)td->hdmi_mode != (unsigned char)u32val) { if (u32val) - det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; else - det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; - bounce = MAX_BOUNCE; - pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", __func__, (unsigned char)hdmi_mode, (unsigned char)u32val, tc358743_mode_list[(unsigned char)hdmi_mode & 0xf], fps, bounce, det_work_timeout); - hdmi_mode = u32val; - } else if (bounce) { - bounce--; - det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + td->bounce = MAX_BOUNCE; + pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", + __func__, (unsigned char)td->hdmi_mode, + (unsigned char)u32val, + tc358743_mode_list[td->hdmi_mode & 0xf].name, + td->fps, td->bounce, td->det_work_timeout); + td->hdmi_mode = u32val; + sensor->streamcap.capturemode = tc358743_mode_list[td->hdmi_mode & 0xf].mode; + td->det_changed = 1; + } else if (td->bounce) { + td->bounce--; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; } - if (1 == bounce) { - if (hdmi_mode >= 0xe) { + if (1 == td->bounce) { + if (td->hdmi_mode >= 0xe) { reg = 0x852f; - ret = tc358743_read_reg(reg, &u32val); + ret = tc358743_read_reg(sensor, reg, &u32val); if (ret > 0) - fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; + td->fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; } reg = 0x8621; - ret = tc358743_read_reg(reg, &u32val); + ret = tc358743_read_reg(sensor, reg, &u32val); if (ret > 0) { - audio = ((unsigned char)u32val) & 0x0f; - report_netlink(); + td->audio = ((unsigned char)u32val) & 0x0f; + report_netlink(td); } } break; @@ -2946,17 +2947,19 @@ static void det_worker(struct work_struct *work) pr_err("%s: Error reading lock\n", __func__); } } else { - det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; } - mutex_unlock(&access_lock); - schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); + mutex_unlock(&td->access_lock); + schedule_delayed_work(&td->det_work, msecs_to_jiffies(td->det_work_timeout)); } static irqreturn_t tc358743_detect_handler(int irq, void *data) { + struct tc_data *td = data; + struct sensor_data *sensor = &td->sensor; - pr_debug("%s: IRQ %d\n", __func__, tc358743_data.i2c_client->irq); - schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); + pr_debug("%s: IRQ %d\n", __func__, sensor->i2c_client->irq); + schedule_delayed_work(&td->det_work, msecs_to_jiffies(td->det_work_timeout)); return IRQ_HANDLED; } @@ -2973,13 +2976,17 @@ static u16 regoffs = 0; static ssize_t tc358743_show_regdump(struct device *dev, struct device_attribute *attr, char *buf) { + struct tc_data *td = g_td; + struct sensor_data *sensor = &td->sensor; int i, len = 0; int retval; u32 u32val; - mutex_lock(&access_lock); + if (!td) + return len; + mutex_lock(&td->access_lock); for (i=0; iaccess_lock); len += sprintf(buf+len, "\n"); return len; } @@ -3026,20 +3033,22 @@ static ssize_t tc358743_store_hpd(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { + struct tc_data *td = g_td; u32 val; int retval; retval = sscanf(buf, "%d", &val); if (1 == retval) - hpd_active = (u16)val; + td->hpd_active = (u16)val; return count; } static ssize_t tc358743_show_hpd(struct device *dev, struct device_attribute *attr, char *buf) { + struct tc_data *td = g_td; int len = 0; - len += sprintf(buf+len, "%d\n", hpd_active); + len += sprintf(buf+len, "%d\n", td->hpd_active); return len; } @@ -3048,9 +3057,10 @@ static DEVICE_ATTR(hpd, S_IRUGO|S_IWUSR, tc358743_show_hpd, tc358743_store_hpd); static ssize_t tc358743_show_hdmirx(struct device *dev, struct device_attribute *attr, char *buf) { + struct tc_data *td = g_td; int len = 0; - len += sprintf(buf+len, "%d\n", hdmi_mode); + len += sprintf(buf+len, "%d\n", td->hdmi_mode); return len; } @@ -3059,21 +3069,23 @@ static DEVICE_ATTR(hdmirx, S_IRUGO, tc358743_show_hdmirx, NULL); static ssize_t tc358743_show_fps(struct device *dev, struct device_attribute *attr, char *buf) { + struct tc_data *td = g_td; int len = 0; - len += sprintf(buf+len, "%d\n", tc358743_fps_list[fps]); + len += sprintf(buf+len, "%d\n", tc358743_fps_list[td->fps]); return len; } static DEVICE_ATTR(fps, S_IRUGO, tc358743_show_fps, NULL); -#ifdef AUDIO_ENABLE +#ifdef CONFIG_TC358743_AUDIO static ssize_t tc358743_show_audio(struct device *dev, struct device_attribute *attr, char *buf) { + struct tc_data *td = g_td; int len = 0; - len += sprintf(buf+len, "%d\n", tc358743_audio_list[audio]); + len += sprintf(buf+len, "%d\n", tc358743_audio_list[td->audio]); return len; } @@ -3087,16 +3099,25 @@ static int tc358743_probe(struct i2c_client *client, struct device *dev = &client->dev; int retval; struct regmap *gpr; - struct sensor_data *sensor = &tc358743_data; + struct tc_data *td; + struct sensor_data *sensor; u32 u32val; + td = kzalloc(sizeof(*td), GFP_KERNEL); + if (!td) + return -ENOMEM; + td->hpd_active = 1; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + td->audio = 2; + mutex_init(&td->access_lock); + sensor = &td->sensor; /* request power down pin */ - pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); - if (!gpio_is_valid(pwn_gpio)) { + td->pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0); + if (!gpio_is_valid(td->pwn_gpio)) { dev_warn(dev, "no sensor pwdn pin available"); } else { - retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH, + retval = devm_gpio_request_one(dev, td->pwn_gpio, GPIOF_OUT_INIT_HIGH, "tc_mipi_pwdn"); if (retval < 0) { dev_warn(dev, "request of pwn_gpio failed"); @@ -3104,12 +3125,12 @@ static int tc358743_probe(struct i2c_client *client, } } /* request reset pin */ - rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); - if (!gpio_is_valid(rst_gpio)) { + td->rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0); + if (!gpio_is_valid(td->rst_gpio)) { dev_warn(dev, "no sensor reset pin available"); return -EINVAL; } - retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH, + retval = devm_gpio_request_one(dev, td->rst_gpio, GPIOF_OUT_INIT_HIGH, "tc_mipi_reset"); if (retval < 0) { dev_warn(dev, "request of tc_mipi_reset failed"); @@ -3117,8 +3138,6 @@ static int tc358743_probe(struct i2c_client *client, } /* Set initial values for the sensor struct. */ - memset(sensor, 0, sizeof(*sensor)); - sensor->mipi_camera = 1; sensor->virtual_channel = 0; sensor->sensor_clk = devm_clk_get(dev, "csi_mclk"); @@ -3163,7 +3182,7 @@ static int tc358743_probe(struct i2c_client *client, clk_prepare_enable(sensor->sensor_clk); - sensor->io_init = tc_reset; + sensor->io_init = tc_io_init; sensor->i2c_client = client; sensor->pix.pixelformat = tc358743_formats[0].pixelformat; sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY | @@ -3190,11 +3209,11 @@ static int tc358743_probe(struct i2c_client *client, pwm_enable(pwm); } - tc_power_on(dev); - tc_reset(); - tc_standby(0); + tc_regulator_init(td, dev); + power_control(td, 1); + tc_reset(td); - retval = tc358743_read_reg(TC358743_CHIP_ID_HIGH_BYTE, &u32val); + retval = tc358743_read_reg(sensor, TC358743_CHIP_ID_HIGH_BYTE, &u32val); if (retval < 0) { pr_err("%s:cannot find camera\n", __func__); retval = -ENODEV; @@ -3224,9 +3243,13 @@ static int tc358743_probe(struct i2c_client *client, __func__); } - tc358743_int_device.priv = sensor; + tc358743_int_device.priv = td; + if (!g_td) + g_td = td; - //retval = device_create_file(&client->dev, &dev_attr_audio); +#ifdef CONFIG_TC358743_AUDIO + retval = device_create_file(&client->dev, &dev_attr_audio); +#endif retval = device_create_file(&client->dev, &dev_attr_fps); retval = device_create_file(&client->dev, &dev_attr_hdmirx); retval = device_create_file(&client->dev, &dev_attr_hpd); @@ -3239,7 +3262,7 @@ static int tc358743_probe(struct i2c_client *client, goto err4; } -#ifdef AUDIO_ENABLE +#ifdef CONFIG_TC358743_AUDIO /* Audio setup */ retval = snd_soc_register_codec(&client->dev, &soc_codec_dev_tc358743, &tc358743_dai, 1); @@ -3256,54 +3279,58 @@ static int tc358743_probe(struct i2c_client *client, goto err4; } - imxpac_tc358743_snd_device = platform_device_alloc("soc-audio", 5); - if (!imxpac_tc358743_snd_device) { + td->snd_device = platform_device_alloc("soc-audio", 5); + if (!td->snd_device) { pr_err("%s: Platform device allocation failed, error=%d\n", __func__, retval); goto err4; } - platform_set_drvdata(imxpac_tc358743_snd_device, &imxpac_tc358743); - retval = platform_device_add(imxpac_tc358743_snd_device); + platform_set_drvdata(td->snd_device, &imxpac_tc358743); + retval = platform_device_add(td->snd_device); if (retval) { pr_err("%s: Platform device add failed, error=%d\n", __func__, retval); - platform_device_put(imxpac_tc358743_snd_device); + platform_device_put(td->snd_device); goto err4; } #endif #if 1 - INIT_DELAYED_WORK(&(det_work), det_worker); + INIT_DELAYED_WORK(&td->det_work, det_worker); if (sensor->i2c_client->irq) { retval = request_irq(sensor->i2c_client->irq, tc358743_detect_handler, IRQF_SHARED | IRQF_TRIGGER_FALLING, - "tc358743_det", sensor); + "tc358743_det", td); if (retval < 0) dev_warn(&sensor->i2c_client->dev, "cound not request det irq %d\n", sensor->i2c_client->irq); } - schedule_delayed_work(&(det_work), msecs_to_jiffies(det_work_timeout)); + schedule_delayed_work(&(td->det_work), msecs_to_jiffies(td->det_work_timeout)); #endif - retval = tc358743_reset(sensor); + retval = tc358743_reset(td); + if (retval) + goto err4; - tc_standby(1); + i2c_set_clientdata(client, td); retval = v4l2_int_device_register(&tc358743_int_device); if (retval) { pr_err("%s: v4l2_int_device_register failed, error=%d\n", __func__, retval); goto err4; } - pr_debug("%s: finished, error=%d\n", - __func__, retval); + power_control(td, 0); + pr_debug("%s: finished, error=%d\n", __func__, retval); return retval; err4: - pr_err("%s: failed, error=%d\n", - __func__, retval); + pr_err("%s: failed, error=%d\n", __func__, retval); + if (g_td == td) + g_td = NULL; + kfree(td); return retval; } @@ -3315,15 +3342,29 @@ static int tc358743_probe(struct i2c_client *client, */ static int tc358743_remove(struct i2c_client *client) { + int i; + struct tc_data *td = i2c_get_clientdata(client); + struct sensor_data *sensor = &td->sensor; + // Stop delayed work - cancel_delayed_work_sync(&(det_work)); + cancel_delayed_work_sync(&td->det_work); + power_control(td, 0); // Remove IRQ - if (tc358743_data.i2c_client->irq) { - free_irq(tc358743_data.i2c_client->irq, &tc358743_data); + if (sensor->i2c_client->irq) { + free_irq(sensor->i2c_client->irq, sensor); } +#ifdef CONFIG_TC358743_AUDIO +/* Audio breakdown */ + snd_soc_unregister_codec(&client->dev); + platform_driver_unregister(&imx_tc358743_audio1_driver); + platform_device_unregister(td->snd_device); +#endif /*Remove sysfs entries*/ +#ifdef CONFIG_TC358743_AUDIO + device_remove_file(&client->dev, &dev_attr_audio); +#endif device_remove_file(&client->dev, &dev_attr_fps); device_remove_file(&client->dev, &dev_attr_hdmirx); device_remove_file(&client->dev, &dev_attr_hpd); @@ -3332,26 +3373,15 @@ static int tc358743_remove(struct i2c_client *client) v4l2_int_device_unregister(&tc358743_int_device); - if (gpo_regulator) { - regulator_disable(gpo_regulator); - regulator_put(gpo_regulator); - } - - if (analog_regulator) { - regulator_disable(analog_regulator); - regulator_put(analog_regulator); - } - - if (core_regulator) { - regulator_disable(core_regulator); - regulator_put(core_regulator); - } - - if (io_regulator) { - regulator_disable(io_regulator); - regulator_put(io_regulator); + for (i = REGULATOR_CNT - 1; i >= 0; i--) { + if (td->regulator[i]) { + regulator_disable(td->regulator[i]); + } } - + mutex_destroy(&td->access_lock); + if (g_td == td) + g_td = NULL; + kfree(td); return 0; } From 9afdc13e9a55e8f61bf47cb631e2498a404b0342 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 6 Jun 2014 10:04:27 -0700 Subject: [PATCH 0712/1983] tc358743_h2c: update, including i2c transfer call for reads --- .../media/platform/mxc/capture/tc358743_h2c.c | 516 ++++++++---------- 1 file changed, 238 insertions(+), 278 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index a3fe221dfa61c3..b5594173d3e79b 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -121,7 +120,7 @@ struct tc_data { struct sensor_data sensor; struct delayed_work det_work; struct mutex access_lock; - int det_work_disable; + int det_work_enable; int det_work_timeout; int det_changed; #define REGULATOR_IO 0 @@ -219,19 +218,15 @@ static int tc_regulator_init(struct tc_data *td, struct device *dev) return ret; } -static void det_work_enable(struct tc_data *td, int i) +static void det_work_enable(struct tc_data *td, int enable) { mutex_lock(&td->access_lock); - if (i) { - td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; - schedule_delayed_work(&(td->det_work), msecs_to_jiffies(td->det_work_timeout)); - td->det_work_disable = 0; - } else { - td->det_work_disable = 1; - td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; - } + td->det_work_enable = enable; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + if (enable) + schedule_delayed_work(&td->det_work, msecs_to_jiffies(10)); mutex_unlock(&td->access_lock); - pr_debug("%s: %d %d\n", __func__, td->det_work_disable, td->det_work_timeout); + pr_debug("%s: %d %d\n", __func__, td->det_work_enable, td->det_work_timeout); } static const u8 cHDMIEDID[256] = { @@ -255,10 +250,6 @@ static const u8 cHDMIEDID[256] = { }; static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0004, 0x00000004, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000040, 0x00000000, 2, 0}, {0x0014, 0x00000000, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, @@ -291,10 +282,8 @@ static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_ {0x8514, 0x00000000, 0x00000000, 1, 0}, {0x8515, 0x00000000, 0x00000000, 1, 0}, {0x8516, 0x00000000, 0x00000000, 1, 0}, -// HDMI Audio RefClk (26 MHz) +// HDMI Audio {0x8531, 0x00000001, 0x00000000, 1, 0}, - {0x8540, 0x0000008c, 0x00000000, 1, 0}, - {0x8541, 0x0000000a, 0x00000000, 1, 0}, {0x8630, 0x000000b0, 0x00000000, 1, 0}, {0x8631, 0x0000001e, 0x00000000, 1, 0}, {0x8632, 0x00000004, 0x00000000, 1, 0}, @@ -303,13 +292,7 @@ static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_ {0x8532, 0x00000080, 0x00000000, 1, 0}, {0x8536, 0x00000040, 0x00000000, 1, 0}, {0x853f, 0x0000000a, 0x00000000, 1, 0}, -// EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, -// {0x8544, 0x00000000, 0x00000000, 1, 1000}, -// {0x8544, 0x00000001, 0x00000000, 1, 100}, {0x8545, 0x00000031, 0x00000000, 1, 0}, {0x8546, 0x0000002d, 0x00000000, 1, 0}, // HDCP Setting @@ -348,10 +331,6 @@ static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_ }; static const struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0004, 0x00000004, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0014, 0x0000ffff, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, @@ -384,9 +363,8 @@ static const struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_ {0x8514, 0x00000000, 0x00000000, 1, 0}, {0x8515, 0x00000000, 0x00000000, 1, 0}, {0x8516, 0x00000000, 0x00000000, 1, 0}, -// HDMI Audio RefClk (26 MHz) +// HDMI Audio {0x8531, 0x00000001, 0x00000000, 1, 0}, - {0x8540, 0x00000a8c, 0x00000000, 1, 0}, {0x8630, 0x00041eb0, 0x00000000, 1, 0}, {0x8670, 0x00000001, 0x00000000, 1, 0}, // HDMI PHY @@ -394,13 +372,8 @@ static const struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_ {0x8536, 0x00000040, 0x00000000, 1, 0}, {0x853f, 0x0000000a, 0x00000000, 1, 0}, // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, - {0x8544, 0x00000000, 0x00000000, 1, 0}, {0x8545, 0x00000031, 0x00000000, 1, 0}, {0x8546, 0x0000002d, 0x00000000, 1, 0}, -// EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // HDCP Setting {0x85d1, 0x00000001, 0x00000000, 1, 0}, {0x8560, 0x00000024, 0x00000000, 1, 0}, @@ -436,9 +409,6 @@ static const struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_ }; static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -499,9 +469,6 @@ static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_1280_720_1 }; static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -563,9 +530,6 @@ static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_1 static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -627,9 +591,6 @@ static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1024_720_2 }; static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -690,9 +651,6 @@ static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1280_720_3 }; static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -753,9 +711,6 @@ static const struct reg_value tc358743_setting_YUV422_4lane_color_bar_1920_1023_ }; static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -816,9 +771,6 @@ static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_17 }; static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -880,10 +832,6 @@ static const struct reg_value tc358743_setting_YUV422_2lane_color_bar_640_480_10 //480p RGB2YUV442 static const struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0004, 0x00000004, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000040, 0x00000000, 2, 0}, // {0x000a, 0x000005a0, 0x00000000, 2, 0}, // {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -918,9 +866,8 @@ static const struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz {0x8514, 0x00000000, 0x00000000, 1, 0}, {0x8515, 0x00000000, 0x00000000, 1, 0}, {0x8516, 0x00000000, 0x00000000, 1, 0}, -// HDMI Audio RefClk (26 MHz) +// HDMI Audio {0x8531, 0x00000001, 0x00000000, 1, 0}, - {0x8540, 0x00000a8c, 0x00000000, 1, 0}, {0x8630, 0x00041eb0, 0x00000000, 1, 0}, {0x8670, 0x00000001, 0x00000000, 1, 0}, // HDMI PHY @@ -928,14 +875,8 @@ static const struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz {0x8536, 0x00000040, 0x00000000, 1, 0}, {0x853f, 0x0000000a, 0x00000000, 1, 0}, // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, - {0x8544, 0x00000000, 0x00000000, 1, 100}, -// {0x8544, 0x00000001, 0x00000000, 1, 100}, {0x8545, 0x00000031, 0x00000000, 1, 0}, {0x8546, 0x0000002d, 0x00000000, 1, 0}, -// EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // HDCP Setting {0x85d1, 0x00000001, 0x00000000, 1, 0}, {0x8560, 0x00000024, 0x00000000, 1, 0}, @@ -971,10 +912,6 @@ static const struct reg_value tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz //480p RGB2YUV442 static const struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, - {0x0004, 0x00000004, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100}, - {0x0002, 0x00000000, 0x00000000, 2, 1000}, {0x0006, 0x00000040, 0x00000000, 2, 0}, {0x000a, 0x000005a0, 0x00000000, 2, 0}, // {0x0010, 0x0000001e, 0x00000000, 2, 0}, @@ -1009,9 +946,8 @@ static const struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz {0x8514, 0x00000000, 0x00000000, 1, 0}, {0x8515, 0x00000000, 0x00000000, 1, 0}, {0x8516, 0x00000000, 0x00000000, 1, 0}, -// HDMI Audio RefClk (27 MHz) +// HDMI Audio {0x8531, 0x00000001, 0x00000000, 1, 0}, - {0x8540, 0x00000a8c, 0x00000000, 1, 0}, {0x8630, 0x00041eb0, 0x00000000, 1, 0}, {0x8670, 0x00000001, 0x00000000, 1, 0}, // HDMI PHY @@ -1019,14 +955,8 @@ static const struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz {0x8536, 0x00000040, 0x00000000, 1, 0}, {0x853f, 0x0000000a, 0x00000000, 1, 0}, // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, - {0x8544, 0x00000000, 0x00000000, 1, 100}, -// {0x8544, 0x00000001, 0x00000000, 1, 100}, {0x8545, 0x00000031, 0x00000000, 1, 0}, {0x8546, 0x0000002d, 0x00000000, 1, 0}, -// EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // HDCP Setting {0x85d1, 0x00000001, 0x00000000, 1, 0}, {0x8560, 0x00000024, 0x00000000, 1, 0}, @@ -1061,10 +991,7 @@ static const struct reg_value tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz }; static const struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, {0x0004, 0x00000084, 0x00000000, 2, 0}, - {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, - {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0014, 0x00000000, 0x00000000, 2, 0}, {0x0016, 0x000005ff, 0x00000000, 2, 0}, @@ -1097,9 +1024,8 @@ static const struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_108 {0x8514, 0x00000000, 0x00000000, 1, 0}, {0x8515, 0x00000000, 0x00000000, 1, 0}, {0x8516, 0x00000000, 0x00000000, 1, 0}, -// HDMI Audio RefClk (27 MHz) +// HDMI Audio {0x8531, 0x00000001, 0x00000000, 1, 0}, - {0x8540, 0x00000a8c, 0x00000000, 1, 0}, {0x8630, 0x00041eb0, 0x00000000, 1, 0}, {0x8670, 0x00000001, 0x00000000, 1, 0}, // HDMI PHY @@ -1107,13 +1033,8 @@ static const struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_108 {0x8536, 0x00000040, 0x00000000, 1, 0}, {0x853f, 0x0000000a, 0x00000000, 1, 0}, // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, - {0x8544, 0x00000010, 0x00000000, 1, 100}, {0x8545, 0x00000031, 0x00000000, 1, 0}, {0x8546, 0x0000002d, 0x00000000, 1, 0}, -// EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // HDCP Setting {0x85d1, 0x00000001, 0x00000000, 1, 0}, {0x8560, 0x00000024, 0x00000000, 1, 0}, @@ -1149,10 +1070,7 @@ static const struct reg_value tc358743_setting_YUV422_4lane_1080P_60fps_1920_108 }; static const struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz[] = { - {0x7080, 0x00000000, 0x00000000, 2, 0}, // IR control resister {0x0004, 0x00000084, 0x00000000, 2, 0}, // Internal Generated output pattern,Do not send InfoFrame data out to CSI2,Audio output to CSI2-TX i/f,I2C address index increments on every data byte transfer, disable audio and video TX buffers - {0x0002, 0x00000f00, 0x00000000, 2, 100},//0}, // Reset devices and set normal operatio (not sleep) - {0x0002, 0x00000000, 0x00000000, 2, 1000},//0}, // Clear reset bits {0x0006, 0x000001f8, 0x00000000, 2, 0}, // FIFO level = 1f8 = 504 {0x0014, 0x00000000, 0x00000000, 2, 0}, // Clear interrupt status bits {0x0016, 0x000005ff, 0x00000000, 2, 0}, // Mask audio mute, CSI-TX, and the other interrups @@ -1186,23 +1104,17 @@ static const struct reg_value tc358743_setting_YUV422_4lane_1080P_30fps_1920_108 {0x8514, 0x00000000, 0x00000000, 1, 0}, // PACKET INTERRUPT MASK: unmask all {0x8515, 0x00000000, 0x00000000, 1, 0}, // CBIT INTERRUPT MASK: unmask all {0x8516, 0x00000000, 0x00000000, 1, 0}, // AUDIO INTERRUPT MASK: unmask all -// HDMI Audio RefClk (27 MHz) +// HDMI Audio {0x8531, 0x00000001, 0x00000000, 1, 0}, // PHY CONTROL0: 27MHz, DDC5V detection operation. - {0x8540, 0x00000a8c, 0x00000000, 1, 0}, // SYS FREQ0 Register: 27MHz {0x8630, 0x00041eb0, 0x00000000, 1, 0}, // Audio FS Lock Detect Control: for 27MHz - {0x8670, 0x00000001, 0x00000000, 1, 0}, // AUDIO PLL Setting: For REFCLK = 27MHz + {0x8670, 0x00000001, 0x00000000, 1, 0}, // HDMI PHY {0x8532, 0x00000080, 0x00000000, 1, 0}, // {0x8536, 0x00000040, 0x00000000, 1, 0}, // {0x853f, 0x0000000a, 0x00000000, 1, 0}, // // HDMI System - {0x8543, 0x00000032, 0x00000000, 1, 0}, // DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms - {0x8544, 0x00000010, 0x00000000, 1, 100}, // HPD Control Register: HOTPLUG output ON/OFF control mode = DDC5V detection interlock {0x8545, 0x00000031, 0x00000000, 1, 0}, // ANA CONTROL: PLL charge pump setting for Audio = normal, DAC/PLL power ON/OFF setting for Audio = ON {0x8546, 0x0000002d, 0x00000000, 1, 0}, // AVMUTE CONTROL: AVM_CTL = 0x2d -// EDID - {0x85c7, 0x00000001, 0x00000000, 1, 0}, // EDID MODE REGISTER: nternal EDID-RAM & DDC2B mode - {0x85cb, 0x00000001, 0x00000000, 1, 0}, // EDID Length REGISTER 2: EDID data size stored in RAM (upper address bits) = 0x1 (Size = 0x100 = 256) // HDCP Setting {0x85d1, 0x00000001, 0x00000000, 1, 0}, // {0x8560, 0x00000024, 0x00000000, 1, 0}, // HDCP MODE: HDCP automatic reset when DVI⇔HDMI switched = on, HDCP Line Rekey timing switch = 7clk mode (Data island delay ON), Bcaps[5] KSVINFO_READY(0x8840[5]) auto clear mode = Auto clear using AKSV write @@ -1450,93 +1362,134 @@ static const struct _reg_size tc358743_read_reg_size[] = {0, 0, 0}, }; -static s32 tc358743_write_reg(struct sensor_data *sensor, u16 reg, u32 val, int len) +int get_reg_size(u16 reg, int len) { - int i = 0; - u32 data = val; - u8 au8Buf[6] = {0}; - int size = 0; + const struct _reg_size *p = tc358743_read_reg_size; + int size; - while (0 != tc358743_read_reg_size[i].startaddr || - 0 != tc358743_read_reg_size[i].endaddr || - 0 != tc358743_read_reg_size[i].size) { - if (tc358743_read_reg_size[i].startaddr <= reg - && tc358743_read_reg_size[i].endaddr >= reg) { - size = tc358743_read_reg_size[i].size; - break; +#if 0 //later #ifndef DEBUG + if (len) + return len; +#endif + while (p->size) { + if ((p->startaddr <= reg) && (reg <= p->endaddr)) { + size = p->size; + if (len && (size != len)) { + pr_err("%s:reg len error:reg=%x %d instead of %d\n", + __func__, reg, len, size); + return 0; + } + if (reg % size) { + pr_err("%s:cannot read from the middle of a register, reg(%x) size(%d)\n", + __func__, reg, size); + return 0; + } + return size; } - i++; - } - if (!size) { - pr_err("%s:write reg error:reg=%x is not found\n",__func__, reg); - return -1; - } - if (size == 3) { - size = 2; - } else if (size != len) { - pr_err("%s:write reg len error:reg=%x %d instead of %d\n", - __func__, reg, len, size); - return 0; + p++; } + pr_err("%s:reg=%x size is not defined\n",__func__, reg); + return 0; +} - while (len > 0) { - i = 0; - au8Buf[i++] = (reg >> 8) & 0xff; - au8Buf[i++] = reg & 0xff; - while (size-- > 0) - { - au8Buf[i++] = (u8)data; - data >>= 8; - } +static s32 tc358743_read_reg(struct sensor_data *sensor, u16 reg, void *rxbuf) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msgs[2]; + u8 txbuf[2]; + int ret; + int size = get_reg_size(reg, 0); - if (i2c_master_send(sensor->i2c_client, au8Buf, i) < 0) { - pr_err("%s:write reg error:reg=%x,val=%x\n", - __func__, reg, val); - return -1; - } - len -= (u8)size; - reg += (u16)size; - } + if (!size) + return -EINVAL; + txbuf[0] = reg >> 8; + txbuf[1] = reg & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = txbuf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = size; + msgs[1].buf = rxbuf; + + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) { + pr_err("%s:reg=%x ret=%d\n", __func__, reg, ret); + return ret; + } +// pr_debug("%s:reg=%x,val=%x\n", __func__, reg, ((char *)rxbuf)[0]); return 0; } -static s32 tc358743_read_reg(struct sensor_data *sensor, u16 reg, u32 *val) +static s32 tc358743_read_reg_val(struct sensor_data *sensor, u16 reg) { - u8 au8RegBuf[2] = {0}; - u32 u32RdVal = 0; - int i=0; - int size = 0; + u32 val = 0; + tc358743_read_reg(sensor, reg, &val); + return val; +} - while (0 != tc358743_read_reg_size[i].startaddr || - 0 != tc358743_read_reg_size[i].endaddr || - 0 != tc358743_read_reg_size[i].size) { - if (tc358743_read_reg_size[i].startaddr <= reg && - tc358743_read_reg_size[i].endaddr >= reg) { - size = tc358743_read_reg_size[i].size; - break; - } - i++; - } - if (!size) - return -1; +static s32 tc358743_write_reg(struct sensor_data *sensor, u16 reg, u32 val, int len) +{ + int ret; + int i = 0; + u32 data = val; + u8 au8Buf[6] = {0}; + int size = get_reg_size(reg, len); - au8RegBuf[0] = reg >> 8; - au8RegBuf[1] = reg & 0xff; + if (!size) + return -EINVAL; - if (2 != i2c_master_send(sensor->i2c_client, au8RegBuf, 2)) { - pr_err("%s:read reg error:reg=%x\n", - __func__, reg); - return -1; + au8Buf[i++] = reg >> 8; + au8Buf[i++] = reg & 0xff; + while (size-- > 0) { + au8Buf[i++] = (u8)data; + data >>= 8; } - if (size /*of(u32RdVal)*/ != i2c_master_recv(sensor->i2c_client, (char *)&u32RdVal, size /*of(u32RdVal)*/)) { - pr_err("%s:read reg error:reg=%x,val=%x\n", - __func__, reg, u32RdVal); - return -1; + ret = i2c_master_send(sensor->i2c_client, au8Buf, i); + if (ret < 0) { + pr_err("%s:write reg error(%d):reg=%x,val=%x\n", + __func__, ret, reg, val); + return ret; + } + if ((reg < 0x7000) || (reg >= 0x7100)) { + pr_debug("%s:reg=%x,val=%x 8543=%02x\n", __func__, reg, val, tc358743_read_reg_val(sensor, 0x8543)); } - *val = u32RdVal; - return size; + return 0; +} + +static void tc358743_software_reset(struct sensor_data *sensor) +{ + int freq = sensor->mclk / 10000; + tc358743_write_reg(sensor, 0x7080, 0, 2); + tc358743_write_reg(sensor, 0x0002, 0x0f00, 2); + msleep(100); + tc358743_write_reg(sensor, 0x0002, 0x0000, 2); + msleep(1000); + tc358743_write_reg(sensor, 0x0004, 0x0004, 2); /* autoinc */ + pr_info("%s:freq=%d\n", __func__, freq); + tc358743_write_reg(sensor, 0x8540, freq, 1); + tc358743_write_reg(sensor, 0x8541, freq >> 8, 1); +} + +static void tc358743_enable_edid(struct sensor_data *sensor) +{ + pr_debug("Activate EDID\n"); + // EDID + tc358743_write_reg(sensor, 0x85c7, 0x01, 1); // EDID MODE REGISTER: nternal EDID-RAM & DDC2B mode + tc358743_write_reg(sensor, 0x85ca, 0x00, 1); + tc358743_write_reg(sensor, 0x85cb, 0x01, 1); // 0x85cb:0x85ca - EDID Length = 0x01:00 (Size = 0x100 = 256) + tc358743_write_reg(sensor, 0x8543, 0x36, 1); // DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms + tc358743_write_reg(sensor, 0x854a, 0x01, 1); // mark init done + pr_debug("%s: c7=%02x ca=%02x cb=%02x 43=%02x 4a=%02x\n", __func__, + tc358743_read_reg_val(sensor, 0x85c7), + tc358743_read_reg_val(sensor, 0x85ca), + tc358743_read_reg_val(sensor, 0x85cb), + tc358743_read_reg_val(sensor, 0x8543), + tc358743_read_reg_val(sensor, 0x854a)); } static int tc358743_write_edid(struct sensor_data *sensor, const u8 *edid, int len) @@ -1568,15 +1521,12 @@ static int tc358743_write_edid(struct sensor_data *sensor, const u8 *edid, int l len -= (u8)size; reg += (u16)size; } - pr_debug("Activate EDID\n"); - tc358743_write_reg(sensor, 0x85c7, 0x01, 1); - tc358743_write_reg(sensor, 0x85ca, 0x00, 1); - tc358743_write_reg(sensor, 0x85cb, 0x01, 1); + tc358743_enable_edid(sensor); return 0; } static s32 power_control(struct tc_data *td, int on) - { +{ struct sensor_data *sensor = &td->sensor; int i; int ret = 0; @@ -1642,7 +1592,6 @@ static int tc358743_init_mode(struct tc_data *td, register u32 Mask = 0; register u32 Val = 0; u8 Length; - u32 RegVal = 0; int retval = 0; void *mipi_csi2_info; u32 mipi_reg; @@ -1687,19 +1636,18 @@ static int tc358743_init_mode(struct tc_data *td, lanes = mipi_csi2_set_lanes(mipi_csi2_info, lanes); pr_debug("Now Using %d lanes\n", lanes); - /*Only reset MIPI CSI2 HW at sensor initialize*/ - if (!td->hdmi_mode) // is this during reset - mipi_csi2_reset(mipi_csi2_info); + mipi_csi2_reset(mipi_csi2_info); pr_debug("%s format: %x\n", __func__, sensor->pix.pixelformat); - for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) + for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) { if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) { sensor->pix.pixelformat = tc358743_formats[ifmt].pixelformat; pr_debug("%s: %s (%x, %x)\n", __func__, tc358743_formats[ifmt].description, sensor->pix.pixelformat, tc358743_formats[ifmt].flags); mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags); break; } + } if (ifmt >= ARRAY_SIZE(tc358743_formats)) { pr_err("currently this sensor format (0x%x) can not be supported!\n", sensor->pix.pixelformat); return -1; @@ -1730,6 +1678,7 @@ static int tc358743_init_mode(struct tc_data *td, mode, sensor->pix.width, sensor->pix.height); + tc358743_software_reset(sensor); for (i = 0; i < iModeSettingArySize; ++i) { pModeSetting = tc358743_mode_info_data[frame_rate][mode].init_data_ptr + i; @@ -1739,6 +1688,8 @@ static int tc358743_init_mode(struct tc_data *td, Mask = pModeSetting->u32Mask; Length = pModeSetting->u8Length; if (Mask) { + u32 RegVal = 0; + retval = tc358743_read_reg(sensor, RegAddr, &RegVal); if (retval < 0) break; @@ -1765,6 +1716,7 @@ static int tc358743_init_mode(struct tc_data *td, } } } + tc358743_enable_edid(sensor); if (retval < 0) { pr_err("%s: Fail to write REGS to tc35874!\n", __func__); goto err; @@ -2069,8 +2021,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) break; } - if (ret) - det_work_enable(td, 1); + det_work_enable(td, 1); return ret; } @@ -2305,17 +2256,11 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, } { - u32 u32val; - int ret = tc358743_read_reg(sensor, 0x8520,&u32val); - pr_debug("SYS_STATUS: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(sensor, 0x8521,&u32val); - pr_debug("VI_STATUS0: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(sensor, 0x8522,&u32val); - pr_debug("VI_STATUS1: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(sensor, 0x8525,&u32val); - pr_debug("VI_STATUS2: 0x%x, ret val: %d \n",u32val,ret); - ret = tc358743_read_reg(sensor, 0x8528,&u32val); - pr_debug("VI_STATUS3: 0x%x, ret val: %d \n",u32val,ret); + pr_debug("SYS_STATUS: 0x%x\n", tc358743_read_reg_val(sensor, 0x8520)); + pr_debug("VI_STATUS0: 0x%x\n", tc358743_read_reg_val(sensor, 0x8521)); + pr_debug("VI_STATUS1: 0x%x\n", tc358743_read_reg_val(sensor, 0x8522)); + pr_debug("VI_STATUS2: 0x%x\n", tc358743_read_reg_val(sensor, 0x8525)); + pr_debug("VI_STATUS3: 0x%x\n", tc358743_read_reg_val(sensor, 0x8528)); pr_debug("%s %d:%d format: %x\n", __func__, pix->width, pix->height, pix->pixelformat); } return 0; @@ -2876,79 +2821,94 @@ static void det_worker(struct work_struct *work) { struct tc_data *td = container_of(work, struct tc_data, det_work.work); struct sensor_data *sensor = &td->sensor; - u32 u32val; - u16 reg; int ret; + u32 u32val; + if (!td->det_work_enable) + return; mutex_lock(&td->access_lock); - if (!td->det_work_disable) { - reg = 0x8621; - ret = tc358743_read_reg(sensor, reg, &u32val); - if (ret > 0) { - if (td->audio != (((unsigned char)u32val) & 0x0f)) { + + if (!td->det_work_enable) { + mutex_unlock(&td->access_lock); + return; + } + u32val = 0; + ret = tc358743_read_reg(sensor, 0x8621, &u32val); + if (ret >= 0) { + if (td->audio != (((unsigned char)u32val) & 0x0f)) { + td->audio = ((unsigned char)u32val) & 0x0f; + report_netlink(td); + } + } + u32val = 0; + ret = tc358743_read_reg(sensor, 0x852f, &u32val); + if (ret < 0) { + pr_err("%s: Error reading lock\n", __func__); + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + goto out; + } + pr_info("%s: 852f=%x\n", __func__, u32val); + if (u32val & TC3587430_HDMI_DETECT) { + pr_info("%s: hdmi detect %x\n", __func__, u32val); + td->lock = u32val & TC3587430_HDMI_DETECT; + u32val = 0; + ret = tc358743_read_reg(sensor, 0x8521, &u32val); + if (ret < 0) { + pr_err("%s: Error reading mode\n", __func__); + } + pr_info("%s: detect 8521=%x\n", __func__, u32val); + } else { + if (td->lock) { // check if it is realy un-plug + td->lock = 0; + u32val = 0x0; + td->hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. + } + u32val = 0; + ret = tc358743_read_reg(sensor, 0x8521, &u32val); + if (ret < 0) { + pr_err("%s: Error reading mode\n", __func__); + } + pr_info("%s: 8521=%x\n", __func__, u32val); + } + if ((unsigned char)td->hdmi_mode != (unsigned char)u32val) { + td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + td->bounce = MAX_BOUNCE; + pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", + __func__, (unsigned char)td->hdmi_mode, + (unsigned char)u32val, + tc358743_mode_list[td->hdmi_mode & 0xf].name, + td->fps, td->bounce, td->det_work_timeout); + td->hdmi_mode = u32val; + sensor->streamcap.capturemode = tc358743_mode_list[td->hdmi_mode & 0xf].mode; + td->det_changed = 1; + } else if (td->bounce) { + td->bounce--; + td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; + + if (!td->bounce) { + if (td->hdmi_mode >= 0xe) { + u32val = 0; + ret = tc358743_read_reg(sensor, 0x852f, &u32val); + if (ret >= 0) + td->fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; + } + u32val = 0; + ret = tc358743_read_reg(sensor, 0x8621, &u32val); + if (ret >= 0) { td->audio = ((unsigned char)u32val) & 0x0f; report_netlink(td); } - } - reg = 0x852f; - ret = tc358743_read_reg(sensor, reg, &u32val); - if (ret > 0) { - while (1) { - if (u32val & TC3587430_HDMI_DETECT) { - td->lock = u32val & TC3587430_HDMI_DETECT; - reg = 0x8521; - ret = tc358743_read_reg(sensor, reg, &u32val); - if (ret < 0) { - pr_err("%s: Error reading mode\n", __func__); - } - } else { - if (td->lock) { // check if it is realy un-plug - td->lock = 0; - u32val = 0x0; - td->hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. - } - } - if ((unsigned char)td->hdmi_mode != (unsigned char)u32val) { - if (u32val) - td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; - else - td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; - td->bounce = MAX_BOUNCE; - pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", - __func__, (unsigned char)td->hdmi_mode, - (unsigned char)u32val, - tc358743_mode_list[td->hdmi_mode & 0xf].name, - td->fps, td->bounce, td->det_work_timeout); - td->hdmi_mode = u32val; - sensor->streamcap.capturemode = tc358743_mode_list[td->hdmi_mode & 0xf].mode; - td->det_changed = 1; - } else if (td->bounce) { - td->bounce--; - td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; - } - - if (1 == td->bounce) { - if (td->hdmi_mode >= 0xe) { - reg = 0x852f; - ret = tc358743_read_reg(sensor, reg, &u32val); - if (ret > 0) - td->fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; - } - reg = 0x8621; - ret = tc358743_read_reg(sensor, reg, &u32val); - if (ret > 0) { - td->audio = ((unsigned char)u32val) & 0x0f; - report_netlink(td); - } - } - break; + if (td->hdmi_mode) { + td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + mutex_unlock(&td->access_lock); + return; } - } else { - pr_err("%s: Error reading lock\n", __func__); } - } else { - td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; + } else if (td->hdmi_mode && !td->bounce) { + mutex_unlock(&td->access_lock); + return; } +out: mutex_unlock(&td->access_lock); schedule_delayed_work(&td->det_work, msecs_to_jiffies(td->det_work_timeout)); } @@ -2959,7 +2919,7 @@ static irqreturn_t tc358743_detect_handler(int irq, void *data) struct sensor_data *sensor = &td->sensor; pr_debug("%s: IRQ %d\n", __func__, sensor->i2c_client->irq); - schedule_delayed_work(&td->det_work, msecs_to_jiffies(td->det_work_timeout)); + schedule_delayed_work(&td->det_work, msecs_to_jiffies(10)); return IRQ_HANDLED; } @@ -2980,24 +2940,29 @@ static ssize_t tc358743_show_regdump(struct device *dev, struct sensor_data *sensor = &td->sensor; int i, len = 0; int retval; - u32 u32val; if (!td) return len; mutex_lock(&td->access_lock); for (i=0; i 0) { - if (0 == (i & 0xf)) - len += sprintf(buf+len, "\n%04X:", regoffs+i); + if (!(i & 0xf)) + len += sprintf(buf+len, "\n%04X:", reg); + if (size == 1) len += sprintf(buf+len, " %02X", u32val&0xff); - u32val >>= 8; - i++; - } + else if (size == 2) + len += sprintf(buf+len, " %04X", u32val&0xffff); + else + len += sprintf(buf+len, " %08X", u32val); + i += size; } mutex_unlock(&td->access_lock); len += sprintf(buf+len, "\n"); @@ -3095,12 +3060,12 @@ static DEVICE_ATTR(audio, S_IRUGO, tc358743_show_audio, NULL); static int tc358743_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct pwm_device *pwm; struct device *dev = &client->dev; int retval; struct regmap *gpr; struct tc_data *td; struct sensor_data *sensor; + u8 chip_id_high; u32 u32val; td = kzalloc(sizeof(*td), GFP_KERNEL); @@ -3202,23 +3167,18 @@ static int tc358743_probe(struct i2c_client *client, sensor->pix.width, sensor->pix.height); - pwm = pwm_get(dev, NULL); - if (!IS_ERR(pwm)) { - dev_info(dev, "found pwm%d, period=%d\n", pwm->pwm, pwm->period); - pwm_config(pwm, pwm->period >> 1, pwm->period); - pwm_enable(pwm); - } - tc_regulator_init(td, dev); power_control(td, 1); tc_reset(td); + u32val = 0; retval = tc358743_read_reg(sensor, TC358743_CHIP_ID_HIGH_BYTE, &u32val); if (retval < 0) { pr_err("%s:cannot find camera\n", __func__); retval = -ENODEV; goto err4; } + chip_id_high = (u8)u32val; gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); if (!IS_ERR(gpr)) { @@ -3309,7 +3269,7 @@ static int tc358743_probe(struct i2c_client *client, sensor->i2c_client->irq); } - schedule_delayed_work(&(td->det_work), msecs_to_jiffies(td->det_work_timeout)); + schedule_delayed_work(&td->det_work, msecs_to_jiffies(td->det_work_timeout)); #endif retval = tc358743_reset(td); if (retval) From ad233a40d000dac47081376ad2bc12d6d9d5fd31 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 17 Jun 2014 14:02:26 -0700 Subject: [PATCH 0713/1983] tc358743_h2c: mipi vs parallel now in mxc_v4l2_capture --- .../media/platform/mxc/capture/tc358743_h2c.c | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index b5594173d3e79b..daa1fd3c4b975f 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #include #include @@ -3062,7 +3060,6 @@ static int tc358743_probe(struct i2c_client *client, { struct device *dev = &client->dev; int retval; - struct regmap *gpr; struct tc_data *td; struct sensor_data *sensor; u8 chip_id_high; @@ -3180,29 +3177,6 @@ static int tc358743_probe(struct i2c_client *client, } chip_id_high = (u8)u32val; - gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); - if (!IS_ERR(gpr)) { - if (of_machine_is_compatible("fsl,imx6q")) { - if (sensor->csi == sensor->ipu_id) { - int mask = sensor->csi ? (1 << 20) : (1 << 19); - - regmap_update_bits(gpr, IOMUXC_GPR1, mask, 0); - } - } else if (of_machine_is_compatible("fsl,imx6dl")) { - int mask = sensor->csi ? (7 << 3) : (7 << 0); - int val = sensor->csi ? (3 << 3) : (0 << 0); - - if (sensor->ipu_id) { - dev_err(dev, "invalid ipu\n"); - return -EINVAL; - } - regmap_update_bits(gpr, IOMUXC_GPR13, mask, val); - } - } else { - pr_err("%s: failed to find fsl,imx6q-iomux-gpr regmap\n", - __func__); - } - tc358743_int_device.priv = td; if (!g_td) g_td = td; From 2e5fde0652158522c913af400f31dc03bb298bd2 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 26 Jun 2014 10:18:52 -0700 Subject: [PATCH 0714/1983] tc358743_h2c: sensor_clk not required --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index daa1fd3c4b975f..bece7dd5fa7721 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -3103,12 +3103,6 @@ static int tc358743_probe(struct i2c_client *client, sensor->mipi_camera = 1; sensor->virtual_channel = 0; sensor->sensor_clk = devm_clk_get(dev, "csi_mclk"); - if (IS_ERR(sensor->sensor_clk)) { - /* assuming clock enabled by default */ - sensor->sensor_clk = NULL; - dev_err(dev, "clock-frequency missing or invalid\n"); - return PTR_ERR(sensor->sensor_clk); - } retval = of_property_read_u32(dev->of_node, "mclk", &(sensor->mclk)); @@ -3142,7 +3136,8 @@ static int tc358743_probe(struct i2c_client *client, return -EINVAL; } - clk_prepare_enable(sensor->sensor_clk); + if (!IS_ERR(sensor->sensor_clk)) + clk_prepare_enable(sensor->sensor_clk); sensor->io_init = tc_io_init; sensor->i2c_client = client; From d5f0fb4ccca3e0dead8811daeea6c04de06ad1f6 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 19 Jun 2014 14:33:28 -0700 Subject: [PATCH 0715/1983] tc358743_h2c: make gstreamer work without specifing capture-mode --- .../media/platform/mxc/capture/tc358743_h2c.c | 603 +++++++++--------- 1 file changed, 314 insertions(+), 289 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index bece7dd5fa7721..3ce445aeb555ff 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -218,12 +218,10 @@ static int tc_regulator_init(struct tc_data *td, struct device *dev) static void det_work_enable(struct tc_data *td, int enable) { - mutex_lock(&td->access_lock); td->det_work_enable = enable; td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; if (enable) schedule_delayed_work(&td->det_work, msecs_to_jiffies(10)); - mutex_unlock(&td->access_lock); pr_debug("%s: %d %d\n", __func__, td->det_work_enable, td->det_work_timeout); } @@ -1454,7 +1452,7 @@ static s32 tc358743_write_reg(struct sensor_data *sensor, u16 reg, u32 val, int return ret; } if ((reg < 0x7000) || (reg >= 0x7100)) { - pr_debug("%s:reg=%x,val=%x 8543=%02x\n", __func__, reg, val, tc358743_read_reg_val(sensor, 0x8543)); + if (0) pr_debug("%s:reg=%x,val=%x 8543=%02x\n", __func__, reg, val, tc358743_read_reg_val(sensor, 0x8543)); } return 0; } @@ -1468,7 +1466,7 @@ static void tc358743_software_reset(struct sensor_data *sensor) tc358743_write_reg(sensor, 0x0002, 0x0000, 2); msleep(1000); tc358743_write_reg(sensor, 0x0004, 0x0004, 2); /* autoinc */ - pr_info("%s:freq=%d\n", __func__, freq); + pr_debug("%s:freq=%d\n", __func__, freq); tc358743_write_reg(sensor, 0x8540, freq, 1); tc358743_write_reg(sensor, 0x8541, freq >> 8, 1); } @@ -1482,7 +1480,7 @@ static void tc358743_enable_edid(struct sensor_data *sensor) tc358743_write_reg(sensor, 0x85cb, 0x01, 1); // 0x85cb:0x85ca - EDID Length = 0x01:00 (Size = 0x100 = 256) tc358743_write_reg(sensor, 0x8543, 0x36, 1); // DDC CONTROL: DDC_ACK output terminal H active, DDC5V_active detect delay 200ms tc358743_write_reg(sensor, 0x854a, 0x01, 1); // mark init done - pr_debug("%s: c7=%02x ca=%02x cb=%02x 43=%02x 4a=%02x\n", __func__, + if (0) pr_debug("%s: c7=%02x ca=%02x cb=%02x 43=%02x 4a=%02x\n", __func__, tc358743_read_reg_val(sensor, 0x85c7), tc358743_read_reg_val(sensor, 0x85ca), tc358743_read_reg_val(sensor, 0x85cb), @@ -1531,7 +1529,6 @@ static s32 power_control(struct tc_data *td, int on) pr_debug("%s: %d\n", __func__, on); if (sensor->on != on) { - mutex_lock(&td->access_lock); if (on) { for (i = 0; i < REGULATOR_CNT; i++) { if (td->regulator[i]) { @@ -1553,7 +1550,6 @@ static s32 power_control(struct tc_data *td, int on) regulator_disable(td->regulator[i]); } } - mutex_unlock(&td->access_lock); } return ret; } @@ -1573,11 +1569,35 @@ static int tc358743_toggle_hpd(struct sensor_data *sensor, int active) return ret; } -void mipi_csi2_swreset(struct mipi_csi2_info *info); -#include "../../../../mxc/mipi/mxc_mipi_csi2.h" -static int tc358743_init_mode(struct tc_data *td, - enum tc358743_frame_rate frame_rate, - enum tc358743_mode mode) +static int get_format_index(enum tc358743_frame_rate frame_rate, enum tc358743_mode mode) +{ + int ifmt; + u32 flags = tc358743_mode_info_data[frame_rate][mode].flags; + + for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) { + if (flags == tc358743_formats[ifmt].flags) + return ifmt; + } + return -1; +} + +static int get_pixelformat(enum tc358743_frame_rate frame_rate, enum tc358743_mode mode) +{ + int ifmt = get_format_index(frame_rate, mode); + + if (ifmt < 0) { + pr_debug("%s: unsupported format, %d, %d\n", __func__, frame_rate, mode); + return 0; + } + pr_debug("%s: %s (%x, %x)\n", __func__, + tc358743_formats[ifmt].description, + tc358743_formats[ifmt].pixelformat, + tc358743_formats[ifmt].flags); + return tc358743_formats[ifmt].pixelformat; +} + +int set_frame_rate_mode(struct tc_data *td, + enum tc358743_frame_rate frame_rate, enum tc358743_mode mode) { struct sensor_data *sensor = &td->sensor; const struct reg_value *pModeSetting = NULL; @@ -1591,190 +1611,207 @@ static int tc358743_init_mode(struct tc_data *td, register u32 Val = 0; u8 Length; int retval = 0; - void *mipi_csi2_info; - u32 mipi_reg; - u32 mipi_reg_test[10]; - pr_debug("%s rate: %d mode: %d\n", __func__, frame_rate, mode); - if ((mode > tc358743_mode_MAX || mode < 0) - && (mode != tc358743_mode_INIT)) { - pr_debug("%s Wrong tc358743 mode detected! %d. Set mode 0\n", __func__, mode); - mode = 0; - } + pModeSetting = + tc358743_mode_info_data[frame_rate][mode].init_data_ptr; + iModeSettingArySize = + tc358743_mode_info_data[frame_rate][mode].init_data_size; + + sensor->pix.pixelformat = get_pixelformat(frame_rate, mode); + sensor->pix.width = + tc358743_mode_info_data[frame_rate][mode].width; + sensor->pix.height = + tc358743_mode_info_data[frame_rate][mode].height; + pr_debug("%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __func__, + iModeSettingArySize, + pModeSetting, + frame_rate, + mode, + sensor->pix.width, + sensor->pix.height); + for (i = 0; i < iModeSettingArySize; ++i) { + pModeSetting = tc358743_mode_info_data[frame_rate][mode].init_data_ptr + i; + + Delay_ms = pModeSetting->u32Delay_ms & (0xffff); + RegAddr = pModeSetting->u16RegAddr; + Val = pModeSetting->u32Val; + Mask = pModeSetting->u32Mask; + Length = pModeSetting->u8Length; + if (Mask) { + u32 RegVal = 0; + + retval = tc358743_read_reg(sensor, RegAddr, &RegVal); + if (retval < 0) { + pr_err("%s: read failed, reg=0x%x\n", __func__, RegAddr); + return retval; + } + RegVal &= ~(u8)Mask; + Val &= Mask; + Val |= RegVal; + } - mipi_csi2_info = mipi_csi2_get_info(); - pr_debug("%s rate: %d mode: %d, info %p\n", __func__, frame_rate, mode, mipi_csi2_info); + retval = tc358743_write_reg(sensor, RegAddr, Val, Length); + if (retval < 0) { + pr_err("%s: write failed, reg=0x%x\n", __func__, RegAddr); + return retval; + } + if (Delay_ms) + msleep(Delay_ms); - /* initial mipi dphy */ - tc358743_toggle_hpd(sensor, !td->hpd_active); - if (mipi_csi2_info) { - pr_debug("%s: mipi_csi2_info:\n" - "mipi_en: %d\n" - "datatype: %d\n" - "dphy_clk: %p\n" - "pixel_clk: %p\n" - "mipi_csi2_base:%p\n" - "pdev: %p\n" - , __func__, - ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en, - ((struct mipi_csi2_info *)mipi_csi2_info)->datatype, - ((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk, - ((struct mipi_csi2_info *)mipi_csi2_info)->pixel_clk, - ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_csi2_base, - ((struct mipi_csi2_info *)mipi_csi2_info)->pdev - ); - if (!mipi_csi2_get_status(mipi_csi2_info)) - mipi_csi2_enable(mipi_csi2_info); - - if (mipi_csi2_get_status(mipi_csi2_info)) { - int ifmt; - int lanes = tc358743_mode_info_data[frame_rate][mode].lanes; - if (!lanes) - lanes = 4; - lanes = mipi_csi2_set_lanes(mipi_csi2_info, lanes); - pr_debug("Now Using %d lanes\n", lanes); - - mipi_csi2_reset(mipi_csi2_info); - - - pr_debug("%s format: %x\n", __func__, sensor->pix.pixelformat); - for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) { - if (tc358743_mode_info_data[frame_rate][mode].flags == tc358743_formats[ifmt].flags) { - sensor->pix.pixelformat = tc358743_formats[ifmt].pixelformat; - pr_debug("%s: %s (%x, %x)\n", __func__, tc358743_formats[ifmt].description, sensor->pix.pixelformat, tc358743_formats[ifmt].flags); - mipi_csi2_set_datatype(mipi_csi2_info, tc358743_formats[ifmt].flags); - break; - } + if (0 != ((pModeSetting->u32Delay_ms>>16) & (0xff))) { + if (!RepeateTimes) { + RepeateTimes = (pModeSetting->u32Delay_ms>>16) & (0xff); + RepeateLines = (pModeSetting->u32Delay_ms>>24) & (0xff); } - if (ifmt >= ARRAY_SIZE(tc358743_formats)) { - pr_err("currently this sensor format (0x%x) can not be supported!\n", sensor->pix.pixelformat); - return -1; + if (--RepeateTimes > 0) { + i -= RepeateLines; } - } else { - pr_err("Can not enable mipi csi2 driver!\n"); - return -1; } - } else { - pr_err("Fail to get mipi_csi2_info!\n"); - return -1; } + tc358743_enable_edid(sensor); + if (!td->hdmi_mode) // is this during reset + if ((retval = tc358743_write_edid(sensor, cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) + pr_err("%s: Fail to write EDID to tc35874!\n", __func__); - { - pModeSetting = - tc358743_mode_info_data[frame_rate][mode].init_data_ptr; - iModeSettingArySize = - tc358743_mode_info_data[frame_rate][mode].init_data_size; - - sensor->pix.width = - tc358743_mode_info_data[frame_rate][mode].width; - sensor->pix.height = - tc358743_mode_info_data[frame_rate][mode].height; - pr_debug("%s: Set %d regs from %p for frs %d mode %d with width %d height %d\n", __func__, - iModeSettingArySize, - pModeSetting, - frame_rate, - mode, - sensor->pix.width, - sensor->pix.height); - tc358743_software_reset(sensor); - for (i = 0; i < iModeSettingArySize; ++i) { - pModeSetting = tc358743_mode_info_data[frame_rate][mode].init_data_ptr + i; - - Delay_ms = pModeSetting->u32Delay_ms & (0xffff); - RegAddr = pModeSetting->u16RegAddr; - Val = pModeSetting->u32Val; - Mask = pModeSetting->u32Mask; - Length = pModeSetting->u8Length; - if (Mask) { - u32 RegVal = 0; - - retval = tc358743_read_reg(sensor, RegAddr, &RegVal); - if (retval < 0) - break; - - RegVal &= ~(u8)Mask; - Val &= Mask; - Val |= RegVal; - } + return retval; +} - retval = tc358743_write_reg(sensor, RegAddr, Val, Length); - if (retval < 0) - break; +void mipi_csi2_swreset(struct mipi_csi2_info *info); +#include "../../../../mxc/mipi/mxc_mipi_csi2.h" - if (Delay_ms) - msleep(Delay_ms); +int mipi_reset(void *mipi_csi2_info, + enum tc358743_frame_rate frame_rate, + enum tc358743_mode mode) +{ + int lanes = tc358743_mode_info_data[frame_rate][mode].lanes; + + if (!lanes) + lanes = 4; + + pr_debug("%s: mipi_csi2_info:\n" + "mipi_en: %d\n" + "datatype: %d\n" + "dphy_clk: %p\n" + "pixel_clk: %p\n" + "mipi_csi2_base:%p\n" + "pdev: %p\n" + , __func__, + ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_en, + ((struct mipi_csi2_info *)mipi_csi2_info)->datatype, + ((struct mipi_csi2_info *)mipi_csi2_info)->dphy_clk, + ((struct mipi_csi2_info *)mipi_csi2_info)->pixel_clk, + ((struct mipi_csi2_info *)mipi_csi2_info)->mipi_csi2_base, + ((struct mipi_csi2_info *)mipi_csi2_info)->pdev + ); + if (mipi_csi2_get_status(mipi_csi2_info)) { + mipi_csi2_disable(mipi_csi2_info); + msleep(1); + } + mipi_csi2_enable(mipi_csi2_info); - if (0 != ((pModeSetting->u32Delay_ms>>16) & (0xff))) { - if (!RepeateTimes) { - RepeateTimes = (pModeSetting->u32Delay_ms>>16) & (0xff); - RepeateLines = (pModeSetting->u32Delay_ms>>24) & (0xff); - } - if (--RepeateTimes > 0) { - i -= RepeateLines; - } - } - } - tc358743_enable_edid(sensor); - if (retval < 0) { - pr_err("%s: Fail to write REGS to tc35874!\n", __func__); - goto err; - } + if (!mipi_csi2_get_status(mipi_csi2_info)) { + pr_err("Can not enable mipi csi2 driver!\n"); + return -1; } - if (!td->hdmi_mode) // is this during reset - if ((retval = tc358743_write_edid(sensor, cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) - pr_err("%s: Fail to write EDID to tc35874!\n", __func__); + lanes = mipi_csi2_set_lanes(mipi_csi2_info, lanes); + pr_debug("Now Using %d lanes\n", lanes); + + mipi_csi2_reset(mipi_csi2_info); + mipi_csi2_set_datatype(mipi_csi2_info, tc358743_mode_info_data[frame_rate][mode].flags); + return 0; +} - tc358743_toggle_hpd(sensor, td->hpd_active); - if (mipi_csi2_info) { - unsigned int i = 0; +int mipi_wait(void *mipi_csi2_info) +{ + unsigned i = 0; + unsigned j; + u32 mipi_reg; + u32 mipi_reg_test[10]; - /* wait for mipi sensor ready */ + /* wait for mipi sensor ready */ + for (;;) { mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); - while ((mipi_reg == 0x200) && (i < 10)) { - mipi_reg_test[i] = mipi_reg; - mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info); - i++; - msleep(10); - } - + mipi_reg_test[i++] = mipi_reg; + if (mipi_reg != 0x200) + break; if (i >= 10) { pr_err("mipi csi2 can not receive sensor clk!\n"); return -1; } + msleep(10); + } - { - int j; - for (j = 0; j < i; j++) - { - pr_debug("%d mipi csi2 dphy status %x\n", j, mipi_reg_test[j]); - } - } + for (j = 0; j < i; j++) { + pr_debug("%d mipi csi2 dphy status %x\n", j, mipi_reg_test[j]); + } - i = 0; + i = 0; - /* wait for mipi stable */ + /* wait for mipi stable */ + for (;;) { mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); - while ((mipi_reg != 0x0) && (i < 10)) { - mipi_reg_test[i] = mipi_reg; - mipi_reg = mipi_csi2_get_error1(mipi_csi2_info); - i++; - msleep(10); - } - + mipi_reg_test[i++] = mipi_reg; + if (!mipi_reg) + break; if (i >= 10) { pr_err("mipi csi2 can not reveive data correctly!\n"); return -1; } + msleep(10); + } - { - int j; - for (j = 0; j < i; j++) { - pr_debug("%d mipi csi2 err1 %x\n", j, mipi_reg_test[j]); - } - } + for (j = 0; j < i; j++) { + pr_debug("%d mipi csi2 err1 %x\n", j, mipi_reg_test[j]); } -err: - return (retval>0)?0:retval; + return 0; +} + +static int tc358743_init_mode(struct tc_data *td, + enum tc358743_frame_rate frame_rate, + enum tc358743_mode mode) +{ + struct sensor_data *sensor = &td->sensor; + int retval = 0; + void *mipi_csi2_info; + + pr_debug("%s rate: %d mode: %d\n", __func__, frame_rate, mode); + if ((mode >= tc358743_mode_MAX || mode < 0) + && (mode != tc358743_mode_INIT)) { + pr_debug("%s Wrong tc358743 mode detected! %d. Set mode 0\n", __func__, mode); + mode = 0; + } + /* initial mipi dphy */ + tc358743_toggle_hpd(sensor, 0); + tc358743_software_reset(sensor); + + mipi_csi2_info = mipi_csi2_get_info(); + pr_debug("%s rate: %d mode: %d, info %p\n", __func__, frame_rate, mode, mipi_csi2_info); + + if (!mipi_csi2_info) { + pr_err("Fail to get mipi_csi2_info!\n"); + return -1; + } + retval = mipi_reset(mipi_csi2_info, frame_rate, tc358743_mode_INIT); + if (retval) + return retval; + retval = set_frame_rate_mode(td, frame_rate, tc358743_mode_INIT); + if (retval) + return retval; + retval = mipi_wait(mipi_csi2_info); + + if (mode != tc358743_mode_INIT) { + tc358743_software_reset(sensor); + retval = mipi_reset(mipi_csi2_info, frame_rate, mode); + if (retval) + return retval; + retval = set_frame_rate_mode(td, frame_rate, mode); + if (retval) + return retval; + retval = mipi_wait(mipi_csi2_info); + } + if (td->hpd_active) + tc358743_toggle_hpd(sensor, td->hpd_active); + return retval; } static int tc358743_minit(struct tc_data *td) @@ -1790,9 +1827,8 @@ static int tc358743_minit(struct tc_data *td) else if (tgt_fps == 30) frame_rate = tc358743_30_fps; - pr_debug("%s: capture mode: %d extended mode: %d fps: %d\n", __func__, - sensor->streamcap.capturemode, - sensor->streamcap.extendedmode, tgt_fps); + pr_debug("%s: capture mode: %d fps: %d\n", __func__, + sensor->streamcap.capturemode, tgt_fps); ret = tc358743_init_mode(td, frame_rate, sensor->streamcap.capturemode); if (ret) @@ -1841,7 +1877,6 @@ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; p->u.bt656.clock_min = TC358743_XCLK_MIN; p->u.bt656.clock_max = TC358743_XCLK_MAX; - p->u.bt656.bt_sync_correct = 1; /* Indicate external vsync */ return 0; } @@ -1857,10 +1892,16 @@ static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) static int ioctl_s_power(struct v4l2_int_device *s, int on) { struct tc_data *td = s->priv; - if (on) - if (!td->hdmi_mode) - return tc358743_reset(td); - return power_control(td, on); + int ret; + + mutex_lock(&td->access_lock); + if (on && !td->hdmi_mode) { + ret = tc358743_reset(td); + } else { + ret = power_control(td, on); + } + mutex_unlock(&td->access_lock); + return ret; } /*! @@ -1905,8 +1946,6 @@ static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) ret = -EINVAL; break; } - - det_work_enable(td, 1); pr_debug("%s done %d\n", __func__, ret); return ret; } @@ -1930,6 +1969,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) int ret = 0; pr_debug("%s\n", __func__); + mutex_lock(&td->access_lock); det_work_enable(td, 0); /* Make sure power on */ power_control(td, 1); @@ -1969,9 +2009,10 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) break; } - if ((u32)a->parm.capture.capturemode > tc358743_mode_MAX) { + if ((u32)a->parm.capture.capturemode >= tc358743_mode_MAX) { a->parm.capture.capturemode = 0; - pr_debug("%s: Forse extended mode: %d \n", __func__,(u32)a->parm.capture.capturemode); + pr_debug("%s: Force mode: %d \n", __func__, + (u32)a->parm.capture.capturemode); } tgt_fps = sensor->streamcap.timeperframe.denominator / @@ -1985,16 +2026,23 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) if (frame_rate_now != frame_rate || sensor->streamcap.capturemode != (u32)a->parm.capture.capturemode || sensor->streamcap.extendedmode != (u32)a->parm.capture.extendedmode) { - sensor->streamcap.timeperframe = *timeperframe; - sensor->streamcap.capturemode = - (u32)a->parm.capture.capturemode; - sensor->streamcap.extendedmode = - (u32)a->parm.capture.extendedmode; - - pr_debug("%s: capture mode: %d extended mode: %d \n", __func__,sensor->streamcap.capturemode, sensor->streamcap.extendedmode); - - ret = tc358743_init_mode(td, frame_rate, - sensor->streamcap.capturemode); + enum tc358743_mode mode; + + mode = (u32)a->parm.capture.capturemode; + + if (mode != tc358743_mode_INIT) { + sensor->streamcap.capturemode = mode; + sensor->streamcap.timeperframe = *timeperframe; + sensor->streamcap.extendedmode = + (u32)a->parm.capture.extendedmode; + pr_debug("%s: capture mode: %d\n", __func__, + mode); + ret = tc358743_init_mode(td, frame_rate, mode); + } else { + a->parm.capture.capturemode = sensor->streamcap.capturemode; + *timeperframe = sensor->streamcap.timeperframe; + a->parm.capture.extendedmode = sensor->streamcap.extendedmode; + } } else { pr_debug("%s: Keep current settings\n", __func__); } @@ -2020,6 +2068,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } det_work_enable(td, 1); + mutex_unlock(&td->access_lock); return ret; } @@ -2121,19 +2170,6 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *vc) return retval; } -static int get_pixelformat(int index) -{ - int ifmt; - - for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) - if (tc358743_mode_info_data[0][index].flags == tc358743_formats[ifmt].flags) - break; - - if (ifmt == ARRAY_SIZE(tc358743_formats)) - ifmt = 0; /* Default = RBG888 */ - return ifmt; -} - /*! * ioctl_enum_framesizes - V4L2 sensor interface handler for * VIDIOC_ENUM_FRAMESIZES ioctl @@ -2145,15 +2181,21 @@ static int get_pixelformat(int index) static int ioctl_enum_framesizes(struct v4l2_int_device *s, struct v4l2_frmsizeenum *fsize) { - pr_debug("%s, INDEX: %d\n", __func__,fsize->index); - if (fsize->index > tc358743_mode_MAX) + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; + int index = fsize->index; + + if (!index) + index = sensor->streamcap.capturemode; + pr_debug("%s, INDEX: %d\n", __func__, index); + if (index >= tc358743_mode_MAX) return -EINVAL; - fsize->pixel_format = tc358743_formats[get_pixelformat(fsize->index)].pixelformat; + fsize->pixel_format = get_pixelformat(0, index); fsize->discrete.width = - tc358743_mode_info_data[0][fsize->index].width; + tc358743_mode_info_data[0][index].width; fsize->discrete.height = - tc358743_mode_info_data[0][fsize->index].height; + tc358743_mode_info_data[0][index].height; pr_debug("%s %d:%d format: %x\n", __func__, fsize->discrete.width, fsize->discrete.height, fsize->pixel_format); return 0; } @@ -2196,11 +2238,17 @@ static int ioctl_init(struct v4l2_int_device *s) static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) { - pr_debug("%s\n", __func__); - if (fmt->index > tc358743_mode_MAX) + struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; + int index = fmt->index; + + if (!index) + index = sensor->streamcap.capturemode; + pr_debug("%s, INDEX: %d\n", __func__, index); + if (index >= tc358743_mode_MAX) return -EINVAL; - fmt->pixelformat = tc358743_formats[get_pixelformat(fmt->index)].pixelformat; + fmt->pixelformat = get_pixelformat(0, index); pr_debug("%s: format: %x\n", __func__, fmt->pixelformat); return 0; @@ -2214,10 +2262,13 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, u32 tgt_fps; /* target frames per secound */ enum tc358743_frame_rate frame_rate; // enum image_size isize; - int ifmt; + int ret = 0; struct v4l2_pix_format *pix = &f->fmt.pix; + int mode; + pr_debug("%s\n", __func__); + mutex_lock(&td->access_lock); tgt_fps = sensor->streamcap.timeperframe.denominator / sensor->streamcap.timeperframe.numerator; @@ -2227,20 +2278,16 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, frame_rate = tc358743_30_fps; } else { pr_debug("%s: %d fps (%d,%d) is not supported\n", __func__, tgt_fps, sensor->streamcap.timeperframe.denominator,sensor->streamcap.timeperframe.numerator); - return -EINVAL; + ret = -EINVAL; + goto out; } + mode = sensor->streamcap.capturemode; + sensor->pix.pixelformat = get_pixelformat(frame_rate, mode); + sensor->pix.width = pix->width = tc358743_mode_info_data[frame_rate][mode].width; + sensor->pix.height = pix->height = tc358743_mode_info_data[frame_rate][mode].height; + pr_debug("%s: %dx%d\n", __func__, sensor->pix.width, sensor->pix.height); - sensor->pix.width = pix->width = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].width; - sensor->pix.height = pix->height = tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].height; - - for (ifmt = 0; ifmt < ARRAY_SIZE(tc358743_formats); ifmt++) - if (tc358743_mode_info_data[frame_rate][sensor->streamcap.capturemode].flags == tc358743_formats[ifmt].flags) - break; - - if (ifmt == ARRAY_SIZE(tc358743_formats)) - ifmt = 0; /* Default = RBG888 */ - - sensor->pix.pixelformat = pix->pixelformat = tc358743_formats[ifmt].pixelformat; + pix->pixelformat = sensor->pix.pixelformat; pix->field = V4L2_FIELD_NONE; pix->bytesperline = pix->width * 4; pix->sizeimage = pix->bytesperline * pix->height; @@ -2261,7 +2308,9 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, pr_debug("VI_STATUS3: 0x%x\n", tc358743_read_reg_val(sensor, 0x8528)); pr_debug("%s %d:%d format: %x\n", __func__, pix->width, pix->height, pix->pixelformat); } - return 0; +out: + mutex_unlock(&td->access_lock); + return ret; } /*! @@ -2275,13 +2324,15 @@ static int ioctl_try_fmt_cap(struct v4l2_int_device *s, static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { struct tc_data *td = s->priv; + struct sensor_data *sensor = &td->sensor; + int mode = sensor->streamcap.capturemode; - pr_debug("%s\n", __func__); - if (td->det_changed) { - td->det_changed = 0; - tc358743_minit(td); - } - return ioctl_try_fmt_cap(s, f); + sensor->pix.pixelformat = get_pixelformat(0, mode); + sensor->pix.width = tc358743_mode_info_data[0][mode].width; + sensor->pix.height = tc358743_mode_info_data[0][mode].height; + f->fmt.pix = sensor->pix; + pr_debug("%s: %dx%d\n", __func__, sensor->pix.width, sensor->pix.height); + return 0; } /*! @@ -2293,48 +2344,16 @@ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) static int ioctl_dev_init(struct v4l2_int_device *s) { struct tc_data *td = s->priv; - struct sensor_data *sensor = &td->sensor; - u32 tgt_xclk; /* target xclk */ - u32 tgt_fps; /* target frames per secound */ - int ret = 0; - enum tc358743_frame_rate frame_rate; - void *mipi_csi2_info; - - pr_debug("%s\n", __func__); - - /* mclk */ - tgt_xclk = sensor->mclk; - tgt_xclk = min(tgt_xclk, (u32)TC358743_XCLK_MAX); - tgt_xclk = max(tgt_xclk, (u32)TC358743_XCLK_MIN); -// sensor->mclk = tgt_xclk; - - pr_debug("%s: Setting mclk to %d MHz\n", __func__, sensor->mclk / 1000000); -// set_mclk_rate(&sensor->mclk, sensor->mclk_source); -// pr_debug("%s: After mclk to %d MHz\n", __func__, sensor->mclk / 1000000); - - /* Default camera frame rate is set in probe */ - tgt_fps = sensor->streamcap.timeperframe.denominator / - sensor->streamcap.timeperframe.numerator; - - if (tgt_fps == 60) - frame_rate = tc358743_60_fps; - else if (tgt_fps == 30) - frame_rate = tc358743_30_fps; - else - return -EINVAL; - - mipi_csi2_info = mipi_csi2_get_info(); - /* enable mipi csi2 */ - if (mipi_csi2_info) { - mipi_csi2_enable(mipi_csi2_info); - } else { - pr_err("Fail to get mipi_csi2_info!\n"); - return -EPERM; + if (td->det_changed) { + mutex_lock(&td->access_lock); + td->det_changed = 0; + pr_debug("%s\n", __func__); + tc358743_minit(td); + mutex_unlock(&td->access_lock); } - - pr_debug("%s done\n", __func__); - return ret; + pr_debug("%s\n", __func__); + return 0; } /*! @@ -2827,8 +2846,7 @@ static void det_worker(struct work_struct *work) mutex_lock(&td->access_lock); if (!td->det_work_enable) { - mutex_unlock(&td->access_lock); - return; + goto out2; } u32val = 0; ret = tc358743_read_reg(sensor, 0x8621, &u32val); @@ -2898,17 +2916,16 @@ static void det_worker(struct work_struct *work) } if (td->hdmi_mode) { td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; - mutex_unlock(&td->access_lock); - return; + goto out2; } } } else if (td->hdmi_mode && !td->bounce) { - mutex_unlock(&td->access_lock); - return; + goto out2; } out: - mutex_unlock(&td->access_lock); schedule_delayed_work(&td->det_work, msecs_to_jiffies(td->det_work_timeout)); +out2: + mutex_unlock(&td->access_lock); } static irqreturn_t tc358743_detect_handler(int irq, void *data) @@ -3064,6 +3081,7 @@ static int tc358743_probe(struct i2c_client *client, struct sensor_data *sensor; u8 chip_id_high; u32 u32val; + int mode = tc358743_mode_INIT; td = kzalloc(sizeof(*td), GFP_KERNEL); if (!td) @@ -3072,6 +3090,7 @@ static int tc358743_probe(struct i2c_client *client, td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; td->audio = 2; mutex_init(&td->access_lock); + mutex_lock(&td->access_lock); sensor = &td->sensor; /* request power down pin */ @@ -3141,23 +3160,21 @@ static int tc358743_probe(struct i2c_client *client, sensor->io_init = tc_io_init; sensor->i2c_client = client; - sensor->pix.pixelformat = tc358743_formats[0].pixelformat; sensor->streamcap.capability = V4L2_MODE_HIGHQUALITY | V4L2_CAP_TIMEPERFRAME; - sensor->streamcap.capturemode = 0; - sensor->streamcap.extendedmode = tc358743_mode_1080P_1920_1080; + sensor->streamcap.capturemode = mode; sensor->streamcap.timeperframe.denominator = DEFAULT_FPS; sensor->streamcap.timeperframe.numerator = 1; - sensor->pix.width = tc358743_mode_info_data[0][sensor->streamcap.capturemode].width; - sensor->pix.height = tc358743_mode_info_data[0][sensor->streamcap.capturemode].height; - pr_debug("%s: format: %x, capture mode: %d extended mode: %d fps: %d width: %d height: %d\n",__func__, - sensor->pix.pixelformat, - sensor->streamcap.capturemode, sensor->streamcap.extendedmode, - sensor->streamcap.timeperframe.denominator * - sensor->streamcap.timeperframe.numerator, - sensor->pix.width, - sensor->pix.height); + sensor->pix.pixelformat = get_pixelformat(0, mode); + sensor->pix.width = tc358743_mode_info_data[0][mode].width; + sensor->pix.height = tc358743_mode_info_data[0][mode].height; + pr_debug("%s: format: %x, capture mode: %d fps: %d width: %d height: %d\n", + __func__, sensor->pix.pixelformat, mode, + sensor->streamcap.timeperframe.denominator * + sensor->streamcap.timeperframe.numerator, + sensor->pix.width, + sensor->pix.height); tc_regulator_init(td, dev); power_control(td, 1); @@ -3245,20 +3262,26 @@ static int tc358743_probe(struct i2c_client *client, goto err4; i2c_set_clientdata(client, td); + mutex_unlock(&td->access_lock); retval = v4l2_int_device_register(&tc358743_int_device); + mutex_lock(&td->access_lock); if (retval) { pr_err("%s: v4l2_int_device_register failed, error=%d\n", __func__, retval); goto err4; } power_control(td, 0); + mutex_unlock(&td->access_lock); pr_debug("%s: finished, error=%d\n", __func__, retval); return retval; err4: + power_control(td, 0); + mutex_unlock(&td->access_lock); pr_err("%s: failed, error=%d\n", __func__, retval); if (g_td == td) g_td = NULL; + mutex_destroy(&td->access_lock); kfree(td); return retval; } @@ -3277,6 +3300,7 @@ static int tc358743_remove(struct i2c_client *client) // Stop delayed work cancel_delayed_work_sync(&td->det_work); + mutex_lock(&td->access_lock); power_control(td, 0); // Remove IRQ @@ -3300,6 +3324,7 @@ static int tc358743_remove(struct i2c_client *client) device_remove_file(&client->dev, &dev_attr_regoffs); device_remove_file(&client->dev, &dev_attr_regdump); + mutex_unlock(&td->access_lock); v4l2_int_device_unregister(&tc358743_int_device); for (i = REGULATOR_CNT - 1; i >= 0; i--) { From e8ffb7920fc805af205ac0f6517f75f63bb055f1 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 19 Aug 2014 18:02:06 -0700 Subject: [PATCH 0716/1983] tc358743_h2c: ioctl_g_fmt_cap: check type --- .../media/platform/mxc/capture/tc358743_h2c.c | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index 3ce445aeb555ff..0bc650bcab6c31 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -2330,8 +2330,27 @@ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) sensor->pix.pixelformat = get_pixelformat(0, mode); sensor->pix.width = tc358743_mode_info_data[0][mode].width; sensor->pix.height = tc358743_mode_info_data[0][mode].height; - f->fmt.pix = sensor->pix; - pr_debug("%s: %dx%d\n", __func__, sensor->pix.width, sensor->pix.height); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->fmt.pix = sensor->pix; + pr_debug("%s: %dx%d\n", __func__, sensor->pix.width, sensor->pix.height); + break; + + case V4L2_BUF_TYPE_SENSOR: + pr_debug("%s: left=%d, top=%d, %dx%d\n", __func__, + sensor->spix.left, sensor->spix.top, + sensor->spix.swidth, sensor->spix.sheight); + f->fmt.spix = sensor->spix; + break; + + case V4L2_BUF_TYPE_PRIVATE: + break; + + default: + f->fmt.pix = sensor->pix; + break; + } return 0; } From 1d617234a46a1b195781cc2bb56eebf16619f8cf Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Wed, 23 Jul 2014 09:24:35 -0700 Subject: [PATCH 0717/1983] tc358743_h2c: turn off annoying messages when input not attached --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index 0bc650bcab6c31..dc39a802edd27d 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -2882,7 +2882,7 @@ static void det_worker(struct work_struct *work) td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; goto out; } - pr_info("%s: 852f=%x\n", __func__, u32val); +// pr_info("%s: 852f=%x\n", __func__, u32val); if (u32val & TC3587430_HDMI_DETECT) { pr_info("%s: hdmi detect %x\n", __func__, u32val); td->lock = u32val & TC3587430_HDMI_DETECT; @@ -2903,7 +2903,7 @@ static void det_worker(struct work_struct *work) if (ret < 0) { pr_err("%s: Error reading mode\n", __func__); } - pr_info("%s: 8521=%x\n", __func__, u32val); +// pr_info("%s: 8521=%x\n", __func__, u32val); } if ((unsigned char)td->hdmi_mode != (unsigned char)u32val) { td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; From 76bed59ab400d5b6872b2d67876f531d395f9a4c Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Thu, 15 Jan 2015 15:48:47 -0700 Subject: [PATCH 0718/1983] tc358743_h2c: rename det_worker to tc_det_worker to disambiguate --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index dc39a802edd27d..be9cc6defa1254 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -2853,7 +2853,7 @@ static void report_netlink(struct tc_data *td) td->det_work_timeout, tc358743_audio_list[td->audio]); } -static void det_worker(struct work_struct *work) +static void tc_det_worker(struct work_struct *work) { struct tc_data *td = container_of(work, struct tc_data, det_work.work); struct sensor_data *sensor = &td->sensor; @@ -3263,7 +3263,7 @@ static int tc358743_probe(struct i2c_client *client, #endif #if 1 - INIT_DELAYED_WORK(&td->det_work, det_worker); + INIT_DELAYED_WORK(&td->det_work, tc_det_worker); if (sensor->i2c_client->irq) { retval = request_irq(sensor->i2c_client->irq, tc358743_detect_handler, IRQF_SHARED | IRQF_TRIGGER_FALLING, From e63432b67949ccaaefee9c65410d01765cfc0ca7 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Fri, 16 Jan 2015 18:10:26 -0700 Subject: [PATCH 0719/1983] tc358743_h2c: remove hdmi_mode, rearrange mode table, add name to it --- .../media/platform/mxc/capture/tc358743_h2c.c | 420 ++++++++++++------ 1 file changed, 288 insertions(+), 132 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index be9cc6defa1254..0b262f888bf2bf 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -22,6 +22,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ +#define DEBUG 1 #include #include #include @@ -81,6 +82,7 @@ enum tc358743_mode { tc358743_mode_INIT5, /*only for sensor init*/ tc358743_mode_INIT6, /*only for sensor init*/ tc358743_mode_720P_1280_720, + tc358743_mode_1024x768, tc358743_mode_MAX , }; @@ -99,6 +101,7 @@ struct reg_value { }; struct tc358743_mode_info { + const char *name; enum tc358743_mode mode; u32 width; u32 height; @@ -132,7 +135,7 @@ struct tc_data { #endif u32 lock; u32 bounce; - u32 hdmi_mode; + enum tc358743_mode mode; u32 fps; u32 audio; int pwn_gpio; @@ -326,6 +329,85 @@ static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_ {0x0004, 0x00000cf7, 0x00000000, 2, 0}, }; +static const struct reg_value tc358743_setting_YUV422_4lane_1024x768_60fps_125MHz[] = { + {0x0006, 0x00000000, 0x00000000, 2, 0}, + {0x0014, 0x0000ffff, 0x00000000, 2, 0}, + {0x0016, 0x000005ff, 0x00000000, 2, 0}, +// Program CSI Tx PLL + {0x0020, 0x0000405c, 0x00000000, 2, 0}, /* Input divide 5(4+1), Feedback divide 92(0x5c+1)*/ + {0x0022, 0x00000613, 0x00000000, 2, 0}, +// CSI Tx PHY (32-bit Registers) + {0x0140, 0x00000000, 0x00000000, 4, 0}, + {0x0144, 0x00000000, 0x00000000, 4, 0}, + {0x0148, 0x00000000, 0x00000000, 4, 0}, + {0x014c, 0x00000000, 0x00000000, 4, 0}, + {0x0150, 0x00000000, 0x00000000, 4, 0}, +// CSI Tx PPI (32-bit Registers) + {0x0210, 0x00000d00, 0x00000000, 4, 0}, + {0x0214, 0x00000001, 0x00000000, 4, 0}, + {0x0218, 0x00000701, 0x00000000, 4, 0}, + {0x021c, 0x00000000, 0x00000000, 4, 0}, + {0x0220, 0x00000001, 0x00000000, 4, 0}, + {0x0224, 0x00004000, 0x00000000, 4, 0}, + {0x0228, 0x00000005, 0x00000000, 4, 0}, + {0x022c, 0x00000000, 0x00000000, 4, 0}, + {0x0234, 0x0000001f, 0x00000000, 4, 0}, + {0x0238, 0x00000001, 0x00000000, 4, 0}, + {0x0204, 0x00000001, 0x00000000, 4, 0}, + {0x0518, 0x00000001, 0x00000000, 4, 0}, + {0x0500, 0xa300be86, 0x00000000, 4, 0}, +// HDMI Interrupt Mask + {0x8502, 0x00000001, 0x00000000, 1, 0}, + {0x8512, 0x000000fe, 0x00000000, 1, 0}, + {0x8514, 0x00000000, 0x00000000, 1, 0}, + {0x8515, 0x00000000, 0x00000000, 1, 0}, + {0x8516, 0x00000000, 0x00000000, 1, 0}, +// HDMI Audio + {0x8531, 0x00000001, 0x00000000, 1, 0}, + {0x8630, 0x00041eb0, 0x00000000, 1, 0}, + {0x8670, 0x00000001, 0x00000000, 1, 0}, +// HDMI PHY + {0x8532, 0x00000080, 0x00000000, 1, 0}, + {0x8536, 0x00000040, 0x00000000, 1, 0}, + {0x853f, 0x0000000a, 0x00000000, 1, 0}, +// HDMI System + {0x8545, 0x00000031, 0x00000000, 1, 0}, + {0x8546, 0x0000002d, 0x00000000, 1, 0}, +// HDCP Setting + {0x85d1, 0x00000001, 0x00000000, 1, 0}, + {0x8560, 0x00000024, 0x00000000, 1, 0}, + {0x8563, 0x00000011, 0x00000000, 1, 0}, + {0x8564, 0x0000000f, 0x00000000, 1, 0}, +// RGB --> YUV Conversion +// {0x8574, 0x00000000, 0x00000000, 1, 0}, + {0x8573, 0x00000081, 0x00000000, 1, 0}, + {0x8571, 0x00000002, 0x00000000, 1, 0}, +// HDMI Audio In Setting + {0x8600, 0x00000000, 0x00000000, 1, 0}, + {0x8602, 0x000000f3, 0x00000000, 1, 0}, + {0x8603, 0x00000002, 0x00000000, 1, 0}, + {0x8604, 0x0000000c, 0x00000000, 1, 0}, + {0x8606, 0x00000005, 0x00000000, 1, 0}, + {0x8607, 0x00000000, 0x00000000, 1, 0}, + {0x8620, 0x00000022, 0x00000000, 1, 0}, + {0x8640, 0x00000001, 0x00000000, 1, 0}, + {0x8641, 0x00000065, 0x00000000, 1, 0}, + {0x8642, 0x00000007, 0x00000000, 1, 0}, + {0x8652, 0x00000002, 0x00000000, 1, 0}, + {0x8665, 0x00000010, 0x00000000, 1, 0}, +// InfoFrame Extraction + {0x8709, 0x000000ff, 0x00000000, 1, 0}, + {0x870b, 0x0000002c, 0x00000000, 1, 0}, + {0x870c, 0x00000053, 0x00000000, 1, 0}, + {0x870d, 0x00000001, 0x00000000, 1, 0}, + {0x870e, 0x00000030, 0x00000000, 1, 0}, + {0x9007, 0x00000010, 0x00000000, 1, 0}, + {0x854a, 0x00000001, 0x00000000, 1, 0}, +// Output Control + {0x0004, 0x00000cf7, 0x00000000, 2, 0}, + +}; + static const struct reg_value tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz[] = { {0x0006, 0x00000000, 0x00000000, 2, 0}, {0x0014, 0x0000ffff, 0x00000000, 2, 0}, @@ -1167,151 +1249,176 @@ static const struct v4l2_fmtdesc tc358743_formats[] = { static const struct tc358743_mode_info tc358743_mode_info_data[2][tc358743_mode_MAX] = { - [0][tc358743_mode_720P_60_1280_720] = - {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, - tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), - MIPI_DT_YUV422 - }, - [0][tc358743_mode_1080P_1920_1080] = - {tc358743_mode_1080P_1920_1080, 1920, 1080, 15, 0x0b, 4, 300, - tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz), - MIPI_DT_YUV422 - }, - [0][tc358743_mode_INIT1] = - {tc358743_mode_INIT1, 1280, 720, 12, 0, 2, 125, - tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), - MIPI_DT_YUV422 - }, - [0][tc358743_mode_INIT2] = - {tc358743_mode_INIT2, 1280, 720, 12, 0, 4, 125, - tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), +/* Color bar test settings */ + [1][tc358743_mode_INIT] = + {"cb640x480-108MHz@30", tc358743_mode_INIT, 640, 480, + 6, 1, 2, 108, + tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), MIPI_DT_YUV422 }, [0][tc358743_mode_INIT] = - {tc358743_mode_INIT, 640, 480, 6, 1, 2, 108, + {"cb640x480-108MHz@60", tc358743_mode_INIT, 640, 480, + 6, 1, 2, 108, tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), MIPI_DT_YUV422 }, + [1][tc358743_mode_INIT4] = + {"cb640x480-174Mhz@30", tc358743_mode_INIT4, 640, 480, + 6, 1, 2, 174, + tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), + MIPI_DT_YUV422 + }, [0][tc358743_mode_INIT4] = - {tc358743_mode_INIT4, 640, 480, 6, 1, 2, 174, + {"cb640x480-174MHz@60", tc358743_mode_INIT4, 640, 480, + 6, 1, 2, 174, tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), MIPI_DT_YUV422 }, - [0][tc358743_mode_INIT3] = - {tc358743_mode_INIT3, 1024, 720, 6, 1, 4, 300, + [1][tc358743_mode_INIT3] = + {"cb1024x720-4lane@30", tc358743_mode_INIT3, 1024, 720, + 6, 1, 4, 300, tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), MIPI_DT_YUV422 }, - [0][tc358743_mode_720P_1280_720] = - {tc358743_mode_720P_1280_720, 1280, 720, 12, (0x3e)<<8|(0x3c), 2, 125, - tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz), - MIPI_DT_YUV422, - }, - [0][tc358743_mode_480P_720_480] = - {tc358743_mode_480P_720_480, 720, 480, 6, (0x02)<<8|(0x00), 2, 125, - tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), - MIPI_DT_YUV422, - }, - [0][tc358743_mode_480P_640_480] = - {tc358743_mode_480P_640_480, 640, 480, 6, (0x02)<<8|(0x00), 2, 125, - tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), - MIPI_DT_YUV422, - }, - [0][tc358743_mode_INIT5] = - {tc358743_mode_INIT5, 1280, 720, 12, 0, 4, 300, - tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), - MIPI_DT_YUV422 - }, - [0][tc358743_mode_INIT6] = - {tc358743_mode_INIT6, 1920, 1023, 15, 0, 4, 300, - tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), - MIPI_DT_YUV422 - }, - [1][tc358743_mode_720P_60_1280_720] = - {tc358743_mode_720P_60_1280_720, 1280, 720, 12, 0, 4, 133, - tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), + [0][tc358743_mode_INIT3] = + {"cb1024x720-4lane@60", tc358743_mode_INIT3, 1024, 720, + 6, 1, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_1080P_1920_1080] = - {tc358743_mode_1080P_1920_1080, 1920, 1080, 15, 0xa, 4, 300, - tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz), + [1][tc358743_mode_INIT1] = + {"cb1280x720-2lane@30", tc358743_mode_INIT1, 1280, 720, + 12, 0, 2, 125, + tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT1] = - {tc358743_mode_INIT1, 1280, 720, 12, 0, 2, 125, + [0][tc358743_mode_INIT1] = + {"cb1280x720-2lane@60", tc358743_mode_INIT1, 1280, 720, + 12, 0, 2, 125, tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, [1][tc358743_mode_INIT2] = - {tc358743_mode_INIT2, 1280, 720, 12, 0, 4, 125, + {"cb1280x720-4lane-125MHz@30", tc358743_mode_INIT2, 1280, 720, + 12, 0, 4, 125, tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - - [1][tc358743_mode_INIT] = - {tc358743_mode_INIT, 640, 480, 6, 1, 2, 108, - tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_108MHz_cont), + [0][tc358743_mode_INIT2] = + {"cb1280x720-4lane-125MHz@60", tc358743_mode_INIT2, 1280, 720, + 12, 0, 4, 125, + tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_125MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT4] = - {tc358743_mode_INIT4, 640, 480, 6, 1, 2, 174, - tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_color_bar_640_480_174MHz), + [1][tc358743_mode_INIT5] = + {"cb1280x720-4lane-300MHz@30", tc358743_mode_INIT5, 1280, 720, + 12, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT3] = - {tc358743_mode_INIT3, 1024, 720, 6, 1, 4, 300, - tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1024_720_200MHz), + [0][tc358743_mode_INIT5] = + {"cb1280x720-4lane-300MHz@60", tc358743_mode_INIT5, 1280, 720, + 12, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_720P_1280_720] = - {tc358743_mode_720P_1280_720, 1280, 720, 12, (0x3e)<<8|(0x3c), 2, 125, - tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz), + [1][tc358743_mode_INIT6] = + {"cb1920x1023@30", tc358743_mode_INIT6, 1920, 1023, + 15, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), + MIPI_DT_YUV422 + }, + [0][tc358743_mode_INIT6] = + {"cb1920x1023@60", tc358743_mode_INIT6, 1920, 1023, + 15, 0, 4, 300, + tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), + MIPI_DT_YUV422 + }, +/* Input settings */ + [tc358743_60_fps][tc358743_mode_480P_640_480] = + {"640x480@60", tc358743_mode_480P_640_480, 640, 480, + 1, (0x02)<<8|(0x00), 2, 125, + tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), MIPI_DT_YUV422, }, [1][tc358743_mode_480P_720_480] = - {tc358743_mode_480P_720_480, 720, 480, 6, (0x02)<<8|(0x00), 2, 125, + {"720x480@30", tc358743_mode_480P_720_480, 720, 480, + 6, (0x02)<<8|(0x00), 2, 125, tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), MIPI_DT_YUV422, }, - [0][tc358743_mode_480P_640_480] = - {tc358743_mode_480P_640_480, 640, 480, 1, (0x02)<<8|(0x00), 2, 125, - tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz, - ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_640_480_125Mhz), + [tc358743_60_fps][tc358743_mode_480P_720_480] = + {"720x480@60", tc358743_mode_480P_720_480, 720, 480, + 6, (0x02)<<8|(0x00), 2, 125, + tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_60fps_720_480_125Mhz), MIPI_DT_YUV422, }, - [1][tc358743_mode_INIT5] = - {tc358743_mode_INIT5, 1280, 720, 12, 0, 4, 300, - tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1280_720_300MHz), + [tc358743_60_fps][tc358743_mode_1024x768] = + {"1024x768@60", tc358743_mode_1024x768, 1024, 768, + 16, 60, 4, 125, + tc358743_setting_YUV422_4lane_1024x768_60fps_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_1024x768_60fps_125MHz), MIPI_DT_YUV422 }, - [1][tc358743_mode_INIT6] = - {tc358743_mode_INIT6, 1920, 1023, 15, 0, 4, 300, - tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz, - ARRAY_SIZE(tc358743_setting_YUV422_4lane_color_bar_1920_1023_300MHz), + [1][tc358743_mode_720P_1280_720] = + {"1280x720-2lane@30", tc358743_mode_720P_1280_720, 1280, 720, + 12, (0x3e)<<8|(0x3c), 2, 125, + tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz), + MIPI_DT_YUV422, + }, + [0][tc358743_mode_720P_1280_720] = + {"1280x720-2lane@60", tc358743_mode_720P_1280_720, 1280, 720, + 12, (0x3e)<<8|(0x3c), 2, 125, + tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz, + ARRAY_SIZE(tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz), + MIPI_DT_YUV422, + }, + [1][tc358743_mode_720P_60_1280_720] = + {"1280x720-4lane-133Mhz@30", tc358743_mode_720P_60_1280_720, 1280, 720, + 12, 0, 4, 133, + tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), MIPI_DT_YUV422 - }, + }, + [tc358743_60_fps][tc358743_mode_720P_60_1280_720] = + {"1280x720-4lane@60", tc358743_mode_720P_60_1280_720, 1280, 720, + 12, 0, 4, 133, + tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_720P_60fps_1280_720_133Mhz), + MIPI_DT_YUV422 + }, + [1][tc358743_mode_1080P_1920_1080] = + {"1920x1080@30", tc358743_mode_1080P_1920_1080, 1920, 1080, + 15, 0xa, 4, 300, + tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_30fps_1920_1080_300MHz), + MIPI_DT_YUV422 + }, + [tc358743_60_fps][tc358743_mode_1080P_1920_1080] = + {"1920x1080@60", tc358743_mode_1080P_1920_1080, 1920, 1080, + 15, 0x0b, 4, 300, + tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz, + ARRAY_SIZE(tc358743_setting_YUV422_4lane_1080P_60fps_1920_1080_300MHz), + MIPI_DT_YUV422 + }, }; static int tc358743_probe(struct i2c_client *adapter, @@ -1427,6 +1534,45 @@ static s32 tc358743_read_reg_val(struct sensor_data *sensor, u16 reg) return val; } +static s32 tc358743_read_reg_val16(struct sensor_data *sensor, u16 reg) +{ + struct i2c_client *client = sensor->i2c_client; + struct i2c_msg msgs[3]; + u8 txbuf[4]; + u8 rxbuf1[4]; + u8 rxbuf2[4]; + int ret; + int size = get_reg_size(reg, 0); + + if (size != 1) + return -EINVAL; + + txbuf[0] = reg >> 8; + txbuf[1] = reg & 0xff; + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 2; + msgs[0].buf = txbuf; + + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = size; + msgs[1].buf = rxbuf1; + + msgs[2].addr = client->addr; + msgs[2].flags = I2C_M_RD; + msgs[2].len = size; + msgs[2].buf = rxbuf2; + + ret = i2c_transfer(client->adapter, msgs, 3); + if (ret < 0) { + pr_err("%s:reg=%x ret=%d\n", __func__, reg, ret); + return ret; + } +// pr_debug("%s:reg=%x,val=%x\n", __func__, reg, ((char *)rxbuf)[0]); + return rxbuf1[0] | (rxbuf2[0] << 8); +} + static s32 tc358743_write_reg(struct sensor_data *sensor, u16 reg, u32 val, int len) { int ret; @@ -1669,7 +1815,7 @@ int set_frame_rate_mode(struct tc_data *td, } } tc358743_enable_edid(sensor); - if (!td->hdmi_mode) // is this during reset + if (!td->mode) // is this during reset if ((retval = tc358743_write_edid(sensor, cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) pr_err("%s: Fail to write EDID to tc35874!\n", __func__); @@ -1895,7 +2041,7 @@ static int ioctl_s_power(struct v4l2_int_device *s, int on) int ret; mutex_lock(&td->access_lock); - if (on && !td->hdmi_mode) { + if (on && !td->mode) { ret = tc358743_reset(td); } else { ret = power_control(td, on); @@ -2786,7 +2932,7 @@ struct tc_mode_list { enum tc358743_mode mode; }; -static const struct tc_mode_list tc358743_mode_list[16] = +static const struct tc_mode_list tc358743_mode_list[] = { {"None", 0}, /* 0 */ {"VGA", tc358743_mode_480P_640_480}, /* 1 */ @@ -2806,7 +2952,6 @@ static const struct tc_mode_list tc358743_mode_list[16] = {"1080p", tc358743_mode_1080P_1920_1080}, /* 15 */ }; - static char tc358743_fps_list[tc358743_max_fps+1] = { [tc358743_60_fps] = 60, @@ -2842,14 +2987,14 @@ static void report_netlink(struct tc_data *td) envp[0] = &str_on[0]; envp[1] = NULL; sprintf(envp[0], "HDMI RX: %d (%s) %d %d", - td->hdmi_mode & 0xf, - tc358743_mode_list[td->hdmi_mode & 0xf].name, + td->mode, + tc358743_mode_info_data[td->fps][td->mode].name, tc358743_fps_list[td->fps], tc358743_audio_list[td->audio]); kobject_uevent_env(&(sensor->i2c_client->dev.kobj), KOBJ_CHANGE, envp); td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; pr_debug("%s: HDMI RX (%d) mode: %s fps: %d (%d, %d) audio: %d\n", - __func__, td->hdmi_mode, - tc358743_mode_list[td->hdmi_mode & 0xf].name, td->fps, td->bounce, + __func__, td->mode, + tc358743_mode_info_data[td->fps][td->mode].name, td->fps, td->bounce, td->det_work_timeout, tc358743_audio_list[td->audio]); } @@ -2858,7 +3003,9 @@ static void tc_det_worker(struct work_struct *work) struct tc_data *td = container_of(work, struct tc_data, det_work.work); struct sensor_data *sensor = &td->sensor; int ret; - u32 u32val; + u32 u32val, u852f; + enum tc358743_mode mode = tc358743_mode_INIT; + if (!td->det_work_enable) return; @@ -2875,29 +3022,43 @@ static void tc_det_worker(struct work_struct *work) report_netlink(td); } } - u32val = 0; - ret = tc358743_read_reg(sensor, 0x852f, &u32val); + u852f = 0; + ret = tc358743_read_reg(sensor, 0x852f, &u852f); if (ret < 0) { pr_err("%s: Error reading lock\n", __func__); td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; goto out; } // pr_info("%s: 852f=%x\n", __func__, u32val); - if (u32val & TC3587430_HDMI_DETECT) { - pr_info("%s: hdmi detect %x\n", __func__, u32val); - td->lock = u32val & TC3587430_HDMI_DETECT; + if (u852f & TC3587430_HDMI_DETECT) { + pr_info("%s: hdmi detect %x\n", __func__, u852f); + td->lock = u852f & TC3587430_HDMI_DETECT; u32val = 0; ret = tc358743_read_reg(sensor, 0x8521, &u32val); if (ret < 0) { pr_err("%s: Error reading mode\n", __func__); } pr_info("%s: detect 8521=%x\n", __func__, u32val); + u32val &= 0x0f; + td->fps = tc358743_60_fps; + if (!u32val) { + int hsize, vsize; + + hsize = tc358743_read_reg_val16(sensor, 0x8582); + vsize = tc358743_read_reg_val16(sensor, 0x8588); + pr_info("%s: detect hsize=%d, vsize=%d\n", __func__, hsize, vsize); + if ((hsize == 1024) && (vsize == 768)) + mode = tc358743_mode_1024x768; + } else { + mode = tc358743_mode_list[u32val].mode; + if (td->mode != mode) + pr_debug("%s: %s detected\n", __func__, tc358743_mode_list[u32val].name); + if (u852f >= 0xe) + td->fps = ((u852f & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; + } } else { - if (td->lock) { // check if it is realy un-plug + if (td->lock) td->lock = 0; - u32val = 0x0; - td->hdmi_mode = 0xF0; // fake mode to detect un-plug if mode was not detected before. - } u32val = 0; ret = tc358743_read_reg(sensor, 0x8521, &u32val); if (ret < 0) { @@ -2905,40 +3066,35 @@ static void tc_det_worker(struct work_struct *work) } // pr_info("%s: 8521=%x\n", __func__, u32val); } - if ((unsigned char)td->hdmi_mode != (unsigned char)u32val) { + if (td->mode != mode) { td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; td->bounce = MAX_BOUNCE; pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", - __func__, (unsigned char)td->hdmi_mode, - (unsigned char)u32val, - tc358743_mode_list[td->hdmi_mode & 0xf].name, + __func__, td->mode, mode, + tc358743_mode_info_data[td->fps][td->mode].name, td->fps, td->bounce, td->det_work_timeout); - td->hdmi_mode = u32val; - sensor->streamcap.capturemode = tc358743_mode_list[td->hdmi_mode & 0xf].mode; + td->mode = mode; + sensor->streamcap.capturemode = mode; + sensor->spix.swidth = tc358743_mode_info_data[td->fps][mode].width; + sensor->spix.sheight = tc358743_mode_info_data[td->fps][mode].height; td->det_changed = 1; } else if (td->bounce) { td->bounce--; td->det_work_timeout = DET_WORK_TIMEOUT_DEFAULT; if (!td->bounce) { - if (td->hdmi_mode >= 0xe) { - u32val = 0; - ret = tc358743_read_reg(sensor, 0x852f, &u32val); - if (ret >= 0) - td->fps = ((((unsigned char)u32val) & 0x0f) > 0xa)? tc358743_60_fps: tc358743_30_fps; - } u32val = 0; ret = tc358743_read_reg(sensor, 0x8621, &u32val); if (ret >= 0) { td->audio = ((unsigned char)u32val) & 0x0f; report_netlink(td); } - if (td->hdmi_mode) { + if (td->mode) { td->det_work_timeout = DET_WORK_TIMEOUT_DEFERRED; goto out2; } } - } else if (td->hdmi_mode && !td->bounce) { + } else if (td->mode && !td->bounce) { goto out2; } out: @@ -3059,7 +3215,7 @@ static ssize_t tc358743_show_hdmirx(struct device *dev, struct tc_data *td = g_td; int len = 0; - len += sprintf(buf+len, "%d\n", td->hdmi_mode); + len += sprintf(buf+len, "%d\n", td->mode); return len; } From 60edb258a96b9c2de459a5b7d80bd30b60051681 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 20 Jan 2015 18:18:43 -0700 Subject: [PATCH 0720/1983] tc358743_h2c: document edid, don't allow setting mode if not currently selecting a colorbar --- .../media/platform/mxc/capture/tc358743_h2c.c | 207 ++++++++++++++---- 1 file changed, 164 insertions(+), 43 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index 0b262f888bf2bf..a93d9387905ffd 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -69,21 +69,24 @@ #define TC_VOLTAGE_DIGITAL_GPO 1500000 #define TC_VOLTAGE_ANALOG 2800000 +#define MAX_COLORBAR tc358743_mode_INIT6 +#define IS_COLORBAR(a) (a <= MAX_COLORBAR) + enum tc358743_mode { - tc358743_mode_INIT, /*only for sensor init*/ - tc358743_mode_INIT1, /*only for sensor init*/ + tc358743_mode_INIT, + tc358743_mode_INIT1, + tc358743_mode_INIT2, + tc358743_mode_INIT3, + tc358743_mode_INIT4, + tc358743_mode_INIT5, + tc358743_mode_INIT6, tc358743_mode_480P_720_480, tc358743_mode_720P_60_1280_720, tc358743_mode_480P_640_480, tc358743_mode_1080P_1920_1080, - tc358743_mode_INIT2, /*only for sensor init*/ - tc358743_mode_INIT3, /*only for sensor init*/ - tc358743_mode_INIT4, /*only for sensor init*/ - tc358743_mode_INIT5, /*only for sensor init*/ - tc358743_mode_INIT6, /*only for sensor init*/ tc358743_mode_720P_1280_720, tc358743_mode_1024x768, - tc358743_mode_MAX , + tc358743_mode_MAX, }; enum tc358743_frame_rate { @@ -141,6 +144,7 @@ struct tc_data { int pwn_gpio; int rst_gpio; u16 hpd_active; + int edid_initialized; }; static struct tc_data *g_td; @@ -230,22 +234,100 @@ static void det_work_enable(struct tc_data *td, int enable) static const u8 cHDMIEDID[256] = { /* FIXME! This is the edid that my ASUS HDMI monitor returns */ - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x04, 0x69, 0xf3, 0x24, 0xd6, 0x12, 0x00, 0x00, - 0x16, 0x16, 0x01, 0x03, 0x80, 0x34, 0x1d, 0x78, 0x2a, 0xc7, 0x20, 0xa4, 0x55, 0x49, 0x99, 0x27, - 0x13, 0x50, 0x54, 0xbf, 0xef, 0x00, 0x71, 0x4f, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00, - 0xd1, 0xc0, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x45, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xff, 0x00, 0x43, 0x36, 0x4c, - 0x4d, 0x54, 0x46, 0x30, 0x30, 0x34, 0x38, 0x32, 0x32, 0x0a, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x37, - 0x4b, 0x1e, 0x55, 0x10, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x48, 0x32, 0x34, 0x32, 0x48, 0x0a, 0x20, 0x01, 0x78, - 0x02, 0x03, 0x22, 0x71, 0x4f, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f, - 0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, 0x07, 0x01, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0c, 0x00, - 0x10, 0x00, 0x8c, 0x0a, 0xd0, 0x8a, 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0x09, 0x25, - 0x21, 0x00, 0x00, 0x18, 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, - 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x01, 0x1d, 0x00, 0xbc, 0x52, 0xd0, 0x1e, 0x20, 0xb8, 0x28, - 0x55, 0x40, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, 0x8c, 0x0a, 0xd0, 0x90, 0x20, 0x40, 0x31, 0x20, - 0x0c, 0x40, 0x55, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, //0 - header[8] - fixed pattern + 0x04, 0x69, //8 - manufacturer_name[2] + 0xf3, 0x24, //10 - product_code[2] + 0xd6, 0x12, 0x00, 0x00, //12 - serial_number[4] + 0x16, //16 - week + 0x16, //17 - year 22+1990=2012 + 0x01, //18 - version + 0x03, //19 - revision + 0x80, //20 - video_input_definition(digital input) + 0x34, //21 - max_size_horizontal 52cm + 0x1d, //22 - max_size_vertical 29cm + 0x78, //23 - gamma + 0x3a, //24 - feature_support (RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2) + 0xc7, 0x20, 0xa4, 0x55, 0x49, 0x99, 0x27, 0x13, 0x50, 0x54, //25 - color_characteristics[10] + 0xbf, 0xef, 0x00, //35 - established_timings[3] + 0x71, 0x4f, 0x81, 0x40, 0x81, 0x80, 0x95, 0x00, 0xb3, 0x00, 0xd1, 0xc0, 0x01, 0x01, 0x01, 0x01, //38 - standard_timings[8] +/* 1080P */ + 0x02, 0x3a, //54(0) - descriptor[0], 0x3a02 = 148.50 MHz + 0x80, //56(2) h - active 0x780 (1920) + 0x18, //57(3) h - blank 0x118 (280) + 0x71, //58(4) + 0x38, //59(5) v - active 0x438 (1080) + 0x2d, //60(6) v - blank 0x02d(45) + 0x40, //61(7) + 0x58, //62(8) - h sync offset(0x58) + 0x2c, //63(9) - h sync width(0x2c) + 0x45, //64(10) - v sync offset(0x4), v sync width(0x5) + 0x00, //65(11) + 0x09, //66(12) - h display size (0x209) 521 mm + 0x25, //67(13) - v display size (0x125) 293 mm + 0x21, //68(14) + 0x00, //69(15) - h border pixels + 0x00, //70(16) - v border pixels + 0x1e, //71(17) - no stereo, digital separate, hsync+, vsync+ + 0x00, 0x00, 0x00, 0xff, 0x00, //72 - descriptor[1] + 0x43, 0x36, 0x4c, 0x4d, 0x54, 0x46, 0x30, 0x30, 0x34, 0x38, 0x32, 0x32, 0x0a, //"C6LMTF004822\n" + 0x00, 0x00, 0x00, 0xfd, 0x00, //90 - descriptor[2] + 0x37, 0x4b, 0x1e, 0x55, 0x10, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0xfc, 0x00, //108 - descriptor[3] + 0x41, 0x53, 0x55, 0x53, 0x20, 0x56, 0x48, 0x32, 0x34, 0x32, 0x48, 0x0a, 0x20, //"ASUS VH242H\n " + 0x01, //126 - extension_flag + 0x68, //127 - checksum + + 0x02, //0 - cea-861 extension + 0x03, //1 - rev 3 + 0x22, //2 - detailed timings at offset 34 + 0x71, //3 - # of detailed timing descriptor + 0x4f, 0x01, 0x02, 0x03, 0x11, 0x12, //4 + 0x13, 0x04, 0x14, 0x05, 0x0e, 0x0f, //10 + 0x1d, 0x1e, 0x1f, 0x10, 0x23, 0x09, //16 + 0x07, 0x01, 0x83, 0x01, 0x00, 0x00, //22 + 0x65, 0x03, 0x0c, 0x00, 0x10, 0x00, //28 +/* 720x480@59.94 27000000/858/525 = 59.94 Hz */ + 0x8c, 0x0a, //34 - descriptor[0] - 0x0a8c - 27 Mhz + 0xd0, //h - active 0x2d0 (720) + 0x8a, //h - blank 0x8a(138) + 0x20, + 0xe0, //v - active 0x1e0 (480) + 0x2d, //v - blank 0x2d (45) + 0x10, + 0x10, 0x3e, 0x96, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x18, +/* 1280x720@60 74250000/1650/750 = 60 Hz*/ + 0x01, 0x1d, //52 - 0x1d01 74.25MHz + 0x00, //h - active (0x500)1280 + 0x72, //h - blank (0x172)370 + 0x51, + 0xd0, //v active 0x2d0(720) + 0x1e, //v blank 0x1e(30) + 0x20, + 0x6e, 0x28, 0x55, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, +/* 1280x720@50 74250000/1980/750 = 50 Hz */ + 0x01, 0x1d, //70 - 0x1d01 74.25MHz + 0x00, //h - active (0x500)1280 + 0xbc, //h - blank (0x2bc)700 + 0x52, + 0xd0, //v active 0x2d0 (720) + 0x1e, //v blank 0x1e(30) + 0x20, + 0xb8, 0x28, 0x55, 0x40, 0x09, 0x25, 0x21, 0x00, 0x00, 0x1e, +/* 720x576@50 27000000/864/625 = 50 Hz */ + 0x8c, 0x0a, //88 0x0a8c - 27 Mhz + 0xd0, //h - active 0x2d0(720) + 0x90, //h - blank 0x90(144) + 0x20, + 0x40, //v active 0x240(576) + 0x31, //v blanking 0x31(49) + 0x20, + 0x0c, 0x40, 0x55, 0x00, 0x09, 0x25, 0x21, 0x00, 0x00, 0x18, +/* done */ + 0x00, 0x00, 0x00, 0x00, 0x00, //106 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, //124 + 0x00, //126 - extension_flag + 0x73, //127 - checksum }; static const struct reg_value tc358743_setting_YUV422_2lane_30fps_720P_1280_720_125MHz[] = { @@ -1536,6 +1618,7 @@ static s32 tc358743_read_reg_val(struct sensor_data *sensor, u16 reg) static s32 tc358743_read_reg_val16(struct sensor_data *sensor, u16 reg) { +#if 0 struct i2c_client *client = sensor->i2c_client; struct i2c_msg msgs[3]; u8 txbuf[4]; @@ -1571,6 +1654,14 @@ static s32 tc358743_read_reg_val16(struct sensor_data *sensor, u16 reg) } // pr_debug("%s:reg=%x,val=%x\n", __func__, reg, ((char *)rxbuf)[0]); return rxbuf1[0] | (rxbuf2[0] << 8); +#else + u32 val1 = 0; + u32 val2 = 0; + tc358743_read_reg(sensor, reg, &val1); + tc358743_read_reg(sensor, reg+1, &val2); + return val1 | (val2 << 8); + +#endif } static s32 tc358743_write_reg(struct sensor_data *sensor, u16 reg, u32 val, int len) @@ -1639,6 +1730,7 @@ static int tc358743_write_edid(struct sensor_data *sensor, const u8 *edid, int l int i = 0, off = 0; u8 au8Buf[16+2] = {0}; int size = 0; + int checksum = 0; u16 reg; reg = 0x8C00; @@ -1652,7 +1744,18 @@ static int tc358743_write_edid(struct sensor_data *sensor, const u8 *edid, int l if (size > len) size = len; while (i < size + 2) { - au8Buf[i++] = edid[off++]; + u8 byte = edid[off++]; + if ((off & 0x7f) == 0) { + checksum &= 0xff; + if (checksum != byte) { + pr_info("%schecksum=%x, byte=%x\n", __func__, checksum, byte); + byte = checksum; + checksum = 0; + } + } else { + checksum -= byte; + } + au8Buf[i++] = byte; } if (i2c_master_send(sensor->i2c_client, au8Buf, i) < 0) { @@ -1815,9 +1918,13 @@ int set_frame_rate_mode(struct tc_data *td, } } tc358743_enable_edid(sensor); - if (!td->mode) // is this during reset - if ((retval = tc358743_write_edid(sensor, cHDMIEDID, ARRAY_SIZE(cHDMIEDID)))) - pr_err("%s: Fail to write EDID to tc35874!\n", __func__); + if (!td->edid_initialized) { + retval = tc358743_write_edid(sensor, cHDMIEDID, ARRAY_SIZE(cHDMIEDID)); + if (retval) + pr_err("%s: Fail to write EDID(%d) to tc35874!\n", __func__, retval); + else + td->edid_initialized = 1; + } return retval; } @@ -2112,6 +2219,7 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; u32 tgt_fps; /* target frames per secound */ enum tc358743_frame_rate frame_rate = tc358743_60_fps, frame_rate_now = tc358743_60_fps; + enum tc358743_mode mode; int ret = 0; pr_debug("%s\n", __func__); @@ -2169,12 +2277,19 @@ static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) else if (tgt_fps == 30) frame_rate_now = tc358743_30_fps; + mode = td->mode; + if (IS_COLORBAR(mode)) { + mode = (u32)a->parm.capture.capturemode; + } else { + a->parm.capture.capturemode = mode; + frame_rate = td->fps; + timeperframe->denominator = (frame_rate == tc358743_60_fps) ? 60 : 30; + timeperframe->numerator = 1; + } + if (frame_rate_now != frame_rate || - sensor->streamcap.capturemode != (u32)a->parm.capture.capturemode || + sensor->streamcap.capturemode != mode || sensor->streamcap.extendedmode != (u32)a->parm.capture.extendedmode) { - enum tc358743_mode mode; - - mode = (u32)a->parm.capture.capturemode; if (mode != tc358743_mode_INIT) { sensor->streamcap.capturemode = mode; @@ -2328,20 +2443,24 @@ static int ioctl_enum_framesizes(struct v4l2_int_device *s, struct v4l2_frmsizeenum *fsize) { struct tc_data *td = s->priv; - struct sensor_data *sensor = &td->sensor; - int index = fsize->index; + enum tc358743_mode query_mode= fsize->index; + enum tc358743_mode mode = td->mode; - if (!index) - index = sensor->streamcap.capturemode; - pr_debug("%s, INDEX: %d\n", __func__, index); - if (index >= tc358743_mode_MAX) - return -EINVAL; + if (IS_COLORBAR(mode)) { + if (query_mode > MAX_COLORBAR) + return -EINVAL; + mode = query_mode; + } else { + if (query_mode) + return -EINVAL; + } + pr_debug("%s, mode: %d\n", __func__, mode); - fsize->pixel_format = get_pixelformat(0, index); + fsize->pixel_format = get_pixelformat(0, mode); fsize->discrete.width = - tc358743_mode_info_data[0][index].width; + tc358743_mode_info_data[0][mode].width; fsize->discrete.height = - tc358743_mode_info_data[0][index].height; + tc358743_mode_info_data[0][mode].height; pr_debug("%s %d:%d format: %x\n", __func__, fsize->discrete.width, fsize->discrete.height, fsize->pixel_format); return 0; } @@ -2491,10 +2610,12 @@ static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) break; case V4L2_BUF_TYPE_PRIVATE: + pr_debug("%s: private\n", __func__); break; default: f->fmt.pix = sensor->pix; + pr_debug("%s: type=%d, %dx%d\n", __func__, f->type, sensor->pix.width, sensor->pix.height); break; } return 0; @@ -3071,7 +3192,7 @@ static void tc_det_worker(struct work_struct *work) td->bounce = MAX_BOUNCE; pr_debug("%s: HDMI RX (%d != %d) mode: %s fps: %d (%d, %d)\n", __func__, td->mode, mode, - tc358743_mode_info_data[td->fps][td->mode].name, + tc358743_mode_info_data[td->fps][mode].name, td->fps, td->bounce, td->det_work_timeout); td->mode = mode; sensor->streamcap.capturemode = mode; From dd2bbf3b8d4ab385547991b07c884d11df0f8741 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 7 Sep 2015 10:07:11 +0200 Subject: [PATCH 0721/1983] media: tc358743_h2c: fixup build for this tree This fixes some includes for that are custom for this tree. --- drivers/media/platform/mxc/capture/tc358743_h2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/tc358743_h2c.c b/drivers/media/platform/mxc/capture/tc358743_h2c.c index a93d9387905ffd..9efa675aa5297d 100644 --- a/drivers/media/platform/mxc/capture/tc358743_h2c.c +++ b/drivers/media/platform/mxc/capture/tc358743_h2c.c @@ -22,7 +22,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -#define DEBUG 1 + #include #include #include @@ -38,7 +38,7 @@ #include #include #include -#include +#include "v4l2-int-device.h" #include #include #include From a7dacec392d8506b63e035e5a163326e51b94b9e Mon Sep 17 00:00:00 2001 From: Thomas Genty Date: Fri, 11 Sep 2015 14:48:34 +0200 Subject: [PATCH 0722/1983] imx6qdl-cm-fx6.dtsi : add restart_poweroff --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index a317c56012ef1d..d88ef9b580fd06 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -183,6 +183,9 @@ status = "okay"; }; + restart_poweroff { + compatible = "fsl,snvs-poweroff"; + }; }; &iomuxc { From 1370e13df19a63fb15cee4747cbaa6dad63a5323 Mon Sep 17 00:00:00 2001 From: Uli Middelberg Date: Mon, 14 Sep 2015 21:38:34 +0200 Subject: [PATCH 0723/1983] Additional operation points inserted up to 1.25GHz --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index d88ef9b580fd06..d07ac33e43799d 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -427,6 +427,9 @@ &cpu0 { operating-points = < /* kHz uV */ + 1248000 1300000 + 1200000 1275000 + 1128000 1275000 996000 1250000 852000 1250000 792000 1150000 @@ -434,6 +437,9 @@ >; fsl,soc-operating-points = < /* ARM kHz SOC-PU uV */ + 1248000 1300000 + 1200000 1275000 + 1128000 1275000 996000 1250000 852000 1250000 792000 1175000 From 3d06f9cc8252a0b4b3d238b8491aff07ee996c50 Mon Sep 17 00:00:00 2001 From: Rudi Date: Tue, 15 Sep 2015 20:16:17 +0200 Subject: [PATCH 0724/1983] net: fec: Properly shutdown time_keep work queue --- drivers/net/ethernet/freescale/fec_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index da60e94661ad13..60c8a9db15331e 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3456,6 +3456,8 @@ fec_probe(struct platform_device *pdev) failed_mii_init: failed_irq: failed_init: + if (fep->bufdesc_ex) + cancel_delayed_work_sync(&fep->time_keep); if (fep->reg_phy) regulator_disable(fep->reg_phy); if (fep->ptp_clock) @@ -3477,7 +3479,8 @@ fec_drv_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct fec_enet_private *fep = netdev_priv(ndev); - cancel_delayed_work_sync(&fep->time_keep); + if (fep->bufdesc_ex) + cancel_delayed_work_sync(&fep->time_keep); cancel_work_sync(&fep->tx_timeout_work); unregister_netdev(ndev); fec_enet_mii_remove(fep); From 2b38fa81e1f5a182dd60dccbbc4deebe6c3a4b19 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Sun, 20 Sep 2015 07:27:57 +0200 Subject: [PATCH 0725/1983] dts: cm-fx6: Add missing reg_3p3v regulator This is the definition for the fixed 3.3 volt regulator on the iMX6. It was missing in this dts file so it would not compile. --- arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi index d07ac33e43799d..ae99167a1a38b7 100644 --- a/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi +++ b/arch/arm/boot/dts/imx6qdl-cm-fx6.dtsi @@ -34,6 +34,14 @@ #address-cells = <1>; #size-cells = <0>; + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + /* regulator for usb otg */ reg_usb_otg_vbus: usb_otg_vbus { compatible = "regulator-fixed"; From f0ab5e2928c650a4b1e835f79eef578524c663e9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 12 Jun 2015 11:45:02 +0200 Subject: [PATCH 0726/1983] mmc: sdhci: fix low memory corruption Upstream-commit: 62a7f368ffbc13d9aedfdd7aeae711b177db69ac When dma mapping (dma_map_sg) fails in sdhci_pre_dma_transfer, -EINVAL is returned. There are 3 callers of sdhci_pre_dma_transfer: * sdhci_pre_req and sdhci_adma_table_pre: handle negative return * sdhci_prepare_data: handles 0 (error) and "else" (good) only sdhci_prepare_data is therefore broken. When it receives -EINVAL from sdhci_pre_dma_transfer, it assumes 1 sg mapping was mapped. Later, this non-existent mapping with address 0 is kmap'ped and written to: Corrupted low memory at ffff880000001000 (1000 phys) = 22b7d67df2f6d1cf Corrupted low memory at ffff880000001008 (1008 phys) = 63848a5216b7dd95 Corrupted low memory at ffff880000001010 (1010 phys) = 330eb7ddef39e427 Corrupted low memory at ffff880000001018 (1018 phys) = 8017ac7295039bda Corrupted low memory at ffff880000001020 (1020 phys) = 8ce039eac119074f ... So teach sdhci_prepare_data to understand negative return values from sdhci_pre_dma_transfer and disable DMA in that case, as well as for zero. It was introduced in 348487cb28e66b032bae1b38424d81bf5b444408 (mmc: sdhci: use pipeline mmc requests to improve performance). The commit seems to be suspicious also by assigning host->sg_count both in sdhci_pre_dma_transfer and sdhci_adma_table_pre. Signed-off-by: Jiri Slaby Cc: stable@vger.kernel.org # 4.0+ Fixes: 348487cb28e6 Cc: Ulf Hansson Cc: Haibo Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 766a8dd14b43c3..5f1a2d6f15cc09 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -823,7 +823,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) int sg_cnt; sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); - if (sg_cnt == 0) { + if (sg_cnt <= 0) { /* * This only happens when someone fed * us an invalid request. From 4306e724429f86ce206daa9c5dfd253a7741b235 Mon Sep 17 00:00:00 2001 From: David Jander Date: Tue, 23 Jun 2015 11:43:52 +0200 Subject: [PATCH 0727/1983] mmc: core: Optimize case for exactly one erase-group budget Upstream-commit: 642c28ab86f7666d2ac62a0dc391b4c3121f1d6e In the (not so unlikely) case that the mmc controller timeout budget is enough for exactly one erase-group, the simplification of allowing one sector has an enormous performance penalty. We optimize this special case by introducing a flag that prohibits erase-group boundary crossing, so that we can allow trimming more than one sector at a time. Signed-off-by: David Jander Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 38 ++++++++++++++++++++++++++++++++++---- include/linux/mmc/card.h | 1 + 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7d1a3722a53992..268a654f77787a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2049,6 +2049,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, unsigned int arg) { unsigned int rem, to = from + nr; + int err; if (!(card->host->caps & MMC_CAP_ERASE) || !(card->csd.cmdclass & CCC_ERASE)) @@ -2099,6 +2100,23 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, /* 'from' and 'to' are inclusive */ to -= 1; + /* + * Special case where only one erase-group fits in the timeout budget: + * If the region crosses an erase-group boundary on this particular + * case, we will be trimming more than one erase-group which, does not + * fit in the timeout budget of the controller, so we need to split it + * and call mmc_do_erase() twice if necessary. This special case is + * identified by the card->eg_boundary flag. + */ + if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && + (from % card->erase_size)) { + rem = card->erase_size - (from % card->erase_size); + err = mmc_do_erase(card, from, from + rem - 1, arg); + from += rem; + if ((err) || (to <= from)) + return err; + } + return mmc_do_erase(card, from, to, arg); } EXPORT_SYMBOL(mmc_erase); @@ -2193,16 +2211,28 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card, if (!qty) return 0; + /* + * When specifying a sector range to trim, chances are we might cross + * an erase-group boundary even if the amount of sectors is less than + * one erase-group. + * If we can only fit one erase-group in the controller timeout budget, + * we have to care that erase-group boundaries are not crossed by a + * single trim operation. We flag that special case with "eg_boundary". + * In all other cases we can just decrement qty and pretend that we + * always touch (qty + 1) erase-groups as a simple optimization. + */ if (qty == 1) - return 1; + card->eg_boundary = 1; + else + qty--; /* Convert qty to sectors */ if (card->erase_shift) - max_discard = --qty << card->erase_shift; + max_discard = qty << card->erase_shift; else if (mmc_card_sd(card)) - max_discard = qty; + max_discard = qty + 1; else - max_discard = --qty * card->erase_size; + max_discard = qty * card->erase_size; return max_discard; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 2e05a177a3a86e..d0573f3d8a29f9 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -282,6 +282,7 @@ struct mmc_card { unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ unsigned int pref_erase; /* in sectors */ + unsigned int eg_boundary; /* don't cross erase-group boundaries */ u8 erased_byte; /* value of erased bytes */ u32 raw_cid[4]; /* raw card CID */ From 42e2b0e70b8c2fa09b7a721bb68329e90f0e15e3 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 10 Jul 2015 11:44:03 +0800 Subject: [PATCH 0728/1983] mmc: block: add fixup of broken CMD23 for Sandisk card Some Sandisk cards(such as "SDMB-32" and "SDM032" cards) can't support CMD23, and would generate CMD timeout. So add FIX-UP for these two types Sandisk cards. Error log: mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 end_request: I/O error, dev mmcblk0, sector 0 Buffer I/O error on device mmcblk0, logical block 0 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c81dfc72724ff3..28a18815d41c83 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2357,6 +2357,10 @@ static const struct mmc_fixup blk_fixups[] = * * N.B. This doesn't affect SD cards. */ + MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BLK_NO_CMD23), MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_BLK_NO_CMD23), MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, From 062de7d370f28fe588946913bd0233e400511a4a Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Wed, 30 Sep 2015 19:27:21 +0200 Subject: [PATCH 0729/1983] Commit c9e799fd2ceb96135197ee2008c0bf56bce7a57b (Add command line switch to disable EDID ...) introduced shortcut in EDID processing by completely avoiding mxc_hdmi_read_edid(). This skips reconfiguration of hotplug event value and it's mask to valid HDMI parameters (as HDMI is assumed as default). Ppractical effect is generation of instant hotplug events if HDMI monitor is attached and turned off. --- drivers/video/mxc/mxc_hdmi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index eb26b4e1bb26f7..e1dbce67a5ac9a 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -1767,11 +1767,6 @@ static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) if (ret < 0) return HDMI_EDID_FAIL; - dev_info(&hdmi->pdev->dev, "%s reports %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap?"HDMI":"DVI"); - hdmi->hp_state = hdmi->edid_cfg.hdmi_cap?HDMI_HOTPLUG_CONNECTED_HDMI:HDMI_HOTPLUG_CONNECTED_DVI; - hdmi->plug_event = hdmi->edid_cfg.hdmi_cap?HDMI_IH_PHY_STAT0_HPD:HDMI_DVI_IH_STAT; - hdmi->plug_mask = hdmi->edid_cfg.hdmi_cap?HDMI_PHY_HPD:HDMI_DVI_STAT; - if (memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN) == 0) { dev_info(&hdmi->pdev->dev, "same edid\n"); return HDMI_EDID_SAME; @@ -2104,6 +2099,11 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) break; } + dev_info(&hdmi->pdev->dev, "%s reports %s mode\n", __func__, hdmi->edid_cfg.hdmi_cap ? "HDMI" : "DVI"); + hdmi->hp_state = hdmi->edid_cfg.hdmi_cap ? HDMI_HOTPLUG_CONNECTED_HDMI : HDMI_HOTPLUG_CONNECTED_DVI; + hdmi->plug_event = hdmi->edid_cfg.hdmi_cap ? HDMI_IH_PHY_STAT0_HPD : HDMI_DVI_IH_STAT; + hdmi->plug_mask = hdmi->edid_cfg.hdmi_cap ? HDMI_PHY_HPD : HDMI_DVI_STAT; + /* Save edid cfg for audio driver */ hdmi_set_edid_cfg(edid_status, &hdmi->edid_cfg); From a84b5a833bd7a5dbd3a2a54d0cb9e655fed63ced Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Wed, 30 Sep 2015 20:41:47 +0200 Subject: [PATCH 0730/1983] Commit e828201b66fe638ac099b5401bec4f5a0c477806 intended to assign all resolutions from fallback modelist as STNADARD. ... but modelist created in mxc_hdmi_disp_init() has lifetime only until first hotplug event. Similar to previous commit, don't limit ignore_edid=1 usability to cases when running with no display attached only. --- drivers/video/mxc/mxc_hdmi.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index e1dbce67a5ac9a..3a12f2ee4dd455 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -1962,7 +1962,7 @@ static void mxc_hdmi_default_edid_cfg(struct mxc_hdmi *hdmi) static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) { u32 i; - const struct fb_videomode *mode; + struct fb_videomode mode; dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); @@ -1976,9 +1976,12 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) /*Add all no interlaced CEA mode to default modelist */ for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { - mode = &mxc_cea_mode[i]; - if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0)) - fb_add_videomode(mode, &hdmi->fbi->modelist); + mode = mxc_cea_mode[i]; + if (!(mode.vmode & FB_VMODE_INTERLACED) && (mode.xres != 0)) { + if (ignore_edid) + mode.flag |= FB_MODE_IS_STANDARD; + fb_add_videomode(&mode, &hdmi->fbi->modelist); + } } fb_new_modelist(hdmi->fbi); From f48b11790d27416e297fa4ac5a7b9b5250a4271b Mon Sep 17 00:00:00 2001 From: "Herton R. Krzesinski" Date: Fri, 14 Aug 2015 15:35:02 -0700 Subject: [PATCH 0731/1983] ipc,sem: fix use after free on IPC_RMID after a task using same semaphore set exits commit 602b8593d2b4138c10e922eeaafe306f6b51817b upstream. The current semaphore code allows a potential use after free: in exit_sem we may free the task's sem_undo_list while there is still another task looping through the same semaphore set and cleaning the sem_undo list at freeary function (the task called IPC_RMID for the same semaphore set). For example, with a test program [1] running which keeps forking a lot of processes (which then do a semop call with SEM_UNDO flag), and with the parent right after removing the semaphore set with IPC_RMID, and a kernel built with CONFIG_SLAB, CONFIG_SLAB_DEBUG and CONFIG_DEBUG_SPINLOCK, you can easily see something like the following in the kernel log: Slab corruption (Not tainted): kmalloc-64 start=ffff88003b45c1c0, len=64 000: 6b 6b 6b 6b 6b 6b 6b 6b 00 6b 6b 6b 6b 6b 6b 6b kkkkkkkk.kkkkkkk 010: ff ff ff ff 6b 6b 6b 6b ff ff ff ff ff ff ff ff ....kkkk........ Prev obj: start=ffff88003b45c180, len=64 000: 00 00 00 00 ad 4e ad de ff ff ff ff 5a 5a 5a 5a .....N......ZZZZ 010: ff ff ff ff ff ff ff ff c0 fb 01 37 00 88 ff ff ...........7.... Next obj: start=ffff88003b45c200, len=64 000: 00 00 00 00 ad 4e ad de ff ff ff ff 5a 5a 5a 5a .....N......ZZZZ 010: ff ff ff ff ff ff ff ff 68 29 a7 3c 00 88 ff ff ........h).<.... BUG: spinlock wrong CPU on CPU#2, test/18028 general protection fault: 0000 [#1] SMP Modules linked in: 8021q mrp garp stp llc nf_conntrack_ipv4 nf_defrag_ipv4 ip6t_REJECT nf_reject_ipv6 nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables binfmt_misc ppdev input_leds joydev parport_pc parport floppy serio_raw virtio_balloon virtio_rng virtio_console virtio_net iosf_mbi crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcspkr qxl ttm drm_kms_helper drm snd_hda_codec_generic i2c_piix4 snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore crc32c_intel virtio_pci virtio_ring virtio pata_acpi ata_generic [last unloaded: speedstep_lib] CPU: 2 PID: 18028 Comm: test Not tainted 4.2.0-rc5+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.8.1-20150318_183358- 04/01/2014 RIP: spin_dump+0x53/0xc0 Call Trace: spin_bug+0x30/0x40 do_raw_spin_unlock+0x71/0xa0 _raw_spin_unlock+0xe/0x10 freeary+0x82/0x2a0 ? _raw_spin_lock+0xe/0x10 semctl_down.clone.0+0xce/0x160 ? __do_page_fault+0x19a/0x430 ? __audit_syscall_entry+0xa8/0x100 SyS_semctl+0x236/0x2c0 ? syscall_trace_leave+0xde/0x130 entry_SYSCALL_64_fastpath+0x12/0x71 Code: 8b 80 88 03 00 00 48 8d 88 60 05 00 00 48 c7 c7 a0 2c a4 81 31 c0 65 8b 15 eb 40 f3 7e e8 08 31 68 00 4d 85 e4 44 8b 4b 08 74 5e <45> 8b 84 24 88 03 00 00 49 8d 8c 24 60 05 00 00 8b 53 04 48 89 RIP [] spin_dump+0x53/0xc0 RSP ---[ end trace 783ebb76612867a0 ]--- NMI watchdog: BUG: soft lockup - CPU#3 stuck for 22s! [test:18053] Modules linked in: 8021q mrp garp stp llc nf_conntrack_ipv4 nf_defrag_ipv4 ip6t_REJECT nf_reject_ipv6 nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables binfmt_misc ppdev input_leds joydev parport_pc parport floppy serio_raw virtio_balloon virtio_rng virtio_console virtio_net iosf_mbi crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcspkr qxl ttm drm_kms_helper drm snd_hda_codec_generic i2c_piix4 snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore crc32c_intel virtio_pci virtio_ring virtio pata_acpi ata_generic [last unloaded: speedstep_lib] CPU: 3 PID: 18053 Comm: test Tainted: G D 4.2.0-rc5+ #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.8.1-20150318_183358- 04/01/2014 RIP: native_read_tsc+0x0/0x20 Call Trace: ? delay_tsc+0x40/0x70 __delay+0xf/0x20 do_raw_spin_lock+0x96/0x140 _raw_spin_lock+0xe/0x10 sem_lock_and_putref+0x11/0x70 SYSC_semtimedop+0x7bf/0x960 ? handle_mm_fault+0xbf6/0x1880 ? dequeue_task_fair+0x79/0x4a0 ? __do_page_fault+0x19a/0x430 ? kfree_debugcheck+0x16/0x40 ? __do_page_fault+0x19a/0x430 ? __audit_syscall_entry+0xa8/0x100 ? do_audit_syscall_entry+0x66/0x70 ? syscall_trace_enter_phase1+0x139/0x160 SyS_semtimedop+0xe/0x10 SyS_semop+0x10/0x20 entry_SYSCALL_64_fastpath+0x12/0x71 Code: 47 10 83 e8 01 85 c0 89 47 10 75 08 65 48 89 3d 1f 74 ff 7e c9 c3 0f 1f 44 00 00 55 48 89 e5 e8 87 17 04 00 66 90 c9 c3 0f 1f 00 <55> 48 89 e5 0f 31 89 c1 48 89 d0 48 c1 e0 20 89 c9 48 09 c8 c9 Kernel panic - not syncing: softlockup: hung tasks I wasn't able to trigger any badness on a recent kernel without the proper config debugs enabled, however I have softlockup reports on some kernel versions, in the semaphore code, which are similar as above (the scenario is seen on some servers running IBM DB2 which uses semaphore syscalls). The patch here fixes the race against freeary, by acquiring or waiting on the sem_undo_list lock as necessary (exit_sem can race with freeary, while freeary sets un->semid to -1 and removes the same sem_undo from list_proc or when it removes the last sem_undo). After the patch I'm unable to reproduce the problem using the test case [1]. [1] Test case used below: #include #include #include #include #include #include #include #include #include #define NSEM 1 #define NSET 5 int sid[NSET]; void thread() { struct sembuf op; int s; uid_t pid = getuid(); s = rand() % NSET; op.sem_num = pid % NSEM; op.sem_op = 1; op.sem_flg = SEM_UNDO; semop(sid[s], &op, 1); exit(EXIT_SUCCESS); } void create_set() { int i, j; pid_t p; union { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; } un; /* Create and initialize semaphore set */ for (i = 0; i < NSET; i++) { sid[i] = semget(IPC_PRIVATE , NSEM, 0644 | IPC_CREAT); if (sid[i] < 0) { perror("semget"); exit(EXIT_FAILURE); } } un.val = 0; for (i = 0; i < NSET; i++) { for (j = 0; j < NSEM; j++) { if (semctl(sid[i], j, SETVAL, un) < 0) perror("semctl"); } } /* Launch threads that operate on semaphore set */ for (i = 0; i < NSEM * NSET * NSET; i++) { p = fork(); if (p < 0) perror("fork"); if (p == 0) thread(); } /* Free semaphore set */ for (i = 0; i < NSET; i++) { if (semctl(sid[i], NSEM, IPC_RMID)) perror("IPC_RMID"); } /* Wait for forked processes to exit */ while (wait(NULL)) { if (errno == ECHILD) break; }; } int main(int argc, char **argv) { pid_t p; srand(time(NULL)); while (1) { p = fork(); if (p < 0) { perror("fork"); exit(EXIT_FAILURE); } if (p == 0) { create_set(); goto end; } /* Wait for forked processes to exit */ while (wait(NULL)) { if (errno == ECHILD) break; }; } end: return 0; } [akpm@linux-foundation.org: use normal comment layout] Signed-off-by: Herton R. Krzesinski Acked-by: Manfred Spraul Cc: Davidlohr Bueso Cc: Rafael Aquini CC: Aristeu Rozanski Cc: David Jeffery Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- ipc/sem.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index bee55541731207..45e1de73f4f80d 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -2055,17 +2055,28 @@ void exit_sem(struct task_struct *tsk) rcu_read_lock(); un = list_entry_rcu(ulp->list_proc.next, struct sem_undo, list_proc); - if (&un->list_proc == &ulp->list_proc) - semid = -1; - else - semid = un->semid; + if (&un->list_proc == &ulp->list_proc) { + /* + * We must wait for freeary() before freeing this ulp, + * in case we raced with last sem_undo. There is a small + * possibility where we exit while freeary() didn't + * finish unlocking sem_undo_list. + */ + spin_unlock_wait(&ulp->lock); + rcu_read_unlock(); + break; + } + spin_lock(&ulp->lock); + semid = un->semid; + spin_unlock(&ulp->lock); + /* exit_sem raced with IPC_RMID, nothing to do */ if (semid == -1) { rcu_read_unlock(); - break; + continue; } - sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, un->semid); + sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, semid); /* exit_sem raced with IPC_RMID, nothing to do */ if (IS_ERR(sma)) { rcu_read_unlock(); From 8f4fed1e07c8ac957a7f511620d80c84662cd659 Mon Sep 17 00:00:00 2001 From: Manfred Spraul Date: Fri, 14 Aug 2015 15:35:10 -0700 Subject: [PATCH 0732/1983] ipc/sem.c: update/correct memory barriers commit 3ed1f8a99d70ea1cd1508910eb107d0edcae5009 upstream. sem_lock() did not properly pair memory barriers: !spin_is_locked() and spin_unlock_wait() are both only control barriers. The code needs an acquire barrier, otherwise the cpu might perform read operations before the lock test. As no primitive exists inside and since it seems noone wants another primitive, the code creates a local primitive within ipc/sem.c. With regards to -stable: The change of sem_wait_array() is a bugfix, the change to sem_lock() is a nop (just a preprocessor redefinition to improve the readability). The bugfix is necessary for all kernels that use sem_wait_array() (i.e.: starting from 3.10). Signed-off-by: Manfred Spraul Reported-by: Oleg Nesterov Acked-by: Peter Zijlstra (Intel) Cc: "Paul E. McKenney" Cc: Kirill Tkhai Cc: Ingo Molnar Cc: Josh Poimboeuf Cc: Davidlohr Bueso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- ipc/sem.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ipc/sem.c b/ipc/sem.c index 45e1de73f4f80d..e53c96f7db42da 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -252,6 +252,16 @@ static void sem_rcu_free(struct rcu_head *head) ipc_rcu_free(head); } +/* + * spin_unlock_wait() and !spin_is_locked() are not memory barriers, they + * are only control barriers. + * The code must pair with spin_unlock(&sem->lock) or + * spin_unlock(&sem_perm.lock), thus just the control barrier is insufficient. + * + * smp_rmb() is sufficient, as writes cannot pass the control barrier. + */ +#define ipc_smp_acquire__after_spin_is_unlocked() smp_rmb() + /* * Wait until all currently ongoing simple ops have completed. * Caller must own sem_perm.lock. @@ -275,6 +285,7 @@ static void sem_wait_array(struct sem_array *sma) sem = sma->sem_base + i; spin_unlock_wait(&sem->lock); } + ipc_smp_acquire__after_spin_is_unlocked(); } /* @@ -326,8 +337,13 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops, /* Then check that the global lock is free */ if (!spin_is_locked(&sma->sem_perm.lock)) { - /* spin_is_locked() is not a memory barrier */ - smp_mb(); + /* + * We need a memory barrier with acquire semantics, + * otherwise we can race with another thread that does: + * complex_count++; + * spin_unlock(sem_perm.lock); + */ + ipc_smp_acquire__after_spin_is_unlocked(); /* Now repeat the test of complex_count: * It can't change anymore until we drop sem->lock. From 128a1d2fa7e447a1b4b465726d8552b82b13b649 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 14 Aug 2015 15:34:56 -0700 Subject: [PATCH 0733/1983] mm/hwpoison: fix page refcount of unknown non LRU page commit 4f32be677b124a49459e2603321c7a5605ceb9f8 upstream. After trying to drain pages from pagevec/pageset, we try to get reference count of the page again, however, the reference count of the page is not reduced if the page is still not on LRU list. Fix it by adding the put_page() to drop the page reference which is from __get_any_page(). Signed-off-by: Wanpeng Li Acked-by: Naoya Horiguchi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory-failure.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 9502057c3c5452..42aeb848b8e9e7 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1510,6 +1510,8 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags) */ ret = __get_any_page(page, pfn, 0); if (!PageLRU(page)) { + /* Drop page reference which is from __get_any_page() */ + put_page(page); pr_info("soft_offline: %#lx: unknown non LRU page type %lx\n", pfn, page->flags); return -EIO; From 065892dd83659650ba90870353647b722051a100 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Wed, 22 Jul 2015 14:40:09 +0800 Subject: [PATCH 0734/1983] xen-blkfront: don't add indirect pages to list when !feature_persistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7b0767502b5db11cb1f0daef2d01f6d71b1192dc upstream. We should consider info->feature_persistent when adding indirect page to list info->indirect_pages, else the BUG_ON() in blkif_free() would be triggered. When we are using persistent grants the indirect_pages list should always be empty because blkfront has pre-allocated enough persistent pages to fill all requests on the ring. Acked-by: Roger Pau Monné Signed-off-by: Bob Liu Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- drivers/block/xen-blkfront.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index efe1b4761735a7..e88556ac8318f7 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1093,8 +1093,10 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info, * Add the used indirect page back to the list of * available pages for indirect grefs. */ - indirect_page = pfn_to_page(s->indirect_grants[i]->pfn); - list_add(&indirect_page->lru, &info->indirect_pages); + if (!info->feature_persistent) { + indirect_page = pfn_to_page(s->indirect_grants[i]->pfn); + list_add(&indirect_page->lru, &info->indirect_pages); + } s->indirect_grants[i]->gref = GRANT_INVALID_REF; list_add_tail(&s->indirect_grants[i]->node, &info->grants); } From 3610c14ceb695d01c02aaac1461268d9ad1f1a8a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 11 Jun 2015 10:32:01 +0200 Subject: [PATCH 0735/1983] perf: Fix fasync handling on inherited events commit fed66e2cdd4f127a43fd11b8d92a99bdd429528c upstream. Vince reported that the fasync signal stuff doesn't work proper for inherited events. So fix that. Installing fasync allocates memory and sets filp->f_flags |= FASYNC, which upon the demise of the file descriptor ensures the allocation is freed and state is updated. Now for perf, we can have the events stick around for a while after the original FD is dead because of references from child events. So we cannot copy the fasync pointer around. We can however consistently use the parent's fasync, as that will be updated. Reported-and-Tested-by: Vince Weaver Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho deMelo Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: eranian@google.com Link: http://lkml.kernel.org/r/1434011521.1495.71.camel@twins Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 60146febb9b3ff..115cc62a371d76 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4218,12 +4218,20 @@ static const struct file_operations perf_fops = { * to user-space before waking everybody up. */ +static inline struct fasync_struct **perf_event_fasync(struct perf_event *event) +{ + /* only the parent has fasync state */ + if (event->parent) + event = event->parent; + return &event->fasync; +} + void perf_event_wakeup(struct perf_event *event) { ring_buffer_wakeup(event); if (event->pending_kill) { - kill_fasync(&event->fasync, SIGIO, event->pending_kill); + kill_fasync(perf_event_fasync(event), SIGIO, event->pending_kill); event->pending_kill = 0; } } @@ -5432,7 +5440,7 @@ static int __perf_event_overflow(struct perf_event *event, else perf_event_output(event, data, regs); - if (event->fasync && event->pending_kill) { + if (*perf_event_fasync(event) && event->pending_kill) { event->pending_wakeup = 1; irq_work_queue(&event->pending); } From a137f9cc17b175c94a1004a8f34d93165a609a3d Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 4 Aug 2015 19:22:49 +0200 Subject: [PATCH 0736/1983] perf: Fix PERF_EVENT_IOC_PERIOD migration race commit c7999c6f3fed9e383d3131474588f282ae6d56b9 upstream. I ran the perf fuzzer, which triggered some WARN()s which are due to trying to stop/restart an event on the wrong CPU. Use the normal IPI pattern to ensure we run the code on the correct CPU. Signed-off-by: Peter Zijlstra (Intel) Cc: Vince Weaver Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Fixes: bad7192b842c ("perf: Fix PERF_EVENT_IOC_PERIOD to force-reset the period") Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 75 ++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 115cc62a371d76..3bf20e36a8e761 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3562,28 +3562,21 @@ static void perf_event_for_each(struct perf_event *event, mutex_unlock(&ctx->mutex); } -static int perf_event_period(struct perf_event *event, u64 __user *arg) -{ - struct perf_event_context *ctx = event->ctx; - int ret = 0, active; +struct period_event { + struct perf_event *event; u64 value; +}; - if (!is_sampling_event(event)) - return -EINVAL; - - if (copy_from_user(&value, arg, sizeof(value))) - return -EFAULT; - - if (!value) - return -EINVAL; +static int __perf_event_period(void *info) +{ + struct period_event *pe = info; + struct perf_event *event = pe->event; + struct perf_event_context *ctx = event->ctx; + u64 value = pe->value; + bool active; - raw_spin_lock_irq(&ctx->lock); + raw_spin_lock(&ctx->lock); if (event->attr.freq) { - if (value > sysctl_perf_event_sample_rate) { - ret = -EINVAL; - goto unlock; - } - event->attr.sample_freq = value; } else { event->attr.sample_period = value; @@ -3602,11 +3595,53 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg) event->pmu->start(event, PERF_EF_RELOAD); perf_pmu_enable(ctx->pmu); } + raw_spin_unlock(&ctx->lock); -unlock: + return 0; +} + +static int perf_event_period(struct perf_event *event, u64 __user *arg) +{ + struct period_event pe = { .event = event, }; + struct perf_event_context *ctx = event->ctx; + struct task_struct *task; + u64 value; + + if (!is_sampling_event(event)) + return -EINVAL; + + if (copy_from_user(&value, arg, sizeof(value))) + return -EFAULT; + + if (!value) + return -EINVAL; + + if (event->attr.freq && value > sysctl_perf_event_sample_rate) + return -EINVAL; + + task = ctx->task; + pe.value = value; + + if (!task) { + cpu_function_call(event->cpu, __perf_event_period, &pe); + return 0; + } + +retry: + if (!task_function_call(task, __perf_event_period, &pe)) + return 0; + + raw_spin_lock_irq(&ctx->lock); + if (ctx->is_active) { + raw_spin_unlock_irq(&ctx->lock); + task = ctx->task; + goto retry; + } + + __perf_event_period(&pe); raw_spin_unlock_irq(&ctx->lock); - return ret; + return 0; } static const struct file_operations perf_fops; From 5720607557db377cd73b06a6c83adac5711b76b0 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 12 Aug 2015 15:10:21 +0100 Subject: [PATCH 0737/1983] dm thin metadata: delete btrees when releasing metadata snapshot commit 7f518ad0a212e2a6fd68630e176af1de395070a7 upstream. The device details and mapping trees were just being decremented before. Now btree_del() is called to do a deep delete. Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-thin-metadata.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index e9d33ad59df5e2..3412b86e79fd29 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1295,8 +1295,8 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) return r; disk_super = dm_block_data(copy); - dm_sm_dec_block(pmd->metadata_sm, le64_to_cpu(disk_super->data_mapping_root)); - dm_sm_dec_block(pmd->metadata_sm, le64_to_cpu(disk_super->device_details_root)); + dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root)); + dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root)); dm_sm_dec_block(pmd->metadata_sm, held_root); return dm_tm_unlock(pmd->tm, copy); From 165b973040fabe8fe1133fd59bad500c41acf340 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Mon, 27 Jul 2015 00:06:55 +0200 Subject: [PATCH 0738/1983] localmodconfig: Use Kbuild files too commit c0ddc8c745b7f89c50385fd7aa03c78dc543fa7a upstream. In kbuild it is allowed to define objects in files named "Makefile" and "Kbuild". Currently localmodconfig reads objects only from "Makefile"s and misses modules like nouveau. Link: http://lkml.kernel.org/r/1437948415-16290-1-git-send-email-richard@nod.at Reported-and-tested-by: Leonidas Spyropoulos Signed-off-by: Richard Weinberger Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- scripts/kconfig/streamline_config.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index 31331723e81074..9969feefb720ae 100644 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -137,7 +137,7 @@ sub read_config { my $kconfig = $ARGV[1]; my $lsmod_file = $ENV{'LSMOD'}; -my @makefiles = `find $ksource -name Makefile 2>/dev/null`; +my @makefiles = `find $ksource -name Makefile -or -name Kbuild 2>/dev/null`; chomp @makefiles; my %depends; From 0ddbb4a18d10385fa3da47dbb0806488d25fae45 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Tue, 21 Jul 2015 11:00:53 +0200 Subject: [PATCH 0739/1983] EDAC, ppc4xx: Access mci->csrows array elements properly commit 5c16179b550b9fd8114637a56b153c9768ea06a5 upstream. The commit de3910eb79ac ("edac: change the mem allocation scheme to make Documentation/kobject.txt happy") changed the memory allocation for the csrows member. But ppc4xx_edac was forgotten in the patch. Fix it. Signed-off-by: Michael Walle Cc: linux-edac Cc: Mauro Carvalho Chehab Link: http://lkml.kernel.org/r/1437469253-8611-1-git-send-email-michael@walle.cc Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/edac/ppc4xx_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index ef6b7e08f48568..5c361f3c66aadc 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -921,7 +921,7 @@ static int ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1) */ for (row = 0; row < mci->nr_csrows; row++) { - struct csrow_info *csi = &mci->csrows[row]; + struct csrow_info *csi = mci->csrows[row]; /* * Get the configuration settings for this From 87fcf9f8396fcc81508dd1bbc5a484df00c88d5e Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 10 Aug 2015 15:28:49 -0400 Subject: [PATCH 0740/1983] drm/radeon: add new OLAND pci id commit e037239e5e7b61007763984aa35a8329596d8c88 upstream. Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- include/drm/drm_pciids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index bb5367d288fbd9..7e9a0a6c655b31 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -172,6 +172,7 @@ {0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6617, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ From aa535a25cba0675110657fdaa54f6d20b8d93105 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 11 Aug 2015 22:31:17 -0700 Subject: [PATCH 0741/1983] drm/vmwgfx: Fix execbuf locking issues commit 3e04e2fe6d87807d27521ad6ebb9e7919d628f25 upstream. This addresses two issues that cause problems with viewperf maya-03 in situation with memory pressure. The first issue causes attempts to unreserve buffers if batched reservation fails due to, for example, a signal pending. While previously the ttm_eu api was resistant against this type of error, it is no longer and the lockdep code will complain about attempting to unreserve buffers that are not reserved. The issue is resolved by avoid calling ttm_eu_backoff_reservation in the buffer reserve error path. The second issue is that the binding_mutex may be held when user-space fence objects are created and hence during memory reclaims. This may cause recursive attempts to grab the binding mutex. The issue is resolved by not holding the binding mutex across fence creation and submission. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index a3480c13eb1b8d..9fe10d1ad2e40c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2475,7 +2475,7 @@ int vmw_execbuf_process(struct drm_file *file_priv, ret = vmw_resources_validate(sw_context); if (unlikely(ret != 0)) - goto out_err; + goto out_err_nores; if (throttle_us) { ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, @@ -2511,6 +2511,7 @@ int vmw_execbuf_process(struct drm_file *file_priv, vmw_resource_relocations_free(&sw_context->res_relocations); vmw_fifo_commit(dev_priv, command_size); + mutex_unlock(&dev_priv->binding_mutex); vmw_query_bo_switch_commit(dev_priv, sw_context); ret = vmw_execbuf_fence_commands(file_priv, dev_priv, @@ -2526,7 +2527,6 @@ int vmw_execbuf_process(struct drm_file *file_priv, DRM_ERROR("Fence submission error. Syncing.\n"); vmw_resource_list_unreserve(&sw_context->resource_list, false); - mutex_unlock(&dev_priv->binding_mutex); ttm_eu_fence_buffer_objects(&ticket, &sw_context->validate_nodes, (void *) fence); From 79521e4bf2409bb5670fe6e0a6cbea6afa0128d5 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 5 Jun 2015 14:20:46 -0700 Subject: [PATCH 0742/1983] libfc: Fix fc_exch_recv_req() error path commit f6979adeaab578f8ca14fdd32b06ddee0d9d3314 upstream. Due to patch "libfc: Do not invoke the response handler after fc_exch_done()" (commit ID 7030fd62) the lport_recv() call in fc_exch_recv_req() is passed a dangling pointer. Avoid this by moving the fc_frame_free() call from fc_invoke_resp() to its callers. This patch fixes the following crash: general protection fault: 0000 [#3] PREEMPT SMP RIP: fc_lport_recv_req+0x72/0x280 [libfc] Call Trace: fc_exch_recv+0x642/0xde0 [libfc] fcoe_percpu_receive_thread+0x46a/0x5ed [fcoe] kthread+0x10a/0x120 ret_from_fork+0x42/0x70 Signed-off-by: Bart Van Assche Signed-off-by: Vasu Dev Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/libfc/fc_exch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 1b3a0947345228..30f9ef0c0d4f8c 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -733,8 +733,6 @@ static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp, if (resp) { resp(sp, fp, arg); res = true; - } else if (!IS_ERR(fp)) { - fc_frame_free(fp); } spin_lock_bh(&ep->ex_lock); @@ -1596,7 +1594,8 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) * If new exch resp handler is valid then call that * first. */ - fc_invoke_resp(ep, sp, fp); + if (!fc_invoke_resp(ep, sp, fp)) + fc_frame_free(fp); fc_exch_release(ep); return; @@ -1695,7 +1694,8 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) fc_exch_hold(ep); if (!rc) fc_exch_delete(ep); - fc_invoke_resp(ep, sp, fp); + if (!fc_invoke_resp(ep, sp, fp)) + fc_frame_free(fp); if (has_rec) fc_exch_timer_set(ep, ep->r_a_tov); fc_exch_release(ep); From ac273c7d6fe897747bb6f52e99ab86a2fcf34426 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 5 Jun 2015 14:20:51 -0700 Subject: [PATCH 0743/1983] libfc: Fix fc_fcp_cleanup_each_cmd() commit 8f2777f53e3d5ad8ef2a176a4463a5c8e1a16431 upstream. Since fc_fcp_cleanup_cmd() can sleep this function must not be called while holding a spinlock. This patch avoids that fc_fcp_cleanup_each_cmd() triggers the following bug: BUG: scheduling while atomic: sg_reset/1512/0x00000202 1 lock held by sg_reset/1512: #0: (&(&fsp->scsi_pkt_lock)->rlock){+.-...}, at: [] fc_fcp_cleanup_each_cmd.isra.21+0xa5/0x150 [libfc] Preemption disabled at:[] fc_fcp_cleanup_each_cmd.isra.21+0xa5/0x150 [libfc] Call Trace: [] dump_stack+0x4f/0x7b [] __schedule_bug+0x6c/0xd0 [] __schedule+0x71a/0xa10 [] schedule+0x32/0x80 [] fc_seq_set_resp+0xac/0x100 [libfc] [] fc_exch_done+0x41/0x60 [libfc] [] fc_fcp_cleanup_each_cmd.isra.21+0xcf/0x150 [libfc] [] fc_eh_device_reset+0x1c3/0x270 [libfc] [] scsi_try_bus_device_reset+0x29/0x60 [] scsi_ioctl_reset+0x258/0x2d0 [] scsi_ioctl+0x150/0x440 [] sd_ioctl+0xad/0x120 [] blkdev_ioctl+0x1b6/0x810 [] block_ioctl+0x38/0x40 [] do_vfs_ioctl+0x2f8/0x530 [] SyS_ioctl+0x81/0xa0 [] system_call_fastpath+0x16/0x7a Signed-off-by: Bart Van Assche Signed-off-by: Vasu Dev Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/libfc/fc_fcp.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 1d7e76e8b447c7..ae6fc1a9456867 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -1039,11 +1039,26 @@ static void fc_fcp_cleanup_each_cmd(struct fc_lport *lport, unsigned int id, fc_fcp_pkt_hold(fsp); spin_unlock_irqrestore(&si->scsi_queue_lock, flags); - if (!fc_fcp_lock_pkt(fsp)) { + spin_lock_bh(&fsp->scsi_pkt_lock); + if (!(fsp->state & FC_SRB_COMPL)) { + fsp->state |= FC_SRB_COMPL; + /* + * TODO: dropping scsi_pkt_lock and then reacquiring + * again around fc_fcp_cleanup_cmd() is required, + * since fc_fcp_cleanup_cmd() calls into + * fc_seq_set_resp() and that func preempts cpu using + * schedule. May be schedule and related code should be + * removed instead of unlocking here to avoid scheduling + * while atomic bug. + */ + spin_unlock_bh(&fsp->scsi_pkt_lock); + fc_fcp_cleanup_cmd(fsp, error); + + spin_lock_bh(&fsp->scsi_pkt_lock); fc_io_compl(fsp); - fc_fcp_unlock_pkt(fsp); } + spin_unlock_bh(&fsp->scsi_pkt_lock); fc_fcp_pkt_release(fsp); spin_lock_irqsave(&si->scsi_queue_lock, flags); From 2cb31bbf684e17ca4a1a2b18e43052fe08661e89 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 26 Jul 2015 21:34:50 -0700 Subject: [PATCH 0744/1983] regmap: regcache-rbtree: Clean new present bits on present bitmap resize commit 8ef9724bf9718af81cfc5132253372f79c71b7e2 upstream. When inserting a new register into a block, the present bit map size is increased using krealloc. krealloc does not clear the additionally allocated memory, leaving it filled with random values. Result is that some registers are considered cached even though this is not the case. Fix the problem by clearing the additionally allocated memory. Also, if the bitmap size does not increase, do not reallocate the bitmap at all to reduce overhead. Fixes: 3f4ff561bc88 ("regmap: rbtree: Make cache_present bitmap per node") Signed-off-by: Guenter Roeck Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/base/regmap/regcache-rbtree.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 2b946bc4212dfb..f3f71369adc727 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -302,11 +302,20 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, if (!blk) return -ENOMEM; - present = krealloc(rbnode->cache_present, - BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL); - if (!present) { - kfree(blk); - return -ENOMEM; + if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) { + present = krealloc(rbnode->cache_present, + BITS_TO_LONGS(blklen) * sizeof(*present), + GFP_KERNEL); + if (!present) { + kfree(blk); + return -ENOMEM; + } + + memset(present + BITS_TO_LONGS(rbnode->blklen), 0, + (BITS_TO_LONGS(blklen) - BITS_TO_LONGS(rbnode->blklen)) + * sizeof(*present)); + } else { + present = rbnode->cache_present; } /* insert the register value in the correct place in the rbnode block */ From 197132781432f715d9dedaa268965e52b9f5a9a6 Mon Sep 17 00:00:00 2001 From: Yann Droneaud Date: Mon, 17 Nov 2014 23:02:19 +0000 Subject: [PATCH 0745/1983] arm64/mm: Remove hack in mmap randomize layout commit d6c763afab142a85e4770b4bc2a5f40f256d5c5d upstream. Since commit 8a0a9bd4db63 ('random: make get_random_int() more random'), get_random_int() returns a random value for each call, so comment and hack introduced in mmap_rnd() as part of commit 1d18c47c735e ('arm64: MMU fault handling and page table management') are incorrects. Commit 1d18c47c735e seems to use the same hack introduced by commit a5adc91a4b44 ('powerpc: Ensure random space between stack and mmaps'), latter copied in commit 5a0efea09f42 ('sparc64: Sharpen address space randomization calculations.'). But both architectures were cleaned up as part of commit fa8cbaaf5a68 ('powerpc+sparc64/mm: Remove hack in mmap randomize layout') as hack is no more needed since commit 8a0a9bd4db63. So the present patch removes the comment and the hack around get_random_int() on AArch64's mmap_rnd(). Cc: David S. Miller Cc: Anton Blanchard Cc: Benjamin Herrenschmidt Acked-by: Will Deacon Acked-by: Dan McGee Signed-off-by: Yann Droneaud Signed-off-by: Will Deacon Cc: Matthias Brugger Signed-off-by: Greg Kroah-Hartman --- arch/arm64/mm/mmap.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c index 8ed6cb1a900f29..8f7ffffc63e96d 100644 --- a/arch/arm64/mm/mmap.c +++ b/arch/arm64/mm/mmap.c @@ -47,22 +47,14 @@ static int mmap_is_legacy(void) return sysctl_legacy_va_layout; } -/* - * Since get_random_int() returns the same value within a 1 jiffy window, we - * will almost always get the same randomisation for the stack and mmap - * region. This will mean the relative distance between stack and mmap will be - * the same. - * - * To avoid this we can shift the randomness by 1 bit. - */ static unsigned long mmap_rnd(void) { unsigned long rnd = 0; if (current->flags & PF_RANDOMIZE) - rnd = (long)get_random_int() & (STACK_RND_MASK >> 1); + rnd = (long)get_random_int() & STACK_RND_MASK; - return rnd << (PAGE_SHIFT + 1); + return rnd << PAGE_SHIFT; } static unsigned long mmap_base(void) From 0a299721fb64575f7ff1f699115c0dd78fa998cd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 17 Aug 2015 11:02:42 -0400 Subject: [PATCH 0746/1983] SCSI: Fix NULL pointer dereference in runtime PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 49718f0fb8c9af192b33d8af3a2826db04025371 upstream. The routines in scsi_rpm.c assume that if a runtime-PM callback is invoked for a SCSI device, it can only mean that the device's driver has asked the block layer to handle the runtime power management (by calling blk_pm_runtime_init(), which among other things sets q->dev). However, this assumption turns out to be wrong for things like the ses driver. Normally ses devices are not allowed to do runtime PM, but userspace can override this setting. If this happens, the kernel gets a NULL pointer dereference when blk_post_runtime_resume() tries to use the uninitialized q->dev pointer. This patch fixes the problem by calling the block layer's runtime-PM routines only if the device's driver really does have a runtime-PM callback routine. Since ses doesn't define any such callbacks, the crash won't occur. This fixes Bugzilla #101371. Signed-off-by: Alan Stern Reported-by: Stanisław Pitucha Reported-by: Ilan Cohen Tested-by: Ilan Cohen Reviewed-by: Johannes Thumshirn Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_pm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index 001e9ceda4c3b5..a59be67b92d512 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -149,15 +149,15 @@ static int sdev_runtime_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; struct scsi_device *sdev = to_scsi_device(dev); - int err; + int err = 0; - err = blk_pre_runtime_suspend(sdev->request_queue); - if (err) - return err; - if (pm && pm->runtime_suspend) + if (pm && pm->runtime_suspend) { + err = blk_pre_runtime_suspend(sdev->request_queue); + if (err) + return err; err = pm->runtime_suspend(dev); - blk_post_runtime_suspend(sdev->request_queue, err); - + blk_post_runtime_suspend(sdev->request_queue, err); + } return err; } @@ -180,11 +180,11 @@ static int sdev_runtime_resume(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int err = 0; - blk_pre_runtime_resume(sdev->request_queue); - if (pm && pm->runtime_resume) + if (pm && pm->runtime_resume) { + blk_pre_runtime_resume(sdev->request_queue); err = pm->runtime_resume(dev); - blk_post_runtime_resume(sdev->request_queue, err); - + blk_post_runtime_resume(sdev->request_queue, err); + } return err; } From 749adf2a7ec04d7f69deb91614640b2900b1c8ab Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 27 Aug 2015 16:10:01 +0100 Subject: [PATCH 0747/1983] arm64: KVM: Fix host crash when injecting a fault into a 32bit guest commit 126c69a0bd0e441bf6766a5d9bf20de011be9f68 upstream. When injecting a fault into a misbehaving 32bit guest, it seems rather idiotic to also inject a 64bit fault that is only going to corrupt the guest state. This leads to a situation where we perform an illegal exception return at EL2 causing the host to crash instead of killing the guest. Just fix the stupid bug that has been there from day 1. Reported-by: Russell King Tested-by: Russell King Signed-off-by: Marc Zyngier Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/inject_fault.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 81a02a8762b054..86825f8883ded5 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -168,8 +168,8 @@ void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) { if (!(vcpu->arch.hcr_el2 & HCR_RW)) inject_abt32(vcpu, false, addr); - - inject_abt64(vcpu, false, addr); + else + inject_abt64(vcpu, false, addr); } /** @@ -184,8 +184,8 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) { if (!(vcpu->arch.hcr_el2 & HCR_RW)) inject_abt32(vcpu, true, addr); - - inject_abt64(vcpu, true, addr); + else + inject_abt64(vcpu, true, addr); } /** @@ -198,6 +198,6 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu) { if (!(vcpu->arch.hcr_el2 & HCR_RW)) inject_undef32(vcpu); - - inject_undef64(vcpu); + else + inject_undef64(vcpu); } From 99d9825cb9974ac27e84e1b66d012f70cce3effc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 13 Sep 2015 09:11:03 -0700 Subject: [PATCH 0748/1983] Linux 3.14.52 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 83275d8ed8803a..3a5d4316c4c7cc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 14 -SUBLEVEL = 51 +SUBLEVEL = 52 EXTRAVERSION = NAME = Remembering Coco From ba6ee3156288a2a1d071fbd220760a228657965c Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 17 Feb 2015 08:11:51 -0800 Subject: [PATCH 0749/1983] input: goodix: add device-tree support Signed-off-by: Tim Harvey --- drivers/input/touchscreen/Kconfig | 13 + drivers/input/touchscreen/goodix.c | 411 +++++++++++++++++++++++++++++ 2 files changed, 424 insertions(+) create mode 100644 drivers/input/touchscreen/goodix.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1f2f1503f23471..c19fd4528abcfd 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -290,6 +290,19 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_GOODIX + tristate "Goodix I2C touchscreen" + depends on I2C + help + Say Y here if you have the Goodix touchscreen (such as one + installed in Onda v975w tablets) connected to your + system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix. + config TOUCHSCREEN_ILI210X tristate "Ilitek ILI210X based touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c new file mode 100644 index 00000000000000..b75a7a8ff8abf4 --- /dev/null +++ b/drivers/input/touchscreen/goodix.c @@ -0,0 +1,411 @@ +/* + * Driver for Goodix Touchscreens + * + * Copyright (c) 2014 Red Hat Inc. + * + * This code is based on gt9xx.c authored by andrew@goodix.com: + * + * 2010 - 2012 Goodix Technology. + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct goodix_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + int abs_x_max; + int abs_y_max; + unsigned int max_touch_num; + unsigned int int_trigger_type; +}; + +#define GOODIX_MAX_HEIGHT 4096 +#define GOODIX_MAX_WIDTH 4096 +#define GOODIX_INT_TRIGGER 1 +#define GOODIX_CONTACT_SIZE 8 +#define GOODIX_MAX_CONTACTS 10 + +#define GOODIX_CONFIG_MAX_LENGTH 240 + +/* Register defines */ +#define GOODIX_READ_COOR_ADDR 0x814E +#define GOODIX_REG_CONFIG_DATA 0x8047 +#define GOODIX_REG_VERSION 0x8140 + +#define RESOLUTION_LOC 1 +#define TRIGGER_LOC 6 + +static const unsigned long goodix_irq_flags[] = { + IRQ_TYPE_EDGE_RISING, + IRQ_TYPE_EDGE_FALLING, + IRQ_TYPE_LEVEL_LOW, + IRQ_TYPE_LEVEL_HIGH, +}; + +/** + * goodix_i2c_read - read data from a register of the i2c slave device. + * + * @client: i2c device. + * @reg: the register to read from. + * @buf: raw write data buffer. + * @len: length of the buffer to write + */ +static int goodix_i2c_read(struct i2c_client *client, + u16 reg, u8 *buf, int len) +{ + struct i2c_msg msgs[2]; + u16 wbuf = cpu_to_be16(reg); + int ret; + + msgs[0].flags = 0; + msgs[0].addr = client->addr; + msgs[0].len = 2; + msgs[0].buf = (u8 *) &wbuf; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = client->addr; + msgs[1].len = len; + msgs[1].buf = buf; + + ret = i2c_transfer(client->adapter, msgs, 2); + return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0); +} + +static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) +{ + int touch_num; + int error; + + error = goodix_i2c_read(ts->client, GOODIX_READ_COOR_ADDR, data, + GOODIX_CONTACT_SIZE + 1); + if (error) { + dev_err(&ts->client->dev, "I2C transfer error: %d\n", error); + return error; + } + + touch_num = data[0] & 0x0f; + if (touch_num > GOODIX_MAX_CONTACTS) + return -EPROTO; + + if (touch_num > 1) { + data += 1 + GOODIX_CONTACT_SIZE; + error = goodix_i2c_read(ts->client, + GOODIX_READ_COOR_ADDR + + 1 + GOODIX_CONTACT_SIZE, + data, + GOODIX_CONTACT_SIZE * (touch_num - 1)); + if (error) + return error; + } + + return touch_num; +} + +static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) +{ + int id = coor_data[0] & 0x0F; + int input_x = get_unaligned_le16(&coor_data[1]); + int input_y = get_unaligned_le16(&coor_data[3]); + int input_w = get_unaligned_le16(&coor_data[5]); + + input_mt_slot(ts->input_dev, id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); +} + +/** + * goodix_process_events - Process incoming events + * + * @ts: our goodix_ts_data pointer + * + * Called when the IRQ is triggered. Read the current device state, and push + * the input events to the user space. + */ +static void goodix_process_events(struct goodix_ts_data *ts) +{ + u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; + int touch_num; + int i; + + touch_num = goodix_ts_read_input_report(ts, point_data); + if (touch_num < 0) + return; + + for (i = 0; i < touch_num; i++) + goodix_ts_report_touch(ts, + &point_data[1 + GOODIX_CONTACT_SIZE * i]); + + input_mt_sync_frame(ts->input_dev); + input_sync(ts->input_dev); +} + +/** + * goodix_ts_irq_handler - The IRQ handler + * + * @irq: interrupt number. + * @dev_id: private data pointer. + */ +static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id) +{ + static const u8 end_cmd[] = { + GOODIX_READ_COOR_ADDR >> 8, + GOODIX_READ_COOR_ADDR & 0xff, + 0 + }; + struct goodix_ts_data *ts = dev_id; + + goodix_process_events(ts); + + if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0) + dev_err(&ts->client->dev, "I2C write end_cmd error\n"); + + return IRQ_HANDLED; +} + +/** + * goodix_read_config - Read the embedded configuration of the panel + * + * @ts: our goodix_ts_data pointer + * + * Must be called during probe + */ +static void goodix_read_config(struct goodix_ts_data *ts) +{ + u8 config[GOODIX_CONFIG_MAX_LENGTH]; + int error; + + error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA, + config, + GOODIX_CONFIG_MAX_LENGTH); + if (error) { + dev_warn(&ts->client->dev, + "Error reading config (%d), using defaults\n", + error); + ts->abs_x_max = GOODIX_MAX_WIDTH; + ts->abs_y_max = GOODIX_MAX_HEIGHT; + ts->int_trigger_type = GOODIX_INT_TRIGGER; + return; + } + + ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); + ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; + if (!ts->abs_x_max || !ts->abs_y_max) { + dev_err(&ts->client->dev, + "Invalid config, using defaults\n"); + ts->abs_x_max = GOODIX_MAX_WIDTH; + ts->abs_y_max = GOODIX_MAX_HEIGHT; + } +} + + +/** + * goodix_read_version - Read goodix touchscreen version + * + * @client: the i2c client + * @version: output buffer containing the version on success + */ +static int goodix_read_version(struct i2c_client *client, u16 *version) +{ + int error; + u8 buf[6]; + + error = goodix_i2c_read(client, GOODIX_REG_VERSION, buf, sizeof(buf)); + if (error) { + dev_err(&client->dev, "read version failed: %d\n", error); + return error; + } + + if (version) + *version = get_unaligned_le16(&buf[4]); + + dev_info(&client->dev, "IC VERSION: %6ph\n", buf); + + return 0; +} + +/** + * goodix_i2c_test - I2C test function to check if the device answers. + * + * @client: the i2c client + */ +static int goodix_i2c_test(struct i2c_client *client) +{ + int retry = 0; + int error; + u8 test; + + while (retry++ < 2) { + error = goodix_i2c_read(client, GOODIX_REG_CONFIG_DATA, + &test, 1); + if (!error) + return 0; + + dev_err(&client->dev, "i2c test failed attempt %d: %d\n", + retry, error); + msleep(20); + } + + return error; +} + +/** + * goodix_request_input_dev - Allocate, populate and register the input device + * + * @ts: our goodix_ts_data pointer + * + * Must be called during probe + */ +static int goodix_request_input_dev(struct goodix_ts_data *ts) +{ + int error; + + ts->input_dev = devm_input_allocate_device(&ts->client->dev); + if (!ts->input_dev) { + dev_err(&ts->client->dev, "Failed to allocate input device."); + return -ENOMEM; + } + + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | + BIT_MASK(EV_KEY) | + BIT_MASK(EV_ABS); + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, + ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, + ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + + ts->input_dev->name = "Goodix Capacitive TouchScreen"; + ts->input_dev->phys = "input/ts"; + ts->input_dev->id.bustype = BUS_I2C; + ts->input_dev->id.vendor = 0x0416; + ts->input_dev->id.product = 0x1001; + ts->input_dev->id.version = 10427; + + error = input_register_device(ts->input_dev); + if (error) { + dev_err(&ts->client->dev, + "Failed to register input device: %d", error); + return error; + } + + return 0; +} + +static int goodix_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct goodix_ts_data *ts; + unsigned long irq_flags; + int error; + u16 version_info; + + dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C check functionality failed.\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->client = client; + i2c_set_clientdata(client, ts); + + error = goodix_i2c_test(client); + if (error) { + dev_err(&client->dev, "I2C communication failure: %d\n", error); + return error; + } + + error = goodix_read_version(client, &version_info); + if (error) { + dev_err(&client->dev, "Read version failed.\n"); + return error; + } + + goodix_read_config(ts); + + error = goodix_request_input_dev(ts); + if (error) + return error; + + irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT; + error = devm_request_threaded_irq(&ts->client->dev, client->irq, + NULL, goodix_ts_irq_handler, + irq_flags, client->name, ts); + if (error) { + dev_err(&client->dev, "request IRQ failed: %d\n", error); + return error; + } + + return 0; +} + +static const struct i2c_device_id goodix_ts_id[] = { + { "GDIX1001:00", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, goodix_ts_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id goodix_acpi_match[] = { + { "GDIX1001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id goodix_of_match[] = { + { .compatible = "gdx,gt9xx", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, goodix_of_match); +#endif + +static struct i2c_driver goodix_ts_driver = { + .probe = goodix_ts_probe, + .id_table = goodix_ts_id, + .driver = { + .name = "Goodix-TS", + .owner = THIS_MODULE, +#ifdef CONFIG_ACPI + .acpi_match_table = goodix_acpi_match, +#endif +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(goodix_of_match), +#endif + }, +}; +module_i2c_driver(goodix_ts_driver); + +MODULE_AUTHOR("Benjamin Tissoires "); +MODULE_AUTHOR("Bastien Nocera "); +MODULE_DESCRIPTION("Goodix touchscreen driver"); +MODULE_LICENSE("GPL v2"); From 594d2eaf08d7a05eb49655b8c7e02d547d508ee6 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 19 Feb 2015 15:48:41 -0800 Subject: [PATCH 0750/1983] input: goodix: add support for performing init sqequence The Goodix chips require that the INT signal be driven low for 50ms after a 5ms sampling period when coming out of reset. Signed-off-by: Tim Harvey --- drivers/input/touchscreen/goodix.c | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index b75a7a8ff8abf4..b6942211a7585c 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include struct goodix_ts_data { @@ -322,6 +324,10 @@ static int goodix_ts_probe(struct i2c_client *client, unsigned long irq_flags; int error; u16 version_info; +#ifdef CONFIG_OF + int reset_pin; + int int_pin; +#endif dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -334,6 +340,49 @@ static int goodix_ts_probe(struct i2c_client *client, if (!ts) return -ENOMEM; +#ifdef CONFIG_OF + /* + * The GT9110 requires that INT be driven low by the host for 50ms + * after sampling the state of INT to determine its slave address. + * If reset-gpio and int-gpio are provided in the device-tree, perform + * this init sequence. + */ + reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpio", 0); + int_pin = of_get_named_gpio(client->dev.of_node, "int-gpio", 0); + if (gpio_is_valid(reset_pin) && gpio_is_valid(int_pin)) { + int int_val = (client->addr == 0x14) ? + GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + + error = devm_gpio_request_one(&client->dev, + reset_pin, GPIOF_OUT_INIT_LOW, + "gtxx reset"); + if (error) { + dev_err(&client->dev, + "Failed to request gpio%d reset pin: %d\n", + reset_pin, error); + return error; + } + error = devm_gpio_request_one(&client->dev, + int_pin, int_val, "gtxx int"); + if (error) { + dev_err(&client->dev, + "Failed to request gpio%d int pin: %d\n", + int_pin, error); + return error; + } + + msleep(5); + gpio_set_value(reset_pin, 1); + msleep(5); + gpio_set_value(int_pin, 0); + msleep(50); + gpio_direction_input(int_pin); + + dev_info(&client->dev, "Performed init sequence using " + "gpio%d/gpio%d as RST#/INT\n", reset_pin, int_pin); + } +#endif + ts->client = client; i2c_set_clientdata(client, ts); From abb23e7a2fba28dbc85f3b7dff651b68f3ee8e8d Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Fri, 20 Feb 2015 11:02:02 -0800 Subject: [PATCH 0751/1983] input: goodix: add invert and rotate support via device-tree When a LVDS display is bonded to a touch sensor and touch controller it can be rotated either 90deg, 180deg, or 270deg such that the display origin does not match the origin of the touch sensor. Allowing an invert and rotate property allows for supporting these orientations. Signed-off-by: Tim Harvey --- drivers/input/touchscreen/goodix.c | 34 ++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index b6942211a7585c..2daab14a4e6788 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -34,6 +34,8 @@ struct goodix_ts_data { int abs_y_max; unsigned int max_touch_num; unsigned int int_trigger_type; + bool invert; + bool rotate; }; #define GOODIX_MAX_HEIGHT 4096 @@ -127,8 +129,17 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) input_mt_slot(ts->input_dev, id); input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); - input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + if (ts->invert) { + input_x = ts->abs_x_max - input_x; + input_y = ts->abs_y_max - input_y; + } + if (ts->rotate) { + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_y); + } else { + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + } input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w); } @@ -207,8 +218,13 @@ static void goodix_read_config(struct goodix_ts_data *ts) return; } - ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); - ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + if (ts->rotate) { + ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC]); + } else { + ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); + ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); + } ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; if (!ts->abs_x_max || !ts->abs_y_max) { dev_err(&ts->client->dev, @@ -327,6 +343,7 @@ static int goodix_ts_probe(struct i2c_client *client, #ifdef CONFIG_OF int reset_pin; int int_pin; + struct device_node *np = client->dev.of_node; #endif dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr); @@ -347,8 +364,8 @@ static int goodix_ts_probe(struct i2c_client *client, * If reset-gpio and int-gpio are provided in the device-tree, perform * this init sequence. */ - reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpio", 0); - int_pin = of_get_named_gpio(client->dev.of_node, "int-gpio", 0); + reset_pin = of_get_named_gpio(np, "reset-gpio", 0); + int_pin = of_get_named_gpio(np, "int-gpio", 0); if (gpio_is_valid(reset_pin) && gpio_is_valid(int_pin)) { int int_val = (client->addr == 0x14) ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; @@ -381,6 +398,11 @@ static int goodix_ts_probe(struct i2c_client *client, dev_info(&client->dev, "Performed init sequence using " "gpio%d/gpio%d as RST#/INT\n", reset_pin, int_pin); } + + if (of_property_read_bool(np, "invert")) + ts->invert = true; + if (of_property_read_bool(np, "rotate")) + ts->rotate = true; #endif ts->client = client; From 1cf6ca0f0be50afb22bab721787d0fffea3a6480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:23:02 -0700 Subject: [PATCH 0752/1983] Input: edt-ft5x06 - add DT support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lothar Waßmann Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/edt-ft5x06.txt | 55 +++++++ drivers/input/touchscreen/edt-ft5x06.c | 145 ++++++++++++++---- 2 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt new file mode 100644 index 00000000000000..76db96704a602e --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -0,0 +1,55 @@ +FocalTech EDT-FT5x06 Polytouch driver +===================================== + +There are 3 variants of the chip for various touch panel sizes +FT5206GE1 2.8" .. 3.8" +FT5306DE4 4.3" .. 7" +FT5406EE8 7" .. 8.9" + +The software interface is identical for all those chips, so that +currently there is no need for the driver to distinguish between the +different chips. Nevertheless distinct compatible strings are used so +that a distinction can be added if necessary without changing the DT +bindings. + + +Required properties: + - compatible: "edt,edt-ft5206" + or: "edt,edt-ft5306" + or: "edt,edt-ft5406" + + - reg: I2C slave address of the chip (0x38) + - interrupt-parent: a phandle pointing to the interrupt controller + serving the interrupt for this chip + - interrupts: interrupt specification for the touchdetect + interrupt + +Optional properties: + - reset-gpios: GPIO specification for the RESET input + - wake-gpios: GPIO specification for the WAKE input + + - pinctrl-names: should be "default" + - pinctrl-0: a phandle pointing to the pin settings for the + control gpios + + - threshold: allows setting the "click"-threshold in the range + from 20 to 80. + + - gain: allows setting the sensitivity in the range from 0 to + 31. Note that lower values indicate higher + sensitivity. + + - offset: allows setting the edge compensation in the range from + 0 to 31. + +Example: + polytouch: edt-ft5x06@38 { + compatible = "edt,edt-ft5406", "edt,edt-ft5x06"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5x06_pins>; + interrupt-parent = <&gpio2>; + interrupts = <5 0>; + reset-gpios = <&gpio2 6 1>; + wake-gpios = <&gpio4 9 0>; + }; diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 412a85ec9ba5a9..f9b7080495da7c 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Simon Budig, + * Lothar Waßmann (DT support) * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -65,6 +67,10 @@ struct edt_ft5x06_ts_data { u16 num_x; u16 num_y; + int reset_pin; + int irq_pin; + int wake_pin; + #if defined(CONFIG_DEBUG_FS) struct dentry *debug_dir; u8 *raw_buffer; @@ -617,24 +623,38 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) static int edt_ft5x06_ts_reset(struct i2c_client *client, - int reset_pin) + struct edt_ft5x06_ts_data *tsdata) { int error; - if (gpio_is_valid(reset_pin)) { + if (gpio_is_valid(tsdata->wake_pin)) { + error = devm_gpio_request_one(&client->dev, + tsdata->wake_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 wake"); + if (error) { + dev_err(&client->dev, + "Failed to request GPIO %d as wake pin, error %d\n", + tsdata->wake_pin, error); + return error; + } + + mdelay(5); + gpio_set_value(tsdata->wake_pin, 1); + } + if (gpio_is_valid(tsdata->reset_pin)) { /* this pulls reset down, enabling the low active reset */ - error = devm_gpio_request_one(&client->dev, reset_pin, - GPIOF_OUT_INIT_LOW, - "edt-ft5x06 reset"); + error = devm_gpio_request_one(&client->dev, + tsdata->reset_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 reset"); if (error) { dev_err(&client->dev, "Failed to request GPIO %d as reset pin, error %d\n", - reset_pin, error); + tsdata->reset_pin, error); return error; } mdelay(50); - gpio_set_value(reset_pin, 1); + gpio_set_value(tsdata->reset_pin, 1); mdelay(100); } @@ -675,6 +695,20 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, pdata->name <= edt_ft5x06_attr_##name.limit_high) \ edt_ft5x06_register_write(tsdata, reg, pdata->name) +#define EDT_GET_PROP(name, reg) { \ + u32 val; \ + if (of_property_read_u32(np, #name, &val) == 0) \ + edt_ft5x06_register_write(tsdata, reg, val); \ +} + +static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np, + struct edt_ft5x06_ts_data *tsdata) +{ + EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD); + EDT_GET_PROP(gain, WORK_REGISTER_GAIN); + EDT_GET_PROP(offset, WORK_REGISTER_OFFSET); +} + static void edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, const struct edt_ft5x06_platform_data *pdata) @@ -702,6 +736,30 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); } +#ifdef CONFIG_OF +static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) +{ + struct device_node *np = dev->of_node; + + /* + * irq_pin is not needed for DT setup. + * irq is associated via 'interrupts' property in DT + */ + tsdata->irq_pin = -EINVAL; + tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); + tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0); + + return 0; +} +#else +static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) +{ + return -ENODEV; +} +#endif + static int edt_ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -714,32 +772,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); + if (!tsdata) { + dev_err(&client->dev, "failed to allocate driver data.\n"); + return -ENOMEM; + } + if (!pdata) { - dev_err(&client->dev, "no platform data?\n"); - return -EINVAL; + error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata); + if (error) { + dev_err(&client->dev, + "DT probe failed and no platform data present\n"); + return error; + } + } else { + tsdata->reset_pin = pdata->reset_pin; + tsdata->irq_pin = pdata->irq_pin; + tsdata->wake_pin = -EINVAL; } - error = edt_ft5x06_ts_reset(client, pdata->reset_pin); + error = edt_ft5x06_ts_reset(client, tsdata); if (error) return error; - if (gpio_is_valid(pdata->irq_pin)) { - error = devm_gpio_request_one(&client->dev, pdata->irq_pin, - GPIOF_IN, "edt-ft5x06 irq"); + if (gpio_is_valid(tsdata->irq_pin)) { + error = devm_gpio_request_one(&client->dev, tsdata->irq_pin, + GPIOF_IN, "edt-ft5x06 irq"); if (error) { dev_err(&client->dev, "Failed to request GPIO %d, error %d\n", - pdata->irq_pin, error); + tsdata->irq_pin, error); return error; } } - tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); - if (!tsdata) { - dev_err(&client->dev, "failed to allocate driver data.\n"); - return -ENOMEM; - } - input = devm_input_allocate_device(&client->dev); if (!input) { dev_err(&client->dev, "failed to allocate input device.\n"); @@ -757,7 +823,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } - edt_ft5x06_ts_get_defaults(tsdata, pdata); + if (!pdata) + edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata); + else + edt_ft5x06_ts_get_defaults(tsdata, pdata); + edt_ft5x06_ts_get_parameters(tsdata); dev_dbg(&client->dev, @@ -787,10 +857,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, edt_ft5x06_ts_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->name, tsdata); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + edt_ft5x06_ts_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, tsdata); if (error) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); return error; @@ -801,19 +871,21 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; error = input_register_device(input); - if (error) { - sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); - return error; - } + if (error) + goto err_remove_attrs; edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); device_init_wakeup(&client->dev, 1); dev_dbg(&client->dev, - "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", - pdata->irq_pin, pdata->reset_pin); + "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", + client->irq, tsdata->wake_pin, tsdata->reset_pin); return 0; + +err_remove_attrs: + sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); + return error; } static int edt_ft5x06_ts_remove(struct i2c_client *client) @@ -857,10 +929,21 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = { }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); +#ifdef CONFIG_OF +static const struct of_device_id edt_ft5x06_of_match[] = { + { .compatible = "edt,edt-ft5206", }, + { .compatible = "edt,edt-ft5306", }, + { .compatible = "edt,edt-ft5406", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); +#endif + static struct i2c_driver edt_ft5x06_ts_driver = { .driver = { .owner = THIS_MODULE, .name = "edt_ft5x06", + .of_match_table = of_match_ptr(edt_ft5x06_of_match), .pm = &edt_ft5x06_ts_pm_ops, }, .id_table = edt_ft5x06_ts_id, From f559e886cbff1a2273766332d79aa2e37ff65b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:27:50 -0700 Subject: [PATCH 0753/1983] Input: edt-ft5x06 - adjust delays to conform datasheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FT5x06 datasheet specifies a minimum reset width of 5ms and a delay between deassertion of reset and start of reporting of 300ms. Adjust the delays to conform to the datasheet. With the original delays I sometimes experienced communication timeouts when initializing the controller. Signed-off-by: Lothar Waßmann Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index f9b7080495da7c..001b6a794b87fd 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -638,7 +638,7 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client, return error; } - mdelay(5); + msleep(5); gpio_set_value(tsdata->wake_pin, 1); } if (gpio_is_valid(tsdata->reset_pin)) { @@ -653,9 +653,9 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client, return error; } - mdelay(50); + msleep(5); gpio_set_value(tsdata->reset_pin, 1); - mdelay(100); + msleep(300); } return 0; From 5360efccbfa24bd64a238dc31389cd9824a32279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:28:17 -0700 Subject: [PATCH 0754/1983] Input: edt-ft5x06 - ignore touchdown events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chip may report invalid coordinates on touchdown events, so don't report the initial touchdown event. Signed-off-by: Lothar Waßmann Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 001b6a794b87fd..09dede67366a8d 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -176,6 +176,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (type == TOUCH_EVENT_RESERVED) continue; + /* ignore TOUCH_DOWN events, might have bogus coordinates */ + if (type == TOUCH_EVENT_DOWN) + continue; + x = ((buf[0] << 8) | buf[1]) & 0x0fff; y = ((buf[2] << 8) | buf[3]) & 0x0fff; id = (buf[2] >> 4) & 0x0f; From fcd49c30cb9ce33afc9beb52e3232b309e82cfda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:31:14 -0700 Subject: [PATCH 0755/1983] Input: edt-ft5x06 - add support for M09 firmware version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a new firmware version for the EDT-FT5x06 chip. Add support for detecting the firmware version and handle the differences appropriately. Signed-off-by: Lothar Waßmann Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 358 +++++++++++++++++++------ 1 file changed, 276 insertions(+), 82 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 09dede67366a8d..fac46c49b8fb7b 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Simon Budig, + * Daniel Wagener (M09 firmware support) * Lothar Waßmann (DT support) * * This software is licensed under the terms of the GNU General Public @@ -47,6 +48,14 @@ #define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_Y 0x34 +#define M09_REGISTER_THRESHOLD 0x80 +#define M09_REGISTER_GAIN 0x92 +#define M09_REGISTER_OFFSET 0x93 +#define M09_REGISTER_NUM_X 0x94 +#define M09_REGISTER_NUM_Y 0x95 + +#define NO_REGISTER 0xff + #define WORK_REGISTER_OPMODE 0x3c #define FACTORY_REGISTER_OPMODE 0x01 @@ -61,6 +70,20 @@ #define EDT_RAW_DATA_RETRIES 100 #define EDT_RAW_DATA_DELAY 1 /* msec */ +enum edt_ver { + M06, + M09, +}; + +struct edt_reg_addr { + int reg_threshold; + int reg_report_rate; + int reg_gain; + int reg_offset; + int reg_num_x; + int reg_num_y; +}; + struct edt_ft5x06_ts_data { struct i2c_client *client; struct input_dev *input; @@ -85,6 +108,9 @@ struct edt_ft5x06_ts_data { int report_rate; char name[EDT_NAME_LEN]; + + struct edt_reg_addr reg_addr; + enum edt_ver version; }; static int edt_ft5x06_ts_readwrite(struct i2c_client *client, @@ -142,33 +168,58 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) { struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; - u8 cmd = 0xf9; - u8 rdbuf[26]; + u8 cmd; + u8 rdbuf[29]; int i, type, x, y, id; + int offset, tplen, datalen; int error; + switch (tsdata->version) { + case M06: + cmd = 0xf9; /* tell the controller to send touch data */ + offset = 5; /* where the actual touch data starts */ + tplen = 4; /* data comes in so called frames */ + datalen = 26; /* how much bytes to listen for */ + break; + + case M09: + cmd = 0x02; + offset = 1; + tplen = 6; + datalen = 29; + break; + + default: + goto out; + } + memset(rdbuf, 0, sizeof(rdbuf)); error = edt_ft5x06_ts_readwrite(tsdata->client, sizeof(cmd), &cmd, - sizeof(rdbuf), rdbuf); + datalen, rdbuf); if (error) { dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", error); goto out; } - if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { - dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", - rdbuf[0], rdbuf[1], rdbuf[2]); - goto out; - } + /* M09 does not send header or CRC */ + if (tsdata->version == M06) { + if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || + rdbuf[2] != datalen) { + dev_err_ratelimited(dev, + "Unexpected header: %02x%02x%02x!\n", + rdbuf[0], rdbuf[1], rdbuf[2]); + goto out; + } - if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) - goto out; + if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) + goto out; + } for (i = 0; i < MAX_SUPPORT_POINTS; i++) { - u8 *buf = &rdbuf[i * 4 + 5]; + u8 *buf = &rdbuf[i * tplen + offset]; bool down; type = buf[0] >> 6; @@ -176,8 +227,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (type == TOUCH_EVENT_RESERVED) continue; - /* ignore TOUCH_DOWN events, might have bogus coordinates */ - if (type == TOUCH_EVENT_DOWN) + /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ + if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN) continue; x = ((buf[0] << 8) | buf[1]) & 0x0fff; @@ -207,12 +258,25 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, { u8 wrbuf[4]; - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[2] = value; - wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; - - return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[2] = value; + wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; + return edt_ft5x06_ts_readwrite(tsdata->client, 4, + wrbuf, 0, NULL); + case M09: + wrbuf[0] = addr; + wrbuf[1] = value; + + return edt_ft5x06_ts_readwrite(tsdata->client, 3, + wrbuf, 0, NULL); + + default: + return -EINVAL; + } } static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, @@ -221,19 +285,35 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, u8 wrbuf[2], rdbuf[2]; int error; - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); - if (error) + error = edt_ft5x06_ts_readwrite(tsdata->client, + 2, wrbuf, 2, rdbuf); return error; - if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { - dev_err(&tsdata->client->dev, - "crc error: 0x%02x expected, got 0x%02x\n", - wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); - return -EIO; + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], + rdbuf[1]); + return -EIO; + } + break; + + case M09: + wrbuf[0] = addr; + error = edt_ft5x06_ts_readwrite(tsdata->client, 1, + wrbuf, 1, rdbuf); + if (error) + return error; + break; + + default: + return -EINVAL; } return rdbuf[0]; @@ -244,19 +324,21 @@ struct edt_ft5x06_attribute { size_t field_offset; u8 limit_low; u8 limit_high; - u8 addr; + u8 addr_m06; + u8 addr_m09; }; -#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \ + _limit_low, _limit_high) \ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ .dattr = __ATTR(_field, _mode, \ edt_ft5x06_setting_show, \ edt_ft5x06_setting_store), \ - .field_offset = \ - offsetof(struct edt_ft5x06_ts_data, _field), \ + .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ + .addr_m06 = _addr_m06, \ + .addr_m09 = _addr_m09, \ .limit_low = _limit_low, \ .limit_high = _limit_high, \ - .addr = _addr, \ } static ssize_t edt_ft5x06_setting_show(struct device *dev, @@ -271,6 +353,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, int val; size_t count = 0; int error = 0; + u8 addr; mutex_lock(&tsdata->mutex); @@ -279,15 +362,33 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, goto out; } - val = edt_ft5x06_register_read(tsdata, attr->addr); - if (val < 0) { - error = val; - dev_err(&tsdata->client->dev, - "Failed to fetch attribute %s, error %d\n", - dattr->attr.name, error); + switch (tsdata->version) { + case M06: + addr = attr->addr_m06; + break; + + case M09: + addr = attr->addr_m09; + break; + + default: + error = -ENODEV; goto out; } + if (addr != NO_REGISTER) { + val = edt_ft5x06_register_read(tsdata, addr); + if (val < 0) { + error = val; + dev_err(&tsdata->client->dev, + "Failed to fetch attribute %s, error %d\n", + dattr->attr.name, error); + goto out; + } + } else { + val = *field; + } + if (val != *field) { dev_warn(&tsdata->client->dev, "%s: read (%d) and stored value (%d) differ\n", @@ -312,6 +413,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, u8 *field = (u8 *)((char *)tsdata + attr->field_offset); unsigned int val; int error; + u8 addr; mutex_lock(&tsdata->mutex); @@ -329,14 +431,29 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, goto out; } - error = edt_ft5x06_register_write(tsdata, attr->addr, val); - if (error) { - dev_err(&tsdata->client->dev, - "Failed to update attribute %s, error: %d\n", - dattr->attr.name, error); + switch (tsdata->version) { + case M06: + addr = attr->addr_m06; + break; + + case M09: + addr = attr->addr_m09; + break; + + default: + error = -ENODEV; goto out; } + if (addr != NO_REGISTER) { + error = edt_ft5x06_register_write(tsdata, addr, val); + if (error) { + dev_err(&tsdata->client->dev, + "Failed to update attribute %s, error: %d\n", + dattr->attr.name, error); + goto out; + } + } *field = val; out: @@ -344,12 +461,14 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, return error ?: count; } -static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); -static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); -static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, - WORK_REGISTER_THRESHOLD, 20, 80); -static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, - WORK_REGISTER_REPORT_RATE, 3, 14); +static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, + M09_REGISTER_GAIN, 0, 31); +static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, + M09_REGISTER_OFFSET, 0, 31); +static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, + M09_REGISTER_THRESHOLD, 20, 80); +static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, + NO_REGISTER, 3, 14); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, @@ -384,6 +503,9 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) } /* mode register is 0x3c when in the work mode */ + if (tsdata->version == M09) + goto m09_out; + error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); if (error) { dev_err(&client->dev, @@ -416,12 +538,18 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) enable_irq(client->irq); return error; + +m09_out: + dev_err(&client->dev, "No factory mode support for M09\n"); + return -EINVAL; + } static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) { struct i2c_client *client = tsdata->client; int retries = EDT_SWITCH_MODE_RETRIES; + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; int ret; int error; @@ -454,13 +582,14 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) tsdata->raw_buffer = NULL; /* restore parameters */ - edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, + edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, tsdata->threshold); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, + edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, tsdata->gain); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, tsdata->offset); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, + if (reg_addr->reg_report_rate) + edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, tsdata->report_rate); enable_irq(client->irq); @@ -666,30 +795,60 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client, } static int edt_ft5x06_ts_identify(struct i2c_client *client, - char *model_name, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata, + char *fw_version) { u8 rdbuf[EDT_NAME_LEN]; char *p; int error; + char *model_name = tsdata->name; + /* see what we find if we assume it is a M06 * + * if we get less than EDT_NAME_LEN, we don't want + * to have garbage in there + */ + memset(rdbuf, 0, sizeof(rdbuf)); error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", EDT_NAME_LEN - 1, rdbuf); if (error) return error; - /* remove last '$' end marker */ - rdbuf[EDT_NAME_LEN - 1] = '\0'; - if (rdbuf[EDT_NAME_LEN - 2] == '$') - rdbuf[EDT_NAME_LEN - 2] = '\0'; + /* if we find something consistent, stay with that assumption + * at least M09 won't send 3 bytes here + */ + if (!(strnicmp(rdbuf + 1, "EP0", 3))) { + tsdata->version = M06; + + /* remove last '$' end marker */ + rdbuf[EDT_NAME_LEN - 1] = '\0'; + if (rdbuf[EDT_NAME_LEN - 2] == '$') + rdbuf[EDT_NAME_LEN - 2] = '\0'; + + /* look for Model/Version separator */ + p = strchr(rdbuf, '*'); + if (p) + *p++ = '\0'; + strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); + strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + } else { + /* since there are only two versions around (M06, M09) */ + tsdata->version = M09; + + error = edt_ft5x06_ts_readwrite(client, 1, "\xA6", + 2, rdbuf); + if (error) + return error; - /* look for Model/Version separator */ - p = strchr(rdbuf, '*'); - if (p) - *p++ = '\0'; + strlcpy(fw_version, rdbuf, 2); - strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); - strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + error = edt_ft5x06_ts_readwrite(client, 1, "\xA8", + 1, rdbuf); + if (error) + return error; + + snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09", + rdbuf[0] >> 4, rdbuf[0] & 0x0F); + } return 0; } @@ -708,36 +867,69 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np, struct edt_ft5x06_ts_data *tsdata) { - EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD); - EDT_GET_PROP(gain, WORK_REGISTER_GAIN); - EDT_GET_PROP(offset, WORK_REGISTER_OFFSET); + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + + EDT_GET_PROP(threshold, reg_addr->reg_threshold); + EDT_GET_PROP(gain, reg_addr->reg_gain); + EDT_GET_PROP(offset, reg_addr->reg_offset); } static void edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, const struct edt_ft5x06_platform_data *pdata) { + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + if (!pdata->use_parameters) return; /* pick up defaults from the platform data */ - EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); - EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); - EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); - EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); + EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold); + EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain); + EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset); + if (reg_addr->reg_report_rate != NO_REGISTER) + EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate); } static void edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) { + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + tsdata->threshold = edt_ft5x06_register_read(tsdata, - WORK_REGISTER_THRESHOLD); - tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); - tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); - tsdata->report_rate = edt_ft5x06_register_read(tsdata, - WORK_REGISTER_REPORT_RATE); - tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); - tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); + reg_addr->reg_threshold); + tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); + tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_report_rate != NO_REGISTER) + tsdata->report_rate = edt_ft5x06_register_read(tsdata, + reg_addr->reg_report_rate); + tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x); + tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y); +} + +static void +edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) +{ + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + + switch (tsdata->version) { + case M06: + reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD; + reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; + reg_addr->reg_gain = WORK_REGISTER_GAIN; + reg_addr->reg_offset = WORK_REGISTER_OFFSET; + reg_addr->reg_num_x = WORK_REGISTER_NUM_X; + reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; + break; + + case M09: + reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; + reg_addr->reg_gain = M09_REGISTER_GAIN; + reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_num_x = M09_REGISTER_NUM_X; + reg_addr->reg_num_y = M09_REGISTER_NUM_Y; + break; + } } #ifdef CONFIG_OF @@ -821,12 +1013,14 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->input = input; tsdata->factory_mode = false; - error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); + error = edt_ft5x06_ts_identify(client, tsdata, fw_version); if (error) { dev_err(&client->dev, "touchscreen probe failed\n"); return error; } + edt_ft5x06_ts_set_regs(tsdata); + if (!pdata) edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata); else From 83064bd8daba9721278ac4313d7c76b5c0a5daee Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 3 Apr 2014 09:17:05 -0700 Subject: [PATCH 0756/1983] Input: edt-ft5x06 - add a missing condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The if condition was accidentally deleted here so we return every time instead of returning on error. Fixes: fd335ab04b3f ('Input: edt-ft5x06 - add support for M09 firmware version') Signed-off-by: Dan Carpenter Reviewed-by: Jingoo Han Acked-By: Lothar Waßmann Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index fac46c49b8fb7b..d4bf4ae6e4c7cc 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -291,9 +291,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - error = edt_ft5x06_ts_readwrite(tsdata->client, - 2, wrbuf, 2, rdbuf); - return error; + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, + rdbuf); + if (error) + return error; if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { dev_err(&tsdata->client->dev, From b8fb8521b72c69aed44661e8db5ef113cf47999c Mon Sep 17 00:00:00 2001 From: Robert Woerle Date: Sat, 7 Jun 2014 22:20:23 -0700 Subject: [PATCH 0757/1983] Input: edt-ft5x06 - fix an i2c write for M09 support The driver sends 3 bytes instead of 2 when accessing a register on the M09 firmware, so writing to gain seems to overflow into the offset register. Signed-off-by: Robert Woerle Acked-By: Simon Budig Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d4bf4ae6e4c7cc..f2960bbf68587a 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -271,7 +271,7 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, wrbuf[0] = addr; wrbuf[1] = value; - return edt_ft5x06_ts_readwrite(tsdata->client, 3, + return edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 0, NULL); default: From fdb32a570a4407d8191667f4d34c2ef79f94f52e Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Wed, 4 Feb 2015 11:52:34 -0800 Subject: [PATCH 0758/1983] input: edt-ft5x06 - add device-tree config for screen res and inversion Added the following properties to the device-tree for the edt-ft5x06: - invert - boolean value that will invert both axis if present - screen_x - maximum X value reported to userspace - screen_y - maximum Y value reported to userspace Signed-off-by: Tim Harvey --- drivers/input/touchscreen/edt-ft5x06.c | 34 +++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index f2960bbf68587a..77654dd13ff3e7 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -111,6 +111,10 @@ struct edt_ft5x06_ts_data { struct edt_reg_addr reg_addr; enum edt_ver version; + + bool invert; + int screen_x; + int screen_y; }; static int edt_ft5x06_ts_readwrite(struct i2c_client *client, @@ -236,6 +240,13 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) id = (buf[2] >> 4) & 0x0f; down = (type != TOUCH_EVENT_UP); + /* invert axis */ + if (tsdata->invert) { + x = tsdata->screen_x - x; + y = tsdata->screen_y - y; + } + dev_dbg(dev, "%d: %dx%d %s\n", id, x, y, down ? "UP" : "DOWN"); + input_mt_slot(tsdata->input, id); input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); @@ -873,6 +884,11 @@ static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np, EDT_GET_PROP(threshold, reg_addr->reg_threshold); EDT_GET_PROP(gain, reg_addr->reg_gain); EDT_GET_PROP(offset, reg_addr->reg_offset); + + if (of_property_read_bool(np, "invert")) + tsdata->invert = true; + of_property_read_u32(np, "screen-x", &tsdata->screen_x); + of_property_read_u32(np, "screen-y", &tsdata->screen_y); } static void @@ -1028,10 +1044,16 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, edt_ft5x06_ts_get_defaults(tsdata, pdata); edt_ft5x06_ts_get_parameters(tsdata); + if (!tsdata->screen_x || !tsdata->screen_y) { + tsdata->screen_x = tsdata->num_x * 64 - 1; + tsdata->screen_y = tsdata->num_y * 64 - 1; + } dev_dbg(&client->dev, - "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", - tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + "Model \"%s\", Rev. \"%s\", %dx%d sensors (%dx%d) %sinverted\n", + tsdata->name, fw_version, tsdata->num_x, tsdata->num_y, + tsdata->screen_x, tsdata->screen_y, + tsdata->invert ? "" : "non"); input->name = tsdata->name; input->id.bustype = BUS_I2C; @@ -1041,12 +1063,12 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, __set_bit(EV_KEY, input->evbit); __set_bit(EV_ABS, input->evbit); __set_bit(BTN_TOUCH, input->keybit); - input_set_abs_params(input, ABS_X, 0, tsdata->num_x * 64 - 1, 0, 0); - input_set_abs_params(input, ABS_Y, 0, tsdata->num_y * 64 - 1, 0, 0); + input_set_abs_params(input, ABS_X, 0, tsdata->screen_x - 1, 0, 0); + input_set_abs_params(input, ABS_Y, 0, tsdata->screen_y - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_X, - 0, tsdata->num_x * 64 - 1, 0, 0); + 0, tsdata->screen_x - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, - 0, tsdata->num_y * 64 - 1, 0, 0); + 0, tsdata->screen_y - 1, 0, 0); error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); From d075fec85460a191126c77116fa42b320eb4ba49 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Mon, 13 Apr 2015 14:00:13 -0700 Subject: [PATCH 0759/1983] cpufreq: imx: show soc voltage when debugging Signed-off-by: Tim Harvey --- drivers/cpufreq/imx6q-cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index dd3942894b43f9..eaa9add8a44778 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -70,9 +70,9 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) rcu_read_unlock(); volt_old = regulator_get_voltage(arm_reg); - dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n", + dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld/%d mV\n", old_freq / 1000, volt_old / 1000, - new_freq / 1000, volt / 1000); + new_freq / 1000, volt / 1000, imx6_soc_volt[index] / 1000); /* * CPU freq is increasing, so need to ensure * that bus frequency is increased too. From 41608a15f2b872fefed1813443462c79f025c6d2 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 7 Oct 2014 22:12:13 -0700 Subject: [PATCH 0760/1983] cpufreq: imx: add a 25mV tollerance to the operating point voltages The operating-points defined in imx6q.dtsi and imx6dl.dtsi are set to 25mV above the minimum values in the IMX6Q and IMX6DL datasheets therefore we will allow that tollerance when setting regulator voltages. This allows using PMIC regulators that don't have setpoints that exactly match the operating points but are still within tollerance. Signed-off-by: Tim Harvey --- drivers/cpufreq/imx6q-cpufreq.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index eaa9add8a44778..f603fc13177452 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -50,6 +50,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) unsigned long freq_hz, volt, volt_old; unsigned int old_freq, new_freq; int ret; + int tol = 25000; /* 25mv tollerance */ mutex_lock(&set_cpufreq_lock); @@ -84,7 +85,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) if (new_freq > old_freq) { if (!IS_ERR(pu_reg) && regulator_is_enabled(pu_reg)) { ret = regulator_set_voltage_tol(pu_reg, - imx6_soc_volt[index], 0); + imx6_soc_volt[index], tol); if (ret) { dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret); @@ -92,13 +93,13 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) return ret; } } - ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); + ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], tol); if (ret) { dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret); mutex_unlock(&set_cpufreq_lock); return ret; } - ret = regulator_set_voltage_tol(arm_reg, volt, 0); + ret = regulator_set_voltage_tol(arm_reg, volt, tol); if (ret) { dev_err(cpu_dev, "failed to scale vddarm up: %d\n", ret); @@ -137,27 +138,27 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ret = clk_set_rate(arm_clk, new_freq * 1000); if (ret) { dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); - regulator_set_voltage_tol(arm_reg, volt_old, 0); + regulator_set_voltage_tol(arm_reg, volt_old, tol); mutex_unlock(&set_cpufreq_lock); return ret; } /* scaling down? scale voltage after frequency */ if (new_freq < old_freq) { - ret = regulator_set_voltage_tol(arm_reg, volt, 0); + ret = regulator_set_voltage_tol(arm_reg, volt, tol); if (ret) { dev_warn(cpu_dev, "failed to scale vddarm down: %d\n", ret); ret = 0; } - ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); + ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], tol); if (ret) { dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret); ret = 0; } if (!IS_ERR(pu_reg) && regulator_is_enabled(pu_reg)) { ret = regulator_set_voltage_tol(pu_reg, - imx6_soc_volt[index], 0); + imx6_soc_volt[index], tol); if (ret) { dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", From 31a625e578888486fdfa66cbd7cac9f3952520b5 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Fri, 27 Dec 2013 12:19:25 -0800 Subject: [PATCH 0761/1983] hwmon: mma8451: add fxos8700 accel support The FXOS8700 has a 3-axis accellerometer as well as a 3-axis magnetomoter. This adds support for the 3-axis accellerometer. Signed-off-by: Tim Harvey --- drivers/hwmon/mxc_mma8451.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/mxc_mma8451.c b/drivers/hwmon/mxc_mma8451.c index 0289f9c3f12f0b..533b20d46116c0 100644 --- a/drivers/hwmon/mxc_mma8451.c +++ b/drivers/hwmon/mxc_mma8451.c @@ -39,6 +39,7 @@ #define MMA8451_ID 0x1A #define MMA8452_ID 0x2A #define MMA8453_ID 0x3A +#define FXOS8700_ID 0xC7 #define POLL_INTERVAL_MIN 1 #define POLL_INTERVAL_MAX 500 @@ -445,10 +446,10 @@ static int mma8451_probe(struct i2c_client *client, client_id = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I); if (client_id != MMA8451_ID && client_id != MMA8452_ID - && client_id != MMA8453_ID) { + && client_id != MMA8453_ID && client_id != FXOS8700_ID) { dev_err(&client->dev, - "read chip ID 0x%x is not equal to 0x%x or 0x%x!\n", - result, MMA8451_ID, MMA8452_ID); + "read chip ID 0x%x is not equal to 0x%x,0x%x,0x%x!\n", + result, MMA8451_ID, MMA8452_ID, FXOS8700_ID); result = -EINVAL; goto err_out; } From d91c85276b1b041ae0fa293161794a6a44939628 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 16 Jan 2014 13:27:01 -0800 Subject: [PATCH 0762/1983] hwmon: mma8451: add fxos8700 magnetometer support The FXOS8700 contains a 3-axis magnetometer in addition to the 3-axis accelerometer. This adds 2 additional linux input devices required by the sensor HAL implemented by freescale in hardware/imx/libsensors/MagSensor.cpp. TODO: when changing from hybrid mode to accel_only mode the accelerometer stops updating. Not sure how to overcome this yet so we always leave it in hybrid mode. Signed-off-by: Tim Harvey --- drivers/hwmon/mxc_mma8451.c | 169 ++++++++++++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/mxc_mma8451.c b/drivers/hwmon/mxc_mma8451.c index 533b20d46116c0..94597004f48eb2 100644 --- a/drivers/hwmon/mxc_mma8451.c +++ b/drivers/hwmon/mxc_mma8451.c @@ -106,6 +106,16 @@ enum { MMA8451_OFF_Y, MMA8451_OFF_Z, + FXOS8700_M_DR_STATUS, + FXOS8700_M_OUT_X_MSB, + FXOS8700_M_OUT_X_LSB, + FXOS8700_M_OUT_Y_MSB, + FXOS8700_M_OUT_Y_LSB, + FXOS8700_M_OUT_Z_MSB, + FXOS8700_M_OUT_Z_LSB, + + FXOS8700_M_CTRL_REG1 = 0x5B, + MMA8451_REG_END, }; @@ -124,18 +134,28 @@ enum { MMA_ACTIVED, }; +enum { + FXOS_ACCEL_ONLY = 0, + FXOS_MAG_ONLY, + FXOS_NONE, + FXOS_HYBRID, +}; + /* mma8451 status */ struct mma8451_status { u8 mode; u8 ctl_reg1; int active; int position; + int hybrid_mode; }; static struct mma8451_status mma_status; static struct input_polled_dev *mma8451_idev; +static struct input_polled_dev *fxos8700_m_idev; static struct device *hwmon_dev; static struct i2c_client *mma8451_i2c_client; +static int client_id; static int senstive_mode = MODE_2G; static int ACCHAL[8][3][3] = { @@ -182,6 +202,14 @@ static int mma8451_change_mode(struct i2c_client *client, int mode) goto out; mma_status.active = MMA_STANDBY; + if (client_id == FXOS8700_ID) { + mma_status.hybrid_mode = FXOS_HYBRID; + result = i2c_smbus_write_byte_data(client, FXOS8700_M_CTRL_REG1, FXOS_HYBRID); + if (result < 0) + goto out; + } + + mma_status.mode = mode; result = i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG, mode); if (result < 0) @@ -195,13 +223,15 @@ static int mma8451_change_mode(struct i2c_client *client, int mode) return result; } -static int mma8451_read_data(short *x, short *y, short *z) +static int mma8451_read_data(struct input_polled_dev *idev, + short *x, short *y, short *z) { u8 tmp_data[MMA8451_BUF_SIZE]; + char reg = (idev == mma8451_idev)?MMA8451_OUT_X_MSB:FXOS8700_M_OUT_X_MSB; int ret; ret = i2c_smbus_read_i2c_block_data(mma8451_i2c_client, - MMA8451_OUT_X_MSB, 7, tmp_data); + reg, 7, tmp_data); if (ret < MMA8451_BUF_SIZE) { dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n"); return -EIO; @@ -213,54 +243,64 @@ static int mma8451_read_data(short *x, short *y, short *z) return 0; } -static void report_abs(void) +static void report_abs(struct input_polled_dev *idev) { short x, y, z; int result; int retry = 3; + char reg = (idev == mma8451_idev)?MMA8451_STATUS:FXOS8700_M_DR_STATUS; mutex_lock(&mma8451_lock); if (mma_status.active == MMA_STANDBY) goto out; /* wait for the data ready */ do { - result = i2c_smbus_read_byte_data(mma8451_i2c_client, - MMA8451_STATUS); + result = i2c_smbus_read_byte_data(mma8451_i2c_client, reg); retry--; msleep(1); } while (!(result & MMA8451_STATUS_ZYXDR) && retry > 0); if (retry == 0) goto out; - if (mma8451_read_data(&x, &y, &z) != 0) + if (mma8451_read_data(idev, &x, &y, &z) != 0) goto out; mma8451_adjust_position(&x, &y, &z); - input_report_abs(mma8451_idev->input, ABS_X, x); - input_report_abs(mma8451_idev->input, ABS_Y, y); - input_report_abs(mma8451_idev->input, ABS_Z, z); - input_sync(mma8451_idev->input); + input_report_abs(idev->input, ABS_X, x); + input_report_abs(idev->input, ABS_Y, y); + input_report_abs(idev->input, ABS_Z, z); + input_sync(idev->input); out: mutex_unlock(&mma8451_lock); } static void mma8451_dev_poll(struct input_polled_dev *dev) { - report_abs(); + report_abs(dev); } static ssize_t mma8451_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct input_polled_dev *idev = dev_get_drvdata(dev); struct i2c_client *client; u8 val; - int enable; + int enable = 0; mutex_lock(&mma8451_lock); client = mma8451_i2c_client; val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); - if ((val & 0x01) && mma_status.active == MMA_ACTIVED) - enable = 1; - else - enable = 0; + if ((val & 0x01) && mma_status.active == MMA_ACTIVED) { + if (client_id == FXOS8700_ID) { + if (mma_status.hybrid_mode == FXOS_HYBRID) + enable = 1; + else if (mma_status.hybrid_mode == FXOS_ACCEL_ONLY + && idev == mma8451_idev) + enable = 1; + else if (mma_status.hybrid_mode == FXOS_MAG_ONLY) + enable = 1; + } + else + enable = 1; + } mutex_unlock(&mma8451_lock); return sprintf(buf, "%d\n", enable); } @@ -269,6 +309,7 @@ static ssize_t mma8451_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct input_polled_dev *idev = dev_get_drvdata(dev); struct i2c_client *client; int ret; unsigned long enable; @@ -283,6 +324,45 @@ static ssize_t mma8451_enable_store(struct device *dev, mutex_lock(&mma8451_lock); client = mma8451_i2c_client; enable = (enable > 0) ? 1 : 0; + if (client_id == FXOS8700_ID) { + val = mma_status.hybrid_mode; + if (idev == mma8451_idev) { + if (enable) { + if (val == FXOS_NONE) + val = FXOS_ACCEL_ONLY; + else if (val == FXOS_MAG_ONLY) + val = FXOS_HYBRID; + } else { + if (val == FXOS_HYBRID) + val = FXOS_MAG_ONLY; + else if (val == FXOS_ACCEL_ONLY) + val = FXOS_NONE; + } + } else { + if (enable) { + if (val == FXOS_NONE) + val = FXOS_MAG_ONLY; + else if (val == FXOS_ACCEL_ONLY) + val = FXOS_HYBRID; + } else { + if (val == FXOS_HYBRID) + val = FXOS_ACCEL_ONLY; + else if (val == FXOS_MAG_ONLY) + val = FXOS_NONE; + } + } +/* + if (val != FXOS_NONE) { + ret = i2c_smbus_write_byte_data(client, + FXOS8700_M_CTRL_REG1, val & 0x03); + } +*/ + mma_status.hybrid_mode = val; + if (mma_status.hybrid_mode == FXOS_NONE) + enable = 0; + else + enable = 1; + } if (enable && mma_status.active == MMA_STANDBY) { val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1); ret = @@ -410,7 +490,7 @@ static const struct attribute_group mma8451_attr_group = { static int mma8451_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int result, client_id; + int result; struct input_dev *idev; struct i2c_adapter *adapter; u32 pos; @@ -469,18 +549,22 @@ static int mma8451_probe(struct i2c_client *client, goto err_out; } + /* create a polled input device for accelerometer */ mma8451_idev = input_allocate_polled_device(); if (!mma8451_idev) { result = -ENOMEM; dev_err(&client->dev, "alloc poll device failed!\n"); - goto err_alloc_poll_device; + goto err_register_polled_device; } mma8451_idev->poll = mma8451_dev_poll; mma8451_idev->poll_interval = POLL_INTERVAL; mma8451_idev->poll_interval_min = POLL_INTERVAL_MIN; mma8451_idev->poll_interval_max = POLL_INTERVAL_MAX; idev = mma8451_idev->input; - idev->name = "mma845x"; + if (client_id == FXOS8700_ID) + idev->name = "mma845x_a"; + else + idev->name = "mma845x"; idev->id.bustype = BUS_I2C; idev->evbit[0] = BIT_MASK(EV_ABS); @@ -500,12 +584,51 @@ static int mma8451_probe(struct i2c_client *client, goto err_register_polled_device; } + /* create a polled input device for magnetometer */ + if (client_id == FXOS8700_ID) { + fxos8700_m_idev = input_allocate_polled_device(); + if (!fxos8700_m_idev) { + result = -ENOMEM; + dev_err(&client->dev, "alloc poll device failed!\n"); + goto err_alloc_poll_device; + } + fxos8700_m_idev->poll = mma8451_dev_poll; + fxos8700_m_idev->poll_interval = POLL_INTERVAL; + fxos8700_m_idev->poll_interval_min = POLL_INTERVAL_MIN; + fxos8700_m_idev->poll_interval_max = POLL_INTERVAL_MAX; + idev = fxos8700_m_idev->input; + idev->name = "fxos8700_m"; + idev->id.bustype = BUS_I2C; + idev->evbit[0] = BIT_MASK(EV_ABS); + + input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT); + + result = input_register_polled_device(fxos8700_m_idev); + if (result) { + dev_err(&client->dev, "register poll device failed!\n"); + goto err_register_polled_device1; + } + result = sysfs_create_group(&idev->dev.kobj, &mma8451_attr_group); + if (result) { + dev_err(&client->dev, "create device file failed!\n"); + result = -EINVAL; + goto err_create_sysfs1; + } + } + result = of_property_read_u32(of_node, "position", &pos); if (result) pos = DEFAULT_POSITION; mma_status.position = (int)pos; return 0; + +err_create_sysfs1: + input_unregister_polled_device(fxos8700_m_idev); +err_register_polled_device1: + input_free_polled_device(fxos8700_m_idev); err_register_polled_device: input_free_polled_device(mma8451_idev); err_alloc_poll_device: @@ -530,6 +653,13 @@ static int mma8451_remove(struct i2c_client *client) { int ret; ret = mma8451_stop_chip(client); + + if (client_id == FXOS8700_ID) { + input_unregister_polled_device(fxos8700_m_idev); + input_free_polled_device(fxos8700_m_idev); + } + input_unregister_polled_device(mma8451_idev); + input_free_polled_device(mma8451_idev); hwmon_device_unregister(hwmon_dev); return ret; @@ -557,6 +687,7 @@ static int mma8451_resume(struct device *dev) static const struct i2c_device_id mma8451_id[] = { {"mma8451", 0}, + { } }; MODULE_DEVICE_TABLE(i2c, mma8451_id); From 41d92341f862499d8736567ca2e58ccca6f3ce3e Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Tue, 20 May 2014 12:12:16 +0200 Subject: [PATCH 0763/1983] regulator: core: Fix the init of DT defined fixed regulators When a regulator is defined using DT and it has a single voltage the regulator init always tries to apply this voltage. However it fails if the regulator isn't settable because it is using an internal low level function. To overcome this we now first query the regulator and only set it if needed. Signed-off-by: Alban Bedel Signed-off-by: Mark Brown --- drivers/regulator/core.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 14241be2f6fb2f..e8cdee8cdfb8ed 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -844,13 +844,22 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, /* do we need to apply the constraint voltage */ if (rdev->constraints->apply_uV && rdev->constraints->min_uV == rdev->constraints->max_uV) { - ret = _regulator_do_set_voltage(rdev, - rdev->constraints->min_uV, - rdev->constraints->max_uV); - if (ret < 0) { - rdev_err(rdev, "failed to apply %duV constraint\n", - rdev->constraints->min_uV); - return ret; + int current_uV = _regulator_get_voltage(rdev); + if (current_uV < 0) { + rdev_err(rdev, "failed to get the current voltage\n"); + return current_uV; + } + if (current_uV < rdev->constraints->min_uV || + current_uV > rdev->constraints->max_uV) { + ret = _regulator_do_set_voltage( + rdev, rdev->constraints->min_uV, + rdev->constraints->max_uV); + if (ret < 0) { + rdev_err(rdev, + "failed to apply %duV constraint\n", + rdev->constraints->min_uV); + return ret; + } } } From 6af172da77ade8af5f7779b620f2b570ba197ba1 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 7 Oct 2014 21:56:45 -0700 Subject: [PATCH 0764/1983] regulator: Add LTC3676 support This patch adds support for the Linear Technology LTC3676 8-output I2C voltage regulator IC. Signed-off-by: Tim Harvey Signed-off-by: Pushpal Sidhu --- .../devicetree/bindings/regulator/ltc3676.txt | 103 ++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/ltc3676.c | 497 ++++++++++++++++++ 4 files changed, 608 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/ltc3676.txt create mode 100644 drivers/regulator/ltc3676.c diff --git a/Documentation/devicetree/bindings/regulator/ltc3676.txt b/Documentation/devicetree/bindings/regulator/ltc3676.txt new file mode 100644 index 00000000000000..e004f890e4ee54 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/ltc3676.txt @@ -0,0 +1,103 @@ +Linear Technology LTC3676 8-output regulators + +Required properties: +- compatible: "lltc,ltc3676" +- reg: I2C slave address + +Required child node: +- regulators: Contains eight regulator child nodes sw1, sw2, sw3, sw4, + ldo1, ldo2, ldo3, and ldo4, specifying the initialization data as + documented in Documentation/devicetree/bindings/regulator/regulator.txt. + +Each regulator is defined using the standard binding for regulators. The +nodes for sw1, sw2, sw3, sw4, ldo1, ldo2 and ldo4 additionally need to specify +the resistor values of their external feedback voltage dividers: + +Required properties (not on ldo3): +- lltc,fb-voltage-divider: An array of two integers containing the resistor + values R1 and R2 of the feedback voltage divider in ohms. + +Regulators sw1, sw2, sw3, sw4 can regulate the feedback reference from: +412.5mV to 800mV in 12.5 mV steps. The output voltage thus ranges between +0.4125 * (1 + R1/R2) V and 0.8 * (1 + R1/R2) V. + +Regulators ldo1, ldo2, and ldo4 have a fixed 0.725 V reference and thus output +0.725 * (1 + R1/R2) V. The ldo3 regulator is fixed to 1.8 V. The ldo1 standby +regulator can not be disabled and thus should have the regulator-always-on +property set. + +Example: + + ltc3676: pmic@3c { + compatible = "lltc,ltc3676"; + reg = <0x3c>; + + regulators { + sw1_reg: sw1 { + regulator-min-microvolt = <674400>; + regulator-max-microvolt = <1308000>; + lltc,fb-voltage-divider = <127000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <1033310>; + regulator-max-microvolt = <200400>; + lltc,fb-voltage-divider = <301000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3_reg: sw3 { + regulator-min-microvolt = <674400>; + regulator-max-microvolt = <130800>; + lltc,fb-voltage-divider = <127000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <868310>; + regulator-max-microvolt = <168400>; + lltc,fb-voltage-divider = <221000 200000>; + regulator-ramp-delay = <7000>; + regulator-boot-on; + regulator-always-on; + }; + + /* unused */ + ldo1_reg: ldo1 { + regulator-min-microvolt = <0>; + regulator-max-microvolt = <0>; + lltc,fb-voltage-divider = <0 0>; + regulator-boot-on; + regulator-always-on; + }; + + ldo2_reg: ldo2 { + regulator-min-microvolt = <2490375>; + regulator-max-microvolt = <2490375>; + lltc,fb-voltage-divider = <487000 200000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo3_reg: ldo3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + }; + + ldo4_reg: ldo4 { + regulator-min-microvolt = <3023250>; + regulator-max-microvolt = <3023250>; + lltc,fb-voltage-divider = <634000 200000>; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index ed96adc6efd6e9..4757166fd3829b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -264,6 +264,13 @@ config REGULATOR_MAX14577 This driver controls a Maxim 14577 regulator via I2C bus. The regulators include safeout LDO and current regulator 'CHARGER'. +config REGULATOR_LTC3676 + tristate "LTC3676 regulator driver" + depends on I2C + select REGMAP_I2C + help + Say y here to support for the LTC3676 regulators controlled via I2C. + config REGULATOR_MAX1586 tristate "Maxim 1586/1587 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index a2163caf10d24c..fe33e786261b2f 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o +obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX17135) += max17135-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c new file mode 100644 index 00000000000000..69c3b7af676af5 --- /dev/null +++ b/drivers/regulator/ltc3676.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2014 Gateworks Corporation, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "ltc3676" + +/* LTC3676 Registers */ +#define LTC3676_BUCK1 0x01 +#define LTC3676_BUCK2 0x02 +#define LTC3676_BUCK3 0x03 +#define LTC3676_BUCK4 0x04 +#define LTC3676_LDOA 0x05 +#define LTC3676_LDOB 0x06 +#define LTC3676_SQD1 0x07 +#define LTC3676_SQD2 0x08 +#define LTC3676_CNTRL 0x09 +#define LTC3676_DVB1A 0x0A +#define LTC3676_DVB1B 0x0B +#define LTC3676_DVB2A 0x0C +#define LTC3676_DVB2B 0x0D +#define LTC3676_DVB3A 0x0E +#define LTC3676_DVB3B 0x0F +#define LTC3676_DVB4A 0x10 +#define LTC3676_DVB4B 0x11 +#define LTC3676_MSKIRQ 0x12 +#define LTC3676_MSKPG 0x13 +#define LTC3676_USER 0x14 +#define LTC3676_IRQSTAT 0x15 +#define LTC3676_PGSTATL 0x16 +#define LTC3676_PGSTATRT 0x17 +#define LTC3676_HRST 0x1E +#define LTC3676_CLIRQ 0x1F + +#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3) +#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4) +#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5) +#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6) +#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7) + +enum ltc3676_reg { + LTC3676_SW1, + LTC3676_SW2, + LTC3676_SW3, + LTC3676_SW4, + LTC3676_LDO1, + LTC3676_LDO2, + LTC3676_LDO3, + LTC3676_LDO4, + LTC3676_NUM_REGULATORS, +}; + +struct ltc3676_regulator { + struct regulator_desc desc; + struct device_node *np; + + /* External feedback voltage divider */ + unsigned int r1; + unsigned int r2; +}; + +struct ltc3676 { + struct regmap *regmap; + struct device *dev; + struct ltc3676_regulator regulator_descs[LTC3676_NUM_REGULATORS]; + struct regulator_dev *regulators[LTC3676_NUM_REGULATORS]; +}; + +static int ltc3676_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct ltc3676 *ltc3676 = rdev_get_drvdata(rdev); + struct device *dev = ltc3676->dev; + int dcdc = rdev_get_id(rdev); + int sel; + + dev_dbg(dev, "%s id=%d uV=%d\n", __func__, dcdc, uV); + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return sel; + + /* DVBB register follows right after the corresponding DVBA register */ + return regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg + 1, + rdev->desc->vsel_mask, sel); +} + +static int ltc3676_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct ltc3676 *ltc3676= rdev_get_drvdata(rdev); + struct device *dev = ltc3676->dev; + int mask, bit = 0; + int dcdc = rdev_get_id(rdev); + + dev_dbg(dev, "%s id=%d mode=%d\n", __func__, dcdc, mode); + + /* DVB reference select is bit5 of DVBA reg */ + mask = 1 << 5; + + if (mode != REGULATOR_MODE_STANDBY) + bit = mask; /* Select DVBB */ + + return regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg, + mask, bit); +} + +/* SW1, SW2, SW3, SW4 linear 0.8V-3.3V with scalar via R1/R2 feeback res */ +static struct regulator_ops ltc3676_linear_regulator_ops = +{ + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_suspend_voltage = ltc3676_set_suspend_voltage, + .set_suspend_mode = ltc3676_set_suspend_mode, +}; + +/* always on fixed regulators */ +static struct regulator_ops ltc3676_fixed_standby_regulator_ops = { +}; + +#define LTC3676_REG(_name, _ops, en_reg, en_bit, dvba_reg, dvb_mask) \ + [LTC3676_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = (dvb_mask) + 1, \ + .min_uV = (dvba_reg) ? 412500 : 0, \ + .uV_step = (dvba_reg) ? 12500 : 0, \ + .ramp_delay = (dvba_reg) ? 800 : 0, \ + .fixed_uV = (dvb_mask) ? 0 : 725000, \ + .ops = <c3676_ ## _ops ## _regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = LTC3676_ ## _name, \ + .owner = THIS_MODULE, \ + .vsel_reg = (dvba_reg), \ + .vsel_mask = (dvb_mask), \ + .enable_reg = (en_reg), \ + .enable_mask = (1 << en_bit), \ + }, \ + } + +#define LTC3676_LINEAR_REG(_name, _en, _dvba) \ + LTC3676_REG(_name, linear, \ + LTC3676_ ## _en, 7, \ + LTC3676_ ## _dvba, 0x1f) + +#define LTC3676_FIXED_REG(_name) \ + LTC3676_REG(_name, fixed_standby, 0, 0, 0, 0) + +static struct ltc3676_regulator ltc3676_regulators[LTC3676_NUM_REGULATORS] = { + LTC3676_LINEAR_REG(SW1, BUCK1, DVB1A), + LTC3676_LINEAR_REG(SW2, BUCK2, DVB2A), + LTC3676_LINEAR_REG(SW3, BUCK3, DVB3A), + LTC3676_LINEAR_REG(SW4, BUCK4, DVB4A), + LTC3676_FIXED_REG(LDO1), + LTC3676_FIXED_REG(LDO2), + LTC3676_FIXED_REG(LDO3), + LTC3676_FIXED_REG(LDO4), +}; + +#ifdef CONFIG_OF +static struct of_regulator_match ltc3676_matches[] = { + { .name = "sw1", }, + { .name = "sw2", }, + { .name = "sw3", }, + { .name = "sw4", }, + { .name = "ldo1", }, + { .name = "ldo2", }, + { .name = "ldo3", }, + { .name = "ldo4", }, +}; + +static int ltc3676_parse_regulators_dt(struct ltc3676 *ltc3676) +{ + struct device *dev = ltc3676->dev; + struct device_node *node; + int i, ret; + + node = of_find_node_by_name(dev->of_node, "regulators"); + if (!node) { + dev_err(dev, "regulators node not found\n"); + return -EINVAL; + } + + ret = of_regulator_match(dev, node, ltc3676_matches, + ARRAY_SIZE(ltc3676_matches)); + of_node_put(node); + if (ret < 0) { + dev_err(dev, "Error parsing regulator init data: %d\n", ret); + return -EINVAL; + } + + /* parse feedback voltage deviders: LDO3 doesn't have them */ + for (i = 0; i < LTC3676_NUM_REGULATORS; i++) { + struct ltc3676_regulator *rdesc = <c3676->regulator_descs[i]; + struct device_node *np = ltc3676_matches[i].of_node; + u32 vdiv[2]; + + rdesc->np = ltc3676_matches[i].of_node; + if (i == LTC3676_LDO3 || !rdesc->np) + continue; + ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider", + vdiv, 2); + if (ret) { + dev_err(dev, "Failed to parse voltage divider: %d\n", + ret); + return ret; + } + + rdesc->r1 = vdiv[0]; + rdesc->r2 = vdiv[1]; + } + + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return ltc3676_matches[index].init_data; +} + +static inline struct device_node *match_of_node(int index) +{ + return ltc3676_matches[index].of_node; +} +#else +static int ltc3676_parse_regulators_dt(struct ltc3676_chip *chip) +{ + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return NULL; +} + +static inline struct device_node *match_of_node(int index) +{ + return NULL; +} +#endif + +static bool ltc3676_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC3676_IRQSTAT: + case LTC3676_BUCK1: + case LTC3676_BUCK2: + case LTC3676_BUCK3: + case LTC3676_BUCK4: + case LTC3676_LDOA: + case LTC3676_LDOB: + case LTC3676_SQD1: + case LTC3676_SQD2: + case LTC3676_CNTRL: + case LTC3676_DVB1A: + case LTC3676_DVB1B: + case LTC3676_DVB2A: + case LTC3676_DVB2B: + case LTC3676_DVB3A: + case LTC3676_DVB3B: + case LTC3676_DVB4A: + case LTC3676_DVB4B: + case LTC3676_MSKIRQ: + case LTC3676_MSKPG: + case LTC3676_USER: + case LTC3676_HRST: + case LTC3676_CLIRQ: + return true; + } + return false; +} + +static bool ltc3676_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC3676_IRQSTAT: + case LTC3676_BUCK1: + case LTC3676_BUCK2: + case LTC3676_BUCK3: + case LTC3676_BUCK4: + case LTC3676_LDOA: + case LTC3676_LDOB: + case LTC3676_SQD1: + case LTC3676_SQD2: + case LTC3676_CNTRL: + case LTC3676_DVB1A: + case LTC3676_DVB1B: + case LTC3676_DVB2A: + case LTC3676_DVB2B: + case LTC3676_DVB3A: + case LTC3676_DVB3B: + case LTC3676_DVB4A: + case LTC3676_DVB4B: + case LTC3676_MSKIRQ: + case LTC3676_MSKPG: + case LTC3676_USER: + case LTC3676_HRST: + case LTC3676_CLIRQ: + return true; + } + return false; +} + +static bool ltc3676_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case LTC3676_IRQSTAT: + case LTC3676_PGSTATL: + case LTC3676_PGSTATRT: + return true; + } + return false; +} + +static const struct regmap_config ltc3676_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = ltc3676_writeable_reg, + .readable_reg = ltc3676_readable_reg, + .volatile_reg = ltc3676_volatile_reg, + .max_register = LTC3676_CLIRQ, + .use_single_rw = true, + .cache_type = REGCACHE_RBTREE, +}; + +static irqreturn_t ltc3676_isr(int irq, void *dev_id) +{ + struct ltc3676 *ltc3676 = dev_id; + struct device *dev = ltc3676->dev; + unsigned int i, irqstat, event; + + regmap_read(ltc3676->regmap, LTC3676_IRQSTAT, &irqstat); + + dev_dbg(dev, "irq%d irqstat=0x%02x\n", irq, irqstat); + if (irqstat & LTC3676_IRQSTAT_THERMAL_WARN) { + dev_info(dev, "Over-temperature Warning\n"); + event = REGULATOR_EVENT_OVER_TEMP; + for (i = 0; i < LTC3676_NUM_REGULATORS; i++) + regulator_notifier_call_chain(ltc3676->regulators[i], + event, NULL); + } + + if (irqstat & LTC3676_IRQSTAT_UNDERVOLT_WARN) { + dev_info(dev, "Undervoltage Warning\n"); + event = REGULATOR_EVENT_UNDER_VOLTAGE; + for (i = 0; i < LTC3676_NUM_REGULATORS; i++) + regulator_notifier_call_chain(ltc3676->regulators[i], + event, NULL); + } + + /* Clear warning condition */ + regmap_write(ltc3676->regmap, LTC3676_CLIRQ, 0); + + return IRQ_HANDLED; +} + +static inline unsigned int ltc3676_scale(unsigned int uV, u32 r1, u32 r2) +{ + uint64_t tmp; + if (uV == 0) + return 0; + tmp = (uint64_t)uV * r1; + do_div(tmp, r2); + return uV + (unsigned int)tmp; +} + +static void ltc3676_apply_fb_voltage_divider(struct ltc3676_regulator *rdesc) +{ + struct regulator_desc *desc = &rdesc->desc; + + if (!rdesc->r1 || !rdesc->r2) + return; + + desc->min_uV = ltc3676_scale(desc->min_uV, rdesc->r1, rdesc->r2); + desc->uV_step = ltc3676_scale(desc->uV_step, rdesc->r1, rdesc->r2); + desc->fixed_uV = ltc3676_scale(desc->fixed_uV, rdesc->r1, rdesc->r2); +} + +static int ltc3676_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ltc3676_regulator *descs; + struct ltc3676 *ltc3676; + int i, ret; + + ltc3676 = devm_kzalloc(dev, sizeof(*ltc3676), GFP_KERNEL); + if (!ltc3676) + return -ENOMEM; + + i2c_set_clientdata(client, ltc3676); + ltc3676->dev = dev; + + descs = ltc3676->regulator_descs; + memcpy(descs, ltc3676_regulators, sizeof(ltc3676_regulators)); + descs[LTC3676_LDO3].desc.fixed_uV = 1800000; + + ltc3676->regmap = devm_regmap_init_i2c(client, <c3676_regmap_config); + if (IS_ERR(ltc3676->regmap)) { + ret = PTR_ERR(ltc3676->regmap); + dev_err(dev, "failed to initialize regmap: %d\n", ret); + return ret; + } + + ret = ltc3676_parse_regulators_dt(ltc3676); + if (ret) + return ret; + + for (i = 0; i < LTC3676_NUM_REGULATORS; i++) { + struct ltc3676_regulator *rdesc = <c3676->regulator_descs[i]; + struct regulator_desc *desc = &rdesc->desc; + struct regulator_init_data *init_data; + struct regulator_config config = { }; + + init_data = match_init_data(i); + + if (!rdesc->np) + continue; + + if (i != LTC3676_LDO3) { + /* skip unused (defined by r1=r2=0) */ + if (rdesc->r1 == 0 && rdesc->r2 == 0) + continue; + ltc3676_apply_fb_voltage_divider(rdesc); + } + + config.dev = dev; + config.init_data = init_data; + config.driver_data = ltc3676; + config.of_node = match_of_node(i); + + ltc3676->regulators[i] = regulator_register(desc, &config); + if (IS_ERR(ltc3676->regulators[i])) { + ret = PTR_ERR(ltc3676->regulators[i]); + dev_err(dev, "failed to register regulator %s: %d\n", + desc->name, ret); + return ret; + } + } + + regmap_write(ltc3676->regmap, LTC3676_CLIRQ, 0); + ret = devm_request_threaded_irq(dev, client->irq, NULL, ltc3676_isr, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, ltc3676); + if (ret) { + dev_err(dev, "Failed to request IRQ: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id ltc3676_i2c_id[] = { + { "ltc3676" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id); + +static struct i2c_driver ltc3676_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = ltc3676_regulator_probe, + .id_table = ltc3676_i2c_id, +}; +module_i2c_driver(ltc3676_driver); + +MODULE_AUTHOR("Tim Harvey "); +MODULE_DESCRIPTION("Regulator Driver for Linear Technology LTC1376"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("i2c:ltc3676"); From 0f45717711ad657dbab6ce96643ec63eb7aca6ef Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 10 Jun 2014 10:43:04 -0700 Subject: [PATCH 0765/1983] mxc_ipuv3: show ipu_id/disp_id of registered device Signed-off-by: Tim Harvey --- drivers/video/mxc/mxc_ipuv3_fb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 89b666783caa9d..db97f885db3b34 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -1973,7 +1973,8 @@ static int mxcfb_dispdrv_init(struct platform_device *pdev, mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting); if (IS_ERR(mxcfbi->dispdrv)) { ret = PTR_ERR(mxcfbi->dispdrv); - dev_err(&pdev->dev, "NO mxc display driver found!\n"); + dev_err(&pdev->dev, "NO mxc display driver found for %s!\n", + disp_dev); return ret; } else { /* fix-up */ @@ -1988,6 +1989,8 @@ static int mxcfb_dispdrv_init(struct platform_device *pdev, setting.if_fmt, setting.default_bpp, mxcfbi->ipu_di, mxcfbi->ipu_id); } + dev_info(&pdev->dev, "registered mxc display driver %s IPU%d_DISP%d\n", + disp_dev, mxcfbi->ipu_id + 1, mxcfbi->ipu_di); dev_info(&pdev->dev, "registered mxc display driver %s\n", disp_dev); From c6cb8ee7ff7afa62fd1b26b152007ec9f0b03caa Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 10 Jun 2014 12:57:55 -0700 Subject: [PATCH 0766/1983] mxc_ipuv3: show device name for overlay fb Signed-off-by: Tim Harvey --- drivers/video/mxc/mxc_ipuv3_fb.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index db97f885db3b34..9a3764f3d8f8e4 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -1923,8 +1923,16 @@ static ssize_t show_disp_dev(struct device *dev, struct fb_info *info = dev_get_drvdata(dev); struct mxcfb_info *mxcfbi = (struct mxcfb_info *)info->par; - if (mxcfbi->ipu_ch == MEM_FG_SYNC) - return sprintf(buf, "overlay\n"); + /* foreground */ + if (mxcfbi->ipu_ch == MEM_FG_SYNC) { + struct fb_info *fbi_tmp; + fbi_tmp = found_registered_fb(MEM_BG_SYNC, mxcfbi->ipu_id); + if (!fbi_tmp) + return sprintf(buf, "overlay\n"); + mxcfbi = ((struct mxcfb_info *)(fbi_tmp->par)); + return sprintf(buf, "%s overlay\n", mxcfbi->dispdrv->drv->name); + } + /* background */ else return sprintf(buf, "%s\n", mxcfbi->dispdrv->drv->name); } From a76dd4d3eaaf5950090a826e924c288b903f9341 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 19 Mar 2015 08:43:39 -0700 Subject: [PATCH 0767/1983] mxc_hdmi: present sysfs edid as binary data instead of ascii Its more flexible to present EDID as binary data (if users want to process it with any number of tools that expect a binary file). If an ascii hexdump is desired just use 'hexdump' on it. Signed-off-by: Tim Harvey --- drivers/video/mxc/mxc_hdmi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index eb26b4e1bb26f7..dd8405b82f6bb3 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -306,16 +306,12 @@ static ssize_t mxc_hdmi_show_edid(struct device *dev, struct device_attribute *attr, char *buf) { struct mxc_hdmi *hdmi = dev_get_drvdata(dev); - int i, j, len = 0; + int j; - for (j = 0; j < HDMI_EDID_LEN/16; j++) { - for (i = 0; i < 16; i++) - len += sprintf(buf+len, "0x%02X ", - hdmi->edid[j*16 + i]); - len += sprintf(buf+len, "\n"); - } + for (j = 0; j < HDMI_EDID_LEN; j++) + buf[j] = hdmi->edid[j]; - return len; + return HDMI_EDID_LEN; } static DEVICE_ATTR(edid, S_IRUGO, mxc_hdmi_show_edid, NULL); From 15a4242cf4ed504d3c319288312e0e64085dfa60 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 12 Jun 2014 14:27:38 -0700 Subject: [PATCH 0768/1983] mxc_capture: add device registration info Show the ipu/csi and video device node info when registering a capture device. Signed-off-by: Tim Harvey --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index b3e4d780fcddb2..a243b5f6c13c9a 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -3031,8 +3031,11 @@ static int mxc_v4l2_probe(struct platform_device *pdev) pr_err("ERROR: v4l2 capture: video_register_device failed\n"); return -1; } - pr_debug(" Video device registered: %s #%d\n", - cam->video_dev->name, cam->video_dev->minor); + + pr_info("V4L2 device '%s' on IPU%d_CSI%d registered as %s\n", + cam->video_dev->name, + cam->ipu_id + 1, cam->csi, + video_device_node_name(cam->video_dev)); if (device_create_file(&cam->video_dev->dev, &dev_attr_fsl_v4l2_capture_property)) From 2b294b5b7d93d9be391f58f03d2e2c560a433b7d Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 4 Mar 2014 10:13:10 -0800 Subject: [PATCH 0769/1983] mxc_capture: check for init_camera_struct failure Return with error if init_camera_struct has failed instead of crashing. Signed-off-by: Tim Harvey --- drivers/media/platform/mxc/capture/mxc_v4l2_capture.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index a243b5f6c13c9a..efe039c4d05407 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -3009,6 +3009,8 @@ static DEVICE_ATTR(fsl_csi_property, S_IRUGO, show_csi, NULL); */ static int mxc_v4l2_probe(struct platform_device *pdev) { + int err; + /* Create cam and initialize it. */ cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL); if (cam == NULL) { @@ -3016,7 +3018,9 @@ static int mxc_v4l2_probe(struct platform_device *pdev) return -1; } - init_camera_struct(cam, pdev); + err = init_camera_struct(cam, pdev); + if (err) + return err; pdev->dev.release = camera_platform_release; /* Set up the v4l2 device and register it*/ From e7361cdebe912da0c7a4fb41bb9dbac09c2af27b Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 3 Sep 2013 16:30:47 -0700 Subject: [PATCH 0770/1983] mfd: add tda1997x HDMI receiver support Signed-off-by: Tim Harvey --- Documentation/tda1997x | 31 + .../media/platform/mxc/capture/ipu_csi_enc.c | 15 +- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/tda1997x-core.c | 4605 +++++++++++++++++ include/linux/mfd/tda1997x-core.h | 225 + 6 files changed, 4884 insertions(+), 1 deletion(-) create mode 100644 Documentation/tda1997x create mode 100644 drivers/mfd/tda1997x-core.c create mode 100644 include/linux/mfd/tda1997x-core.h diff --git a/Documentation/tda1997x b/Documentation/tda1997x new file mode 100644 index 00000000000000..01142249d3b784 --- /dev/null +++ b/Documentation/tda1997x @@ -0,0 +1,31 @@ +The NXP TDA19971/19972 are HDMI receiver devices that decode HDMI +input signals and present a configurable parallel video output bus and +audio output bus. The internal video bus is 36bits while the output bus +differs per device. These devices offer High Definition (HD) video resolutions +up to 1080p50/60 or WUXGA and HD audio formats up to 8 channels such as DTS HD +and Dolby True HD. The chips optionally include an HDCP 1.4 engine with +pre-programmed keys stored into an internal NV memory. Additionally the chips +also supports several HDMI 1.4b options such as 3D formats up to 1080p50/60, +Deep Colors up to 36bpp and extended colorimetry. + +The TDA19971 has one HDMI input (HDMI-A) and 24bit output bus and the TDA19972 +has two HDMI inputs (HDMI-A/B) and a 36bit video output bus. + +Driver Details: +--------------- + +The chips respond to two i2c slave addresses, the first allows access to +HDMI input, audio, and video status and configuration and the second allows +access to CEC. + +The tda1997x-core driver attaches to the i2c slave that controls the device. +It also manages the 2nd i2c slave for CEC. The platform data structure +provides details about the desired video output bus configuration and the +desired audio output bus configuration. An ASoC codec driver is also provided +however this is merely a skeleton driver as the audio output format cannot +be changed and is dependent upon the HDMI input signal. A separate platform +specific device video driver can interact with the core to obtain information +about the video data format which is dependent upon the HDMI input signal. A +separate platform specific ASoC SoC DAI driver can interact with the core +to obtain information about the audio data format which is dependent upon +the HDMI input signal. diff --git a/drivers/media/platform/mxc/capture/ipu_csi_enc.c b/drivers/media/platform/mxc/capture/ipu_csi_enc.c index 1a0ffe449764fd..8719870f785b65 100644 --- a/drivers/media/platform/mxc/capture/ipu_csi_enc.c +++ b/drivers/media/platform/mxc/capture/ipu_csi_enc.c @@ -69,6 +69,12 @@ static int csi_enc_setup(cam_data *cam) u32 pixel_fmt; int err = 0, sensor_protocol = 0; dma_addr_t dummy = cam->dummy_frame.buffer.m.offset; +#ifdef CONFIG_MXC_MIPI_CSI2 + void *mipi_csi2_info; + int ipu_id; + int csi_id; +#endif + struct v4l2_format cam_fmt; CAMERA_TRACE("In csi_enc_setup\n"); if (!cam) { @@ -105,7 +111,14 @@ static int csi_enc_setup(cam_data *cam) else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) pixel_fmt = IPU_PIX_FMT_YUV422P; else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) - pixel_fmt = IPU_PIX_FMT_UYVY; + { + cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt); + if (cam_fmt.fmt.pix.pixelformat == IPU_PIX_FMT_GENERIC_16) + pixel_fmt = cam_fmt.fmt.pix.pixelformat; + else + pixel_fmt = IPU_PIX_FMT_UYVY; + } else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) pixel_fmt = IPU_PIX_FMT_YUYV; else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 459c14cd01c04b..40c6d3eccf4bbc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1215,6 +1215,14 @@ config MFD_STW481X in various ST Microelectronics and ST-Ericsson embedded Nomadik series. +config MFD_TDA1997X + tristate "TDA1997X HDMI Receiver Core" + select MFD_CORE + depends on I2C=y + help + This is the core driver for the TDA1997X HDMI Reciver. This MFD + driver connects with video and audio drivers for HDMI Input. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c0aedc393eea77..1436f639c97748 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -168,3 +168,4 @@ obj-$(CONFIG_MFD_AS3711) += as3711.o obj-$(CONFIG_MFD_AS3722) += as3722.o obj-$(CONFIG_MFD_STW481X) += stw481x.o obj-$(CONFIG_MFD_MXC_HDMI) += mxc-hdmi-core.o +obj-$(CONFIG_MFD_TDA1997X) += tda1997x-core.o diff --git a/drivers/mfd/tda1997x-core.c b/drivers/mfd/tda1997x-core.c new file mode 100644 index 00000000000000..0b9bffa438ff44 --- /dev/null +++ b/drivers/mfd/tda1997x-core.c @@ -0,0 +1,4605 @@ +/* + * Copyright (C) 2013 Gateworks Corporation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * TODO + * - add gpio reset pin + * - add gpio pwrdn pin + * - document devicetree bindings + * - unload/reload module interrupts never fire (something not getting reset) + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Voltage regulators */ +#define TDA1997X_VOLTAGE_DIGITAL_IO 3300000 +#define TDA1997X_VOLTAGE_DIGITAL_CORE 1800000 +#define TDA1997X_VOLTAGE_ANALOG 1800000 + +static struct regulator *dvddio_regulator; +static struct regulator *dvdd_regulator; +static struct regulator *avdd_regulator; + +/* Page 0x00 */ +#define REG_VERSION 0x0000 +#define REG_INPUT_SEL 0x0001 +#define REG_SERVICE_MODE 0x0002 +#define REG_HPD_MAN_CTRL 0x0003 +#define REG_RT_MAN_CTRL 0x0004 +#define REG_STANDBY_SOFT_RST 0x000A +#define REG_HDMI_SOFT_RST 0x000B +#define REG_HDMI_INFO_RST 0x000C +#define REG_INT_FLG_CLR_TOP 0x000E +#define REG_INT_FLG_CLR_SUS 0x000F +#define REG_INT_FLG_CLR_DDC 0x0010 +#define REG_INT_FLG_CLR_RATE 0x0011 +#define REG_INT_FLG_CLR_MODE 0x0012 +#define REG_INT_FLG_CLR_INFO 0x0013 +#define REG_INT_FLG_CLR_AUDIO 0x0014 +#define REG_INT_FLG_CLR_HDCP 0x0015 +#define REG_INT_FLG_CLR_AFE 0x0016 +#define REG_INT_MASK_TOP 0x0017 +#define REG_INT_MASK_SUS 0x0018 +#define REG_INT_MASK_DDC 0x0019 +#define REG_INT_MASK_RATE 0x001A +#define REG_INT_MASK_MODE 0x001B +#define REG_INT_MASK_INFO 0x001C +#define REG_INT_MASK_AUDIO 0x001D +#define REG_INT_MASK_HDCP 0x001E +#define REG_INT_MASK_AFE 0x001F +#define REG_DETECT_5V 0x0020 +#define REG_SUS_STATUS 0x0021 +#define REG_V_PER 0x0022 +#define REG_H_PER 0x0025 +#define REG_HS_WIDTH 0x0027 +#define REG_FMT_H_TOT 0x0029 +#define REG_FMT_H_ACT 0x002b +#define REG_FMT_H_FRONT 0x002d +#define REG_FMT_H_SYNC 0x002f +#define REG_FMT_H_BACK 0x0031 +#define REG_FMT_V_TOT 0x0033 +#define REG_FMT_V_ACT 0x0035 +#define REG_FMT_V_FRONT_F1 0x0037 +#define REG_FMT_V_FRONT_F2 0x0038 +#define REG_FMT_V_SYNC 0x0039 +#define REG_FMT_V_BACK_F1 0x003a +#define REG_FMT_V_BACK_F2 0x003b +#define REG_FMT_DE_ACT 0x003c +#define REG_RATE_CTRL 0x0040 +#define REG_CLK_MIN_RATE 0x0043 +#define REG_CLK_MAX_RATE 0x0046 +#define REG_CLK_A_STATUS 0x0049 +#define REG_CLK_A_RATE 0x004A +#define REG_DRIFT_CLK_A_REG 0x004D +#define REG_CLK_B_STATUS 0x004E +#define REG_CLK_B_RATE 0x004F +#define REG_DRIFT_CLK_B_REG 0x0052 +#define REG_HDCP_CTRL 0x0060 +#define REG_HDCP_KDS 0x0061 +#define REG_HDCP_BCAPS 0x0063 +#define REG_HDCP_KEY_CTRL 0x0064 +#define REG_INFO_CTRL 0x0076 +#define REG_INFO_EXCEED 0x0077 +#define REG_PIX_REPEAT 0x007B +#define REG_AUDIO_PATH 0x007C +#define REG_AUDIO_SEL 0x007D +#define REG_AUDIO_OUT_ENABLE 0x007E +#define REG_AUDIO_OUT_HIZ 0x007F +#define REG_VDP_CTRL 0x0080 +#define REG_VHREF_CTRL 0x00A0 +#define REG_PXCNT_PR 0x00A2 +#define REG_PXCNT_NPIX 0x00A4 +#define REG_LCNT_PR 0x00A6 +#define REG_LCNT_NLIN 0x00A8 +#define REG_HREF_S 0x00AA +#define REG_HREF_E 0x00AC +#define REG_HS_S 0x00AE +#define REG_HS_E 0x00B0 +#define REG_VREF_F1_S 0x00B2 +#define REG_VREF_F1_WIDTH 0x00B4 +#define REG_VREF_F2_S 0x00B5 +#define REG_VREF_F2_WIDTH 0x00B7 +#define REG_VS_F1_LINE_S 0x00B8 +#define REG_VS_F1_LINE_WIDTH 0x00BA +#define REG_VS_F2_LINE_S 0x00BB +#define REG_VS_F2_LINE_WIDTH 0x00BD +#define REG_VS_F1_PIX_S 0x00BE +#define REG_VS_F1_PIX_E 0x00C0 +#define REG_VS_F2_PIX_S 0x00C2 +#define REG_VS_F2_PIX_E 0x00C4 +#define REG_FREF_F1_S 0x00C6 +#define REG_FREF_F2_S 0x00C8 +#define REG_FDW_S 0x00ca +#define REG_FDW_E 0x00cc +#define REG_BLK_GY 0x00da +#define REG_BLK_BU 0x00dc +#define REG_BLK_RV 0x00de +#define REG_FILTERS_CTRL 0x00e0 +#define REG_DITHERING_CTRL 0x00E9 +#define REG_OF_CTRL 0x00EA +#define REG_CLKOUT_CTRL 0x00EB +#define REG_HS_HREF_SEL 0x00EC +#define REG_VS_VREF_SEL 0x00ED +#define REG_DE_FREF_SEL 0x00EE +#define REG_VP35_32_CTRL 0x00EF +#define REG_VP31_28_CTRL 0x00F0 +#define REG_VP27_24_CTRL 0x00F1 +#define REG_VP23_20_CTRL 0x00F2 +#define REG_VP19_16_CTRL 0x00F3 +#define REG_VP15_12_CTRL 0x00F4 +#define REG_VP11_08_CTRL 0x00F5 +#define REG_VP07_04_CTRL 0x00F6 +#define REG_VP03_00_CTRL 0x00F7 +#define REG_CURPAGE_00H 0xFF +#define MASK_VPER 0x3fffff +#define MASK_HPER 0x0fff +#define MASK_HSWIDTH 0x03ff + +/* Page 0x01 */ +#define REG_HDMI_FLAGS 0x0100 +#define REG_DEEP_COLOR_MODE 0x0101 +#define REG_AUDIO_FLAGS 0x0108 +#define REG_AUDIO_FREQ 0x0109 +#define REG_ACP_PACKET_TYPE 0x0141 +#define REG_ISRC1_PACKET_TYPE 0x0161 +#define REG_ISRC2_PACKET_TYPE 0x0181 +#define REG_GBD_PACKET_TYPE 0x01a1 +#define ISRC_PACKET_HDR_LEN 3 +#define ISRC_PACKET_DAT_LEN 16 +#define GDB_PACKET_HDR_LEN 3 +#define GDB_PACKET_DAT_LEN 28 +#define ACP_PACKET_HDR_LEN 3 +#define ACP_PACKET_DAT_LEN 16 +#define MASK_AUDIO_DST_RATE 0x80 +#define MASK_AUDIO_FREQ 0x07 +#define MASK_DC_PIXEL_PHASE 0xf0 +#define MASK_DC_COLOR_DEPTH 0x0f + +/* Page 0x12 */ +#define REG_CLK_CFG 0x1200 +#define REG_CLK_OUT_CFG 0x1201 +#define REG_CFG1 0x1202 +#define REG_CFG2 0x1203 +#define REG_WDL_CFG 0x1210 +#define REG_DELOCK_DELAY 0x1212 +#define REG_PON_OVR_EN 0x12A0 +#define REG_PON_CBIAS 0x12A1 +#define REG_PON_RESCAL 0x12A2 +#define REG_PON_RES 0x12A3 +#define REG_PON_CLK 0x12A4 +#define REG_PON_PLL 0x12A5 +#define REG_PON_EQ 0x12A6 +#define REG_PON_DES 0x12A7 +#define REG_PON_OUT 0x12A8 +#define REG_PON_MUX 0x12A9 +#define REG_MODE_RECOVER_CFG1 0x12F8 +#define REG_MODE_RECOVER_CFG2 0x12F9 +#define REG_MODE_RECOVER_STS 0x12FA +#define REG_AUDIO_LAYOUT 0x12D0 + +/* Page 0x13 */ +#define REG_DEEP_COLOR_CTRL 0x1300 +#define REG_CGU_DEBUG_SEL 0x1305 +#define REG_HDCP_DDC_ADDR 0x1310 +#define REG_HDCP_KIDX 0x1316 +#define REG_DEEP_PLL7 0x1347 +#define REG_HDCP_DE_CTRL 0x1370 +#define REG_HDCP_EP_FILT_CTRL 0x1371 +#define REG_HDMI_CTRL 0x1377 +#define REG_HMTP_CTRL 0x137a +#define REG_TIMER_D 0x13CF +#define REG_SUS_SET_RGB0 0x13E1 +#define REG_SUS_SET_RGB1 0x13E2 +#define REG_SUS_SET_RGB2 0x13E3 +#define REG_SUS_SET_RGB3 0x13E4 +#define REG_SUS_SET_RGB4 0x13E5 +#define REG_MAN_SUS_HDMI_SEL 0x13E8 +#define REG_MAN_HDMI_SET 0x13E9 +#define REG_SUS_CLOCK_GOOD 0x13EF + +/* CGU_DEBUG_SEL bits */ +#define CGU_DEBUG_CFG_CLK_MASK 0x18 +#define CGU_DEBUG_XO_FRO_SEL (1<<2) +#define CGU_DEBUG_VDP_CLK_SEL (1<<1) +#define CGU_DEBUG_PIX_CLK_SEL (1<<0) + +/* REG_MAN_SUS_HDMI_SEL / REG_MAN_HDMI_SET bits */ +#define MAN_DIS_OUT_BUF (1<<7) +#define MAN_DIS_ANA_PATH (1<<6) +#define MAN_DIS_HDCP (1<<5) +#define MAN_DIS_TMDS_ENC (1<<4) +#define MAN_DIS_TMDS_FLOW (1<<3) +#define MAN_RST_HDCP (1<<2) +#define MAN_RST_TMDS_ENC (1<<1) +#define MAN_RST_TMDS_FLOW (1<<0) + +/* Page 0x14 */ +#define REG_FIFO_LATENCY_VAL 0x1403 +#define REG_AUDIO_CLOCK_MODE 0x1411 +#define REG_TEST_NCTS_CTRL 0x1415 +#define REG_TEST_AUDIO_FREQ 0x1426 +#define REG_TEST_MODE 0x1437 + +/* Page 0x20 */ +#define REG_EDID_IN_BYTE0 0x2000 /* EDID base */ +#define REG_EDID_IN_VERSION 0x2080 +#define REG_EDID_ENABLE 0x2081 +#define REG_HPD_POWER 0x2084 +#define REG_HPD_AUTO_CTRL 0x2085 +#define REG_HPD_DURATION 0x2086 +#define REG_RX_HPD_HEAC 0x2087 + +/* Page 0x21 */ +#define REG_EDID_IN_BYTE128 0x2100 /* CEA Extension block */ +#define REG_EDID_IN_SPA_SUB 0x2180 +#define REG_EDID_IN_SPA_AB_A 0x2181 +#define REG_EDID_IN_SPA_CD_A 0x2182 +#define REG_EDID_IN_CKSUM_A 0x2183 +#define REG_EDID_IN_SPA_AB_B 0x2184 +#define REG_EDID_IN_SPA_CD_B 0x2185 +#define REG_EDID_IN_CKSUM_B 0x2186 + +/* Page 0x30 */ +#define REG_RT_AUTO_CTRL 0x3000 +#define REG_EQ_MAN_CTRL0 0x3001 +#define REG_EQ_MAN_CTRL1 0x3002 +#define REG_OUTPUT_CFG 0x3003 +#define REG_MUTE_CTRL 0x3004 +#define REG_SLAVE_ADDR 0x3005 +#define REG_CMTP_REG6 0x3006 +#define REG_CMTP_REG7 0x3007 +#define REG_CMTP_REG8 0x3008 +#define REG_CMTP_REG9 0x3009 +#define REG_CMTP_REGA 0x300A +#define REG_CMTP_REGB 0x300B +#define REG_CMTP_REGC 0x300C +#define REG_CMTP_REGD 0x300D +#define REG_CMTP_REGE 0x300E +#define REG_CMTP_REGF 0x300F +#define REG_CMTP_REG10 0x3010 +#define REG_CMTP_REG11 0x3011 + +/* CEC */ +#define REG_PWR_CONTROL 0x80F4 +#define REG_OSC_DIVIDER 0x80F5 +#define REG_EN_OSC_PERIOD_LSB 0x80F8 +#define REG_CONTROL 0x80FF + +/* global interrupt flags (INT_FLG_CRL_TOP) */ +#define INTERRUPT_AFE (1<<7) /* AFE module */ +#define INTERRUPT_HDCP (1<<6) /* HDCP module */ +#define INTERRUPT_AUDIO (1<<5) /* Audio module */ +#define INTERRUPT_INFO (1<<4) /* Infoframe module */ +#define INTERRUPT_MODE (1<<3) /* HDMI mode module */ +#define INTERRUPT_RATE (1<<2) /* rate module */ +#define INTERRUPT_DDC (1<<1) /* DDC module */ +#define INTERRUPT_SUS (1<<0) /* SUS module */ + +/* INT_FLG_CLR_HDCP bits */ +#define MASK_HDCP_MTP (1<<7) /* HDCP MTP busy */ +#define MASK_HDCP_DLMTP (1<<4) /* HDCP end download MTP to SRAM */ +#define MASK_HDCP_DLRAM (1<<3) /* HDCP end download keys from SRAM */ +#define MASK_HDCP_ENC (1<<2) /* HDCP ENC */ +#define MASK_STATE_C5 (1<<1) /* HDCP State C5 reached */ +#define MASK_AKSV (1<<0) /* AKSV received (start of auth) */ + +/* INT_FLG_CLR_RATE bits */ +#define MASK_RATE_B_DRIFT (1<<7) /* Rate measurement drifted */ +#define MASK_RATE_B_ST (1<<6) /* Rate measurement stability change */ +#define MASK_RATE_B_ACT (1<<5) /* Rate measurement activity change */ +#define MASK_RATE_B_PST (1<<4) /* Rate measreument presence change */ +#define MASK_RATE_A_DRIFT (1<<3) /* Rate measurement drifted */ +#define MASK_RATE_A_ST (1<<2) /* Rate measurement stability change */ +#define MASK_RATE_A_ACT (1<<1) /* Rate measurement presence change */ +#define MASK_RATE_A_PST (1<<0) /* Rate measreument presence change */ + +/* INT_FLG_CLR_SUS (Start Up Sequencer) bits */ +#define MASK_MPT_BIT (1<<7) /* Config MTP end of process */ +#define MASK_FMT_BIT (1<<5) /* Video format changed */ +#define MASK_RT_PULSE_BIT (1<<4) /* End of termination resistance pulse */ +#define MASK_SUS_END_BIT (1<<3) /* SUS last state reached */ +#define MASK_SUS_ACT_BIT (1<<2) /* Activity of selected input changed */ +#define MASK_SUS_CH_BIT (1<<1) /* Selected input changed */ +#define MASK_SUS_ST_BIT (1<<0) /* SUS state changed */ + +/* INT_FLG_CLR_DDC bits */ +#define MASK_EDID_MTP (1<<7) /* EDID MTP end of process */ +#define MASK_DDC_ERR (1<<6) /* master DDC error */ +#define MASK_DDC_CMD_DONE (1<<5) /* master DDC cmd send correct */ +#define MASK_READ_DONE (1<<4) /* End of down EDID read */ +#define MASK_RX_DDC_SW (1<<3) /* Output DDC switching finished */ +#define MASK_HDCP_DDC_SW (1<<2) /* HDCP DDC switching finished */ +#define MASK_HDP_PULSE_END (1<<1) /* End of Hot Plug Detect pulse */ +#define MASK_DET_5V (1<<0) /* Detection of +5V */ + +/* INT_FLG_CLR_MODE bits */ +#define MASK_HDMI_FLG (1<<7) /* HDMI mode, avmute, encrypt-on, FIFO fail */ +#define MASK_GAMUT (1<<6) /* Gamut packet */ +#define MASK_ISRC2 (1<<5) /* ISRC2 packet */ +#define MASK_ISRC1 (1<<4) /* ISRC1 packet */ +#define MASK_ACP (1<<3) /* Audio Content Protection packet */ +#define MASK_DC_NO_GCP (1<<2) /* GCP not recieved in 5 frames */ +#define MASK_DC_PHASE (1<<1) /* deep color mode pixel phase needs update */ +#define MASK_DC_MODE (1<<0) /* deep color mode color depth changed */ + +/* INT_FLG_CLR_INFO bits */ +#define MASK_MPS_IF (1<<6) /* MPEG Source Product IF change */ +#define MASK_AUD_IF (1<<5) /* Audio IF change */ +#define MASK_SPD_IF (1<<4) /* Source Product Descriptor IF change */ +#define MASK_AVI_IF (1<<3) /* Auxiliary Video information IF change */ +#define MASK_VS_IF_OTHER_BK2 (1<<2) /* Vendor Specific IF (bank2) change */ +#define MASK_VS_IF_OTHER_BK1 (1<<1) /* Vendor Specific IF (bank1) change */ +#define MASK_VS_IF_HDMI (1<<0) /* Vendor Specific IF (with HDMI LLC reg code) change */ + +/* INT_FLG_CLR_AUDIO bits */ +#define MASK_AUDIO_FREQ_FLG (1<<5) /* Audio freq change */ +#define MASK_AUDIO_FLG (1<<4) /* DST, OBA, HBR, ASP change */ +#define MASK_MUTE_FLG (1<<3) /* Audio Mute */ +#define MASK_CH_STATE (1<<2) /* Channel status */ +#define MASK_UNMUTE_FIFO (1<<1) /* Audio Unmute */ +#define MASK_ERROR_FIFO_PT (1<<0) /* Audio FIFO pointer error */ + +/* INT_FLG_CLR_AFE bits */ +#define MASK_AFE_WDL_UNLOCKED (1<<7) /* Wordlocker was unlocked */ +#define MASK_AFE_GAIN_DONE (1<<6) /* Gain calibration done */ +#define MASK_AFE_OFFSET_DONE (1<<5) /* Offset calibration done */ +#define MASK_AFE_ACTIVITY_DET (1<<4) /* Activity detected on data */ +#define MASK_AFE_PLL_LOCK (1<<3) /* TMDS PLL is locked */ +#define MASK_AFE_TRMCAL_DONE (1<<2) /* Termination calibration done */ +#define MASK_AFE_ASU_STATE (1<<1) /* ASU state is reached */ +#define MASK_AFE_ASU_READY (1<<0) /* AFE calibration done: TMDS ready */ + +/* OF_CTRL bits */ +#define VP_OUT (1<<7) /* enable VP[35:0], HS, VS, DE, V_CLK */ +#define VP_HIZ (1<<6) /* unused VP pins Hi-Z */ +#define VP_BLK (1<<4) /* Insert blanking code in data */ +#define VP_TRC (1<<3) /* Insert timing code (SAV/EAV) in data*/ +#define VP_FORMAT_SEL_MASK 0x7 /* format selection */ + +/* HDMI_SOFT_RST bits */ +#define RESET_DC (1<<7) /* Reset deep color module */ +#define RESET_HDCP (1<<6) /* Reset HDCP module */ +#define RESET_KSV (1<<5) /* Reset KSV-FIFO */ +#define RESET_SCFG (1<<4) /* Reset HDCP and repeater function */ +#define RESET_HCFG (1<<3) /* Reset HDCP DDC part */ +#define RESET_PA (1<<2) /* Reset polarity adjust */ +#define RESET_EP (1<<1) /* Reset Error protection */ +#define RESET_TMDS (1<<0) /* Reset TMDS (calib, encoding, flow) */ + +/* HDMI_INFO_RST bits */ +#define NACK_HDCP (1<<7) /* No ACK on HDCP request */ +#define RESET_FIFO (1<<4) /* Reset Audio FIFO control */ +#define RESET_GAMUT (1<<3) /* Clear Gamut packet */ +#define RESET_AI (1<<2) /* Clear ACP and ISRC packets */ +#define RESET_IF (1<<1) /* Clear all Audio infoframe packets */ +#define RESET_AUDIO (1<<0) /* Reset Audio FIFO control */ + +/* HDCP_BCAPS bits */ +#define HDCP_HDMI (1<<7) /* HDCP suports HDMI (vs DVI only) */ +#define HDCP_REPEATER (1<<6) /* HDCP supports repeater function */ +#define HDCP_READY (1<<5) /* set by repeater function */ +#define HDCP_FAST (1<<4) /* Up to 400kHz */ +#define HDCP_11 (1<<1) /* HDCP 1.1 supported */ +#define HDCP_FAST_REAUTH (1<<0) /* fast reauthentication suported */ + +/* masks for interrupt status registers */ +#define MASK_SUS_STATE_VALUE 0x1F +#define LAST_STATE_REACHED 0x1B +#define MASK_CLK_STABLE 0x04 +#define MASK_CLK_ACTIVE 0x02 +#define MASK_SUS_STATE_BIT 0x10 +#define MASK_SR_FIFO_FIFO_CTRL 0x30 +#define MASK_AUDIO_FLAG 0x10 + +/* Power Control */ +#define MASK_OF_CTRL_OUT_HIZ 0x80 +#define MASK_AUDIO_PLL_PD 0x80 +#define DC_PLL_PD 0x01 +#define DC_PLL_PON 0x00 +#define MASK_XTAL_OSC_PD 0x02 +#define MASK_TMDS_CLK_DIS 0x08 +#define CBIAS_PON 0x01 +#define CBIAS_POFF 0x00 +#define TMDS_AUTO_PON 0x00 +#define TMDS_MAN_PON 0x01 +#define MASK_LOW_PW_EDID 0x01 + +/* Rate measurement */ +#define RATE_REFTIM_ENABLE 0x01 +#define CLK_MIN_RATE 0x0057e4 +#define CLK_MAX_RATE 0x0395f8 +#define WDL_CFG_VAL 0x82 +#define DC_FILTER_VAL 0x31 + +/* Infoframe */ +#define VS_HDMI_IF_UPDATE 0x0200 +#define VS_HDMI_IF_TYPE 0x0201 +#define VS_BK1_IF_UPDATE 0x0220 +#define VS_BK1_IF_TYPE 0x0221 +#define VS_BK2_IF_UPDATE 0x0240 +#define VS_BK2_IF_TYPE 0x0241 +#define AVI_IF_UPDATE 0x0260 +#define AVI_IF_TYPE 0x0261 +#define AVI_IF_NB_DATA 17 +#define SPD_IF_UPDATE 0x0280 +#define SPD_IF_TYPE 0x0281 +#define SPD_IF_NB_DATA 31 +#define AUD_IF_UPDATE 0x02a0 +#define AUD_IF_TYPE 0x02a1 +#define AUD_IF_NB_DATA 14 +#define MPS_IF_UPDATE 0x02c0 +#define MPS_IF_TYPE 0x02c1 +#define MPS_IF_NB_DATA 14 +#define MAX_IF_DATA 40 +#define VS_IF_NB 31 + +/* Input Selection */ +#define MASK_DIG_INPUT 0x01 +#define MASK_DIG_INPUT_VDPR_FMT 0x85 +#define MASK_HDMIOUTMODE 0x02 +#define FORMAT_RESET 0x80 + +/* Colorspace Conversion Registers */ +#define MAT_OFFSET_NB 3 +#define MAT_COEFF_NB 9 +#define OFFSET_LOOP_NB 2 +#define MIN_VAL_OFFSET -4096 +#define MAX_VAL_OFFSET 4095 +#define MIN_VAL_COEFF -16384 +#define MAX_VAL_COEFF 16383 +#define MASK_MAT_COEFF_LSB 0x00FF + +/* Blanking code values depend on output colorspace (RGB or YUV) */ +typedef struct +{ + s16 blankingCodeGy; + s16 blankingCodeBu; + s16 blankingCodeRv; +} blankingcodes_t; + +blankingcodes_t RGBBlankingCode = {64, 64, 64}; +blankingcodes_t YUVBlankingCode = {64, 512, 512}; + +/* Video Colorspace formats */ +typedef enum { + COLORSPACE_RGB, + COLORSPACE_YCBCR_422, + COLORSPACE_YCBCR_444, + COLORSPACE_FUTURE, +} tda1997x_colorspace_t; + +/* Video Colorimetry formats */ +typedef enum { + COLORIMETRY_NONE, + COLORIMETRY_ITU601, + COLORIMETRY_ITU709, + COLORIMETRY_XVYCC, +} tda1997x_colorimetry_t; + +/* Video colormode formats */ +typedef enum { + DEEPCOLORMODE_NOT_INDICATED = 0x00, + DEEPCOLORMODE_24 = 0x04, + DEEPCOLORMODE_30 = 0x05, + DEEPCOLORMODE_36 = 0x06, + DEEPCOLORMODE_48 = 0x07, +} tda1997x_deepcolor_t; + +/* resolution type */ +typedef enum { + RESTYPE_SDTV, + RESTYPE_HDTV, + RESTYPE_PC, +} tda1997x_restype_t; + +/* Video output port format */ +const char *vidfmt_names[] = { + "RGB444/YUV444", /* RGB/YUV444 16bit data bus, 8bpp */ + "YUV422 semi-planar", /* YUV422 16bit data base, 8bpp */ + "YUV422 CCIR656", /* BT656 (YUV 8bpp 2 clock per pixel) */ +}; + +static char *colorspace_names[] = { + "RGB", "YUV422", "YUV444", "Future" +}; + +static char *colorimetry_names[] = { + "", "ITU601", "ITU709", "XVYCC" +}; + +/* HDCP */ +#define RX_SEED_TABLE_LEN 10 /* HDCP Seed */ +typedef enum +{ + HDCP_DECRYPTKEY_OFF = 0x00, + HDCP_DECRYPTKEY_ON = 0x02 +} hdcp_key_t; +typedef enum +{ + DISABLE = 0x00, + ENABLE = 0x01 +} enable_t; + +/* MTP */ +typedef enum { + MTP_START_DOWNLOAD, + MTP_START_READ, +} mtp_command_t; + +/* HPD modes */ +typedef enum { + HPD_LOW, /* HPD low and pulse of at least 100ms */ + HPD_LOW_OTHER, /* HPD low and pulse of at least 100ms */ + HPD_HIGH, /* HIGH */ + HPD_HIGH_OTHER, + HPD_PULSE, /* HPD low pulse */ +} hpdmode_t; + +/** configure colorspace conversion matrix + * The color conversion matrix will convert between the colorimetry of the + * HDMI input to the desired output format RGB|YUV + */ +typedef enum { + ITU709_RGBLimited, + RGBLimited_ITU601, + ITU601_RGBLimited, +} colorconversion_t; + +/* Colorspace conversion matrix coefficients and offsets + */ +typedef struct +{ + /* Input offsets */ + s16 offInt1; + s16 offInt2; + s16 offInt3; + /* Coeficients */ + s16 P11Coef; + s16 P12Coef; + s16 P13Coef; + s16 P21Coef; + s16 P22Coef; + s16 P23Coef; + s16 P31Coef; + s16 P32Coef; + s16 P33Coef; + /* Output offsets */ + s16 offOut1; + s16 offOut2; + s16 offOut3; +} colormatrixcoefs_t; + +/* Conversion matrixes */ +colormatrixcoefs_t conversion_matrix[] = { + /* ITU709 -> RGBLimited */ + { + -256, -2048, -2048, /*Input Offset*/ + 4096, -1875, -750, + 4096, 6307, 0, + 4096, 0, 7431, + 256, 256, 256 /*Output Offset*/ + }, + /* RGBLimited -> ITU601 */ + { + -256, -256, -256, /*Input Offset*/ + 2404, 1225, 467, + -1754, 2095, -341, + -1388, -707, 2095, /*RGB limited range => ITU-601 YUV limited range */ + 256, 2048, 2048 /*Output Offset*/ + }, + /* YUV601 -> RGBLimited */ + { + -256, -2048, -2048, /*Input Offset*/ + 4096, -2860, -1378, + 4096, 5615, 0, + 4096, 0, 7097, /*ITU-601 YUV limited range => RGB limited range */ + 256, 256, 256 /*Output Offset*/ + } +}; + +/* HDCP seed table, arranged as pairs of 16bit integrers: lookup val, seed val + * If no table is programmed or KEY_SED in config file is null, HDCP will be + * disabled + */ +typedef struct { + u16 lookUpVal; + u16 seedVal; +} hdmi_cfg_seed_t; + +const hdmi_cfg_seed_t rx_seed_table[RX_SEED_TABLE_LEN] = { + {0xF0, 0x1234}, + {0xF1, 0xDBE6}, + {0xF2, 0xDBE6}, + {0, 0x1234}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} +}; + +/** Video Input mode database + * TODO: can I use something like modedb instead? + * More recent kernels have some CEA data + */ +static char *restype_names[] = { + "SDTV", "HDTV", "PC", +}; + +typedef enum +{ + VIDEORES_1280_720p_24HZ, + VIDEORES_1280_720p_25HZ, + VIDEORES_1280_720p_30HZ, + VIDEORES_1920_1080p_24HZ, + VIDEORES_1920_1080p_25HZ, + VIDEORES_1920_1080p_30HZ, + + VIDEORES_720_480p_60HZ, + VIDEORES_1280_720p_60HZ, + VIDEORES_1920_1080i_60HZ, + VIDEORES_720_480i_60HZ, + VIDEORES_1920_1080p_60HZ, + + VIDEORES_720_576p_50HZ, + VIDEORES_1280_720p_50HZ, + VIDEORES_1920_1080i_50HZ, + VIDEORES_720_576i_50HZ, + VIDEORES_1920_1080p_50HZ, + + VIDEORES_640_480p_60HZ, /* VGA */ + VIDEORES_800_600p_60HZ, /* SVGA */ + VIDEORES_1024_768p_60HZ, /* XGA */ + VIDEORES_1280_768p_60HZ, /* WXGA */ + VIDEORES_1280_960p_60HZ, /* ???? */ + VIDEORES_1280_1024p_60HZ, /* SXGA */ + VIDEORES_1440_900p_60HZ, /* ???? */ + VIDEORES_1600_1200p_60HZ, /* UGA */ + VIDEORES_1680_1050p_60HZ_RB, /* WSXGA */ + VIDEORES_1920_1200p_60HZ_RB, /* WUXGA */ + + VIDEORES_640_480p_75HZ, /* VGA */ + VIDEORES_800_600p_75HZ, /* SVGA */ + VIDEORES_1024_768p_75HZ, /* XGA */ + VIDEORES_1280_768p_75HZ, /* WXGA */ + VIDEORES_1280_1024p_75HZ, /* SXGA */ + + VIDEORES_640_480p_85HZ, /* VGA */ + VIDEORES_800_600p_85HZ, /* SVGA */ + VIDEORES_1024_768p_85HZ, /* XGA */ + VIDEORES_1280_768p_85HZ, /* WXGA */ + VIDEORES_1280_1024p_85HZ, /* SXGA */ + + VIDEORES_720_240p_60HZ_M1, /* 720(1440, 2880)x240p 60Hz mode 1 */ + VIDEORES_720_240p_60HZ_M2, /* 720(1440, 2880)x240p 60Hz mode 2 */ + + VIDEORES_720_288p_50HZ_M1, /* 720(1440)x288p 50Hz mode 1 */ + VIDEORES_720_288p_50HZ_M2, /* 720(1440)x288p 50Hz mode 1 */ + VIDEORES_720_288p_50HZ_M3, /* 720(1440)x288p 50Hz mode 1 */ + + VIDEORES_1360_768p_60HZ, /* 1360x768p 60Hz (PC resolution) */ + VIDEORES_1400_1050p_60HZ, /* 1400x1050p 60Hz (PC resolution) */ + VIDEORES_1400_1050p_60HZ_RB, /* 1400x1050p 60Hz Reduced Blanking (PC) */ + + VIDEORES_1024_768p_70HZ, /* XGA */ + VIDEORES_640_480p_72HZ, /* VGA */ + VIDEORES_800_600p_72HZ, /* SVGA */ + + VIDEORES_640_350p_85HZ, /* 640x350p 85Hz (PC) */ + VIDEORES_640_400p_85HZ, /* 640x400p 85Hz (PC) */ + VIDEORES_720_400p_85HZ, /* 720x400p 85Hz (PC) */ + VIDEORES_UNKNOWN +} resolutionid_t; + +/* structure for video format measurements */ +typedef struct +{ + u8 videoFormat; /* 1=interlaced or 0=progressive */ + u8 vsPolarity; /* 1=negative 0=positive */ + u8 hsPolarity; /* 1=negative 0=positive */ + u16 horizontalTotalPeriod; /* period of 1 line (pixel clocks) */ + u16 horizontalVideoActiveWidth; /* period of 1 active line (pixel clocks) */ + u16 horizontalFrontPorchWidth; /* width of front porch */ + u16 horizontalBackPorchWidth; /* width of back porch */ + u16 horizontalSyncWidthPixClk; + u16 verticalTotalPeriod; /* period of a frame in line numbers */ + u16 verticalVideoActiveWidth; + u16 verticalFrontPorchWidthF1; /* vertical front porch width of field 1 */ + u16 verticalFrontPorchWidthF2; /* vertical front porch width of field 2*/ + u16 verticalSyncWidth; /* width of the VS in line numbers */ + u16 verticalBackPorchWidthF1; /* vertical back porch width of field 1 */ + u16 verticalBackPorchWidthF2; /* vertical back porch width of field 2 */ + u16 dataEnablePresent; /* 1=DE signal present */ +} videoFormatDetails; + +typedef struct +{ + u8 resolutionID; + u16 width; + u16 height; + u8 horizfreq; + u8 interlaced; + u32 verticalPeriodMin; /* = MCLK(27MHz) / VFreq minus 0.7% */ + u32 verticalPeriodMax; /* same + 0.7% */ + u16 horizontalPeriodMin; /* = MCLK(27MHz) / HFreq minus 1% */ + u16 horizontalPeriodMax; + u16 hsWidthMin; /* = MCLK(27MHz) / pixclk * hWidth minux ...% */ + u16 hsWidthMax; +} resolution_data_t; + +typedef struct +{ + u16 href_start; + u16 href_end; + u16 vref_f1_start; + u8 vref_f1_width; + u16 vref_f2_start; + u8 vref_f2_width; + u16 fieldref_f1_start; + u8 fieldPolarity; + u16 fieldref_f2_start; +} vhref_values_t; + +typedef struct +{ + u8 resolutionID; + u16 pixCountPreset; + u16 pixCountNb; + u16 lineCountPreset; + u16 lineCountNb; + vhref_values_t vhref_values; +} resolution_timings_t; + +const resolution_timings_t resolution_timings[] = { + /* Low TV */ + {VIDEORES_1280_720p_24HZ, 1, 3300, 1, 750, + {261, 1541, 745, 30, 0, 0, 1, 0, 0} + }, + {VIDEORES_1280_720p_25HZ, 1, 3960, 1, 750, + {261, 1541, 745, 30, 0, 0, 1, 0, 0} + }, + {VIDEORES_1280_720p_30HZ, 1, 3300, 1, 750, + {261, 1541, 745, 30, 0, 0, 1, 0, 0} + }, + {VIDEORES_1920_1080p_24HZ, 1, 2750, 1, 1125, + {193, 2113, 1121, 45, 0, 0, 1, 0, 0} + }, + {VIDEORES_1920_1080p_25HZ, 1, 2640, 1, 1125, + {193, 2113, 1121, 45, 0, 0, 1, 0, 0} + }, + {VIDEORES_1920_1080p_30HZ, 1, 2200, 1, 1125, + {193, 2113, 1121, 45, 0, 0, 1, 0, 0} + }, + + /* 60 Hz TV */ + {VIDEORES_720_480p_60HZ, 1, 858, 1, 525, + {123, 843, 516, 45, 0, 0, 1, 0, 0} + }, + {VIDEORES_1280_720p_60HZ, 1, 1650, 1, 750, + {261, 1541, 745, 30, 0, 0, 1, 0, 0} + }, + {VIDEORES_1920_1080i_60HZ, 1, 2200, 1, 1125, + {193, 2113, 1123, 22, 560, 23, 1, 0, 563} + }, + {VIDEORES_720_480i_60HZ, 1, 858, 1, 525, + {120, 840, 521, 22, 258, 23, 1, 0, 263} + }, + {VIDEORES_1920_1080p_60HZ, 1, 2200, 1, 1125, + {193, 2113, 1121, 45, 0, 0, 1, 0, 0} + }, + + /* 50 Hz TV */ + {VIDEORES_720_576p_50HZ, 1, 864, 1, 625, + {133, 853, 620, 49, 0, 0, 1, 0, 0} + }, + {VIDEORES_1280_720p_50HZ, 1, 1980, 1, 750, + {261, 1541, 745, 30, 0, 0, 1, 0, 0} + }, + {VIDEORES_1920_1080i_50HZ, 1, 2640, 1, 1125, + {193, 2113, 1123, 22, 560, 23, 1, 0, 563} + }, + {VIDEORES_720_576i_50HZ, 1, 864, 1, 625, + {133, 853, 623, 24, 310, 25, 1, 0, 313 } + }, + {VIDEORES_1920_1080p_50HZ, 1, 2640, 1, 1125, + {193, 2113, 1121, 45, 0, 0, 1, 0, 0} + }, + + /* 60 Hz PC */ + {VIDEORES_640_480p_60HZ, 1, 800, 1, 525, + {145, 785, 515, 45, 0, 0, 1, 0, 0} + }, + {VIDEORES_800_600p_60HZ, 1, 1056, 1, 628, + {217, 1017, 627, 28, 0, 0, 0, 0, 0} + }, + {VIDEORES_1024_768p_60HZ, 1, 1344, 1, 806, + {297, 1321, 803, 38, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_768p_60HZ, 1, 1440, 1, 790, + {321, 1601, 795, 30, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_960p_60HZ, 1, 1800, 1, 1000, + {425, 1705, 999, 40, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_1024p_60HZ, 1, 1688, 1, 1066, + {361, 1641, 1065, 42, 0, 0, 0, 0, 0} + }, + {VIDEORES_1440_900p_60HZ, 1, 1904, 1, 934, + {385, 1825, 931, 34, 0, 0, 0, 0, 0} + }, + {VIDEORES_1600_1200p_60HZ, 1, 2160, 1, 1250, + {497, 2097, 1249, 50, 0, 0, 0, 0, 0} + }, + {VIDEORES_1680_1050p_60HZ_RB, 1, 1840, 1, 1080, + {113, 1793, 1077, 30, 0, 0, 0, 0, 0} + }, + {VIDEORES_1920_1200p_60HZ_RB, 1, 2080, 1, 1235, + {113, 2033, 1232, 35, 0, 0, 0, 0, 0} + }, + + /* 75 HZ PC */ + {VIDEORES_640_480p_75HZ, 1, 840, 1, 500, + {185, 825, 499, 20, 0, 0, 1, 0, 0} + }, + {VIDEORES_800_600p_75HZ, 1, 1056, 1, 625, + {241, 1041, 624, 25, 0, 0, 0, 0, 0} + }, + {VIDEORES_1024_768p_75HZ, 1, 1312, 1, 800, + {273, 1297, 799, 32, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_768p_75HZ, 1, 1696, 1, 805, + {337, 1617, 802, 37, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_1024p_75HZ, 1, 1688, 1, 1066, + {393, 1673, 1065, 42, 0, 0, 0, 0, 0} + }, + + /* 85 HZ PC */ + {VIDEORES_640_480p_85HZ, 1, 832, 1, 509, + {137, 777, 508, 29, 0, 0, 1, 0, 0} + }, + {VIDEORES_800_600p_85HZ, 1, 1048, 1, 631, + {217, 1017, 630, 31, 0, 0, 0, 0, 0} + }, + {VIDEORES_1024_768p_85HZ, 1, 1376, 1, 808, + {305, 1329, 807, 40, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_768p_85HZ, 1, 1712, 1, 908, + {353, 1633, 905, 140, 0, 0, 0, 0, 0} + }, + {VIDEORES_1280_1024p_85HZ, 1, 1728, 1, 1072, + {385, 1665, 1071, 48, 0, 0, 0, 0, 0} + }, + + /* Other resolutions */ + {VIDEORES_720_240p_60HZ_M1, 1, 858, 1, 262, + {120, 840, 258, 22, 0, 0, 0, 0, 0} + }, + {VIDEORES_720_240p_60HZ_M2, 1, 858, 1, 263, + {120, 840, 258, 23, 0, 0, 0, 0, 0} + }, + {VIDEORES_720_288p_50HZ_M1, 1, 864, 1, 312, + {133, 853, 310, 24, 0, 0, 0, 0, 0} + }, + {VIDEORES_720_288p_50HZ_M2, 1, 864, 1, 313, + {133, 853, 310, 25, 0, 0, 0, 0, 0} + }, + {VIDEORES_720_288p_50HZ_M3, 1, 864, 1, 314, + {133, 853, 310, 26, 0, 0, 0, 0, 0} + }, + {VIDEORES_1360_768p_60HZ, 1, 1792, 1, 795, + {369, 1729, 792, 27, 0, 0, 0, 0, 0} + }, + {VIDEORES_1400_1050p_60HZ, 1, 1864, 1, 1089, + {377, 1777, 1086, 39, 0, 0, 0, 0, 0} + }, + {VIDEORES_1400_1050p_60HZ_RB, 1, 1560, 1, 1080, + {113, 1513, 1077, 30, 0, 0, 0, 0, 0} + }, + {VIDEORES_1024_768p_70HZ, 1, 1328, 1, 806, + {281, 1305, 803, 38, 0, 0, 0, 0, 0} + }, + {VIDEORES_640_480p_72HZ, 1, 832, 1, 520, + {169, 809, 511, 40, 0, 0, 0, 0, 0} + }, + {VIDEORES_800_600p_72HZ, 1, 1040, 1, 666, + {185, 985, 629, 66, 0, 0, 0, 0, 0} + }, + {VIDEORES_640_350p_85HZ, 1, 832, 1, 445, + {161, 801, 413, 95, 0, 0, 0, 0, 0} + }, + {VIDEORES_640_400p_85HZ, 1, 832, 1, 445, + {161, 801, 444, 45, 0, 0, 0, 0, 0} + }, + {VIDEORES_720_400p_85HZ, 1, 936, 1, 446, + {181, 901, 445, 46, 0, 0, 0, 0, 0} + } +}; + +const resolution_data_t supported_res[] = +{ + /* Low TV */ + {VIDEORES_1280_720p_24HZ, 1280,720,24,0, 1117178, 1134065, 1488, 1513, 17, 19}, + {VIDEORES_1280_720p_25HZ, 1280,720,25,0, 1072491, 1087614, 1428, 1451, 13, 15}, + {VIDEORES_1280_720p_30HZ, 1280,720,30,0, 893742, 907252, 1190, 1210, 13, 15}, + {VIDEORES_1920_1080p_24HZ, 1920,1080,24,0, 1117178, 1134065, 992, 1009, 14, 17}, + {VIDEORES_1920_1080p_25HZ, 1920,1080,25,0, 1072491, 1087614, 952, 967, 14, 17}, + {VIDEORES_1920_1080p_30HZ, 1920,1080,30,0, 893742, 907252, 794, 806, 14, 17}, + + /* 60 Hz TV */ + {VIDEORES_720_480p_60HZ, 720,480,60,0, 446870, 453626, 850, 865, 60, 63}, + {VIDEORES_1280_720p_60HZ, 1280,720,60,0, 446870, 453626, 594, 605, 13, 15}, + {VIDEORES_1920_1080i_60HZ, 1920,1080,60,1, 446870, 453626, 793, 807, 14, 17}, + {VIDEORES_720_480i_60HZ, 720,480,60,1, 446870, 453626, 1701, 1729, 122, 125}, + {VIDEORES_1920_1080p_60HZ, 1920,1080,60,0, 446870, 453626, 396, 404, 6, 9}, + + /* 50 Hz TV */ + {VIDEORES_720_576p_50HZ, 720,576,50,0, 536245, 543807, 856, 871, 62, 65}, + {VIDEORES_1280_720p_50HZ, 1280,720,50,0, 536245, 543807, 713, 726, 13, 15}, + {VIDEORES_1920_1080i_50HZ, 1920,1080,50,1, 536245, 543807, 952, 967, 14, 17}, + {VIDEORES_720_576i_50HZ, 720,576,50,1, 536245, 543807, 1714, 1741, 124, 127}, + {VIDEORES_1920_1080p_50HZ, 1920,1080,50,0, 536245, 543807, 475, 484, 6, 9}, + + /* 60 HZ PC */ + {VIDEORES_640_480p_60HZ, 640,480,60,0, 446870, 453626, 850, 865, 101, 104}, + {VIDEORES_800_600p_60HZ, 800,600,60,0, 444523, 450791, 708, 718, 84, 88}, + {VIDEORES_1024_768p_60HZ, 1024,768,60,0, 446842, 453142, 554, 562, 54, 58}, + {VIDEORES_1280_768p_60HZ, 1280,768,60,0, 447842, 454156, 561, 569, 41, 46}, + {VIDEORES_1280_960p_60HZ, 1280,960,60,0, 446872, 453172, 447, 453, 26, 30}, + {VIDEORES_1280_1024p_60HZ, 1280,1024,60,0, 446723, 453021, 419, 425, 26, 30}, + {VIDEORES_1440_900p_60HZ, 1440,900,60,0, 446723, 453021, 478, 486, 35, 40}, + {VIDEORES_1600_1200p_60HZ, 1600,1200,60,0, 446872, 453172, 357, 363, 30, 34}, + {VIDEORES_1680_1050p_60HZ_RB, 1680,1050,60,0, 447745, 454058, 415, 420, 5, 9}, + {VIDEORES_1920_1200p_60HZ_RB, 1920,1200,60,0, 447235, 453550, 362, 367, 4, 8}, + + /* 75 HZ PC */ + {VIDEORES_640_480p_75HZ, 640,480,75,0, 357498, 362538, 715, 725, 53, 57}, + {VIDEORES_800_600p_75HZ, 800,600,75,0, 357498, 362538, 572, 580, 42, 46}, + {VIDEORES_1024_768p_75HZ, 1024,768,75,0, 357359, 362398, 447, 453, 31, 35}, + {VIDEORES_1280_768p_75HZ, 1280,768,75,0, 357480, 362520, 444, 450, 32, 36}, + {VIDEORES_1280_1024p_75HZ, 1280,1024,75,0, 357378, 362417, 335, 340, 27, 31}, + + /* 85 HZ PC */ + {VIDEORES_640_480p_85HZ, 640,480,85,0, 315409, 319856, 620, 628, 40, 44}, + {VIDEORES_800_600p_85HZ, 800,600,85,0, 315213, 319657, 500, 507, 29, 33}, + {VIDEORES_1024_768p_85HZ, 1024,768,85,0, 315450, 319898, 390, 396, 25, 29}, + {VIDEORES_1280_768p_85HZ, 1280,768,85,0, 315423, 319871, 391, 396, 29, 33}, + {VIDEORES_1280_1024p_85HZ, 1280,1024,85,0, 315350, 319796, 294, 298, 26, 30}, + + /* Other resolutions */ + {VIDEORES_720_240p_60HZ_M1, 720,240,60,0, 446017, 452305, 1702, 1726, 122, 126}, + {VIDEORES_720_240p_60HZ_M2, 720,240,60,0, 447723, 454035, 1702, 1726, 122, 126}, + {VIDEORES_720_288p_50HZ_M1, 720,288,50,0, 535390, 542938, 1716, 1740, 124, 128}, + {VIDEORES_720_288p_50HZ_M2, 720,288,50,0, 537106, 544678, 1716, 1740, 124, 128}, + {VIDEORES_720_288p_50HZ_M3, 720,288,50,0, 538822, 546419, 1716, 1740, 124, 128}, + {VIDEORES_1360_768p_60HZ, 1360,768,60,0, 446760, 453059, 562, 570, 33, 37}, + {VIDEORES_1400_1050p_60HZ, 1400,1050,60,0, 447036, 453338, 411, 416, 30, 34}, + {VIDEORES_1400_1050p_60HZ_RB, 1400,1050,60,0, 447260, 453565, 414, 420, 7, 11}, + + {VIDEORES_1024_768p_70HZ, 1024,768,70,0, 382656, 388051, 475, 481, 47, 51}, + {VIDEORES_640_480p_72HZ, 640,480,72,0, 368255, 373447, 708, 718, 32, 36}, + {VIDEORES_800_600p_72HZ, 800,600,72,0, 371423, 376660, 558, 566, 63, 67}, + + {VIDEORES_640_350p_85HZ, 640,350,85,0, 315142, 319585, 708, 718, 53, 57}, + {VIDEORES_640_400p_85HZ, 640,400,85,0, 315142, 319585, 708, 718, 53, 57}, + {VIDEORES_720_400p_85HZ, 720,400,85,0, 315294, 319740, 707, 717, 53, 57} +}; + +/* Platform Data */ +struct tda1997x_platform_data { + + /* Misc */ + char hdcp; /* enable HDCP */ + char external_edid; /* use external EDID */ + char ddc_slave; /* DDC i2c slave address */ + + /* Audio */ + tda1997x_audiofmt_t audout_format; /* output data format */ + tda1997x_audiosysclk_t audout_sysclk; /* clock config */ + tda1997x_audiolayout_t audout_layout; /* physical bus layout */ + bool audio_force_channel_assignment; /* use AUD IF info if unset */ + bool audio_auto_mute; /* enable hardware audio auto-mute */ + bool audout_invert_clk; /* data valid on rising edge of BCLK */ + + /* Video */ + tda1997x_videofmt_t vidout_format; /* video output data format */ + bool vidout_blc; /* insert blanking codes (SAV/EAV) */ + bool vidout_trc; /* insert timing codes */ + tda1997x_videoclkmode_t vidout_clkmode; /* clock mode */ + /* pin polarity (1=invert) */ + bool vidout_invert_de; + bool vidout_invert_hs; + bool vidout_invert_vs; + /* clock delays (0=-8, 1=-7 ... 15=+7 pixels) */ + char vidout_delay_hs; + char vidout_delay_vs; + char vidout_delay_de; + char vidout_delay_clk; + /* sync selections (controls how sync pins are derived) */ + tda1997x_sync_output_hs_t vidout_sel_hs; + tda1997x_sync_output_vs_t vidout_sel_vs; + tda1997x_sync_output_de_t vidout_sel_de; + /* Video port configs */ + u8 vidout_port_config[9]; + u8 vidout_port_config_no; + /* Max pixel rate (MP/sec) */ + int max_pixel_rate; +}; + +/** + * Maintains the information on the current state of the chip + */ +struct tda1997x_data { + struct i2c_client *client; + struct i2c_client *client_cec; + char page; + struct work_struct work; + int irq; + tda1997x_state_t state; + tda1997x_input_t input; + spinlock_t lock; + struct tda1997x_platform_data *pdata; + struct mutex page_lock; + struct mutex cec_lock; + + /* detected info from chip */ + int chip; + int chip_version; + int chip_revision; + char eess_detected; + char hdmi_detected; + char hdcp_detected; + char internal_edid; + char port_30bit; + char output_2p5; + char tmdsb_clk; + char tmdsb_soc; + char cec_enabled; + char cec_slave; + + /* status info */ + char hdmi_status; + char vsi_received; + char mptrw_in_progress; + char state_c5_reached; + char activity_status_reg; + char input_detect[2]; + char vendor[12]; + char product[18]; + u16 key_decryption_seed; + + /* video source */ + tda1997x_colorspace_t colorspace; + tda1997x_colorimetry_t colorimetry; + tda1997x_restype_t resolutiontype; + + /* video source and output format */ + tda1997x_vidout_fmt_t video_mode; + + /* audio source */ + u8 channel_assignment; + int source_channels; + + /* audio output format */ + tda1997x_audout_fmt_t audio_mode; +}; + +#ifdef DEBUG +/* kernel parameters */ +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); + +#define DPRINTK(x, fmt, args...) { if (debug>=x) printk(KERN_DEBUG fmt, ## args); } + +/* HPD modes */ +const char *hpd_names[] = { + "HPD_LOW", + "HPD_LOW_OTHER", + "HPD_HIGH", + "HPD_HIGH_OTHER", + "HPD_PULSE", +}; + +/* Audio output port layout (hardware pins) */ +const char *audlayout_names[] = { + "AUDIO_LAYOUT_AUTO", + "AUDIO_LAYOUT_0", + "AUDIO_LAYOUT_1", +}; + +/* Audio format */ +const char *audfmt_names[] = { + "AUDIO_FMT_I2S16", + "AUDIO_FMT_I2S32", + "AUDIO_FMT_SPDIF", + "AUDIO_FMT_OBA", + "AUDIO_FMT_I2S16_HBR_STRAIGHT", + "AUDIO_FMT_I2S16_HBR_DEMUX", + "AUDIO_FMT_I2S32_HBR_DEMUX", + "AUDIO_FMT_SPDIF_HBR_DEMUX", + "AUDIO_FMT_DST", +}; +#else +#define DPRINTK(x, fmt, args...) +#endif + +static struct tda1997x_data tda1997x_data; + +static int tda1997x_manual_hpd(struct tda1997x_data *tda1997x, hpdmode_t mode); + +/** API for CEC + */ +int tda1997x_cec_read(u8 reg) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + int val; + + mutex_lock(&tda1997x->cec_lock); + val = i2c_smbus_read_byte_data(tda1997x_data.client_cec, reg); + if (val < 0) { + dev_dbg(&tda1997x_data.client_cec->dev, + "%s:read reg error: reg=%2x\n", __func__, reg); + val = -1; + } + + mutex_unlock(&tda1997x->cec_lock); + return val; +} +EXPORT_SYMBOL(tda1997x_cec_read); + +int tda1997x_cec_write(u8 reg, u8 val) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + int ret = 0; + + mutex_unlock(&tda1997x->cec_lock); + ret = i2c_smbus_write_byte_data(tda1997x_data.client_cec, reg, val); + if (ret < 0) { + dev_dbg(&tda1997x_data.client_cec->dev, + "%s:write reg error:reg=%2x,val=%2x\n", __func__, + reg, val); + ret = -1; + } + mutex_unlock(&tda1997x->cec_lock); + + return ret; +} +EXPORT_SYMBOL(tda1997x_cec_write); + +/** API for MFD children + */ + +/** get current state + */ +tda1997x_state_t +tda1997x_get_state(void) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + tda1997x_state_t state; + unsigned long flags; + + dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); + spin_lock_irqsave(&tda1997x->lock, flags); + state = tda1997x->state; + spin_unlock_irqrestore(&tda1997x->lock, flags); + + return state; +} +EXPORT_SYMBOL(tda1997x_get_state); + +int +tda1997x_get_vidout_fmt(tda1997x_vidout_fmt_t *fmt) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + unsigned long flags; + + dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); + spin_lock_irqsave(&tda1997x->lock, flags); + memcpy(fmt, &(tda1997x->video_mode), sizeof(*fmt)); + fmt->sensor_vidfmt = tda1997x->pdata->vidout_format; + fmt->sensor_clkmode = tda1997x->pdata->vidout_clkmode; + spin_unlock_irqrestore(&tda1997x->lock, flags); + + return 0; +} +EXPORT_SYMBOL(tda1997x_get_vidout_fmt); + +int +tda1997x_get_audout_fmt(tda1997x_audout_fmt_t *fmt) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + unsigned long flags; + + dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); + spin_lock_irqsave(&tda1997x->lock, flags); + memcpy(fmt, &(tda1997x->audio_mode), sizeof(*fmt)); + spin_unlock_irqrestore(&tda1997x->lock, flags); + + return 0; +} +EXPORT_SYMBOL(tda1997x_get_audout_fmt); + + +/*********************************************************************** + * I2C transfer + ***********************************************************************/ + +/** set the current page + * + * @param page number + * @returns 0 if success, an error code otherwise. + */ +static int tda1997x_setpage(u8 page) { + int ret; + + if (tda1997x_data.page != page) { + ret = i2c_smbus_write_byte_data(tda1997x_data.client, + REG_CURPAGE_00H, page); + if (ret < 0) { + dev_dbg(&tda1997x_data.client->dev, + "%s:write reg error:reg=%2x,val=%2x\n", __func__, + REG_CURPAGE_00H, page); + return -1; + } + tda1997x_data.page = page; + } + return 0; +} + +/** Read one register from a tda1997x i2c slave device. + * + * @param reg register in the device we wish to access. + * high byte is page, low byte is reg + * @returns 0 if success, an error code otherwise. + */ +static inline int io_read(u16 reg) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + int val; + + /* page 80 denotes CEC reg which needs to go out cec_client */ + BUG_ON(reg>>8 == 0x80); + + mutex_lock(&tda1997x->page_lock); + if (tda1997x_setpage(reg>>8)) { + val = -1; + goto out; + } + + val = i2c_smbus_read_byte_data(tda1997x_data.client, reg&0xff); + if (!(reg == REG_INT_FLG_CLR_TOP && val == 0x00)) { + DPRINTK(3, "<< 0x%04x=0x%02x\n", reg, val); + } + if (val < 0) { + dev_dbg(&tda1997x_data.client->dev, + "%s:read reg error: reg=%2x\n", __func__, reg&0xff); + val = -1; + goto out; + } + +out: + mutex_unlock(&tda1997x->page_lock); + return val; +} +/* 16bit read */ +static inline long io_read16(u16 reg) +{ + u8 val; + long lval = 0; + + if ( (val = io_read(reg)) < 0) + return -1; + lval |= (val<<8); + if ( (val = io_read(reg+1)) < 0) + return -1; + lval |= val; + + return lval; +} +/* 24bit read */ +static inline long io_read24(u16 reg) +{ + u8 val; + long lval = 0; + + if ( (val = io_read(reg)) < 0) + return -1; + lval |= (val<<16); + if ( (val = io_read(reg+1)) < 0) + return -1; + lval |= (val<<8); + if ( (val = io_read(reg+2)) < 0) + return -1; + lval |= val; + + return lval; +} +/* n-byte read */ +static unsigned int io_readn(u16 reg, u8 len, u8 *data) +{ + int i; + int sz = 0; + u8 val; + + for (i = 0; i < len; i++) { + if ( (val = io_read(reg + i)) < 0) + break; + data[i] = val; + sz++; + } + + return sz; +} + +/** Write one register of a tda1997x i2c slave device. + * + * @param reg register in the device we wish to access. + * high byte is page, low byte is reg + * @returns 0 if success, an error code otherwise. + */ +static int io_write(u16 reg, u8 val) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + s32 ret = 0; + + /* page 80 denotes CEC reg which needs to go out cec_client */ + BUG_ON(reg>>8 == 0x80); + + mutex_lock(&tda1997x->page_lock); + if (tda1997x_setpage(reg>>8)) { + ret = -1; + goto out; + } + + DPRINTK(3, ">> 0x%04x=0x%02x\n", reg, val); + ret = i2c_smbus_write_byte_data(tda1997x_data.client, reg&0xff, val); + if (ret < 0) { + dev_dbg(&tda1997x_data.client->dev, + "%s:write reg error:reg=%2x,val=%2x\n", __func__, + reg&0xff, val); + ret = -1; + goto out; + } + +out: + mutex_unlock(&tda1997x->page_lock); + return ret; +} +/* 16bit write */ +static int io_write16(u16 reg, u16 val) +{ + if (io_write(reg, (val>>8)&0xff) < 0) + return -1; + if (io_write(reg+1, val&0xff) < 0) + return -1; + return 0; +} +/* 24bit write */ +static int io_write24(u16 reg, u32 val) +{ + if (io_write(reg, (val>>16)&0xff) < 0) + return -1; + if (io_write(reg+1, (val>>8)&0xff) < 0) + return -1; + if (io_write(reg+2, val&0xff) < 0) + return -1; + return 0; +} +/* n-byte write */ +static unsigned int io_writen(u16 reg, u8 len, u8 *data) +{ + int i; + int sz = 0; + + for (i = 0; i < len; i++) { + io_write(reg + i, data[i]); + sz++; + } + + return sz; +} + + +/*********************************************************************** + * EDID + ***********************************************************************/ + +/* Modified from the NXP exapp71a application for Ventana */ +u8 edid_block[256] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x1e, 0xe3, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x17, 0x01, 0x03, 0xa2, 0x00, 0x00, 0x00, + 0x02, 0xee, 0x95, 0xa3, 0x54, 0x4c, 0x99, 0x26, + 0x0f, 0x50, 0x54, 0x20, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xc4, 0x8e, 0x21, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x47, 0x57, 0x20, + 0x56, 0x65, 0x6e, 0x74, 0x61, 0x6e, 0x61, 0x0a, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x1e, + 0x3c, 0x0f, 0x44, 0x09, 0x00, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfe, + 0x00, 0x31, 0x30, 0x38, 0x30, 0x70, 0x44, 0x43, + 0x78, 0x76, 0x43, 0x33, 0x44, 0x0a, 0x01, 0x89, + 0x02, 0x03, 0x21, 0xf0, 0x4c, 0x22, 0x20, 0x21, + 0x04, 0x13, 0x03, 0x12, 0x05, 0x14, 0x07, 0x16, + 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, + 0x00, 0x67, 0x03, 0x0c, 0x00, 0x00, 0x00, 0xb8, + 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x38, 0x2d, + 0x40, 0x58, 0x2c, 0x45, 0x00, 0x80, 0x38, 0x74, + 0x00, 0x00, 0x3f, 0x01, 0x1d, 0x00, 0x72, 0x51, + 0xd0, 0x1e, 0x20, 0x6e, 0x50, 0x55, 0x00, 0x00, + 0xd0, 0x52, 0x00, 0x00, 0x39, 0xa0, 0x0f, 0x20, + 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 0x14, + 0x00, 0x20, 0x58, 0x32, 0x00, 0x00, 0x3f, 0xd7, + 0x09, 0x80, 0xa0, 0x20, 0xe0, 0x2d, 0x10, 0x08, + 0x60, 0x22, 0x01, 0x80, 0xe0, 0x21, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, +}; + +/* DDC config 0 (page 0x20) */ +u8 ddc_config0[8] = { + /* 0x80: EDID_VERSION */ + 0x19, /* main=0x19 */ + + /* 0x81: EDID_ENABLE bits: + * 7 - unused + * 6 - edid_only + * 5:2 - unused + * 1 - edid_b_en + * 0 - edid_a_en + */ + 0x41, /* enable EDID for A only */ + + /* 0x82: EDID_BLOCK_SELECT bits: + * 7:6 - ddc_b_blk1_sel + * 5:4 - ddc_b_blk0_sel + * 3:2 - ddc_a_blk1_sel + * 1:0 - ddc_a_blk0_sel + */ + 0x44, /* ddc_b_blk1_sel=1 ddc_a_blk1_sel=1 */ + + /* 0x83: empty */ + 0x00, + + /* 0x84: HPD_POWER bits: + * 7:5 - unused + * 4:3 - hpd_bp + * 2 - hpd_edid_only + * 1:0 - unused + */ + 0x06, /* hpd_bp=1 hpd_edid_only=1 */ + + /* 0x85: HPD_AUTO_CTRL bits + * 7 - read_edid + * 6 - unused + * 5 - hpd_f3tech + * 4 - hpd_other + * 3 - hpd_unsel + * 2 - hpd_all_ch + * 1 - hpd_prv_ch + * 0 - hpd_new_ch + */ + 0x18, /* hpd_other=1, hdp_unsel=1 */ + + /* 0x86: HPD_DURATION */ + 0x00, /* hpd_duration=0 */ + + /* 0x87: RX_HPD_HEAC bits + * 7:2 - unused + * 1 - heac_b_en + * 0 - heac_a_en + */ + 0x00, +}; + +/* RT config (page 0x30) */ +u8 rt_config[6] = { + /* 0x00: RT_AUTO_CTRL bits: + * 7 - unused + * 6 - rt_other + * 5 - rt_hpd_low + * 4 - rt_no_5v + * 3 - rt_unsel + * 2 - rt_all_ch + * 1 - rt_prv_ch + * 0 - rt_new_ch + */ + 0x78, + + /* 0x01: EQ_MAN_CTRL0 bits: + * 7-3 - unused + * 2-0 - ch0_man_gain + */ + 0x03, /* ch0_man_gain=3 */ + + /* 0x02: EQ_MAN_CTRL1 bits: + * 7 - unused + * 6-4 - ch1_man_gain + * 3 - unused + * 2-0 - ch2_man_gain + */ + 0x33, /* ch1_man_gain=3 ch2_man_gain=3 */ + + /* 0x03: OUTPUT_CFG bits + * 7 - eq_cal_act + * 6 - eq_cal_ch + * 5 - eq_cal_5v + * 4 - eq_cal_cond + * 3 - unused + * 2 - out_idle + * 1 - ap_idle + * 0 - vp_idle + */ + 0xf0, /* eq_cal_act=1 eq_cal_ch=1 eq_cal_5v=1 eq_cal_cond=1 */ + + /* 0x04: MUTE_CTRL bits + * 7:3 - unused + * 2 - mute_man + * 1 - mute_pol + * 0 - mute_ena + */ + 0x00, + + /* 0x05: SLAVE_ADDR bits + * 7:6 - unused + * 5 - i2c_cec_a1 + * 4 - i2c_cec_a0 + * 3 - unused + * 2 - i2c_a2 + * 1 - i2c_a1 + * 0 - i2c_a0 + */ + 0x10, /* i2c_cec_a0=1 */ +}; + +static u32 reg = 0; + +/** Loads EDID data into embedded EDID memory of receiver device + * @edid - pointer to two block EDID (array of 256 bytes) common to both inputs + * + * A common EDID block is used for both inputs with the exception of the + * SPA (Source Physical Address) used for CEC. Therefore, we locate the SPA + * within the EDID passed and treating it as the SPA for InputA while adding + * 1 to it for the SPA for InputB. The EDID is written into nvram as well + * as the SPA offset, SPA's and adjusted checksums for both inputs. + */ +static int tda1997x_load_edid_data(u8 *edid) +{ + u8 chksum, chksum_spa; + int i, n, spa_offset = 0; + u16 spa; + + DPRINTK(0,"%s\n", __func__); + + /* sanity check EDID data: base block, extensions, checksums */ + if (!drm_edid_is_valid((struct edid *) edid)) { + printk(KERN_ERR "edid data is invalid\n"); + return -EINVAL; + } + if (edid[0x7e] != 1) { + printk(KERN_ERR "edid requires a single CEA extension block\n"); + return -EINVAL; + } + + /* Find SPA offset within CEA extentsion */ + for (i = 1; i <= edid[0x7e]; i++) { + u8 *ext = edid + (i * EDID_LENGTH); + + /* look for CEA v3 extension */ + if (ext[0] != 0x02 || ext[1] != 0x03) + continue; + + /* make sure we have a DBC */ + if (ext[2] < 5) + continue; + + /* iterate through DBC's until we find the Vendor block */ + for (n = 4; n < ext[2]; n++) { + char type = (ext[n] & 0xe0) >> 5; + char len = (ext[n] & 0x1f); + if (type == 3) { + if (ext[n+1] == 0x03 && ext[n+2] == 0x0c) { + spa_offset = n+4; + spa = ext[spa_offset] << 8 | + ext[spa_offset+1]; + } + } + n += len; + } + } + if (!spa_offset) { + printk(KERN_ERR + "EDID requires an HDMI Vendor Specific Data Block\n"); + return -EINVAL; + } + + /* calculate ext block checksum w/o SPA */ + chksum = 0; + for (i = 0; i < 127; i++) + if (i != spa_offset && i != (spa_offset + 1)) + chksum += edid[i+128]; + + /* write base EDID */ + for (i = 0; i < 128; i++) + io_write(REG_EDID_IN_BYTE0 + i, edid[i]); + + /* write CEA Extension */ + for (i = 0; i < 128; i++) + io_write(REG_EDID_IN_BYTE128 + i, edid[i+128]); + + /* SPA for InputA */ + io_write16(REG_EDID_IN_SPA_AB_A, spa); + chksum_spa = (u8)((spa & 0xff00)>>8) + (u8)(spa & 0x00ff) + chksum; + chksum_spa = (u8)((0xff - chksum_spa) + 0x01); /* 2's complement */ + io_write(REG_EDID_IN_CKSUM_A, chksum_spa); + + /* SPA for InputB */ + spa += 1; + io_write16(REG_EDID_IN_SPA_AB_B, spa); + chksum_spa = (u8)((spa & 0xff00)>>8) + (u8)(spa & 0x00ff) + chksum; + chksum_spa = (u8)((0xff - chksum_spa) + 0x01); /* 2's comp */ + io_write(REG_EDID_IN_CKSUM_B, chksum_spa); + + /* write source physical address subaddress offset */ + io_write(REG_EDID_IN_SPA_SUB, spa_offset); + + return 0; +} + +/* + * sysfs hooks + */ +static ssize_t b_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int rz = 0; + const char *name = attr->attr.name; + struct tda1997x_data *tda1997x = &tda1997x_data; + unsigned long flags; + const char *sname = ""; + + spin_lock_irqsave(&tda1997x->lock, flags); + if (strcasecmp(name, "state") == 0) { + switch(tda1997x->state) { + case STATE_NOT_INITIALIZED: sname = "not initialized"; break; + case STATE_INITIALIZED: sname = "initalized"; break; + case STATE_LOCKED: sname = "locked"; break; + case STATE_UNLOCKED: sname = "unlocked"; break; + case STATE_CONFIGURED: sname = "configured"; break; + } + rz = sprintf(buf, "%s\n", sname); + } + else if (strcasecmp(name, "vidmode") == 0) { + if (tda1997x->state == STATE_LOCKED) { + rz = sprintf(buf, "%dx%d%c@%dHz\n", + tda1997x->video_mode.width, tda1997x->video_mode.height, + (tda1997x->video_mode.interlaced)?'i':'p', + tda1997x->video_mode.fps); + } else + rz = sprintf(buf, "no signal\n"); + } else if (strcasecmp(name, "colorspace") == 0) { + rz = sprintf(buf, "%s %s\n", + colorspace_names[tda1997x->colorspace], + colorimetry_names[tda1997x->colorimetry]); + } else if (strcasecmp(name, "audmode") == 0) { + if (tda1997x->state == STATE_LOCKED) { + rz = sprintf(buf, "%dHz\n", tda1997x->audio_mode.samplerate); + } else + rz = sprintf(buf, "no signal\n"); + } else if (strcasecmp(name, "vendor") == 0) { + rz = sprintf(buf, "%s\n", tda1997x->vendor); + } else if (strcasecmp(name, "product") == 0) { + rz = sprintf(buf, "%s\n", tda1997x->product); + } else if (strcasecmp(name, "info") == 0) { + rz = sprintf(buf, "%s%s%s\n", + (tda1997x->hdmi_detected)?"HDMI ":"", + (tda1997x->eess_detected)?"EESS ":"", + (tda1997x->hdcp_detected)?"HDCP ":""); + } else if (strcasecmp(name, "edid") == 0) { + for (rz = 0; rz < sizeof(edid_block); rz++) + buf[rz] = edid_block[rz]; + } else if (strcasecmp(name, "reg") == 0) { + rz = sprintf(buf, "%02x\n", io_read(reg) ); + printk(KERN_INFO "TDA1997x-core: Register 0x%04x=%s\n", reg, buf); + } else { + rz = sprintf(buf, "invalid attr\n"); + } + spin_unlock_irqrestore(&tda1997x->lock, flags); + + return rz; +} + +static ssize_t b_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 val = 0; + const char *name = attr->attr.name; + struct tda1997x_data *tda1997x = &tda1997x_data; + int i; + + + if (strcasecmp(name, "edid") == 0) { + for (i = 0; i < count; i++) + edid_block[i] = buf[i]; + printk(KERN_INFO "TDA1997x-core: New EDID being loaded\n"); + /* Set HPD low */ + tda1997x_manual_hpd(tda1997x, HPD_LOW); + tda1997x_load_edid_data(edid_block); + /* Set HPD high (now that EDID is ready) */ + tda1997x_manual_hpd(tda1997x, HPD_HIGH); + } else if (strcasecmp(name, "reg") == 0) { + i = sscanf(buf, "%x %x", ®, &val); + if (i == 2) { + io_write( (u16)reg, (u8)val); + printk(KERN_INFO "TDA1997x-core: Register 0x%04x=0x%02x\n", reg, val); + } else { + printk(KERN_INFO "TDA1997x-core: Register 0x%04x\n", reg); + } + } else { + printk(KERN_ERR "invalid name '%s'\n", attr->attr.name); + } + + return count; +} + +/* + * Create a group of attributes so that we can create and destory them all + * at once. + */ +static struct device_attribute attr_state = + __ATTR(state, 0660, b_show, b_store); +static struct device_attribute attr_vidmode = + __ATTR(vidmode, 0660, b_show, NULL); +static struct device_attribute attr_audmode = + __ATTR(audmode, 0660, b_show, NULL); +static struct device_attribute attr_vendor = + __ATTR(vendor, 0660, b_show, NULL); +static struct device_attribute attr_product = + __ATTR(product, 0660, b_show, NULL); +static struct device_attribute attr_info = + __ATTR(info, 0660, b_show, NULL); +static struct device_attribute attr_edid = + __ATTR(edid, 0770, b_show, b_store); +static struct device_attribute attr_colorspace = + __ATTR(colorspace, 0660, b_show, NULL); +static struct device_attribute attr_reg = + __ATTR(reg, 0660, b_show, b_store); + +static struct attribute *tda1997x_attrs[] = { + &attr_state.attr, + &attr_vidmode.attr, + &attr_audmode.attr, + &attr_vendor.attr, + &attr_product.attr, + &attr_info.attr, + &attr_edid.attr, + &attr_colorspace.attr, + &attr_reg.attr, + NULL +}; + +static struct attribute_group attr_group = { + .attrs = tda1997x_attrs, +}; + +/** Loads DDC and RT configuration data into embedded memory of receiver device + * @ddc_config - pointer to the DDC block configuration (8 bytes) + * @rt_config - pointer to the RT block configuration (6 bytes) + */ +static int tda1997x_load_config_data(struct tda1997x_data *tda1997x, + const u8 *ddc, const u8 *rt) +{ + int i; + + if (!tda1997x->internal_edid) + return -EPERM; + + /* DDC to page 20h */ + for (i = 0; i < 8; i++) + io_write(REG_EDID_IN_VERSION + i, ddc[i]); + /* RT to page 30h */ + for (i = 0; i < 6; i++) + io_write(REG_RT_AUTO_CTRL + i, rt[i]); + + return 0; +} + + +/*********************************************************************** + * Signal Control + ***********************************************************************/ + +/** manual HPD (Hot Plug Detect) control + * @param hdp_mode + * @returns 0 on success + */ +static int tda1997x_manual_hpd(struct tda1997x_data *tda1997x, hpdmode_t mode) +{ + u8 hpd_auto, hpd_pwr, hpd_man; + + DPRINTK(0, "%s: %s\n", __func__, hpd_names[mode]); + + /* HPD_POWER bits: + * 7:4 - unused + * 3:2 - hpd_bp[1:0] + * 1 - hpd_edid_only + * 0 - unused + * + * HPD_AUTO_CTRL bits: + * 7 - read_edid + * 6 - unused + * 5 - hpd_f3tech + * 4 - hp_other + * 3 - hpd_unsel + * 2 - hpd_all_ch + * 1 - hpd_prv_ch + * 0 - hpd_new_ch + * + * HPD_MAN bits: + * 7 - ? + * 6-3 - unused + * 2:0 - man_gain + */ + hpd_auto = io_read(REG_HPD_AUTO_CTRL); + hpd_pwr = io_read(REG_HPD_POWER); + hpd_man = io_read(REG_HPD_MAN_CTRL); + hpd_man &= 0x87; + + switch(mode) { + /* HPD low and pulse of at least 100ms */ + case HPD_LOW: + hpd_man &= ~0x03; /* man_gain=0 */ + hpd_pwr &= ~0x0c; /* hpd_bp=0 */ + io_write(REG_HPD_POWER, hpd_pwr); + io_write(REG_HPD_MAN_CTRL, hpd_man); + break; + /* HPD high */ + case HPD_HIGH: + /* hpd_bp=1 */ + hpd_pwr = (hpd_pwr & ~0x0c) | 0x04; + io_write(REG_HPD_POWER, hpd_pwr); + break; + /* HPD low and pulse of at least 100ms */ + case HPD_LOW_OTHER: + hpd_man &= ~0x03; /* man_gain=0 */ + hpd_auto &= ~0x10; /* hp_other=0 */ + io_write(REG_HPD_AUTO_CTRL, hpd_auto); + io_write(REG_HPD_MAN_CTRL, hpd_man); + break; + /* HPD high */ + case HPD_HIGH_OTHER: + hpd_auto |= 0x10; /* hp_other=1 */ + io_write(REG_HPD_AUTO_CTRL, hpd_auto); + break; + /* HPD low pulse */ + case HPD_PULSE: + hpd_man &= ~0x03; /* man_gain=0 */ + /* write HPD_MAN to have HPD low pulse */ + io_write(REG_HPD_MAN_CTRL, hpd_man); + break; + } + + return 0; +} + +/** configure the receiver with the new colorspace + * @returns 0 on success + * colorspace conversion depends on input format and output format + * blanking codes depend on the output colorspace + */ +static int tda1997x_configure_conversion(struct tda1997x_data *tda1997x, + tda1997x_colorspace_t colorspace, tda1997x_colorimetry_t colorimetry, + tda1997x_videofmt_t vidout_format) +{ + colormatrixcoefs_t *pCoefficients = NULL; + blankingcodes_t *pBlankingCodes = NULL; + u8 data[31]; + + printk(KERN_INFO "%s: %s %s => %s\n", KBUILD_MODNAME, + colorspace_names[colorspace], + (colorspace == COLORSPACE_RGB) ? "" : + colorimetry_names[colorimetry], + vidfmt_names[vidout_format]); + + switch (vidout_format) { + /* RGB 4:4:4 output */ + case VIDEOFMT_444: + pBlankingCodes = &RGBBlankingCode; + if (colorspace != COLORSPACE_RGB) { + if (colorimetry == COLORIMETRY_ITU709) + pCoefficients = &conversion_matrix[ITU709_RGBLimited]; + else + pCoefficients = &conversion_matrix[ITU601_RGBLimited]; + } + break; + + /* YUV422 output */ + case VIDEOFMT_422_SMP: /* YUV422 semi-planar */ + case VIDEOFMT_422_CCIR:/* YUV422 CCIR656 */ + pBlankingCodes = &YUVBlankingCode; + if (colorspace == COLORSPACE_RGB) + pCoefficients = &conversion_matrix[RGBLimited_ITU601]; + break; + } + + if (pCoefficients) { + s16 *pTabCoeff = &(pCoefficients->offInt1); + u8 i, j; + + /* test the range of the coefs */ + for (i = 0; i < OFFSET_LOOP_NB; i++) { + for (j = 0; j < MAT_OFFSET_NB; j++) { + if ( (pTabCoeff[j] < MIN_VAL_OFFSET) || pTabCoeff[j] > MAX_VAL_OFFSET) + return -EINVAL; + } + pTabCoeff = &(pCoefficients->offOut1); + } + pTabCoeff = &(pCoefficients->P11Coef); + + for (i = 0; i < MAT_COEFF_NB; i++) { + if ( (pTabCoeff[i] < MIN_VAL_OFFSET) || pTabCoeff[i] > MAX_VAL_OFFSET) + return -EINVAL; + } + + /* enable matrix conversion by disabling bypass: mat_bp=0 */ + data[0] = io_read(REG_VDP_CTRL); + data[0] &= ~(1<<0); + /* offset input 1 value */ + data[1] = (u8) ((u16)(pCoefficients->offInt1) >> 8); + data[2] = (u8) (pCoefficients->offInt1 & MASK_MAT_COEFF_LSB); + /* offset input 2 value */ + data[3] = (u8) ((u16)(pCoefficients->offInt2) >> 8); + data[4] = (u8) (pCoefficients->offInt2 & MASK_MAT_COEFF_LSB); + /* offset input 3 value */ + data[5] = (u8) ((u16)(pCoefficients->offInt3) >> 8); + data[6] = (u8) (pCoefficients->offInt3 & MASK_MAT_COEFF_LSB); + /* Coefficient (1,1) value */ + data[7] = (u8) ((u16)(pCoefficients->P11Coef) >> 8); + data[8] = (u8) (pCoefficients->P11Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (1,2) value */ + data[9] = (u8) ((u16)(pCoefficients->P12Coef) >> 8); + data[10] = (u8) (pCoefficients->P12Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (1,3) value */ + data[11] = (u8) ((u16)(pCoefficients->P13Coef) >> 8); + data[12] = (u8) (pCoefficients->P13Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (2,1) value */ + data[13] = (u8) ((u16)(pCoefficients->P21Coef) >> 8); + data[14] = (u8) (pCoefficients->P21Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (2,2) value */ + data[15] = (u8) ((u16)(pCoefficients->P22Coef) >> 8); + data[16] = (u8) (pCoefficients->P22Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (2,3) value */ + data[17] = (u8) ((u16)(pCoefficients->P23Coef) >> 8); + data[18] = (u8) (pCoefficients->P23Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (3,1) value */ + data[19] = (u8) ((u16)(pCoefficients->P31Coef) >> 8); + data[20] = (u8) (pCoefficients->P31Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (3,2) value */ + data[21] = (u8) ((u16)(pCoefficients->P32Coef) >> 8); + data[22] = (u8) (pCoefficients->P32Coef & MASK_MAT_COEFF_LSB); + /* Coefficient (3,3) value */ + data[23] = (u8) ((u16)(pCoefficients->P33Coef) >> 8); + data[24] = (u8) (pCoefficients->P33Coef & MASK_MAT_COEFF_LSB); + /* Offset output 1 value */ + data[25] = (u8) ((u16)(pCoefficients->offOut1) >> 8); + data[26] = (u8) (pCoefficients->offOut1 & MASK_MAT_COEFF_LSB); + /* Offset output 2 value */ + data[27] = (u8) ((u16)(pCoefficients->offOut2) >> 8); + data[28] = (u8) (pCoefficients->offOut2 & MASK_MAT_COEFF_LSB); + /* Offset output 3 value */ + data[29] = (u8) ((u16)(pCoefficients->offOut3) >> 8); + data[30] = (u8) (pCoefficients->offOut3 & MASK_MAT_COEFF_LSB); + + io_writen(REG_VDP_CTRL, 31, data); + } else { + /* enable matrix conversion bypass (mat_b) */ + data[0] = io_read(REG_VDP_CTRL); + data[0] |= (1<<0); /* mat_bp=1 */ + io_write(REG_VDP_CTRL, data[0]); + } + + /* SetBlankingCodes */ + if (pBlankingCodes) { + io_write16(REG_BLK_GY, pBlankingCodes->blankingCodeGy); + io_write16(REG_BLK_BU, pBlankingCodes->blankingCodeBu); + io_write16(REG_BLK_RV, pBlankingCodes->blankingCodeRv); + } + + return 0; +} + +/** Configure the active input to the given resolution + * @param resolution ID + * @returns 0 on success + */ +static int +tda1997x_configure_input_resolution(resolutionid_t resolution) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + u8 reg; + const resolution_timings_t *timings = NULL; + const vhref_values_t *vh; + int i; + + DPRINTK(0,"%s\n", __func__); + + /* scan through resolution table looking for a match for timings */ + for (i = 0; i < ARRAY_SIZE(resolution_timings); i++) { + if (resolution == resolution_timings[i].resolutionID) + { + timings = &resolution_timings[i]; + vh = &timings->vhref_values; + break; + } + } + if (!timings) { + printk(KERN_INFO "%s: resolution not supported\n", KBUILD_MODNAME); + return -1; + } + + /* Configure Frame Detection Window: + * define the horizontal area where the VHREF modules consider a VSYNC a + * new frame (values typically depend on the video mode being processed + */ + /* start position of the frame detection window */ + io_write16(REG_FDW_S, 0x2ef & 0x3fff); + /* end position of the frame detection window */ + io_write16(REG_FDW_E, 0x141 & 0x3fff); + + /* Set Pixel And Line Counters */ + if (tda1997x->chip_revision == 0) + /* add 1 line to lineCountPreset */ + io_write16(REG_PXCNT_PR, (timings->pixCountPreset + 3) & 0x3fff); + else + io_write16(REG_PXCNT_PR, timings->pixCountPreset & 0x3fff); + io_write16(REG_PXCNT_NPIX, timings->pixCountNb & 0x3fff); + io_write16(REG_LCNT_PR, timings->lineCountPreset & 0x3fff); + io_write16(REG_LCNT_NLIN, timings->lineCountNb & 0x3fff); + + /* Configure VHRef: + * configure the VHRef timing generator responsible for rebuilding all + * horiz and vert synch and ref signals from its input allowing automatic + * detection algorithms and forcing predefined modes (480i & 576i) + * + * REG_VHREF_CTRL bits: + * bit7 - interlaced_det - interlace detect method: 1=alternate,0=framefield + * bit6:5 - vsync_type: 0=Auto,1=FDW,2=Even,3=Odd + * bit4:3 - std_det: 0=PAL,1=NTSC,2=AUTO,3=OFF + * bit2 - href_src - VREF source: 1=from standard, 0=manual + * bit1 - href_src - HREF source: 1=from standard, 0=manual + * bit0 - hsync_sel - HSYNC signal: 1=HS,0=VS + */ + reg = io_read(REG_VHREF_CTRL); + io_write(REG_VHREF_CTRL, 0x3<<3 /* std_det=off */); + + /* Set VHRef: + * configure the VHRef timing values. In case the VHREF generator has + * been configured in manual mode, this will allow to manually set all horiz + * and vert ref values (non-active pixel areas) of the generator + * and allows setting the frame reference params. These values typically + * depend on the video mode being processed + */ + /* horizontal reference start/end */ + io_write16(REG_HREF_S, vh->href_start & 0x3fff); + io_write16(REG_HREF_E, vh->href_end & 0x3fff); + /* vertical reference f1 start/end */ + io_write16(REG_VREF_F1_S, vh->vref_f1_start & 0x3fff); + io_write (REG_VREF_F1_WIDTH, vh->vref_f1_width); + /* vertical reference f2 start/end */ + io_write16(REG_VREF_F2_S, vh->vref_f2_start & 0x3fff); + io_write (REG_VREF_F2_WIDTH, vh->vref_f2_width); + /* F1/F2 FREF, field polarity */ + io_write16(REG_FREF_F1_S, (vh->fieldref_f1_start & 0x3fff) + || (vh->fieldPolarity<<8)); + io_write16(REG_FREF_F2_S, vh->fieldref_f2_start & 0x3fff); + + /* set the state machine */ + tda1997x->state = STATE_CONFIGURED; + + return 0; +} + +/** Configure Audio output formatter + * @param pdata with audio output configuration + * @param channel_assignment + * @returns 0 on success + */ +static int +tda1997x_configure_audio_formatter(struct tda1997x_platform_data *pdata, + u8 channel_assignment) +{ + u8 fifo_latency = 0x80; + bool sp_used_by_fifo = 0; + bool ws_active = 0; + u8 reg; + + DPRINTK(0,"%s: %s %s channel_assignment=%d enable_auto_mute=%d\n", __func__, + audlayout_names[pdata->audout_layout], + audfmt_names[pdata->audout_format], + channel_assignment, pdata->audio_auto_mute); + + /* AUDIO_PATH bits: + * 7:0 - channel assignment (CEA-861-D Table 20) + */ + io_write(REG_AUDIO_PATH, channel_assignment); + + /* AUDIO_SEL bits: + * 7 - aclk_inv - Polarity of clock signal on A_CLK pin (1=invert) + * 6 - test_tone - Audio test tone generator (1=on) + * 5 - i2s_spdif - Selection of audio output format (0=I2S, 1=SPDIF) + * 4 - i2s_32 - Size of I2S samples (0=16bit, 1=32bit) + * 3 - hwmute_ena - Automatic audio mute (1=enabled) + * 2 - hbr_demux - High Bit Rate output mode: + * 0:straight via AP0 + * 1:demuxed via AP0,AP1,AP2,AP3 + * 1:0 - audio_type - Selection of audio output packet mode + * 00: Audio samples + * 01: High-Bit Rate (HBR) + * 10: One Bit Audio (OBA) + * 11: Direct Stream Transfer (DST) + */ + reg = io_read(REG_AUDIO_SEL); + if (pdata->audio_auto_mute) + reg |= 1<<3; + else + reg &= ~(1<<3); + switch(pdata->audout_format) { + case AUDIO_FMT_I2S16: + /* 16bit I2S mode, SP flag used by FIFO */ + reg &= ~(1<<4); + reg &= ~(1<<5); + sp_used_by_fifo = 1; + ws_active = 1; + break; + case AUDIO_FMT_I2S32: + /* 32bit I2S mode, SP flag used by FIFO */ + reg |= (1<<4); + reg &= ~(1<<5); + sp_used_by_fifo = 1; + ws_active = 1; + break; + case AUDIO_FMT_OBA: + /* One Bit Audio */ + reg &= ~(1<<5); + sp_used_by_fifo = 1; + ws_active = 1; + break; + case AUDIO_FMT_SPDIF: /* SPDIF */ + /* SPDIF mode, SP flag used by FIFO */ + reg |= (1<<5); + sp_used_by_fifo = 1; + break; + case AUDIO_FMT_I2S16_HBR_DEMUX: + /* 16bit I2S High Bit Rate demux */ + reg &= ~(1<<4); + reg &= ~(1<<5); + sp_used_by_fifo = 1; + ws_active = 1; + break; + case AUDIO_FMT_I2S32_HBR_DEMUX: + /* 32bit I2S High Bit Rate demux */ + reg |= (1<<4); + reg &= ~(1<<5); + sp_used_by_fifo = 1; + ws_active = 1; + break; + case AUDIO_FMT_DST: + /* Direct Stream Transfer */ + sp_used_by_fifo = 0; + break; + case AUDIO_FMT_SPDIF_HBR_DEMUX: + /* SPDIF High Bit Rate demux */ + reg |= (1<<5); + sp_used_by_fifo = 0; + break; + default: + break; + } + /* Clock polarity */ + reg &= ~(1<<7); + if (pdata->audout_invert_clk) + reg |= 1<<7; + io_write(REG_AUDIO_SEL, reg); + + /* AUDIO_LAYOUT bits: + * 2 - sp_flag : + * 0 - sp flag ignored by FIFO-control (4 subpackets written in FIFO) + * 1 - sp flag used by FIFO-control (Present samples subpackets written in FIFO) + * 1 - layout_man: + * 0 - layout defined by audio packet header + * 1 - manual control of layout + * 0 - layout: value of layout in case of manual selection + * 0 - layout0 + * 1 - layout1 + */ + io_write(REG_AUDIO_LAYOUT, + ((sp_used_by_fifo)?(1<<2):0) | pdata->audout_layout); + + /* FIFO Latency value */ + io_write(REG_FIFO_LATENCY_VAL, fifo_latency); + + /* AUDIO_OUT_ENABLE bits: enable AP, ACLK and WS if needed + * 5 - aclk_out - 1=Audio clock port active + * 4 - ws_out - 1=Word selection port active + * 3 - ap3_out - 1=AP0 active + * 2 - ap2_out - 1=AP1 active + * 1 - ap1_out - 1=AP2 active + * 0 - ap0_out - 1=AP3 active + */ + if (!sp_used_by_fifo) { + reg = 0x0f; + } else { + /* TODO: ensure audio out DAI allows AP1,2,3? */ + reg = 0x01; /* AP0 always enabled */ + if (channel_assignment >= 0x01) + reg |= 2; /* >1 also need AP1 */ + if (channel_assignment >= 0x04) + reg |= 4; /* >4 also need AP2 */ + if (channel_assignment >= 0x0C) + reg |= 8; /* >12 also need AP3 */ + /* specific cases where AP1 is not used */ + if ((channel_assignment == 0x04) + || (channel_assignment == 0x08) + || (channel_assignment == 0x0c) + || (channel_assignment == 0x10) + || (channel_assignment == 0x14) + || (channel_assignment == 0x18) + || (channel_assignment == 0x1c)) + reg &= ~2; + /* specific cases where AP2 is not used */ + if ((channel_assignment >= 0x14) + && (channel_assignment <= 0x17)) + reg &= ~4; + } + if (ws_active) + reg |= 0x30; + io_write(REG_AUDIO_OUT_ENABLE, reg); + + /* reset test mode to normal audio freq auto selection */ + io_write(REG_TEST_MODE, 0x00); + + /* reset sw_ncts_en & ncts_en if i2s layout_0 */ + if (reg & 0x18) { + reg = io_read(REG_TEST_NCTS_CTRL); + reg &= ~0x03; + io_write(REG_TEST_NCTS_CTRL, reg); + } + + return 0; +} + + +/** Selects HDMI input + * here as well. + * + * @param input + * @returns 0 on success + */ +int +tda1997x_select_input(tda1997x_input_t input) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + u8 reg; + + DPRINTK(0,"%s: HDMI-%c\n", __func__, input?'B':'A'); + reg = io_read(REG_INPUT_SEL); + /* keep loop mode for TDA19973 */ + if (tda1997x->chip == 19973) + reg &= ~MASK_DIG_INPUT_VDPR_FMT; + else + reg &= ~(MASK_DIG_INPUT_VDPR_FMT | MASK_HDMIOUTMODE); + reg |= 0x80; /* RESET_FTM - vdp reset */ + reg |= input; + reg |= FORMAT_RESET; /* Soft reset of format measurement timing */ + /* update INPUT_SEL */ + io_write(REG_INPUT_SEL, reg); + + return 0; +} +EXPORT_SYMBOL(tda1997x_select_input); + + +/** configure video output format (output pins and sync config) + * @param pdata with video output configuration + * @returns 0 on success + */ +static int +tda1997x_set_video_outputformat(struct tda1997x_platform_data *pdata) +{ + u8 reg; + + DPRINTK(0,"%s: %s\n", __func__, vidfmt_names[pdata->vidout_format]); + + /* Configure pixel clock generator: + * - delay and polarity from platform data + * - clock per output format + * + * CLKOUT_CTRL bits: + * 7 - unused + * 6:4 - clkout_del - Delay of clock signal on V_CLK pin + * 3 - unused + * 2 - clkout_tog - polarity of clock signal on V_CLK pin (1 - invert) + * 1:0 - clkout_sel: Selection of the clock signal output on CKP pin + * 00: Pix_clock (and selection for Chroma/Luma in case of CCIR-656, so + * clock for CCIR-656 DDR) + * 01: Pix_clock_x2 (for 422_CCIR) + * 10: Pix_clock_div2 + * 11: Pix_clock_div4 + */ + reg = pdata->vidout_delay_clk << 4; + if (pdata->vidout_clkmode == CLOCK_SINGLE_EDGE) { /* single edge */ + switch (pdata->vidout_format) { + case VIDEOFMT_444: /* RGB444/YUV444 */ + case VIDEOFMT_422_SMP: /* YUV422 semi-planar */ + break; + case VIDEOFMT_422_CCIR:/* YUV422 CCIR656 */ + reg |= 0x01; /* clk_x2 */ + break; + } + } else { /* dual edge */ + switch (pdata->vidout_format) { + case VIDEOFMT_444: /* RGB444/YUV444 */ + case VIDEOFMT_422_SMP: /* YUV422 semi-planar */ + reg |= 0x02; /* clk_div2 */ + break; + case VIDEOFMT_422_CCIR:/* YUV422 CCIR656 */ + break; + } + } + io_write(REG_CLKOUT_CTRL, reg); + + /* Configure pre-filter: + * + * FILTERS_CTRL: + * 3:2 - Bu Filter control + * 00: Off + * 01: 2 Taps + * 10: 7 Taps + * 11: 2/7 Taps + * 1:0 - Rv Filter control + * 00: Off + * 01: 2 Taps + * 10: 7 Taps + * 11: 2/7 Taps + */ + reg = 0x00; /* filters off */ + /* 4:2:2 mode requires conversion */ + if ((pdata->vidout_format == VIDEOFMT_422_SMP) + || (pdata->vidout_format == VIDEOFMT_422_CCIR)) + reg = 0x0f; /* 27taps for Rv and Bu */ + io_write(REG_FILTERS_CTRL, reg); + + /* Enable Blanking code and timing ref (EAV/SAV) insertion */ + reg = VP_OUT | pdata->vidout_format; + if (pdata->vidout_blc) + reg |= VP_BLK; + if (pdata->vidout_trc) + reg |= VP_TRC; + io_write(REG_OF_CTRL, reg); + + /* Configure bypass: + * VDP_CTRL bits: + * 5 - compdel_bp - 1:bypass compdel + * 4 - formatter_bp - 1:bypass formatter + * 3 - ? + * 2 - ? + * 1 - prefilter_bp - 1:bypass prefilter + * 0 - mat_bp - 1:bypass matrix conversion + */ + reg = io_read(REG_VDP_CTRL); + /* bypass pre-filter if not needed (REG_FILTERS_CTRL == 0) */ + if ( (io_read(REG_FILTERS_CTRL) & 0x0f) == 0) + reg |= (1<<1); /* disable pre-filter */ + else + reg &= ~(1<<1); /* enable pre-filter */ + /* bypass formatter if not needed: + * (444 mode and no insertion of timing/blanking codes */ + if ( (pdata->vidout_format == VIDEOFMT_444) + && !pdata->vidout_blc + && !pdata->vidout_trc + ) { + reg |= (1<<4); /* disable formatter */ + } else { + /* enable formatter and compdel: needed for timing/blanking codes */ + reg &= ~((1<<4)|(1<<5)); + } + /* activate compdel for small sync delays */ + if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4)) { + reg &= ~(1<<5); + } + io_write(REG_VDP_CTRL, reg); + + /* Configure DE output signal: + * - delay, polarity, and source from platform data + * + * DE_FREF_SEL bits: + * 7:4 - de_del - DE from HDMI delay (Latency datapath + [-8..+7] pixels) + * 3 - de_pxq - DE pixel qualification (only when del_sel = 00) + * 0: Timing codes are not signaled by DE + * 1: Timing codes are signaled by DE + * 2 - de_pol - Polarity of signal output on DE/FREF pin + * 0: No specific action + * 1: Invert signal + * 1:0 - de_sel - Selection of signal output on DE/FREF pin + * 00: DE from VHREF [HREF and not(VREF)] + * 01: FREF from VHREF + * 10: FREF from HDMI + */ + io_write(REG_DE_FREF_SEL, + (pdata->vidout_delay_de << 4) | + (pdata->vidout_invert_de << 2) | + (pdata->vidout_sel_de)); + + /* HS_HREF_SEL bits: + * 7:4 - hs_del - HS from HDMI delay (Latency datapath + [-8..+7] pixels) + * 3 - href_pxq - HREF pixel qualifcation: + * 0 - Timing codes are not signaled by HREF + * 1 - Timing codes are signaled by HREF + * 2 - hsync_pol: Polarity of signal output on HS/HREF pin + * 0 - No specific action + * 1 - Invert signal + * 1:0 - hsync_sel - Selection of signal output on HS/HREF pin + * 00: HS from VHREF (Select HS signal output from internal sync generator + * on HS_HREF hardware pin) + * 01: HREF from VHREF (Select HREF signal output from internal sync + * generator on HS_HREF hardware pin) + * 10: HREF from HDMI (Select HREF signal output from HDMI input sync + * processor on HS_HREF hardware pin) + */ + io_write(REG_HS_HREF_SEL, + (pdata->vidout_delay_hs << 4) | + (pdata->vidout_invert_hs << 2) | + (pdata->vidout_sel_hs)); + + /* VS_VREF_SEL bits: + * 7:4 - vs_del - VS from HDMI delay (Latency datapath + [-8..+7] pixels) + * 3 - unused + * 2 - vsync_pol: Polarity of signal output on VS/VREF pin + * 0 - No specific action + * 1 - Invert signal + * 1:0 - vsync_sel - Selection of signal output on VS/VREF pin + * 00: VS from VHREF (Select VS signal output from internal sync generator + * on HS_VREF hardware pin) (BSLHDMIRX_SYNCOUTPUT_VSYNC_VHREF) + * 01: VREF from VHREF (Select VREF signal output from internal sync + * generator on HS_VREF hardware pin) (BSLHDMIRX_SYNCOUTPUT_VREF_VHREF) + * 10: VREF from HDMI (Select HREF signal output from HDMI input sync + * processor on HS_VREF hardware pin) (BSLHDMIRX_SYNCOUTPUT_VSYNC_HDMI) + */ + io_write(REG_VS_VREF_SEL, + (pdata->vidout_delay_vs << 4) | + (pdata->vidout_invert_vs << 2) | + (pdata->vidout_sel_vs)); + + return 0; +} + + +/** Soft Reset of specific hdmi info + * @param info_rst - reset to apply to HDMI_INFO_RST + * @param bresetSus - reset start-up sequencer + * @returns 0 on success + */ +static int +tda1997x_hdmi_info_reset(u8 info_rst, bool bresetSus) +{ + u8 reg; + + DPRINTK(0,"%s: 0x%02x %s\n", __func__, info_rst, bresetSus?"RESET_SUS":""); + + reg = io_read(REG_HDMI_INFO_RST); + io_write(REG_HDMI_INFO_RST, info_rst); + + /* if IF has been reset, clear INT_FLG_MODE as all ITs are raided by the + * reset IF */ + if (reg & RESET_IF) { + reg = io_read(REG_INT_FLG_CLR_MODE); + io_write(REG_INT_FLG_CLR_MODE, reg); + } + + /* Write SUS_RESET register (bit hdcp_dcc_man does not exist on TDA19972) */ + if (!bresetSus) { + reg = io_read(REG_RATE_CTRL); + reg |= RATE_REFTIM_ENABLE; + reg = io_write(REG_RATE_CTRL, reg); + } else { + reg = io_read(REG_RATE_CTRL); + reg &= ~RATE_REFTIM_ENABLE; + reg = io_write(REG_RATE_CTRL, reg); + } + + return 0; +} + +/* Configure HDCP: set basic configuration for HDCP module: + * enable/disable, key encryption, DDC address, key description seed + * + * @param decrypt_keys - Key internal decryption (on/off) + * @param hdcp_enable - Enable/disable HDCP function + * @param i2c_addr - Display data channel I2C slave address + * @param key_decryption_seed - Key decryption Seed + * @returns 0 on success + */ +static int +tda1997x_configure_hdcp(struct tda1997x_data *tda1997x, + hdcp_key_t decrypt_keys, + enable_t hdcp_enable, + u8 ddc_i2c_addr, + u16 key_decryption_seed) +{ + u8 reg; + u8 regs[3]; + + DPRINTK(0,"%s: enable=%d ddc=0x%02x seed=0x%04x\n", + __func__, hdcp_enable, ddc_i2c_addr, key_decryption_seed); + + /* HDCP control */ + regs[0] = decrypt_keys | hdcp_enable; + /* keys description seed MSB */ + regs[1] = key_decryption_seed >> 8; + /* keys description seed LSB */ + regs[2] = key_decryption_seed & 0x00ff; + + if (tda1997x->chip_revision == 0) { + /* enable clock on TMDS PLL by using FRO */ + io_write(REG_CLK_CFG, 0x03); + io_write(REG_PON_CBIAS, 0x01); + io_write(REG_PON_PLL, 0x01); + io_write(REG_PON_OVR_EN, 0x01); + } + + /* write HDCP_CTRL regs */ + io_writen(REG_HDCP_CTRL, 3, regs); + + /* write i2c addr */ + io_write(REG_HDCP_DDC_ADDR, ddc_i2c_addr); + + if (tda1997x->chip_revision == 0) { + /* disable clock on TMDS PLL by using FRO */ + io_write(REG_CLK_CFG, 0x00); + io_write(REG_PON_OVR_EN, 0x00); + + /* Restore clock */ + io_write(REG_PON_CBIAS, 0x00); + io_write(REG_CGU_DEBUG_SEL, 0x08); + + /* Clear HDMI mode flag in BCAPS (for N1) */ + io_write(REG_CLK_CFG, 0x03); + io_write(REG_PON_OVR_EN, 0x01); + io_write(REG_PON_CBIAS, 0x01); + io_write(REG_PON_PLL, 0x01); + reg = io_read(REG_MODE_RECOVER_CFG1); + reg &= ~0x06; + reg |= 0x02; + io_write(REG_MODE_RECOVER_CFG1, reg); + io_write(REG_CLK_CFG, 0x00); + io_write(REG_PON_OVR_EN, 0x00); + reg = io_read(REG_MODE_RECOVER_CFG1); + reg &= ~0x06; + io_write(REG_MODE_RECOVER_CFG1, reg); + } + + /* clear HDCP interrupt status bits that may have been raised during this + * process + */ + io_write(REG_INT_FLG_CLR_HDCP, 0x07); + + return 0; +} + +/* Configure HDCP MTP + * @param cmd - command to be sent (download or read) + * @returns 0 on success + */ +static int +tda1997x_configure_mtp(struct tda1997x_data *tda1997x, mtp_command_t cmd) +{ + u8 reg; + unsigned int timeout; + + switch (cmd) { + case MTP_START_DOWNLOAD: + DPRINTK(0,"%s: MTP_START_DOWNLOAD\n", __func__); + if (tda1997x->chip_revision == 0) { + /* disable termination and enable HDCP block */ + io_write(REG_RT_MAN_CTRL, 0x00); + io_write(REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); + + /* enable clock on TMDS PLL by using FRO */ + io_write(REG_CLK_CFG, 0x03); + io_write(REG_PON_CBIAS, 0x01); + io_write(REG_PON_PLL, 0x01); + io_write(REG_PON_OVR_EN, 0x01); + io_write(REG_CGU_DEBUG_SEL, 0x00); + + /* reset HDCP block */ + io_write(REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); + io_write(REG_MAN_HDMI_SET, 0x04); + io_write(REG_MAN_HDMI_SET, 0x00); + + /* remove force */ + io_write(REG_PON_OVR_EN, 0x00); + io_write(REG_CLK_CFG, 0x03); + + /* copy byte KEY_0(39) (0x4002) into private_area (0x425f) */ + reg = io_read(0x4002 /*REG_MTP_KEY39_LSB*/); + io_write(0x425f /*REG_MTP_PRIVATE_AREA*/, reg); + } + + /* Enable HDCP */ + io_write(REG_HDMI_INFO_RST, 0x00); + io_write(REG_HDCP_CTRL, 0x03); + + /* clear flag hdcp_dlmtp: 0x0015:4 */ + io_write(REG_INT_FLG_CLR_HDCP, 0x18); + + /* download key into HDCP engine */ + io_write(REG_HDCP_KEY_CTRL, 0x01); + + /* check flag hdcp_dlram: 0x0015:3 */ + timeout = jiffies + msecs_to_jiffies(100); + do { + reg = io_read(REG_INT_FLG_CLR_HDCP); + } while ( (jiffies < timeout) & ((reg & 0x08) != 0x08)); + + /* download MTP into SRAM: hmtp_dl_all (0x137a) */ + io_write(REG_HMTP_CTRL, 0x01); + + /* check flag hdcp_dlmtp: 0x0015:4 */ + timeout = jiffies + msecs_to_jiffies(100); + do { + reg = io_read(REG_INT_FLG_CLR_HDCP); + } while ( (jiffies < timeout) & ((reg & 0x10) != 0x10)); + + /* clear HDCP interrupt status bits that may have been raised */ + io_write(REG_INT_FLG_CLR_HDCP, 0x07); + break; + + case MTP_START_READ: + DPRINTK(0,"%s: MTP_START_READ\n", __func__); + /* clear flag hdcp_dlmtp */ + io_write(REG_INT_FLG_CLR_HDCP, 0x10); + + /* Download MTP into SRAM: hmtp_dl_all */ + io_write(REG_HMTP_CTRL, 0x01); + + /* check flag hdcp_dlmtp */ + timeout = jiffies + msecs_to_jiffies(100); + do { + reg = io_read(REG_INT_FLG_CLR_HDCP); + } while ( (jiffies < timeout) & ((reg & 0x10) != 0x10)); + break; + } + + return 0; +} + + +/** set power mode + */ +static void +tda1997x_power_mode(struct tda1997x_data *tda1997x, bool bEnable) +{ + u8 reg; + + dev_dbg(&tda1997x->client->dev, "%s %s\n", __func__, bEnable?"on":"off"); + DPRINTK(0,"%s %s\n", __func__, bEnable?"on":"off"); + + if (bEnable) { + /* Power on sequence */ + +#if 0 + /* Disable low power mode */ + reg = io_read(REG_EDID_POWER); + reg &= ~MASK_LOW_PW_EDID; + io_write(REG_EDID_POWER, reg); +#endif + + /* Automatic control of TMDS */ + io_write(REG_PON_OVR_EN, TMDS_AUTO_PON); + + /* Enable current bias unit */ + io_write(REG_CFG1, CBIAS_PON); + +#if 0 + /* Enable TMDS clock in the digital equalizer */ + reg = io_read(REG_SUS_RESET); + reg &= ~MASK_TMDS_CLK_DIS; + io_write(REG_SUS_RESET, reg); + + /* Enable Xtal Osc */ + reg = io_read(REG_XOSC_CFG); + reg &= ~MASK_XTAL_OSC_PD; + io_write(REG_XOSC_CFG, reg); +#endif + + /* Enable deep color PLL */ + io_write(REG_DEEP_PLL7, DC_PLL_PON); + +#if 0 + /* Enable audio PLL */ + reg = io_read(REG_CLOCKS_MODE); + reg &= ~MASK_AUDIO_PLL_PD; + io_write(REG_CLOCKS_MODE, reg); +#endif + + /* Output buffers active */ + reg = io_read(REG_OF_CTRL); + reg &= ~MASK_OF_CTRL_OUT_HIZ; + io_write(REG_OF_CTRL, reg); + + } else { + /* Power down EDID mode sequence */ + + /* Output buffers in HiZ */ + reg = io_read(REG_OF_CTRL); + reg |= MASK_OF_CTRL_OUT_HIZ; + io_write(REG_OF_CTRL, reg); + +#if 0 + /* Disable audio PLL */ + reg = io_read(REG_CLOCKS_MODE); + reg |= MASK_AUDIO_PLL_PD; + io_write(REG_CLOCKS_MODE, reg); +#endif + + /* Disable deep color PLL */ + io_write(REG_DEEP_PLL7, DC_PLL_PD); + +#if 0 + /* Disable Xtal Osc */ + reg = io_read(REG_XOSC_CFG); + reg |= MASK_XTAL_OSC_PD; + io_write(REG_XOSC_CFG, reg); + + /* Disable TMDS clock in the digital equalizer */ + reg = io_read(REG_SUS_RESET); + reg |= MASK_TMDS_CLK_DIS; + io_write(REG_SUS_RESET, reg); +#endif + + /* Disable current bias unit */ + io_write(REG_CFG1, CBIAS_POFF); + + /* Manual control of TMDS */ + io_write(REG_PON_OVR_EN, TMDS_MAN_PON); + +#if 0 + /* Enable low power mode */ + reg = io_read(REG_EDID_POWER); + reg |= MASK_LOW_PW_EDID; + io_write(REG_EDID_POWER, reg); +#endif + } + +} + + +/* check the audio samplerate for change + * @returns 0 on success + */ +static int +tda1997x_get_audio_frequency(struct tda1997x_data *tda1997x) +{ + u8 reg; + long freq = 0; + + reg = io_read(REG_AUDIO_FREQ); + switch(reg & MASK_AUDIO_FREQ) { + case 0x00: break; + case 0x01: freq= 32000; break; + case 0x02: freq= 44100; break; + case 0x03: freq= 48000; break; + case 0x04: freq= 88200; break; + case 0x05: freq= 96000; break; + case 0x06: freq=176400; break; + case 0x07: freq=192000; break; + } + + DPRINTK(0, "REG_AUDIO_FREQ=0x%02x: %ldHz\n", reg, freq); + tda1997x->audio_mode.samplerate = freq; + + return 0; +} + +/** detect an appropriate video mode from: + * video input verticalPeriod, horizontalPeriod, and hsWidth + * @returns resolution_data * + */ +static const resolution_data_t * +tda1997x_detect_resolution(struct tda1997x_data *tda1997x) +{ + u32 verticalPeriod; + u16 horizontalPeriod; + u16 hsWidth; + char vPerCmp = 0, hPerCmp = 0, hsWidthCmp = 0; + const resolution_data_t *res; + int i; + + /* Read the FMT registers */ + verticalPeriod = io_read24(REG_V_PER) & MASK_VPER; + horizontalPeriod = io_read16(REG_H_PER) & MASK_HPER; + hsWidth = io_read16(REG_HS_WIDTH) & MASK_HSWIDTH; + DPRINTK(0,"verticalPeriod=%d horizontalPeriod=%d hsWidth=%d\n", + verticalPeriod, horizontalPeriod, hsWidth); + +#if 1 // more details +{ + videoFormatDetails fmt; + videoFormatDetails *pFMT = &fmt; + + /* read the FMT registers */ + pFMT->vsPolarity = io_read(REG_V_PER) & 0x80; + pFMT->hsPolarity = io_read(REG_H_PER) & 0x80; + pFMT->videoFormat = io_read(REG_HS_WIDTH) & 0x80; + pFMT->horizontalTotalPeriod = io_read16(REG_FMT_H_TOT)&0x3fff; + pFMT->horizontalVideoActiveWidth = io_read16(REG_FMT_H_ACT)&0x3fff; + pFMT->horizontalFrontPorchWidth = io_read16(REG_FMT_H_FRONT)&0x3fff; + pFMT->horizontalSyncWidthPixClk = io_read16(REG_FMT_H_SYNC)&0x3fff; + pFMT->horizontalBackPorchWidth = io_read16(REG_FMT_H_BACK)&0x3fff; + pFMT->verticalTotalPeriod = io_read16(REG_FMT_V_TOT)&0x3fff; + pFMT->verticalVideoActiveWidth = io_read16(REG_FMT_V_ACT)&0x3fff; + pFMT->verticalFrontPorchWidthF1 = io_read(REG_FMT_V_FRONT_F1); + pFMT->verticalFrontPorchWidthF2 = io_read(REG_FMT_V_FRONT_F2); + pFMT->verticalSyncWidth = io_read(REG_FMT_V_SYNC); + pFMT->verticalBackPorchWidthF1 = io_read(REG_FMT_V_BACK_F1); + pFMT->verticalBackPorchWidthF2 = io_read(REG_FMT_V_BACK_F2); + pFMT->dataEnablePresent = io_read(REG_FMT_DE_ACT)&0x01; + + DPRINTK(2,"vsPolarity=%d hsPolarity=%d videoFormat=%d\n" + "horizTotalPeriod=%d horizVideoActiveWidth=%d horizFrontPorchWidth=%d\n" + "horizSyncWidthPixClk=%d horizBackPorchWidth=%d\n" + "vertTotalPeriod=%d vertVideoActiveWidth=%d vertFrontPorchWidthF1=%d" + "vertFrontPorchWidthF2=%d\n" + "vertBackPorchWidthF1=%d vertBackPorchWidthF1=%d dataEnablePresent=%d\n", + pFMT->vsPolarity, pFMT->hsPolarity, pFMT->videoFormat, + pFMT->horizontalTotalPeriod, pFMT->horizontalVideoActiveWidth, + pFMT->horizontalFrontPorchWidth, pFMT->horizontalSyncWidthPixClk, + pFMT->horizontalBackPorchWidth, + pFMT->verticalTotalPeriod, pFMT->verticalVideoActiveWidth, + pFMT->verticalFrontPorchWidthF1, pFMT->verticalFrontPorchWidthF2, + pFMT->verticalBackPorchWidthF1, + pFMT->verticalBackPorchWidthF2, + pFMT->dataEnablePresent); +} +#endif + + /* iterate over list of supported resolutions and find best match */ + for (i = 0; i < ARRAY_SIZE(supported_res); i++) { + res = &supported_res[i]; + + vPerCmp = (char) ((verticalPeriod >= res->verticalPeriodMin) && + (verticalPeriod <= res->verticalPeriodMax)); + hPerCmp = (char) ((horizontalPeriod >= res->horizontalPeriodMin) && + (horizontalPeriod <= res->horizontalPeriodMax)); + hsWidthCmp = (char) ((hsWidth >= res->hsWidthMin) && + (hsWidth <= res->hsWidthMax)); + + DPRINTK(1,"\t%02d: %dx%d@%d%c: %d/%d/%d\n", i, res->width, res->height, + res->horizfreq, res->interlaced?'i':'p', + vPerCmp, hPerCmp, hsWidthCmp); + if (vPerCmp && hPerCmp && hsWidthCmp) { + int pixrate; + + /* resolutiontype used to determine Default Colorimetry */ + switch (res->height) { + case 480: + case 576: + case 240: + case 288: + tda1997x->resolutiontype = RESTYPE_SDTV; + break; + case 720: + case 1080: + tda1997x->resolutiontype = RESTYPE_HDTV; + break; + default: + tda1997x->resolutiontype = RESTYPE_PC; + } + + printk(KERN_INFO "%s: matched resolution: %dx%d%c@%d %s\n", + KBUILD_MODNAME, res->width, res->height, + res->interlaced?'i':'p', + res->horizfreq, + restype_names[tda1997x->resolutiontype]); + + /* validate input mode */ + pixrate = (res->width * res->height * res->horizfreq) / + 1000000; + if (res->interlaced) + pixrate /= 2; + switch(tda1997x->pdata->vidout_format) { + case VIDEOFMT_444: + case VIDEOFMT_422_SMP: + /* FIXME: have not figured out how to get HS output asserted on 2nd field */ + if (res->interlaced) { + printk(KERN_INFO "%s: Error %s: interlaced not supported\n", KBUILD_MODNAME, + vidfmt_names[tda1997x->pdata->vidout_format]); + return NULL; + } + break; + case VIDEOFMT_422_CCIR: + /* BT656 requires 2-clocks per pixel */ + pixrate *= 2; + break; + } + if (tda1997x->pdata->max_pixel_rate && + (pixrate > tda1997x->pdata->max_pixel_rate)) + { + printk(KERN_INFO "%s: Error: %dMP/s exceeds max of %dMP/s\n", KBUILD_MODNAME, + pixrate, + tda1997x->pdata->max_pixel_rate); + return NULL; + } + + return res; + } + } + + printk(KERN_ERR "%s: found no video resolution match for: %d/%d/%d\n", + KBUILD_MODNAME, verticalPeriod, horizontalPeriod, hsWidth); + return NULL; +} + +/** read input activity registers + * + * @returns activity_sate bitmask: + * 7: unused + * 6: unused + * 5: unused + * 4: tmds_locked + * 3: inputD_clock_stable (unsupported) + * 2: inputC_clock_stable (unsupported) + * 1: inputB_clock_stable + * 0: inputA_clock_stable + */ +static u8 +tda1997x_read_activity_status_regs(void) +{ + u8 reg, status = 0; + + /* Activity detection must only be notified when stable_clk_x AND active_x + * bits are set to 1. If only stable_clk_x bit is set to 1 but not + * active_x, it means that the TMDS clock is not in the defined range, + * so activity detection must not be notified + * => regStatus must be set to 0 in that case. If stable_clk_x bit is + * set to 0, regStatus must also be set to 0 + */ + + /* Read CLK_A_STATUS register */ + reg = io_read(REG_CLK_A_STATUS); + /* when stable_clk_x is set to 1, check active_x bit */ + if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE) ) + reg &= ~MASK_CLK_STABLE; + status |= ((reg & MASK_CLK_STABLE) >> 2); + + /* Read CLK_B_STATUS register */ + reg = io_read(REG_CLK_B_STATUS); + /* when stable_clk_x is set to 1, check active_x bit */ + if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE) ) + reg &= ~MASK_CLK_STABLE; + status |= ((reg & MASK_CLK_STABLE) >> 1); + + /* Read the SUS_STATUS register */ + reg = io_read(REG_SUS_STATUS); + + /* If state = 5 => TMDS is locked */ + if ( (reg & MASK_SUS_STATE_VALUE) == LAST_STATE_REACHED) + status |= MASK_SUS_STATE_BIT; + else + status &= ~MASK_SUS_STATE_BIT; + + return status; +} + + +/** parse an infoframe and do some sanity checks on it + * + * @type of inforframe + * @returns 0 on success + */ +static unsigned int +tda1997x_parse_infoframe(struct tda1997x_data *tda1997x, int type) +{ + u8 d[MAX_IF_DATA]; + u8 crc; + int len = 0; + int i; + + /* determine length based on type */ + switch(type) { + case MPS_IF_TYPE: len = MPS_IF_NB_DATA; break; + case AUD_IF_TYPE: len = AUD_IF_NB_DATA; break; + case SPD_IF_TYPE: len = SPD_IF_NB_DATA; break; + case AVI_IF_TYPE: len = AVI_IF_NB_DATA; break; + case VS_HDMI_IF_TYPE: + case VS_BK1_IF_TYPE: + case VS_BK2_IF_TYPE: + len = VS_IF_NB; + break; + } + + /* read data */ + if (io_readn(type, len, d) != len) { + printk(KERN_ERR "infoframe type0x%02x failed read\n", type); + return -1; + } + + /* verify crc */ + for (i = 0, crc = 0; i < len; i++) + crc += d[i]; + if (crc) { + printk(KERN_ERR "infoframe type0x%02x failed CRC\n", type); + return -2; + } + + /* parse details */ + switch(type) { + case MPS_IF_TYPE: + /* parse infoframe to get bitrate, fieldrepeat, MPEG_Frame */ + DPRINTK(0,"\t\tbitrate=%d,%d,%d,%d fieldRepeat=%d MPEG_Frame=%d\n", + d[4], d[5], d[6], d[7], d[8] & 0x10, d[8] & 0x03); + break; + + /* Audio InfoFrame: see HDMI spec 8.2.2 */ + case AUD_IF_TYPE: + /* + * CC0..CC2 - Channel Count. See CEA-861-D table 17 + * CT0..CT3 - Coding Type. The CT bits shall always be 0 (use Stream Header) + * SS0..SS1 - Sample Size. The SS bits shall always be 0 (use Stream Header) + * SF0..SF2 - Sample Freq. See CEA-861-D table 18. + * - For L-PCM and IEC 61937 compressed audio streams the SF bits + * shall always be 0 (use Stream Header). + * - For One Bit Audio and DST streams, the SF bits shall equal + * the ACR fs value. + * - For Super Audio CD, the SF bits are typically 0, 1, 0 + * indicating a sample freq of 2.8224MSamples/s (64*44.1kHz) + * CA0..CA7 - Channel/Speaker Allocation. See CEA-861-D Section 6.6.2 + * - this is not valid for IEC 61937 compressed audio streams + * LSV0..LSV3 - Level Shif Value (for downmixing). See CEA-861-D 6.6.2 + * and CEA-861-D table 21 + * DM_INH - Downmix inibit. See CEA-861-D sec 6.6.2 and table 22 + * The DM_INH field is to be set only for DVD-Audio + * + * CT, SS, and SF values of 0 indicate that these items are carried in the + * audio stream itself. + */ + DPRINTK(0,"\t\tcodingType=%d channelCount=%d samplefrequency=%d " + "samplesize=%d dataByte3=%d channelAllocation=%d downmixInhibit=%d " + "levelShiftValue=%d\n", + (d[4] & 0xf0) >> 4, /* CT3, CT2, CT1, CT0 */ + (d[4] & 0x07), /* CC2, CC1, CC0 */ + (d[5] & 0x1c) >> 2, /* SF2, SF1, SF0 */ + (d[5] & 0x03), /* SS1, SS0 */ + d[6], + d[7], /* CA7 .. CA0 */ + (d[8] & 0x80) >> 7, /* DM_INH */ + (d[8] & 0x78) >> 3); /* LSV3, LSV2, LSV1, LSV0 */ + + /* Channel Count */ + tda1997x->source_channels = (d[4] & 0x07) + 1; + DPRINTK(0, "Audio Channels: %d\n", + tda1997x->source_channels); + + /* Channel Assignment */ + if ( (d[7] <= 0x1f) /* MAX_CHANNEL_ALLOC */ + && (d[7] != tda1997x->channel_assignment) + && !tda1997x->pdata->audio_force_channel_assignment) + { + /* use the channel assignment from the audio infoframe */ + DPRINTK(0, "channel assignment changed: %d\n", d[7]); + tda1997x->channel_assignment = d[7]; + + /* configure audio output */ + tda1997x_configure_audio_formatter(tda1997x->pdata, + tda1997x->channel_assignment); + + /* reset the audio FIFO */ + tda1997x_hdmi_info_reset(RESET_AUDIO, 0); + //tda1997x_hdmi_info_reset(0x00, 0); + } + break; + + /* Source Product Descriptor information (SPD) */ + case SPD_IF_TYPE: + for (i = 0; i < 8; i++) + tda1997x->vendor[i] = d[4 + i]; + for (i = 0; i < 16; i++) + tda1997x->product[i] = d[4 + 8 + i]; + printk(KERN_INFO "%s: Source Product Descriptor: %s %s\n", KBUILD_MODNAME, + tda1997x->vendor, tda1997x->product); + break; + + + /* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */ + case AVI_IF_TYPE: { + u8 pixel_repetitionfactor; + u8 reg; + + if (d[0] != 0x82) { + printk(KERN_ERR "INFOFRAME AVI: wrong packet type! (0x%02x)\n", d[0]); + } + DPRINTK(0,"\t\tcolorIndicator=%d activeInfoPresent=%d " + "barInfomationDataValid=%d scanInformation=%d colorimetry=%d " + "pictureAspectRatio=%d activeFormatAspectRation=%d " + "nonUinformPictureScaling=%d videoFormatIdentificationCode=%d " + "pixelRepetitionFactor=%d\n", + (d[4] & 0x60) >> 5, /* colorspace: Y1, Y0 */ + (d[4] & 0x10) >> 4, /* activeInfoPresent: A0 */ + (d[4] & 0x0c) >> 2, /* barInformationValid: B1, B0 */ + (d[4] & 0x03), /* scanInformation: S1, S0 */ + (d[5] & 0xc0) >> 6, /* coloriemtry: C1, C0 */ + (d[5] & 0x30) >> 4, /* pictureAspectRatio: M1, M0 */ + (d[5] & 0x0f), /* activeFormatAspectRatio: R3, R2, R1, R0 */ + (d[6] & 0x03), /* nonUniformPictureScaling: SC1, SC0 */ + (d[7] & 0x7f), /* videoFormatID: VIC6, VIC5, VIC4 */ + (d[8] & 0x0f) /* pixelRepetitionFactor: PR3, PR2, PR1, PR0 */ + ); + tda1997x->colorspace = (d[4] & 0x60) >> 5; /* Y1, Y0 */ + tda1997x->colorimetry = (d[5] & 0xc0) >> 6; /* C1, C0 */ + pixel_repetitionfactor = d[8] & 0x0f; /* PR3, PR2, PR1, PR0 */ + /* If colorimetry not specified, conversion depends on resolutiontype: + * - SDTV: ITU601 for SD (480/576/240/288 line resolution) + * - HDTV: ITU709 for HD (720/1080 line resolution) + * - PC: sRGB + * see HDMI specification section 6.7 + */ + if ( (tda1997x->colorspace == COLORSPACE_YCBCR_422 || + tda1997x->colorspace == COLORSPACE_YCBCR_444) && + (tda1997x->colorimetry == COLORIMETRY_XVYCC || + tda1997x->colorimetry == COLORIMETRY_NONE) ) + { + + if (tda1997x->resolutiontype == RESTYPE_HDTV) + tda1997x->colorimetry = COLORIMETRY_ITU709; + else if (tda1997x->resolutiontype == RESTYPE_SDTV) + tda1997x->colorimetry = COLORIMETRY_ITU601; + else + tda1997x->colorimetry = COLORIMETRY_NONE; + dev_info(&tda1997x->client->dev, + "invalid/undefined colorimetry defaulted to %s (%s)\n", + colorimetry_names[tda1997x->colorimetry], + restype_names[tda1997x->resolutiontype]); + } + + /* configure upsampler per sample format */ + /* ConfigureUpDownSampler: 0=bypass 1=repeatchroma 2=interpolate */ + reg = io_read(REG_PIX_REPEAT); + reg = (reg & ~0x30 /* MASK_UP_SEL */); + if (tda1997x->colorspace == COLORSPACE_YCBCR_422) + reg |= (1 << 4); /* repeatchroma */ + io_write(REG_PIX_REPEAT, reg); + + /* ConfigurePixelRepeater - repeat n-times each pixel */ + reg = io_read(REG_PIX_REPEAT); + reg = (reg & ~0x0f /*MASK_PIX_REP*/) | pixel_repetitionfactor; + io_write(REG_PIX_REPEAT, reg); + + /* configure the receiver with the new colorspace */ + tda1997x_configure_conversion(tda1997x, + tda1997x->colorspace, + tda1997x->colorimetry, + tda1997x->pdata->vidout_format); + + } break; + + case VS_HDMI_IF_TYPE: + case VS_BK1_IF_TYPE: + case VS_BK2_IF_TYPE: + /* read update flag and store at the end */ + d[VS_IF_NB] = io_read(type); + if (type == VS_HDMI_IF_TYPE && d[VS_IF_NB] > 3) /* HDMI_INFO_EXCEED */ + { + DPRINTK(0,"HDMI_INFO_EXCEED\n"); + return -3; + } + DPRINTK(0, "\t\tieee_id[0]=%d ieee_id[1]=%d ieee_id[2]=%d\n",d[0],d[1],d[2]); + break; + } + + return 0; +} + + +/* tda1997x_work - deferred work procedure for handling interrupt + */ +static void tda1997x_work(struct work_struct *work) +{ + struct tda1997x_data *tda1997x = &tda1997x_data; + u8 reg, interrupt_top_flags, source; + + do { + /* read interrupt flags */ + interrupt_top_flags = io_read(REG_INT_FLG_CLR_TOP); + if (interrupt_top_flags == 0) + break; + DPRINTK(0,"interrupt:0x%02x\n", interrupt_top_flags); + + /* SUS interrupt source (Input activity events) */ + if (interrupt_top_flags & INTERRUPT_SUS) { + source = io_read(REG_INT_FLG_CLR_SUS); + io_write(REG_INT_FLG_CLR_SUS, source); + DPRINTK(0,"SUS: 0x%02x\n", source); + + if (source & MASK_MPT_BIT) { + DPRINTK(0,"\tConfig MTP end of process\n"); + + /* reset MTP in use flag if set */ + if (tda1997x->mptrw_in_progress) + tda1997x->mptrw_in_progress = 0; + } + + if (source & MASK_SUS_END_BIT) { + /* reset audio FIFO */ + reg = io_read(REG_HDMI_INFO_RST); + reg |= MASK_SR_FIFO_FIFO_CTRL; + io_write(REG_HDMI_INFO_RST, reg); + reg &= ~MASK_SR_FIFO_FIFO_CTRL; + io_write(REG_HDMI_INFO_RST, reg); + + DPRINTK(0,"\tRESET AUDIO SUS_END\n"); + + /* reset HDMI flags memory and vsi_received flag */ + tda1997x->hdmi_status = 0; + tda1997x->vsi_received = 0; + } + + /* filter FMT interrupt based on SUS state */ + reg = io_read(REG_SUS_STATUS); + if ( ((reg & MASK_SUS_STATE_VALUE) != LAST_STATE_REACHED) + || (source & MASK_MPT_BIT)) + { + DPRINTK(0,"sus_state_value=0x%02x filter video fmt changed\n", + reg & MASK_SUS_STATE_VALUE); + source &= ~MASK_FMT_BIT; + } + + if (source & (MASK_FMT_BIT | MASK_SUS_END_BIT)) { + const resolution_data_t *res; + DPRINTK(0, "\tHDMI LOCKED\n"); + + reg = io_read(REG_SUS_STATUS); + if ((reg & MASK_SUS_STATE_VALUE) != LAST_STATE_REACHED) { + printk(KERN_ERR "%s: BAD SUS STATUS\n", KBUILD_MODNAME); + continue; + } + + /* There is a new activity, the status for HDCP repeater state */ + tda1997x->state_c5_reached = 0; + + /* Detect the new resolution */ + res = tda1997x_detect_resolution(tda1997x); + if (res) { + tda1997x->video_mode.width = res->width; + tda1997x->video_mode.height = res->height; + tda1997x->video_mode.fps = res->horizfreq; + tda1997x->video_mode.interlaced = res->interlaced; + tda1997x->video_mode.signal = 1; + + /* configure the active input to the given resolution */ + tda1997x_configure_input_resolution(res->resolutionID); + + } else { + tda1997x->video_mode.width = 0; + tda1997x->video_mode.height = 0; + tda1997x->video_mode.fps = 0; + tda1997x->video_mode.interlaced = 0; + tda1997x->video_mode.signal = 1; + } + + /* on 'input locked' event, RGB colorspace is forced (the AVI infoframe + * is not received yet at this moment) + * if AVI infoframe is received later, the colorspace will be + * reconfigured in the AVI infoframe handler + */ + tda1997x->colorspace = COLORSPACE_RGB; + tda1997x->colorimetry = COLORIMETRY_NONE; + /* bypass colorspace conversion */ + io_write(REG_VDP_CTRL, io_read(REG_VDP_CTRL) | (1<<0)); + /* SetBlankingCodes */ + io_write16(REG_BLK_GY, RGBBlankingCode.blankingCodeGy); + io_write16(REG_BLK_BU, RGBBlankingCode.blankingCodeBu); + io_write16(REG_BLK_RV, RGBBlankingCode.blankingCodeRv); + + /* set the state machine */ + tda1997x->state = STATE_LOCKED; + } + + if (source & MASK_RT_PULSE_BIT) { + DPRINTK(0,"\tEnd of termination resistance pulse\n"); + } + if (source & MASK_SUS_ACT_BIT) { + DPRINTK(0,"\tActivity of selected input changed\n"); + } + if (source & MASK_SUS_CH_BIT) { + DPRINTK(0,"\tSelected input changed\n"); + } + if (source & MASK_SUS_ST_BIT) { + DPRINTK(0,"\tSUS state changed\n"); + } + } + + /* DDC interrupt source (Display Data Channel) */ + else if (interrupt_top_flags & INTERRUPT_DDC ) { + source = io_read(REG_INT_FLG_CLR_DDC ); + io_write(REG_INT_FLG_CLR_DDC, source); + DPRINTK(0,"DDC: 0x%02x\n", source); + + if (source & MASK_EDID_MTP) { + DPRINTK(0,"\tEDID MTP end of process\n"); + /* reset MTP in use flag if set */ + if (tda1997x->mptrw_in_progress) + tda1997x->mptrw_in_progress = 0; + } + + /* we don't care about these */ + if (source & MASK_DDC_ERR) { + DPRINTK(0,"\tmaster DDC error\n"); + } + if (source & MASK_DDC_CMD_DONE) { + DPRINTK(0,"\tmaster DDC cmd send correct\n"); + } + if (source & MASK_READ_DONE) { + DPRINTK(0,"\tEnd of Down EDID read\n"); + } + if (source & MASK_RX_DDC_SW) { + DPRINTK(0,"\tOutput DDC switching finished\n"); + } + if (source & MASK_HDCP_DDC_SW) { + DPRINTK(0,"\tHDCP DDC switching finished\n"); + } + if (source & MASK_HDP_PULSE_END) { + DPRINTK(0,"\tEnd of Hot Plug Detect pulse\n"); + } + if (source & MASK_DET_5V) { + DPRINTK(0,"\tDetected +5V\n"); + } + } + + /* RATE interrupt source (Digital Input A/B activity: rate/presence/drift) */ + else if (interrupt_top_flags & INTERRUPT_RATE) { + u8 irq_status, last_irq_status; + + source = io_read(REG_INT_FLG_CLR_RATE); + io_write(REG_INT_FLG_CLR_RATE, source); + DPRINTK(0,"RATE: 0x%02x\n", source); + + /* read status regs */ + last_irq_status = irq_status = tda1997x_read_activity_status_regs(); + + /* read clock status reg until INT_FLG_CLR_RATE is still 0 + * after the read to make sure its the last one + */ + reg = source; + while (reg != 0) { + irq_status = tda1997x_read_activity_status_regs(); + reg = io_read(REG_INT_FLG_CLR_RATE); + io_write(REG_INT_FLG_CLR_RATE, reg); + source |= reg; + } + + /* we don't use these indicators */ +#if 0 + if (source & MASK_RATE_B_DRIFT) { + DPRINTK(0, "\tRate measrement of input B drifted\n"); + } + if (source & MASK_RATE_B_ACT) { + DPRINTK(0, "\tRate measurement of input B activity change\n"); + } + if (source & MASK_RATE_B_PST) { + DPRINTK(0, "\tRate measurement of input B presence change\n"); + } + if (source & MASK_RATE_A_DRIFT) { + DPRINTK(0, "\tRate measrement of input A drifted\n"); + } + if (source & MASK_RATE_A_ACT) { + DPRINTK(0, "\tRate measurement of input A activity change\n"); + } + if (source & MASK_RATE_A_PST) { + DPRINTK(0, "\tRate measurement of input A presence change\n"); + } +#endif + + /* we only pay attention to stability change events */ + if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) { + int input = (source & MASK_RATE_A_ST)?0:1; + u8 mask = 1<activity_status_reg); + + /* state change */ + if ((irq_status & mask) != (tda1997x->activity_status_reg & mask)) { + + /* activity lost */ + if ( (irq_status & mask) == 0) { + printk(KERN_INFO "%s: HDMI-%c: Digital Activity Lost\n", + KBUILD_MODNAME, input+'A'); + tda1997x->state = STATE_UNLOCKED; + tda1997x->input_detect[input] = 0; + tda1997x->hdmi_status = 0; + tda1997x->hdmi_detected = 0; + tda1997x->hdcp_detected = 0; + tda1997x->eess_detected = 0; + + /* ConfigureUpDownSampler: 0=bypass 1=repeatchroma 2=interpolate */ + reg = io_read(REG_PIX_REPEAT); + reg = (reg & ~0x30 /*MASK_UP_SEL*/) | (0 << 4); /* bypass */ + io_write(REG_PIX_REPEAT, reg); + + /* ConfigurePixelRepeater - repeat n-times each pixel */ + reg = io_read(REG_PIX_REPEAT); + reg = (reg & ~0x0f /*MASK_PIX_REP*/) | 0; /* 0-times: disable */ + io_write(REG_PIX_REPEAT, reg); + + if (tda1997x->chip_revision == 0) { + /* Clear HDMI mode flag in BCAPS (for N1) */ + io_write(REG_CLK_CFG, 0x03); + io_write(REG_PON_OVR_EN, 0x01); + io_write(REG_PON_CBIAS, 0x01); + io_write(REG_PON_PLL, 0x01); + reg = io_read(REG_MODE_RECOVER_CFG1); + reg &= ~0x06; + reg |= 0x02; + io_write(REG_MODE_RECOVER_CFG1, reg); + io_write(REG_CLK_CFG, 0x00); + io_write(REG_PON_OVR_EN, 0x00); + reg = io_read(REG_MODE_RECOVER_CFG1); + reg &= ~0x06; + io_write(REG_MODE_RECOVER_CFG1, reg); + } + + tda1997x->video_mode.width = 0; + tda1997x->video_mode.height = 0; + tda1997x->video_mode.fps = 0; + tda1997x->video_mode.interlaced = 0; + tda1997x->video_mode.signal = 0; + } + + else { + printk(KERN_INFO "%s: HDMI-%c: Digital Activity Detected\n", + KBUILD_MODNAME, input+'A'); + tda1997x->input_detect[input] = 1; + } + + /* hold onto current state */ + tda1997x->activity_status_reg = (irq_status & mask); + } + } + } + + /* MODE interrupt source (Gamut, ISRC2, ISRC1, ACP, GCP, Deep color flags) */ + else if (interrupt_top_flags & INTERRUPT_MODE) { + source = io_read(REG_INT_FLG_CLR_MODE); + io_write(REG_INT_FLG_CLR_MODE, source); + DPRINTK(0,"MODE: 0x%02x\n", source); + + /* special processing for HDMI Flag IT */ + if (source & MASK_HDMI_FLG) { + u8 statusChange; + + reg = io_read(REG_HDMI_FLAGS); + reg &= 0x7c; /* ignore fifo_fail and fifo_warning bits */ + statusChange = tda1997x->hdmi_status ^ reg; + DPRINTK(0,"\tHDMI_FLAGS:0x%02x prev=0x%02x changed=0x%02x\n", reg, + tda1997x->hdmi_status, statusChange); + if (statusChange) { + tda1997x->hdmi_status = reg; + /* Get HDMI Status */ + if (statusChange & 0x80) + DPRINTK(0,"\t\tAUDIOFLAG - Audio packet in last videoframe\n"); + if (statusChange & 0x40) + DPRINTK(0,"\t\tHDMI mode detected\n"); + tda1997x->hdmi_detected = 1; + if (statusChange & 0x20) + DPRINTK(0,"\t\tEESS mode detected\n"); + tda1997x->eess_detected = 1; + if (statusChange & 0x10) + DPRINTK(0,"\t\tHDCP encryption detected\n"); + tda1997x->hdcp_detected = 1; + if (statusChange & 0x08) + DPRINTK(0,"\t\tAVMUTE\n"); + if (statusChange & 0x04) + DPRINTK(0,"\t\tLayout status Audio Sample Packet\n"); + if (statusChange & 0x02) + DPRINTK(0,"\t\tFIFO read/write pointers are crossed\n"); + if (statusChange & 0x01) + DPRINTK(0,"\t\tFIFO read ptr closer than 2 samples from write ptr\n"); + } + } + if (source & MASK_GAMUT) { + u8 d[GDB_PACKET_HDR_LEN + GDB_PACKET_DAT_LEN]; + io_readn(REG_GBD_PACKET_TYPE, sizeof(d), d); + DPRINTK(0,"\tGamut packet: type=%d nextField=%d GBDProfile=%d " + "affectedGamutSeqNum=%d noCrntGBD=%d packetSeq=%d curSeq=%d\n", + d[0], /* type */ + (d[1] & 0x80) >> 7, /* nextField */ + (d[1] & 0x70) >> 4, /* GBDProfile */ + (d[1] & 0x0f), /* affected Gamut SeqNum */ + (d[2] & 0x80) >> 7, /* noCrntGBD */ + (d[2] & 0x30) >> 4, /* packet SeqNum */ + (d[2] & 0x0f) /* current SeqNum */ + ); + } +#if 0 + if (source & MASK_ISRC2) { + DPRINTK(0,"\tISRC2 packet\n"); + + if (1) // untested + { + u8 d[ISRC_PACKET_HDR_LEN + ISRC_PACKET_DAT_LEN]; + u8 crc = 0, i; + + io_readn(REG_ISRC2_PACKET_TYPE, sizeof(d), d); + for (i = 0; i < sizeof(d); i++) + crc += d[i]; + if (crc) { + printk(KERN_ERR "%s: ISRC2 packet CRC failed\n", KBUILD_MODNAME); + } + } + } + if (source & MASK_ISRC1) { + DPRINTK(0,"\tISRC1 packet\n"); + + if (1) // untested + { + u8 d[ISRC_PACKET_HDR_LEN + ISRC_PACKET_DAT_LEN]; + u8 crc = 0, i; + + io_readn(REG_ISRC1_PACKET_TYPE, sizeof(d), d); + for (i = 0; i < sizeof(d); i++) + crc += d[i]; + if (crc) { + printk(KERN_ERR "%s: ISRC1 packet CRC failed\n", KBUILD_MODNAME); + } else { + DPRINTK(0,"\tISRC1 packet: type=%d ISRCCont=%d ISRCValid=%d " + "ISRCStatus=%d\n", + d[0], + (d[1] & 0x80) >> 7, + (d[1] & 0x40) >> 6, + (d[1] & 0x07) + ); + } + } + } + if (source & MASK_ACP) { + DPRINTK(0,"\tAudio Content Protection (ACP) Packet\n"); + + if (1) // untested + { + u8 d[ACP_PACKET_HDR_LEN + ACP_PACKET_DAT_LEN]; + u8 crc = 0, i; + + io_readn(REG_ACP_PACKET_TYPE, sizeof(d), d); + for (i = 0; i < sizeof(d); i++) + crc += d[i]; + if (crc) { + printk(KERN_ERR "%s: ACP packet CRC failed\n", KBUILD_MODNAME); + } + } + } +#endif + if (source & MASK_DC_NO_GCP) { + DPRINTK(0,"\tGCP not received in 5 frames\n"); + } + if (source & MASK_DC_PHASE) { + DPRINTK(0,"\tDeep color mode pixel phase needs update\n"); + } + if (source & MASK_DC_MODE) { + reg = io_read(REG_DEEP_COLOR_MODE); + DPRINTK(0,"\tDeep color mode color depth changed: " + "pixelPackingPhase=0x%02x mode=%d\n", + reg & MASK_DC_PIXEL_PHASE, + reg & MASK_DC_COLOR_DEPTH); + } + } + + /* Infoframe change interrupt source */ + else if (interrupt_top_flags & INTERRUPT_INFO ) { + source = io_read(REG_INT_FLG_CLR_INFO); + io_write(REG_INT_FLG_CLR_INFO, source); + DPRINTK(0,"INFO: 0x%02x\n", source); + + /* Vendor-Specific Infoframe */ + if (source & MASK_VS_IF_OTHER_BK2 || + source & MASK_VS_IF_OTHER_BK1 || + source & MASK_VS_IF_HDMI) + { + u8 VSIUpdate, VSOther1Update, VSOther2Update; + + /* Read the Update registers */ + VSIUpdate = io_read(VS_HDMI_IF_UPDATE); + VSOther1Update = io_read(VS_BK1_IF_UPDATE); + VSOther2Update = io_read(VS_BK2_IF_UPDATE); + + /* discard ITs that are too old */ + if (VSIUpdate >= 3) source &= ~MASK_VS_IF_HDMI; + if (VSOther1Update >= 3) source &= ~MASK_VS_IF_OTHER_BK1; + if (VSOther2Update >= 3) source &= ~MASK_VS_IF_OTHER_BK2; + + /* No new VSI has been received if these 3 registers are > 3 */ + if (tda1997x->vsi_received + && (VSIUpdate>=3) && (VSOther1Update>=3) && (VSOther2Update>=3)) + { + /* false VSI received */ + tda1997x->vsi_received = 0; + } + else + { + tda1997x->vsi_received = 1; + + if (source & MASK_VS_IF_HDMI) { + DPRINTK(0,"\tVendor-Specific InfoFrame: HDMI\n"); + tda1997x_parse_infoframe(tda1997x, VS_HDMI_IF_TYPE); + } + + if (source & MASK_VS_IF_OTHER_BK1) { + DPRINTK(0,"\tVendor-Specific InfoFrame: BK1\n"); + tda1997x_parse_infoframe(tda1997x, VS_BK1_IF_TYPE); + } + + if (source & MASK_VS_IF_OTHER_BK2) { + DPRINTK(0,"\tVendor-Specific InfoFrame: BK2\n"); + tda1997x_parse_infoframe(tda1997x, VS_BK2_IF_TYPE); + } + } + } + + /* MPEG Source Product infoframe */ + if (source & MASK_MPS_IF) { + DPRINTK(0, "\tMPEG Source Product InfoFrame content change\n"); + tda1997x_parse_infoframe(tda1997x, MPS_IF_TYPE); + } + + /* Audio infoframe */ + if (source & MASK_AUD_IF) { + DPRINTK(0, "\tAudio InfoFrame content change\n"); + tda1997x_parse_infoframe(tda1997x, AUD_IF_TYPE); + } + + /* Source Product Descriptor infoframe change */ + if (source & MASK_SPD_IF) { + DPRINTK(0, "\tSource Product Descriptor InfoFrame change\n"); + tda1997x_parse_infoframe(tda1997x, SPD_IF_TYPE); + } + + /* Auxillary Video Information infoframe */ + if (source & MASK_AVI_IF) { + DPRINTK(0, "\tAuxiliary Video information InfoFrame change\n"); + tda1997x_parse_infoframe(tda1997x, AVI_IF_TYPE); + } + } + + /* Audio interrupt source: + * freq change, DST,OBA,HBR,ASP flags, mute, FIFO err + */ + else if (interrupt_top_flags & INTERRUPT_AUDIO ) { + source = io_read(REG_INT_FLG_CLR_AUDIO); + io_write(REG_INT_FLG_CLR_AUDIO, source); + DPRINTK(0,"AUDIO: 0x%02x\n", source); + + if (source & MASK_ERROR_FIFO_PT || /* Audio FIFO pointer error */ + source & MASK_MUTE_FLG) /* Audio mute */ + { + if (source & MASK_MUTE_FLG) { + DPRINTK(0, "\tAudio MUTE\n"); + } else { + DPRINTK(0, "\tAudio FIFO error\n"); + } + + /* audio reset audio FIFO */ + reg = io_read(REG_SUS_STATUS); + if ((reg & MASK_SUS_STATE_VALUE) == LAST_STATE_REACHED) { + reg = io_read(REG_HDMI_INFO_RST); + reg |= MASK_SR_FIFO_FIFO_CTRL; + io_write(REG_HDMI_INFO_RST, reg); + reg &= ~MASK_SR_FIFO_FIFO_CTRL; + io_write(REG_HDMI_INFO_RST, reg); + + /* reset channel status IT if present */ + source &= ~(MASK_CH_STATE); + } + } + if (source & MASK_AUDIO_FREQ_FLG) { + DPRINTK(0, "\tAudio freq change\n"); + tda1997x_get_audio_frequency(tda1997x); + printk(KERN_INFO "%s: Audio Frequency Change: %dHz\n", + KBUILD_MODNAME, + tda1997x->audio_mode.samplerate); + } + if (source & MASK_AUDIO_FLG) { + reg = io_read(REG_AUDIO_FLAGS); + DPRINTK(0, "\tAudio flag: 0x%02x\n", reg); + if (reg & 0x08) { + DPRINTK(0, "\t\tDTS packets detected\n"); + } + if (reg & 0x04) { + DPRINTK(0, "\t\tOBA packets detected\n"); + } + if (reg & 0x02) { + DPRINTK(0, "\t\tHBR packets detected\n"); + } + if (reg & 0x01) { + DPRINTK(0, "\t\tAudio sample packets detected\n"); + } + } + if (source & MASK_CH_STATE) { + DPRINTK(0, "\tChannel status\n"); + } + if (source & MASK_UNMUTE_FIFO) { + DPRINTK(0, "\tUnmute audio FIFO\n"); + } + } + + /* HDCP interrupt source (content protection) */ + if (interrupt_top_flags & INTERRUPT_HDCP) { + source = io_read(REG_INT_FLG_CLR_HDCP); + io_write(REG_INT_FLG_CLR_HDCP, source); + DPRINTK(0,"HDCP: 0x%02x\n", source); + + /* reset MTP in use flag if set */ + if (source & MASK_HDCP_MTP) { + DPRINTK(0, "\tHDCP MTP in use\n"); + tda1997x->mptrw_in_progress = 0; + } + if (source & MASK_HDCP_DLMTP) { + DPRINTK(0,"\tHDCP end download MTP to SRAM\n"); + } + if (source & MASK_HDCP_DLRAM) { + DPRINTK(0,"\tHDCP end download keys from SRAM to HDCP core\n"); + } + if (source & MASK_HDCP_ENC) { + DPRINTK(0,"\tHDCP_ENC\n"); + } + if (source & MASK_STATE_C5) { + DPRINTK(0,"\tHDCP State C5 reached\n"); + + /* REPEATER: mask AUDIO and IF interrupts to avoid IF during auth */ + reg = io_read(REG_INT_MASK_TOP); + reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO); + io_write(REG_INT_MASK_TOP, reg); + interrupt_top_flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO); + } + if (source & MASK_AKSV) { + DPRINTK(0,"\tAKSV received (Start of Authentication)\n"); + } + } + + /* AFE interrupt source */ + else if (interrupt_top_flags & INTERRUPT_AFE ) { + source = io_read(REG_INT_FLG_CLR_AFE); + io_write(REG_INT_FLG_CLR_AFE, source); + DPRINTK(0,"AFE: 0x%02x\n", source); + } + } while (interrupt_top_flags != 0); + + /* we handled all alerts; re-enable level-triggered IRQ */ + enable_irq(tda1997x->irq); +} + +/** tda1997x interrupt handler + */ +static irqreturn_t tda1997x_isr(int irq, void *d) +{ + struct tda1997x_data *tda1997x = d; + + /* disable level-triggered IRQs until we handle them */ + disable_irq_nosync(irq); + schedule_work(&tda1997x->work); + + return IRQ_HANDLED; +} + +/*********************************************************************** + * I2C client and driver. + ***********************************************************************/ + +/** + * tda1997x I2C detach function. + * Called on rmmod. + * + * @param *client struct i2c_client*. + * @return Error code indicating success or failure. + */ +static int tda1997x_remove(struct i2c_client *client) +{ + struct tda1997x_data *tda1997x = i2c_get_clientdata(client); + dev_dbg(&tda1997x_data.client->dev, + "%s:Removing tda1997x video decoder @ 0x%02X from adapter %s\n", + __func__, client->addr << 1, client->adapter->name); + + tda1997x_power_mode(&tda1997x_data, 0); + + if (client->irq) { + devm_free_irq(&client->dev, client->irq, &tda1997x_data); + } + +/* + if (tda1997x->vid_child) { + platform_device_del(tda1997x->vid_child); + } +*/ + + if (tda1997x->client_cec) + i2c_unregister_device(tda1997x->client_cec); + + sysfs_remove_group(&client->dev.kobj, &attr_group); + + if (dvddio_regulator) + regulator_disable(dvddio_regulator); + if (dvdd_regulator) + regulator_disable(dvdd_regulator); + if (avdd_regulator) + regulator_disable(avdd_regulator); + + return 0; +} + + +static int tda1997x_regulator_enable(struct device *dev, + struct tda1997x_platform_data *pdata) +{ + int ret = 0; + + dvddio_regulator = devm_regulator_get(dev, "DOVDD"); + if (!IS_ERR(dvddio_regulator)) { + regulator_set_voltage(dvddio_regulator, + TDA1997X_VOLTAGE_DIGITAL_IO, + TDA1997X_VOLTAGE_DIGITAL_IO); + ret = regulator_enable(dvddio_regulator); + if (ret) { + dev_err(dev, "set io voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set io voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get io voltage\n"); + } + + dvdd_regulator = devm_regulator_get(dev, "DVDD"); + if (!IS_ERR(dvdd_regulator)) { + ret = regulator_set_voltage(dvdd_regulator, + TDA1997X_VOLTAGE_DIGITAL_CORE, + TDA1997X_VOLTAGE_DIGITAL_CORE); + ret = regulator_enable(dvdd_regulator); + if (ret) { + dev_err(dev, "set core voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set core voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get core voltage\n"); + } + + avdd_regulator = devm_regulator_get(dev, "AVDD"); + if (!IS_ERR(avdd_regulator)) { + ret = regulator_set_voltage(avdd_regulator, + TDA1997X_VOLTAGE_ANALOG, + TDA1997X_VOLTAGE_ANALOG); + ret = regulator_enable(avdd_regulator); + if (ret) { + dev_err(dev, "set analog voltage failed\n"); + return ret; + } else { + dev_dbg(dev, "set analog voltage ok\n"); + } + } else { + dev_warn(dev, "cannot get analog voltage\n"); + } + + return ret; +} + +static int parse_vidout_fmt(const char *mode) +{ + int clkmode; + if (!strcmp(mode, "444")) + clkmode = VIDEOFMT_444; + else if (!strcmp(mode, "422_smp")) + clkmode = VIDEOFMT_422_SMP; + else if (!strcmp(mode, "422_ccir")) + clkmode = VIDEOFMT_422_CCIR; + else + clkmode = -EINVAL; + + return clkmode; +} + +static int parse_vidout_clkmode(const char *mode) +{ + int clkmode; + if (!strcmp(mode, "single_edge")) + clkmode = CLOCK_SINGLE_EDGE; + else if (!strcmp(mode, "dual_edge")) + clkmode = CLOCK_DUAL_EDGE; + else if (!strcmp(mode, "single_edge_toggled")) + clkmode = CLOCK_SINGLE_EDGE_TOGGLED; + else if (!strcmp(mode, "dual_edge_toggled")) + clkmode = CLOCK_DUAL_EDGE_TOGGLED; + else + clkmode = -EINVAL; + + return clkmode; +} + +static int parse_audout_fmt(const char *mode) +{ + int clkmode; + if (!strcmp(mode, "i2s16")) + clkmode = AUDIO_FMT_I2S16; + else if (!strcmp(mode, "i2s32")) + clkmode = AUDIO_FMT_I2S32; + else if (!strcmp(mode, "spdif")) + clkmode = AUDIO_FMT_SPDIF; + else if (!strcmp(mode, "oba")) + clkmode = AUDIO_FMT_OBA; + else if (!strcmp(mode, "i2s16_hbr_straight")) + clkmode = AUDIO_FMT_I2S16_HBR_STRAIGHT; + else if (!strcmp(mode, "i2s16_hbr_demux")) + clkmode = AUDIO_FMT_I2S16_HBR_DEMUX; + else if (!strcmp(mode, "i2s32_hbr_demux")) + clkmode = AUDIO_FMT_I2S32_HBR_DEMUX; + else if (!strcmp(mode, "dst")) + clkmode = AUDIO_FMT_DST; + else + clkmode = -EINVAL; + + return clkmode; +} + +static int tda1997x_get_of_property(struct device *dev, + struct tda1997x_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + const char *vidout_fmt, *vidout_clk, *audout_fmt; + u32 hdcp, ddc_slave, blc, trc; + u32 port_configs; + u32 audout_clk, audout_layout, max_pixel_rate = 0; + int err; + + /* defaults (use inverted vs/hs/de) */ + /* TODO: add of bindings for these */ + pdata->vidout_sel_vs = 1; + pdata->vidout_invert_vs =1; + pdata->vidout_sel_hs = 1; + pdata->vidout_invert_hs =1; + pdata->vidout_sel_de = 1; + pdata->vidout_invert_de =1; + + /* enable HDCP */ + err = of_property_read_u32(np, "hdcp", &hdcp); + if (err) { + dev_dbg(dev, "get of property hdcp fail\n"); + return err; + } + /* DDC Slave address */ + err = of_property_read_u32(np, "ddc_slave", &ddc_slave); + if (err) { + dev_dbg(dev, "get of property ddc_slave fail\n"); + return err; + } + + /* Video output mode */ + err = of_property_read_string(np, "vidout_fmt", &vidout_fmt); + if (err) { + dev_dbg(dev, "get of property vidout_fmt fail\n"); + return err; + } + /* insert timing codes (SAV/EAV) in stream */ + err = of_property_read_u32(np, "vidout_trc", &trc); + if (err) { + dev_dbg(dev, "get of property vidout_trc fail\n"); + return err; + } + /* insert blanking codes in stream */ + err = of_property_read_u32(np, "vidout_blc", &blc); + if (err) { + dev_dbg(dev, "get of property vidout_blc fail\n"); + return err; + } + /* video output clock mode */ + err = of_property_read_string(np, "vidout_clkmode", &vidout_clk); + if (err) { + dev_dbg(dev, "get of property vidout_clkmode fail\n"); + return err; + } + /* video output port config */ + of_find_property(np, "vidout_portcfg", &port_configs); + err = of_property_read_u8_array(np, "vidout_portcfg", + pdata->vidout_port_config, + port_configs); + if (err) { + dev_dbg(dev, "get of property vidout_portcfg fail\n"); + return err; + } + pdata->vidout_port_config_no = port_configs; + /* max pixrate */ + of_property_read_u32(np, "max-pixel-rate", &max_pixel_rate); + + /* audio output format */ + err = of_property_read_string(np, "audout_fmt", &audout_fmt); + if (err) { + dev_dbg(dev, "get of property audout_fmt fail\n"); + return err; + } + /* audio output sysclk */ + err = of_property_read_u32(np, "audout_sysclk", &audout_clk); + if (err) { + dev_dbg(dev, "get of property audout_clkmode fail\n"); + return err; + } + /* audio layout */ + err = of_property_read_u32(np, "audout_layout", &audout_layout); + if (err) { + dev_dbg(dev, "get of property audout_layout fail\n"); + return err; + } + + pdata->hdcp = hdcp; + pdata->ddc_slave = ddc_slave; + pdata->vidout_format = parse_vidout_fmt(vidout_fmt); + pdata->vidout_trc = trc; + pdata->vidout_blc = blc; + pdata->vidout_clkmode = parse_vidout_clkmode(vidout_clk); + pdata->max_pixel_rate = max_pixel_rate; + pdata->audout_layout = audout_layout; + pdata->audout_format = parse_audout_fmt(audout_fmt); + switch (audout_clk) { + default: + case 128: + pdata->audout_sysclk = AUDIO_SYSCLK_128FS; + break; + case 256: + pdata->audout_sysclk = AUDIO_SYSCLK_256FS; + break; + case 512: + pdata->audout_sysclk = AUDIO_SYSCLK_512FS; + break; + } + + return err; +} + +/** + * tda1997x I2C probe function. + * Function set in i2c_driver struct. + * Called by insmod. + * + * @param *adapter I2C adapter descriptor. + * + * @return Error code indicating success or failure. + */ +static int tda1997x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + int i; + u8 reg; + struct tda1997x_data *tda1997x = &tda1997x_data; + struct tda1997x_platform_data *pdata; + struct device *dev = &client->dev; + + dev_dbg(dev, "%s\n", __func__); + pdata = devm_kzalloc(dev, sizeof(struct tda1997x_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + dev->platform_data = pdata; + + if (!client->irq) { + dev_err(dev, "get tda1997x of interrupt fail\n"); + return -EINVAL; + } + + ret = tda1997x_get_of_property(dev, pdata); + if (ret < 0) { + dev_err(dev, "get tda1997x of property fail\n"); + return ret; + } + + /* probe chip */ +/* + ret = i2c_smbus_read_byte_data(client, REG_CMTP_REG10 & 0xff); + if (ret < 0) + return -ENODEV; +*/ + + tda1997x_regulator_enable(dev, pdata); + + memset(tda1997x, 0, sizeof(tda1997x_data)); + i2c_set_clientdata(client, tda1997x); + spin_lock_init(&tda1997x->lock); + tda1997x->page = 0xff; + tda1997x->client = client; + tda1997x->irq = client->irq; + tda1997x->pdata = pdata; + tda1997x->internal_edid = !pdata->external_edid; + INIT_WORK(&tda1997x->work, tda1997x_work); + mutex_init(&tda1997x->page_lock); + mutex_init(&tda1997x->cec_lock); + switch(pdata->audout_layout) { + case AUDIO_LAYOUT_FORCED_0: + tda1997x->audio_mode.channels = 2; + break; + case AUDIO_LAYOUT_FORCED_1: + tda1997x->audio_mode.channels = 8; + break; + default: + tda1997x->audio_mode.channels = 8; + break; + } + switch(pdata->audout_format) { + case AUDIO_FMT_I2S16: + tda1997x->audio_mode.samplesize = 2; + break; + case AUDIO_FMT_I2S32: + tda1997x->audio_mode.samplesize = 2; + break; + default: + tda1997x->audio_mode.samplesize = 0; + } + + /* sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, &attr_group); + if (ret) + printk(KERN_ERR "%s: failed creating sysfs attrs\n", KBUILD_MODNAME); + + tda1997x->colorspace = COLORSPACE_RGB; + tda1997x->colorimetry = COLORIMETRY_NONE; + tda1997x->resolutiontype = RESTYPE_SDTV; + + /* disable/reset HDCP to get correct I2C access to Rx HDMI */ + io_write(REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP); + + /* Read chip configuration*/ + reg = io_read(REG_CMTP_REG10); + tda1997x->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */ + tda1997x->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */ + tda1997x->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */ + tda1997x->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */ + tda1997x->cec_enabled = (reg >> 0) & 0x01; /* CEC enabled */ + switch((reg >> 4) & 0x03) { + case 0x00: + tda1997x->chip = 19971; + tda1997x->input = INPUT_HDMI_A; + break; + case 0x01: + tda1997x->chip = 19972; + tda1997x->input = INPUT_AUTO_DIGITAL; + break; + case 0x02: + case 0x03: + tda1997x->chip = 19973; + tda1997x->input = INPUT_HDMI_A; + break; + } + + /* read chip revision */ + tda1997x->chip_revision = io_read(REG_CMTP_REG11); + + /* if N2 version, reset compdel_bp as it may generate some small pixel + * shifts in case of embedded sync/or delay lower than 4 */ + if (tda1997x->chip_revision != 0) { + io_write(REG_MAN_SUS_HDMI_SEL, 0x00); + io_write (REG_VDP_CTRL, 0x1f); + } + + /* The CEC I2C address is not yet correct. We need to take into account + * possible config setting in SLAVE_ADDR register, however the Hw I2C address + */ + tda1997x->cec_slave = 0x34 + ((io_read(REG_SLAVE_ADDR)>>4) & 0x03); + + pr_info("NXP TDA%d N%d detected: %dbit VP%s\n", + tda1997x->chip, + tda1997x->chip_revision + 1, + (tda1997x->port_30bit)?30:24, + (tda1997x->cec_enabled)?", CEC ":""); + pr_info("video out format: %s\n", vidfmt_names[pdata->vidout_format]); + if (tda1997x->cec_enabled) + pr_info("CEC slave address 0x%02x\n", tda1997x->cec_slave); + if (tda1997x->pdata->max_pixel_rate) + pr_info("max pixel rate: %d MP/sec\n", pdata->max_pixel_rate); + + /* Attach a second dummy i2c_client for CEC register access */ + tda1997x->client_cec = i2c_new_dummy(client->adapter, tda1997x->cec_slave); + if (!tda1997x->client_cec) { + printk(KERN_ERR "%s: failed to register CEC client\n", KBUILD_MODNAME); + } + + /* disable HPD */ + io_write(REG_HPD_AUTO_CTRL, 0x08); /* hpd_unsel */ + + if (tda1997x->chip_revision == 0) { + io_write(REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP); + io_write(REG_CGU_DEBUG_SEL, 0x08); + } + + /* reset infoframe at end of start-up-sequencer */ + io_write(REG_SUS_SET_RGB2, 0x06); + io_write(REG_SUS_SET_RGB3, 0x06); + + /* update page 0 */ + io_write(REG_RT_MAN_CTRL, 0x43); + + /* CEC + */ + /* enable sync measurement timing */ + tda1997x_cec_write(REG_PWR_CONTROL & 0xff, 0x04); + /* adjust CEC clock divider */ + tda1997x_cec_write(REG_OSC_DIVIDER & 0xff, 0x03); + tda1997x_cec_write(REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0); + io_write(REG_TIMER_D, 0x54); + /* enable power switch - SRAM content is always valid + * (in case E-MTP is not or mis-programmed) + */ + reg = tda1997x_cec_read(REG_CONTROL & 0xff); + reg |= 0x20; + tda1997x_cec_write(REG_CONTROL & 0xff, reg); + mdelay(50); + + /* read the chip version */ + reg = io_read(REG_VERSION); + /* get the chip configuration */ + reg = io_read(REG_CMTP_REG10); + + /* init interrupt masks we care about */ + io_write(REG_INT_MASK_TOP, 0x7f); /* hdcp,audio,info,mode,rate,ddc,sus */ + io_write(REG_INT_MASK_SUS, 0xa8); /* config_mtp,fmt,sus_end,sus_st */ + io_write(REG_INT_MASK_DDC, 0x00); /* none */ + io_write(REG_INT_MASK_RATE, 0x44); /* rate_b_st, rate_a_st */ + io_write(REG_INT_MASK_MODE, 0xf9); /* hdmi_flg,gamut,isrc2,isrc1,acp,dc_mode */ + io_write(REG_INT_MASK_INFO, 0x7f); /* mps_if,aud_if,spd_if,avi_if, + vs_if_other_bk2,vs_if_other_bk1, + vs_if_hdmi */ + io_write(REG_INT_MASK_AUDIO,0x3f); /* audio_freq,audio_flg,mute_flg, + ch stat,unmount_fifo,fifo_err */ + io_write(REG_INT_MASK_HDCP, 0x02); /* state_c5 */ + io_write(REG_INT_MASK_AFE, 0x00); /* none */ + + /* clear all interrupts */ + io_write(REG_INT_FLG_CLR_TOP, 0xff); + io_write(REG_INT_FLG_CLR_SUS, 0xff); + io_write(REG_INT_FLG_CLR_DDC, 0xff); + io_write(REG_INT_FLG_CLR_RATE, 0xff); + io_write(REG_INT_FLG_CLR_MODE, 0xff); + io_write(REG_INT_FLG_CLR_INFO, 0xff); + io_write(REG_INT_FLG_CLR_AUDIO, 0xff); + io_write(REG_INT_FLG_CLR_HDCP, 0xff); + io_write(REG_INT_FLG_CLR_AFE, 0xff); + + /* init TMDS equalizer */ + if (tda1997x->chip_revision == 0) + io_write(REG_CGU_DEBUG_SEL, 0x08); + io_write24(REG_CLK_MIN_RATE, CLK_MIN_RATE); + io_write24(REG_CLK_MAX_RATE, CLK_MAX_RATE); + if (tda1997x->chip_revision == 0) + io_write(REG_WDL_CFG, WDL_CFG_VAL); + /* DC filter */ + io_write(REG_DEEP_COLOR_CTRL, DC_FILTER_VAL); + + /* disable test pattern */ + io_write(REG_SERVICE_MODE, 0x00); + + /* update HDMI INFO CTRL */ + io_write(REG_INFO_CTRL, 0xff); + + /* write HDMI INFO EXCEED value */ + io_write(REG_INFO_EXCEED, 3); + + if (tda1997x->chip_revision == 0) { + /* clear HDMI mode flag in BCAPS (for N1) */ + io_write(REG_CLK_CFG, 0x03); + io_write(REG_PON_OVR_EN, 0x01); + io_write(REG_PON_CBIAS, 0x01); + io_write(REG_PON_PLL, 0x01); + + reg = io_read(REG_MODE_RECOVER_CFG1); + reg &= ~0x06; + reg |= 0x02; + io_write(REG_MODE_RECOVER_CFG1, reg); + io_write(REG_CLK_CFG, 0x00); + io_write(REG_PON_OVR_EN, 0x00); + reg = io_read(REG_MODE_RECOVER_CFG1); + reg &= ~0x06; + io_write(REG_MODE_RECOVER_CFG1, reg); + } + + /* No HDCP acknowledge when HDCP is disabled + * and reset SUS to force format detection + */ + tda1997x_hdmi_info_reset(NACK_HDCP, 1); + //tda1997x_hdmi_info_reset(NACK_HDCP, 0); + + /* get key description seed in fuction of seed table if provded */ + tda1997x_configure_mtp(tda1997x, MTP_START_READ); /* Start read */ + reg = io_read(0x4000); /* read from MTP */ + for (i = 0; i < RX_SEED_TABLE_LEN; i++) { + if ((rx_seed_table[i].seedVal == 0) && (rx_seed_table[i].lookUpVal == 0)) + break; + else if (rx_seed_table[i].lookUpVal == reg) { /* MTP_DATA_LSB */ + /* found seed replace seed in key_decryption_seed */ + tda1997x->key_decryption_seed = rx_seed_table[i].seedVal; + break; + } + } + DPRINTK(0,"HDCP decryption seed:0x%04x\n", tda1997x->key_decryption_seed); + + /* disable HDCP */ + tda1997x_configure_hdcp(tda1997x, HDCP_DECRYPTKEY_ON, DISABLE, + tda1997x->pdata->ddc_slave, tda1997x->key_decryption_seed); + + /* set the state machine */ + tda1997x->state = STATE_INITIALIZED; + + /* Set HPD low */ + tda1997x_manual_hpd(tda1997x, HPD_LOW); + + /* Configure receiver capabilities */ + io_write(REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH); + + /* Configure HDMI + * + * HDMI_CTRL bits: + * 3:2 - mute_mode: + * 00: use control packet + * 01: mute off + * 10: mute on + * 1:0 - hdcp_mode: + * 00: auto + * 01: oess + * 10: eess + */ + io_write(REG_HDMI_CTRL, 0x00); /* Auto HDCP mode, packet controlled mute */ + + /* Configure EDID + * + * EDID_ENABLE bits: + * 7 - nack_off + * 6 - edid_only + * 1 - edid_b_en + * 0 - edid_a_en + */ + reg = io_read(REG_EDID_ENABLE); + if (!tda1997x->internal_edid) + reg &= ~0x83; /* EDID Nack ON */ + else + reg |= 0x83; /* EDID Nack OFF */ + io_write(REG_EDID_ENABLE, reg); + + /* HDCP Activation */ + if (pdata->hdcp) { + DPRINTK(0,"%s: Activating HDCP\n", __func__); + + /* No HDCP acknowledge when HDCP is disabled */ + tda1997x_hdmi_info_reset(NACK_HDCP, 0); + + /* disable HDCP */ + tda1997x_configure_hdcp(tda1997x, HDCP_DECRYPTKEY_ON, DISABLE, + tda1997x->pdata->ddc_slave, tda1997x->key_decryption_seed); + + /* index first secret key */ + io_write(REG_HDCP_KIDX, 0x00); + + /* download MTP */ + tda1997x_configure_mtp(tda1997x, MTP_START_DOWNLOAD); + mdelay(20); + + /* enable HDCP */ + tda1997x_configure_hdcp(tda1997x, HDCP_DECRYPTKEY_OFF, ENABLE, + tda1997x->pdata->ddc_slave, tda1997x->key_decryption_seed); + + /* Enable HDCP acknowledge when HDCP is enabled */ + //tda1997x_hdmi_info_reset(0x00, 0); + + /* Configure HDCP error protection + */ + DPRINTK(0,"Configure HDCP error protection\n"); + /* delockDelay - Delay before delocking the word locker */ + io_write(REG_DELOCK_DELAY, 0x07); /* delockDelay (7=max) */ + /* HDCP_DE_CTRL bits: + * 7:6 - de_measurement_mode: + * 00: 1_VDP + * 01: 2_VDP + * 10: 3_VDP + * 11: 4_VDP + * 5 - de_regen_mode: 1-enable + * 4:3 - de_filter_sensitivity: + * 2:0 - de_composition_mode: + * 000: CH0 + * 001: CH1 + * 010: CH2 + * 011: AND + * 100: OR + * 101: MIXED + */ + io_write(REG_HDCP_DE_CTRL, 3<<3); /* de_filter_sensitivity=3 */ + /* HDCP_EP_FILT_CTRL bits: + * 5:4 - ctl_filter_sensitivity + * 3:2 - vs_filter_sensitivity + * 1:0 - hs_filter_sensitivity + */ + /* ctl_filter_sentivity = vs_filter_sensitivity = hs_filter_sensitivity = 3 */ + io_write(REG_HDCP_EP_FILT_CTRL, (0x3<<4) | (0x3<<2) | 0x3); + } + + /* reset start-up-sequencer to force format detection */ + tda1997x_hdmi_info_reset(0x00, 1); + //tda1997x_hdmi_info_reset(0x00, 0); + + /* Set HPD high */ + tda1997x_manual_hpd(tda1997x, HPD_HIGH_OTHER); + + /* Internal EDIDs are enabled - we can now load EDID */ + if (tda1997x->internal_edid) { + /* Load EDID into embedded memory */ + tda1997x_load_edid_data(edid_block); + + /* Load DDC and RT data into embedded memory */ + tda1997x_load_config_data(tda1997x, ddc_config0, rt_config); + } + + /* Set HPD high (now that EDID is ready) */ + tda1997x_manual_hpd(tda1997x, HPD_HIGH); + + /* Select input */ + tda1997x_select_input(tda1997x->input); + + /* enable matrix conversion bypass (no conversion) */ + io_write(REG_VDP_CTRL, io_read(REG_VDP_CTRL) | (1<<0)); + + /* set video output mode */ + tda1997x_set_video_outputformat(tda1997x->pdata); + for (i = 0; i < tda1997x->pdata->vidout_port_config_no; i++) + io_write(REG_VP35_32_CTRL + i, tda1997x->pdata->vidout_port_config[i]); + + /* configure audio output mode */ + tda1997x_configure_audio_formatter(tda1997x->pdata, 0); + /* configure audio clock mode: + * Audio clock generation from Xtal: 128fs, 256fs, 512fs and + * Fs = 32kHz, 44.1kHz, 48kHz, 88.2kHz, 96kHz, 172.4kHz, 192kHz + */ + //io_write(REG_AUDIO_CLOCK_MODE, tda1997x->pdata->audio_sysclk); + io_write(REG_AUDIO_CLOCK_MODE, AUDIO_SYSCLK_128FS); + + /* reset advanced infoframes (ISRC1/ISRC2/ACP) */ + tda1997x_hdmi_info_reset(RESET_AI, 0); + //tda1997x_hdmi_info_reset(0x00, 0); + /* reset infoframe */ + tda1997x_hdmi_info_reset(RESET_IF, 0); + //tda1997x_hdmi_info_reset(0x00, 0); + /* reset audio infoframes */ + tda1997x_hdmi_info_reset(RESET_AUDIO, 0); + //tda1997x_hdmi_info_reset(0x00, 0); + /* reset gamut */ + tda1997x_hdmi_info_reset(RESET_GAMUT, 0); + //tda1997x_hdmi_info_reset(0x00, 0); + + /* get initial HDMI status */ + tda1997x->hdmi_status = io_read(REG_HDMI_FLAGS); + + /* get DEEP color mode */ + reg = io_read(REG_DEEP_COLOR_MODE); + + /* register interrupt handler */ + if (client->irq) { + ret = devm_request_irq(&client->dev, client->irq, tda1997x_isr, 0, + "tda1997x_irq", tda1997x); + if (ret < 0) { + pr_err("%s:interrupt gpio%d registration failed, error=%d \n", + __func__, client->irq, ret); + } + pr_debug("registered irq%d\n", client->irq); + } + + return ret; +} + +static const struct i2c_device_id tda1997x_id[] = { + {"tda1997x", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tda1997x_id); + +//static const struct of_device_id tda1997x_dt_ids[] = { +// { .compatible = "nxp,tda1997x", }, +// { /* sentinel */ } +//}; +//MODULE_DEVICE_TABLE(of, tda1997x_dt_ids); + +static struct i2c_driver tda1997x_i2c_driver = { + .driver = { + .name = "tda1997x", + .owner = THIS_MODULE, + //.of_match_table = tda1997x_dt_ids, + }, + .probe = tda1997x_probe, + .remove = tda1997x_remove, + .id_table = tda1997x_id, +}; + +/** + * tda1997x init function. + * Called on insmod. + * + * @return Error code indicating success or failure. + */ +static __init int tda1997x_init(void) +{ + u8 err = 0; + + pr_debug("%s\n", __func__); + + /* Tells the i2c driver what functions to call for this driver. */ + err = i2c_add_driver(&tda1997x_i2c_driver); + if (err != 0) + pr_err("%s:driver registration failed, error=%d \n", + __func__, err); + + return err; +} + +/** + * tda1997x cleanup function. + * Called on rmmod. + * + * @return Error code indicating success or failure. + */ +static void __exit tda1997x_clean(void) +{ + dev_dbg(&tda1997x_data.client->dev, "%s\n", __func__); + i2c_del_driver(&tda1997x_i2c_driver); +} + +module_init(tda1997x_init); +module_exit(tda1997x_clean); + +MODULE_DESCRIPTION("Core driver for TDA1997X HDMI Receiver"); +MODULE_AUTHOR("Tim Harvey "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tda1997x-core.h b/include/linux/mfd/tda1997x-core.h new file mode 100644 index 00000000000000..2de1833278e347 --- /dev/null +++ b/include/linux/mfd/tda1997x-core.h @@ -0,0 +1,225 @@ +/* + * tda1997x.h - header for TDA1997X HDMI receiver device + * + * Copyright (C) 2013 Gateworks Corporation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __TDA1997X_H_ +#define __TDA1997X_H_ + +#include + +/* + * Video Port Configuration: + * + * 9 byte registers describe the video port output bit mapping (which bits from + * the video data stream output on which pins). Each register controls a + * 'pin group' of 4 bits (nibble) of the internal 36bit video data bus + * with the following bit descriptions: + * + * 7 - vp_out - video port output enable: 1-active + * 6 - vp_hiz - video port output level when not used: 1-HI-Z, 0-Low + * 5 - unused + * 4 - vp_swap - Swap bit allocation: 1-swap + * 3:0 - vp_sel - Select nibble to be routed to this pin-group + * 00: D[03:00] + * 01: D[07:04] + * 02: D[03:08] + * 03: D[07:12] + * 04: D[11:16] + * 05: D[15:20] + * 06: D[19:24] + * 07: D[23:28] + * 08: D[27:32] + * 0a: D[31:35] + * + * The pingroups differ per chip and are as follows: + * + * Byte Register TDA19971(24bit) TDA19972(36bit) + * 1 VP35_32_CTRL VP[24:20] Group8 VP[35:32] Group8 + * 2 VP31_28_CTRL VP[19:16] Group7 VP[31:28] Group7 + * 3 VP27_24_CTRL N/A VP[27:24] Group6 + * 4 VP23_20_CTRL VP[15:12] Group5 VP[23:20] Group5 + * 5 VP19_16_CTRL VP[11:08] Group4 VP[19:16] Group4 + * 6 VP15_12_CTRL N/A VP[15:12] Group3 + * 7 VP11_08_CTRL VP[07:04] Group2 VP[11:08] Group2 + * 8 VP07_04_CTRL VP[03:00] Group1 VP[07:04] Group1 + * 9 VP03_00_CTRL N/A VP[03:00] Group0 + * + * Note that the internal 36bit video bus is aligned to the top of the bus, + * for example 8bit CCIR656 will be output on D[24:16] internally. + * + * If you have a TDA19971 (24bit output) pins VP[0:8] connected to + * an Soc with an 8bit video port (ie for 8bit CCIR656 data) you would + * assign the 9 registers as follows to map D[24:16] to VP[0:8]. + * + * video_out_port = { + * 0x00, // VP35_32_CTRL + * 0x00, // VP31_28_CTRL + * 0x00, // VP27_24_CTRL + * 0x82, // VP23_18_CTRL: vp_out=1 vp_hiz=0 vp_swap=0 vp_sel=2 (Group2) + * 0x81, // VP19_16_CTRL: vp_out=1 vp_hiz=0 vp_swap=0 vp_sel=1 (Group8) + * 0x00, // VP15_12_CTRL + * 0x00, // VP11_08_CTRL + * 0x00, // VP07_04_CTRL + * 0x00, // VP03_00_CTRL + * }; + */ + +/* Video output clock modes */ +typedef enum { + CLOCK_SINGLE_EDGE, + CLOCK_DUAL_EDGE, + CLOCK_SINGLE_EDGE_TOGGLED, + CLOCK_DUAL_EDGE_TOGGLED, +} tda1997x_videoclkmode_t; + +/* Video output data formats */ +typedef enum { + VIDEOFMT_444 = 0x00, /* RGB444/YUV444 */ + VIDEOFMT_422_SMP = 0x01, /* YUV422 semi-planar */ + VIDEOFMT_422_CCIR = 0x02 /* YUV422 CCIR656 */ +} tda1997x_videofmt_t; + +/* HS/HREF signal source */ +typedef enum +{ + SYNCOUTPUT_HSYNC_VHREF = 0x00, /* HS from VHREF - do not use (not programmed) */ + SYNCOUTPUT_HREF_VHREF = 0x01, /* HREF from VHREF */ + SYNCOUTPUT_HREF_HDMI = 0x02, /* HREF from HDMI */ +} tda1997x_sync_output_hs_t; + +/* VS/VREF signal source */ +typedef enum +{ + SYNCOUTPUT_VSYNC_VHREF = 0x00, /* VS from VHREF - do not use (not programmed) */ + SYNCOUTPUT_VREF_VHREF = 0x01, /* VREF from VHREF */ + SYNCOUTPUT_VREF_HDMI = 0x02, /* VREF from HDMI */ +} tda1997x_sync_output_vs_t; + +/* DE/FREF signal source */ +typedef enum +{ + SYNCOUTPUT_DE_VHREF = 0x00, /* DE from VHREF (HREF and not VREF) */ + SYNCOUTPUT_FREF_VHREF = 0x01, /* FREF from VHREF */ + SYNCOUTPUT_FREF_HDMI = 0x02, /* FREF from HDMI */ +} tda1997x_sync_output_de_t; + +/* video details */ +typedef struct { + /* video input data (input to the HDMI receiver) */ + int width; + int height; + int fps; + bool interlaced; + bool signal; + + /* video output data (output from the HDMI receiver) */ + tda1997x_videofmt_t sensor_vidfmt; + tda1997x_videoclkmode_t sensor_clkmode; +} tda1997x_vidout_fmt_t; + +/** Obtain current video format details from core */ +int tda1997x_get_vidout_fmt(tda1997x_vidout_fmt_t *); + +/* + * Audio samples can be output in either S/PDIF or I2S bus formats. + * In I2S mode, the TDF1997X is the master with 16bit or 32bit per word. + * In either modes, up to 8 audio channels can be controlled using the audio + * port pins AP0 to AP3 and A_WS. The audio port mapping depends on the + * channel allocation, layout, and audio input type. + * + * The following table shows the audio port pin usage for the various modes + * possible (pins missing should be unconnected) + * + * | SPDIF | SPDIF | I2S | I2S | HBR demux + * | Layout0 | Layout1 | Layout0 | Layout1 | SPDIF | I2S + * ------+---------+---------+---------+---------+------------+------------ + * A_WS | WS | WS | WS | WS | WS | WS + * AP3 | | SPDIF3 | | SD3 | SPDIF[x+3] | SD[x+3] + * AP2 | | SPDIF2 | | SD2 | SPDIF[x+2] | SD[x+2] + * AP1 | | SPDIF1 | | SD1 | SPDIF[x+1] | SD[x+1] + * AP0 | | SPDIF0 | | SD0 | SPDIF[x] | SD[x] + * A_CLK | (32*Fs) | (32*Fs) |(32*Fs) | (32*Fs) | (32*FsACR) | (32*FsACR) + * | (64*Fs) | (64*Fs) |(64*Fs) | (64*Fs) | (64*FsACR) | (64*FsACR) + * + * Freq(Sysclk) = 2*freq(Aclk) + */ +typedef enum { + AUDIO_LAYOUT_FORCED_0 = 0x00, /* Layout dictated by packet header? */ + AUDIO_LAYOUT_FORCED_1 = 0x01, /* layout1? */ + AUDIO_LAYOUT_FORCED = 0x02, /* layout0? */ +} tda1997x_audiolayout_t; + +/* Audio output data formats */ +typedef enum { + AUDIO_FMT_I2S16, /* I2S 16 bit */ + AUDIO_FMT_I2S32, /* I2S 32 bit */ + AUDIO_FMT_SPDIF, /* SPDIF */ + AUDIO_FMT_OBA, /* One Bit Audio */ + AUDIO_FMT_I2S16_HBR_STRAIGHT, /* HBR straight in I2S 16bit mode */ + AUDIO_FMT_I2S16_HBR_DEMUX, /* HBR demux in I2S 16bit mode */ + AUDIO_FMT_I2S32_HBR_DEMUX, /* HBR demux in I2S 32bit mode */ + AUDIO_FMT_SPDIF_HBR_DEMUX, /* HBR demux in SPDIF mode */ + AUDIO_FMT_DST, /* Direct Stream Transfer */ +} tda1997x_audiofmt_t; + +/* Audio output clock frequencies */ +typedef enum +{ + AUDIO_SYSCLK_128FS = 0x03, + AUDIO_SYSCLK_256FS = 0x04, + AUDIO_SYSCLK_512FS = 0x05, +} tda1997x_audiosysclk_t; + +/* Audio output info */ +typedef struct { + int samplerate; + int channels; + int samplesize; +} tda1997x_audout_fmt_t; + +/** Obtain current audio format details from core */ +int tda1997x_get_audout_fmt(tda1997x_audout_fmt_t *); + +/* possible states of the state machine */ +typedef enum +{ + STATE_NOT_INITIALIZED, /* Driver is not initialized */ + STATE_INITIALIZED, /* Driver is initialized */ + STATE_UNLOCKED, /* Driver is not locked on input signal */ + STATE_LOCKED, /* Driver is locked on input signal */ + STATE_CONFIGURED /* Driver is configured */ +} tda1997x_state_t; + +extern tda1997x_state_t tda1997x_get_state(void); + +/* HDMI Inputs: + * TDA19971: HDMI-A (single input) + * TDA19972: HDMI-A|B (dual input) + */ +typedef enum +{ + INPUT_HDMI_A, + INPUT_HDMI_B, /* TDA19972 only */ + INPUT_AUTO_DIGITAL, /* TDA19972 only */ +} tda1997x_input_t; + +extern int tda1997x_select_input(tda1997x_input_t); + +#endif /* End of __TDA1997X_H */ From a1c0b0840a355a6232194d92a3845d000e22cbea Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 3 Sep 2013 16:34:28 -0700 Subject: [PATCH 0771/1983] imx: add imx tda1997x i2s DAI support Signed-off-by: Tim Harvey --- sound/soc/fsl/Kconfig | 13 +++ sound/soc/fsl/Makefile | 2 + sound/soc/fsl/imx-tda1997x.c | 195 +++++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 sound/soc/fsl/imx-tda1997x.c diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index bb229dadf47f64..d9d523a96e4c96 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -344,6 +344,19 @@ config SND_SOC_IMX_SI476X Say Y if you want to add support for Soc audio for the AMFM Tuner chip SI476x module. +config SND_SOC_IMX_TDA1997X + tristate "SoC Audio support for i.MX boards with tda1997x" + depends on OF && I2C + select SND_SOC_TDA1997X + select SND_SOC_IMX_PCM_DMA + select SND_SOC_IMX_AUDMUX + select SND_SOC_FSL_SSI + select SND_SOC_FSL_UTILS + help + SoC Audio support for i.MX boards with TDA1997x + Say Y if you want to add support for SoC audio on an i.MX board with + a tda1997x codec. + endif # SND_IMX_SOC endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 65f8e6f723a1dc..7f3041fb4c117d 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -55,6 +55,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-cs42888-objs := imx-cs42888.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o +snd-soc-imx-tda1997x-objs := imx-tda1997x.o snd-soc-imx-wm8731-objs := imx-wm8731.o snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-spdif-objs := imx-spdif.o @@ -69,6 +70,7 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_CS42888) += snd-soc-imx-cs42888.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o +obj-$(CONFIG_SND_SOC_IMX_TDA1997X) += snd-soc-imx-tda1997x.o obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o obj-$(CONFIG_SND_SOC_IMX_WM8731) += snd-soc-imx-wm8731.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o diff --git a/sound/soc/fsl/imx-tda1997x.c b/sound/soc/fsl/imx-tda1997x.c new file mode 100644 index 00000000000000..caba9001526010 --- /dev/null +++ b/sound/soc/fsl/imx-tda1997x.c @@ -0,0 +1,195 @@ +/* + * SoC audio for i.MX boards with tda1997x HDMI receiver + * based off sound/soc/fsl/imx-sgtl5000.c + * + * Copyright 2013 Tim Harvey, Gateworks Corporation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-audmux.h" + +#define DAI_NAME_SIZE 32 + +struct imx_tda1997x_data { + struct snd_soc_dai_link dai; + struct snd_soc_card card; + char codec_dai_name[DAI_NAME_SIZE]; + char platform_name[DAI_NAME_SIZE]; +}; + +static int imx_tda1997x_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static int imx_tda1997x_audmux_config(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int int_port, ext_port; + int ret; + + ret = of_property_read_u32(np, "mux-int-port", &int_port); + if (ret) { + dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); + return ret; + } + ret = of_property_read_u32(np, "mux-ext-port", &ext_port); + if (ret) { + dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); + return ret; + } + + /* + * The port numbering in the hardware manual starts at 1, while + * the audmux API expects it starts at 0. + */ + int_port--; + ext_port--; + ret = imx_audmux_v2_configure_port(int_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + (1<<30) | /* use RXFS from port instead of TXFS */ + (1<<25), /* use RXC from port instead of TXC */ + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); + if (ret) { + dev_err(&pdev->dev, "audmux internal port setup failed\n"); + return ret; + } + ret = imx_audmux_v2_configure_port(ext_port, + IMX_AUDMUX_V2_PTCR_SYN, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); + if (ret) { + dev_err(&pdev->dev, "audmux external port setup failed\n"); + return ret; + } + + return 0; +} + +static int imx_tda1997x_probe(struct platform_device *pdev) +{ + struct device_node *cpu_np, *codec_np; + struct platform_device *cpu_pdev; + struct imx_tda1997x_data *data; + int ret; + + cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0); + codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!cpu_np || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + if (strstr(cpu_np->name, "ssi")) { + ret = imx_tda1997x_audmux_config(pdev); + if (ret) + goto fail; + } + + cpu_pdev = of_find_device_by_node(cpu_np); + if (!cpu_pdev) { + dev_err(&pdev->dev, "failed to find SSI platform device\n"); + ret = -EINVAL; + goto fail; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + data->dai.name = "i.MX HDMI Audio Rx"; + data->dai.stream_name = "i.MX HDMI Audio Rx"; + data->dai.codec_dai_name = "tda1997x"; + data->dai.codec_of_node = codec_np; + data->dai.cpu_of_node = cpu_np; + data->dai.platform_of_node = cpu_np; + data->dai.init = &imx_tda1997x_dai_init; + data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + data->card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto fail; + data->card.num_links = 1; + data->card.owner = THIS_MODULE; + data->card.dai_link = &data->dai; + + /* register card with ASoC core */ + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto fail; + } + + platform_set_drvdata(pdev, data); +fail: + if (cpu_np) + of_node_put(cpu_np); + if (codec_np) + of_node_put(codec_np); + + return ret; +} + +static int imx_tda1997x_remove(struct platform_device *pdev) +{ + struct imx_tda1997x_data *data = platform_get_drvdata(pdev); + + snd_soc_unregister_card(&data->card); + + return 0; +} + +static const struct of_device_id imx_tda1997x_dt_ids[] = { + { .compatible = "fsl,imx-audio-tda1997x", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_tda1997x_dt_ids); + +static struct platform_driver imx_tda1997x_audio_driver = { + .driver = { + .name = "imx-tda1997x-audio", + .owner = THIS_MODULE, + .of_match_table = imx_tda1997x_dt_ids, + }, + .probe = imx_tda1997x_probe, + .remove = imx_tda1997x_remove, +}; + +module_platform_driver(imx_tda1997x_audio_driver); + +MODULE_AUTHOR("Tim Harvey "); +MODULE_DESCRIPTION("Freescale i.MX TDA1997X RX ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-tda1997x-audio"); From e9cba78905e59b4d3fe258671080892e92cbab14 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 6 Feb 2014 21:16:45 -0800 Subject: [PATCH 0772/1983] mxc_capture: add tda1997x HDMI receiver video support Signed-off-by: Tim Harvey --- drivers/media/platform/mxc/capture/Kconfig | 6 + drivers/media/platform/mxc/capture/Makefile | 3 + .../platform/mxc/capture/mxc_v4l2_capture.c | 12 +- drivers/media/platform/mxc/capture/tda1997x.c | 451 ++++++++++++++++++ include/media/v4l2-chip-ident.h | 4 + 5 files changed, 472 insertions(+), 4 deletions(-) create mode 100644 drivers/media/platform/mxc/capture/tda1997x.c diff --git a/drivers/media/platform/mxc/capture/Kconfig b/drivers/media/platform/mxc/capture/Kconfig index a3e47d16b47222..8d3b1f10156a2a 100644 --- a/drivers/media/platform/mxc/capture/Kconfig +++ b/drivers/media/platform/mxc/capture/Kconfig @@ -11,6 +11,12 @@ config VIDEO_MXC_IPU_CAMERA depends on VIDEO_MXC_CAPTURE && MXC_IPU default y +config MXC_TVIN_TDA1997X + tristate "NXP TDA1997x HDMI Receiver Input support" + depends on I2C + ---help--- + If you plan to use the NXP tda1997x/tdf1997x HDMI reciever with your i + config MXC_CAMERA_OV5640 tristate "OmniVision ov5640 camera support" depends on !VIDEO_MXC_EMMA_CAMERA && I2C diff --git a/drivers/media/platform/mxc/capture/Makefile b/drivers/media/platform/mxc/capture/Makefile index 48fef0fc5bd603..165f6aab20a8b5 100644 --- a/drivers/media/platform/mxc/capture/Makefile +++ b/drivers/media/platform/mxc/capture/Makefile @@ -22,3 +22,6 @@ adv7180_tvin-objs := adv7180.o obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o obj-$(CONFIG_VIDEO_V4L2_MXC_INT_DEVICE) += v4l2-int-device.o + +tda1997x-video-objs := tda1997x.o +obj-$(CONFIG_MXC_TVIN_TDA1997X) += tda1997x-video.o diff --git a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c index efe039c4d05407..1cf7ea9dcdd551 100644 --- a/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c +++ b/drivers/media/platform/mxc/capture/mxc_v4l2_capture.c @@ -1385,9 +1385,14 @@ void setup_ifparm(cam_data *cam, int init_defrect) // csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE; // break; default: - csi_param.clk_mode = (ifparm.u.bt656.clock_curr == 0) ? - IPU_CSI_CLK_MODE_CCIR656_INTERLACED : - IPU_CSI_CLK_MODE_GATED_CLK; + if (ifparm.u.bt656.clock_curr == 0) { + csi_param.clk_mode = IPU_CSI_CLK_MODE_CCIR656_INTERLACED; + /*protocol bt656 use 27Mhz pixel clock */ + csi_param.mclk = 27000000; + } else if (ifparm.u.bt656.clock_curr == 1) { + csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; + } else + csi_param.clk_mode = IPU_CSI_CLK_MODE_GATED_CLK; } csi_param.pixclk_pol = ifparm.u.bt656.latch_clk_inv; @@ -1571,7 +1576,6 @@ static int mxc_v4l2_s_param(cam_data *cam, struct v4l2_streamparm *parm) /* Get new values. */ setup_ifparm(cam, 0); - exit: if (cam->overlay_on == true) start_preview(cam); diff --git a/drivers/media/platform/mxc/capture/tda1997x.c b/drivers/media/platform/mxc/capture/tda1997x.c new file mode 100644 index 00000000000000..f61c1e4a558faf --- /dev/null +++ b/drivers/media/platform/mxc/capture/tda1997x.c @@ -0,0 +1,451 @@ +/* + * tda1997x.c -- MXC video capture driver for i.MX boards with + * tda1997x HDMI receiver + * + * Copyright (C) 2013 Gateworks Corporation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* The tda1997x-core driver manages the i2c interface with the device. + * This driver merely calls into that driver for current video parameters + * when necessary. The platform data for the core driver contains the + * video output bus configuration that configures the video port + * mapping between the tda1997x and a i.MX6 CSI parallel bus for 8bit + * bt656 with embedded syncs as this is the only video format compatible + * between the tda1997x and the i.MX6 CSI. + * + * see for details + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "v4l2-int-device.h" +#include "mxc_v4l2_capture.h" + +/*! + * Maintains the information on the current state of the sensor. + */ +struct sensor { + struct sensor_data sen; + tda1997x_videofmt_t vidfmt; +} tda1997x_data; + +/*********************************************************************** + * mxc_v4l2_capture interface. + ***********************************************************************/ + +/*********************************************************************** + * IOCTL Functions from v4l2_int_ioctl_desc. + ***********************************************************************/ + +/*! + * ioctl_g_ifparm - V4L2 sensor interface handler for vidioc_int_g_ifparm_num + * s: pointer to standard V4L2 device structure + * p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure + * + * Gets slave interface parameters. + * Calculates the required xclk value to support the requested + * clock parameters in p. This value is returned in the p + * parameter. + * + * vidioc_int_g_ifparm returns platform-specific information about the + * interface settings used by the sensor. + * + * Called on open. + */ +static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) +{ + struct sensor *sensor = s->priv; + tda1997x_vidout_fmt_t fmt; + + if (s == NULL) { + pr_err(" ERROR!! no slave device set!\n"); + return -ENODEV; + } + + if (tda1997x_get_vidout_fmt(&fmt)) + return -ENODEV; + pr_debug("%s: %dx%d%c@%dfps\n", __func__, fmt.width, fmt.height, + fmt.interlaced?'i':'p', fmt.fps); + + /* Initialize structure to 0s then set any non-0 values. */ + memset(p, 0, sizeof(*p)); + p->if_type = V4L2_IF_TYPE_BT656; /* This is the only possibility. */ + + if (sensor->vidfmt == VIDEOFMT_422_SMP) { /* YCbCr 4:2:2 semi-planar */ + p->u.bt656.nobt_vs_inv = 0; + p->u.bt656.nobt_hs_inv = 1; + p->u.bt656.bt_sync_correct = 1; + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_16BIT; + p->u.bt656.clock_curr = -1; /* gated clock mode */ + } else if (sensor->vidfmt == VIDEOFMT_422_CCIR) { /* YCbCr 4:2:2 CCIR656 */ + p->u.bt656.mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT; + p->u.bt656.clock_curr = (fmt.interlaced)?0:1; + } + + return 0; +} + +/*! + * Sets the device power. + * + * s pointer to the device device + * on if 1, power is to be turned on. 0 means power is to be turned off + * + * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num + * @s: pointer to standard V4L2 device structure + * @on: power state to which device is to be set + * + * Sets devices power state to requrested state, if possible. + * This is called on open, close, suspend and resume. +static int ioctl_s_power(struct v4l2_int_device *s, int on) +{ + struct sensor *sensor = s->priv; + + if (on && !sensor->sen.on) { + } else if (!on && sensor->sen.on) { + } + + sensor->sen.on = on; + + return 0; +} + */ + +/*! + * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure + * + * Returns the sensor's video CAPTURE parameters. + */ +static int ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + struct sensor *sensor = s->priv; + struct v4l2_captureparm *cparm = &a->parm.capture; + + switch (a->type) { + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" type is V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + memset(a, 0, sizeof(*a)); + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cparm->capability = sensor->sen.streamcap.capability; + cparm->timeperframe = sensor->sen.streamcap.timeperframe; + cparm->capturemode = sensor->sen.streamcap.capturemode; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + break; + + default: + pr_debug("ioctl_g_parm:type is unknown %d\n", a->type); + break; + } + + return 0; +} + +/*! + * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl + * @s: pointer to standard V4L2 device structure + * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure + * + * Configures the sensor to use the input parameters, if possible. If + * not possible, reverts to the old parameters and returns the + * appropriate error code. + * + * This driver cannot change these settings. + */ +static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +{ + switch (a->type) { + /* These are all the possible cases. */ + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VBI_CAPTURE: + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + break; + + default: + pr_debug(" type is unknown - %d\n", a->type); + break; + } + + return 0; +} + +/*! + * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap + * @s: pointer to standard V4L2 device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the sensor's current pixel format in the v4l2_format + * parameter. + */ +static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +{ + struct sensor *sensor = s->priv; + tda1997x_vidout_fmt_t fmt; + + if (tda1997x_get_vidout_fmt(&fmt)) + return -ENODEV; + + pr_debug("%s: %dx%d%c@%dfps\n", __func__, fmt.width, fmt.height, + fmt.interlaced?'i':'p', fmt.fps); + sensor->sen.pix.height = fmt.height; + sensor->sen.pix.width = fmt.width; + sensor->sen.streamcap.timeperframe.denominator = fmt.fps; + sensor->sen.streamcap.timeperframe.numerator = 1; + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pr_debug(" Returning size of %dx%d\n", + sensor->sen.pix.width, sensor->sen.pix.height); + f->fmt.pix = sensor->sen.pix; + pr_debug(" Returning format of %s\n", (char*)&f->fmt.pix.pixelformat); + break; + + case V4L2_BUF_TYPE_PRIVATE: { + } + break; + + default: + f->fmt.pix = sensor->sen.pix; + break; + } + + return 0; +} + +/*! + * ioctl_enum_framesizes - V4L2 sensor interface handler for + * VIDIOC_ENUM_FRAMESIZES ioctl + * @s: pointer to standard V4L2 device structure + * @fsize: standard V4L2 VIDIOC_ENUM_FRAMESIZES ioctl structure + * + * Return 0 if successful, otherwise -EINVAL. + */ +static int ioctl_enum_framesizes(struct v4l2_int_device *s, + struct v4l2_frmsizeenum *fsize) +{ + struct sensor *sensor = s->priv; + tda1997x_vidout_fmt_t fmt; + + if (fsize->index >= 1) + return -EINVAL; + + if (tda1997x_get_vidout_fmt(&fmt)) + return -ENODEV; + + pr_debug("%s: %dx%d%c@%dfps\n", __func__, fmt.height, fmt.width, + fmt.interlaced?'i':'p', fmt.fps); + sensor->sen.pix.height = fmt.height; + sensor->sen.pix.width = fmt.width; + fsize->discrete.width = sensor->sen.pix.width; + fsize->discrete.height = sensor->sen.pix.height; + + return 0; +} + +/*! + * ioctl_g_chip_ident - V4L2 sensor interface handler for + * VIDIOC_DBG_G_CHIP_IDENT ioctl + * @s: pointer to standard V4L2 device structure + * @id: pointer to int + * + * Return 0. + */ +static int ioctl_g_chip_ident(struct v4l2_int_device *s, int *id) +{ + struct sensor *sensor = s->priv; + ((struct v4l2_dbg_chip_ident *)id)->match.type = + V4L2_CHIP_MATCH_I2C_DRIVER; + if (sensor->vidfmt == VIDEOFMT_422_SMP) { /* YCbCr 4:2:2 semi-planar */ + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, + "tda1997x_decoder_yuv422"); + } else + strcpy(((struct v4l2_dbg_chip_ident *)id)->match.name, + "tda1997x_decoder_bt656"); + ((struct v4l2_dbg_chip_ident *)id)->ident = V4L2_IDENT_TDA19971; + + return 0; +} + +/*! + * This structure defines all the ioctls for this module. + */ +static struct v4l2_int_ioctl_desc tda1997x_ioctl_desc[] = { + +/* {vidioc_int_s_power_num, (v4l2_int_ioctl_func*)ioctl_s_power}, */ + {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*)ioctl_g_ifparm}, + {vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func*)ioctl_g_fmt_cap}, + /*! + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ + {vidioc_int_g_parm_num, (v4l2_int_ioctl_func*)ioctl_g_parm}, + {vidioc_int_s_parm_num, (v4l2_int_ioctl_func*)ioctl_s_parm}, + {vidioc_int_enum_framesizes_num, + (v4l2_int_ioctl_func *) ioctl_enum_framesizes}, + {vidioc_int_g_chip_ident_num, + (v4l2_int_ioctl_func *)ioctl_g_chip_ident}, +}; + +static struct v4l2_int_slave tda1997x_slave = { + .ioctls = tda1997x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tda1997x_ioctl_desc), +}; + +static struct v4l2_int_device tda1997x_int_device = { + .module = THIS_MODULE, + .name = "tda1997x-video", + .type = v4l2_int_type_slave, + .u = { + .slave = &tda1997x_slave, + }, +}; + +static int tda1997x_video_probe(struct platform_device *pdev) +{ + struct sensor *sens = &tda1997x_data; + struct pinctrl *pinctrl; + struct device *dev = &pdev->dev; + struct regmap *gpr; + tda1997x_vidout_fmt_t fmt; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + /* pinctrl */ + pinctrl = devm_pinctrl_get_select_default(dev); + if (IS_ERR(pinctrl)) { + dev_err(dev, "setup pinctrl failed\n"); + return PTR_ERR(pinctrl); + } + + /* Set initial values for the sensor struct. */ + memset(sens, 0, sizeof(tda1997x_data)); + if (tda1997x_get_vidout_fmt(&fmt)) + return -ENODEV; + sens->vidfmt = fmt.sensor_vidfmt; + sens->sen.streamcap.timeperframe.denominator = 0; + sens->sen.streamcap.timeperframe.numerator = 0; + sens->sen.pix.width = 0; + sens->sen.pix.height = 0; + if (sens->vidfmt == VIDEOFMT_422_SMP) + sens->sen.pix.pixelformat = IPU_PIX_FMT_GENERIC_16; + else + sens->sen.pix.pixelformat = V4L2_PIX_FMT_UYVY; + sens->sen.on = true; + + ret = of_property_read_u32(dev->of_node, "csi_id", + &(sens->sen.csi)); + if (ret) { + dev_err(dev, "csi_id invalid\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "ipu_id", + &(sens->sen.ipu_id)); + if (ret) { + dev_err(dev, "ipu_id invalid\n"); + return ret; + } + + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (!IS_ERR(gpr)) { + if (of_machine_is_compatible("fsl,imx6q")) { + int mask = sens->sen.csi ? (1 << 20) : (1 << 19); + + regmap_update_bits(gpr, IOMUXC_GPR1, mask, mask); + } else if (of_machine_is_compatible("fsl,imx6dl")) { + int mask = sens->sen.csi ? (7 << 3) : (7 << 0); + int val = sens->sen.csi ? (4 << 3) : (4 << 0); + + regmap_update_bits(gpr, IOMUXC_GPR13, mask, val); + } + } else { + pr_err("%s: failed to find fsl,imx6q-iomux-gpr regmap\n", + __func__); + } + + dev_dbg(dev, "IPU%d_CSI%d\n", sens->sen.ipu_id + 1, sens->sen.csi); + dev_dbg(dev, "type is %d (expect %d)\n", + tda1997x_int_device.type, v4l2_int_type_slave); + dev_dbg(dev, "num ioctls is %d\n", + tda1997x_int_device.u.slave->num_ioctls); + + /* This function attaches this structure to the /dev/video device */ + tda1997x_int_device.priv = sens; + ret = v4l2_int_device_register(&tda1997x_int_device); + + return ret; +} + +static int tda1997x_video_remove(struct platform_device *pdev) +{ + v4l2_int_device_unregister(&tda1997x_int_device); + + return 0; +} + +static const struct of_device_id tda1997x_video_driver_ids[] = { + { .compatible = "fsl,imx-tda1997x-video", }, + { /* sentinel */ } +}; + +static struct platform_driver tda1997x_video_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "imx-tda1997x-video", + .of_match_table = tda1997x_video_driver_ids, + }, + .probe = tda1997x_video_probe, + .remove = tda1997x_video_remove, +}; +module_platform_driver(tda1997x_video_driver); + +MODULE_AUTHOR("Tim Harvey "); +MODULE_DESCRIPTION("TDA1997X hdmi receiver MXC video capture driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx-tda1997x-video"); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index b4c8416c3d1ff9..326af8f105e922 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -232,6 +232,10 @@ enum { /* module ad9389b: just ident 9389 */ V4L2_IDENT_AD9389B = 9389, + /* module tda1997x */ + V4L2_IDENT_TDA19971 = 19971, + V4L2_IDENT_TDA19973 = 19973, + /* module tda9840: just ident 9840 */ V4L2_IDENT_TDA9840 = 9840, From c91a4fc9df7e0f1c2f68666c7cf6599a010ab486 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Mon, 3 Feb 2014 17:13:58 -0800 Subject: [PATCH 0773/1983] video: mxc: add adv739x TV encoder Features supported: - Support on the fly switch between PAL and NTSC mode. - Support CVBS output based on adv7391 TV encoder. Hardware link between iMX6 and adv7391 TV encoder chip: - IPU1_DI0_DISP_CLK connected to adv7391 CLKIN pin. - IPU1_DISP0_DAT_23~DISP0_DAT_16 connected to adv7391 P7~P0 pins. - IPU1_DI0_PIN2 connected to adv7391 HSYNC pin. (option) - IPU1_DI0_PIN4 connected to adv7391 VSYNC pin. (option) Example kernel cmdline params: - Output BT656 NTSC data to display port with UVYV frame buffer mode: "video=mxcfb0:dev=bt656,BT656-NTSC,if=BT656,fbpix=UYVY16" - Output BT656 NTSC data to display port with RGB565 frame buffer mode: "video=mxcfb0:dev=bt656,BT656-NTSC,if=BT656,fbpix=RGB565" - Output BT656 PAL data to display port with RGB24 frame buffer mode: "video=mxcfb0:dev=bt656,BT656-PAL,if=BT656,fbpix=RGB24" - Output CVBS NTSC signal on adv7391 with UYVY frame buffer mode: "video=mxcfb0:dev=adv739x,BT656-NTSC,if=BT656,fbpix=UYVY16" - Output CVBS PAL signal on adv7391 with RGB565 frame buffer mode: "video=mxcfb0:dev=adv739x,BT656-PAL,if=BT656,fbpix=RGB565" Switch between PAL and NTSC: $ echo D:720x480i-60 > /sys/class/graphics/fb0/mode $ echo D:720x576i-50 > /sys/class/graphics/fb0/mode see: https://community.freescale.com/docs/DOC-94019 Written-by: Qiang Li Device-tree support Written-by: Tim Harvey Signed-off-by: Tim Harvey --- .../devicetree/bindings/fb/fsl_ipuv3_fb.txt | 19 +- drivers/video/mxc/Kconfig | 4 + drivers/video/mxc/Makefile | 1 + drivers/video/mxc/mxcfb_adv739x.c | 397 ++++++++++++++++++ 4 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 drivers/video/mxc/mxcfb_adv739x.c diff --git a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt index b8d27ef108fe38..e7b16ead0338d8 100644 --- a/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt +++ b/Documentation/devicetree/bindings/fb/fsl_ipuv3_fb.txt @@ -21,9 +21,10 @@ Required properties for IPU: Required properties for fb: - compatible : should be "fsl,mxc_sdc_fb". -- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi". -- mode_str : "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel, - "1920x1080M@60" for hdmi. +- disp_dev : display device: "ldb", "lcd", "hdmi", "mipi_dsi", "adv739x". +- mode_str : video mode string: "LDB-XGA" or "LDB-1080P60" for ldb, + "CLAA-WVGA" for lcd, "TRULY-WVGA" for TRULY mipi_dsi lcd panel, + "1920x1080M@60" for hdmi, "BT656-NTSC" or "BT656-PAL" for adv739x. - default_bpp : default bits per pixel: 8/16/24/32 - int_clk : use internal clock as pixel clock: 0 or 1 - late_init : to avoid display channel being re-initialized @@ -103,3 +104,15 @@ Example for mipi dsi display: resets = <&mipi_dsi_reset>; status = "okay"; }; + +Example for adv739x CVBS output: + fb0 { + compatible = "fsl,mxc_sdc_fb"; + disp_dev = "adv739x"; + interface_pix_fmt = "BT656"; + mode_str ="BT656-NTSC"; + default_bpp = <16>; + int_clk = <0>; + late_init = <0>; + status = "okay"; + }; diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig index f0315bb21959d4..a40eca0f2c002a 100644 --- a/drivers/video/mxc/Kconfig +++ b/drivers/video/mxc/Kconfig @@ -18,6 +18,10 @@ config FB_MXC_SYNC_PANEL depends on FB_MXC tristate "Synchronous Panel Framebuffer" +config FB_MXC_TVOUT_ADV739X + tristate "ADV7390/7391 TV Out Encoder" + depends on FB_MXC_SYNC_PANEL && I2C + config FB_MXC_LDB tristate "MXC LDB" depends on FB_MXC_SYNC_PANEL diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile index 3ce5dbbd7f159d..ecbaa4d803dd82 100644 --- a/drivers/video/mxc/Makefile +++ b/drivers/video/mxc/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_FB_MXC_LDB) += ldb.o obj-$(CONFIG_FB_MXC_MIPI_DSI) += mipi_dsi.o obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL) += mxcfb_hx8369_wvga.o +obj-$(CONFIG_FB_MXC_TVOUT_ADV739X) += mxcfb_adv739x.o obj-$(CONFIG_FB_MXC_HDMI) += mxc_hdmi.o obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o diff --git a/drivers/video/mxc/mxcfb_adv739x.c b/drivers/video/mxc/mxcfb_adv739x.c new file mode 100644 index 00000000000000..6a7d41f536d3ed --- /dev/null +++ b/drivers/video/mxc/mxcfb_adv739x.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mxc_dispdrv.h" +#include "crtc.h" + +#define DISPDRV_ADV739X "adv739x" + +#define ADV739X_MODE_NTSC 0 +#define ADV739X_MODE_PAL 1 + + +struct adv739x_data { + struct platform_device *pdev; + struct i2c_client *client; + struct mxc_dispdrv_handle *disp_adv739x; + struct fb_info *fbi; + + int ipu_id; + int disp_id; + int default_ifmt; + + int cur_mode; + int enabled; + struct notifier_block nb; +}; + +/* + * left_margin: used for field0 vStart width in lines + * + * right_margin: used for field0 vEnd width in lines + * + * up_margin: used for field1 vStart width in lines + * + * down_margin: used for field1 vEnd width in lines + * + * hsync_len: EAV Code + Blanking Video + SAV Code (in pixel clock count) + * For BT656 NTSC, it is 4 + 67*4 + 4 = 276. + * For BT1120 NTSC, it is 4 + 67*2 + 4 = 142. + * For BT656 PAL, it is 4 + 70*4 + 4 = 288. + * For BT1120 PAL, it is 4 + 70*2 + 4 = 148. + * + * vsync_len: not used, set to 1 + */ +static struct fb_videomode adv739x_modedb[] = { + { + /* NTSC Interlaced output */ + "BT656-NTSC", 60, 720, 480, 37037, + 19, 3, + 20, 3, + 276, 1, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, + FB_MODE_IS_DETAILED,}, + { + /* PAL Interlaced output */ + "BT656-PAL", 50, 720, 576, 37037, + 22, 2, + 23, 2, + 288, 1, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, + FB_MODE_IS_DETAILED,}, +}; +static int adv739x_modedb_sz = ARRAY_SIZE(adv739x_modedb); + +static int adv739x_write(struct i2c_client *client, u8 reg, u8 data) +{ + int ret = 0; + ret = i2c_smbus_write_byte_data(client, reg, data); + + return ret; +} + +/* +static int adv739x_read(struct i2c_client *client, u8 reg) +{ + int data = 0; + data = i2c_smbus_read_byte_data(client, reg); + + return data; +} +*/ + +static void adv739x_setmode(struct adv739x_data *adv739x, int mode) +{ + struct i2c_client *client = adv739x->client; + + if(adv739x->enabled == 0) + return; + + dev_dbg(&adv739x->client->dev, "adv739x_setmode: mode = %d.\n", mode); + switch (mode) { + case ADV739X_MODE_NTSC: + // Reg 0x17: reset + adv739x_write(client, 0x17, 0x02); + + mdelay(20); + + // Reg 0x00: DAC1~3 power on + adv739x_write(client, 0x00, 0x1C); + + // Reg 0x01: SD input + adv739x_write(client, 0x01, 0x00); + + //NTSC + // Reg 0x80: SD, NTSC + adv739x_write(client, 0x80, 0x10); + + // Reg 0x82: SD, CVBS + adv739x_write(client, 0x82, 0xCB); + break; + + case ADV739X_MODE_PAL: + // Reg 0x17: reset + adv739x_write(client, 0x17, 0x02); + + mdelay(20); + + // Reg 0x00: DAC1~3 power on + adv739x_write(client, 0x00, 0x1C); + + // Reg 0x01: SD input + adv739x_write(client, 0x01, 0x00); + + // Reg 0x80: SD, PAL + adv739x_write(client, 0x80, 0x11); + + // Reg 0x82: SD, CVBS + adv739x_write(client, 0x82, 0xC3); + adv739x_write(client, 0x8C, 0xCB); + adv739x_write(client, 0x8D, 0x8A); + adv739x_write(client, 0x8E, 0x09); + adv739x_write(client, 0x8F, 0x2A); + break; + + default: + dev_err(&adv739x->client->dev, "unsupported mode.\n"); + break; + } +} + +static void adv739x_poweroff(struct adv739x_data *adv739x) +{ + if (adv739x->enabled != 0) { + dev_dbg(&adv739x->client->dev, "adv739x_poweroff.\n"); + + /* power off the adv739x */ + adv739x_write(adv739x->client, 0x00, 0x1F); + + adv739x->enabled = 0; + } +} + +static void adv739x_poweron(struct adv739x_data *adv739x) +{ + if (adv739x->enabled == 0) { + dev_dbg(&adv739x->client->dev, "adv739x_poweron.\n"); + + adv739x->enabled = 1; + adv739x_setmode(adv739x, adv739x->cur_mode); + } +} + +int adv739x_fb_event(struct notifier_block *nb, unsigned long val, void *v) +{ + struct fb_event *event = v; + struct fb_info *fbi = event->info; + struct adv739x_data *adv739x = container_of(nb, struct adv739x_data, nb); + + if (strcmp(event->info->fix.id, adv739x->fbi->fix.id)) + return 0; + + dev_dbg(&adv739x->client->dev, "%s\n", __func__); + + fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var, + &fbi->modelist); + if (!fbi->mode) { + dev_warn(&adv739x->pdev->dev, + "adv739x: can not find mode for xres=%d, yres=%d\n", + fbi->var.xres, fbi->var.yres); + return 0; + } + + switch (val) { + case FB_EVENT_MODE_CHANGE: + if(strcmp(fbi->mode->name, "BT656-NTSC") == 0) + adv739x->cur_mode = ADV739X_MODE_NTSC; + else if(strcmp(fbi->mode->name, "BT656-PAL") == 0) + adv739x->cur_mode = ADV739X_MODE_PAL; + adv739x_setmode(adv739x, adv739x->cur_mode); + break; + case FB_EVENT_BLANK: + if (*((int *)event->data) == FB_BLANK_UNBLANK) + adv739x_poweron(adv739x); + else + adv739x_poweroff(adv739x); + break; + } + return 0; +} + +static int adv739x_disp_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) +{ + int ret = 0, i; + struct adv739x_data *adv739x = mxc_dispdrv_getdata(disp); + struct fb_videomode *modedb = adv739x_modedb; + int modedb_sz = adv739x_modedb_sz; + static bool inited = false; + + dev_dbg(&adv739x->client->dev, "%s\n", __func__); + if (inited) + return -EBUSY; + + inited = true; + + ret = ipu_di_to_crtc(&adv739x->pdev->dev, adv739x->ipu_id, + adv739x->disp_id, &setting->crtc); + if (ret < 0) + return ret; +/* + setting->dev_id = adv739x->ipu_id; + setting->disp_id = adv739x->disp_id; +*/ + + ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str, + modedb, modedb_sz, NULL, setting->default_bpp); + if (!ret) { + fb_videomode_to_var(&setting->fbi->var, &modedb[0]); + setting->if_fmt = adv739x->default_ifmt; + } + + INIT_LIST_HEAD(&setting->fbi->modelist); + for (i = 0; i < modedb_sz; i++) { + fb_add_videomode(&modedb[i], + &setting->fbi->modelist); + } + + adv739x->fbi = setting->fbi; + adv739x->enabled = 0; + adv739x->cur_mode = ADV739X_MODE_NTSC; //default mode + + adv739x->pdev = platform_device_register_simple("mxc_adv739x", 0, NULL, 0); + if (IS_ERR(adv739x->pdev)) { + dev_err(&adv739x->client->dev, + "Unable to register adv739x as a platform device\n"); + ret = PTR_ERR(adv739x->pdev); + goto register_pltdev_failed; + } + + adv739x->nb.notifier_call = adv739x_fb_event; + ret = fb_register_client(&adv739x->nb); + if (ret < 0) + goto reg_fbclient_failed; + + return ret; + +reg_fbclient_failed: + platform_device_unregister(adv739x->pdev); +register_pltdev_failed: + return ret; +} + +static void adv739x_disp_deinit(struct mxc_dispdrv_handle *disp) +{ + struct adv739x_data *adv739x = mxc_dispdrv_getdata(disp); + + dev_dbg(&adv739x->client->dev, "%s\n", __func__); + if (adv739x->client->irq) + free_irq(adv739x->client->irq, adv739x); + + fb_unregister_client(&adv739x->nb); + + adv739x_poweroff(adv739x); + + platform_device_unregister(adv739x->pdev); +} + +static struct mxc_dispdrv_driver adv739x_drv = { + .name = DISPDRV_ADV739X, + .init = adv739x_disp_init, + .deinit = adv739x_disp_deinit, +}; + +static int adv739x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv739x_data *adv739x; + struct device_node *np = client->dev.of_node; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + adv739x = kzalloc(sizeof(struct adv739x_data), GFP_KERNEL); + if (!adv739x) { + ret = -ENOMEM; + goto alloc_failed; + } + + adv739x->client = client; + + ret = of_property_read_u32(np, "ipu_id", &adv739x->ipu_id); + if (ret) { + dev_err(&client->dev, "get of property ipu_id fail\n"); + goto prop_failed; + } + ret = of_property_read_u32(np, "disp_id", &adv739x->disp_id); + if (ret) { + dev_err(&client->dev, "get of property disp_id fail\n"); + goto prop_failed; + } + adv739x->default_ifmt = IPU_PIX_FMT_BT656; + + adv739x->disp_adv739x = mxc_dispdrv_register(&adv739x_drv); + mxc_dispdrv_setdata(adv739x->disp_adv739x, adv739x); + + i2c_set_clientdata(client, adv739x); + + return 0; + +prop_failed: + kfree(adv739x); +alloc_failed: + return ret; + +} + +static int adv739x_remove(struct i2c_client *client) +{ + struct adv739x_data *adv739x = i2c_get_clientdata(client); + + mxc_dispdrv_puthandle(adv739x->disp_adv739x); + mxc_dispdrv_unregister(adv739x->disp_adv739x); + kfree(adv739x); + return 0; +} + +static const struct i2c_device_id adv739x_id[] = { + { "mxc_adv739x", 0 }, +}; + +MODULE_DEVICE_TABLE(i2c, adv739x_id); + +static struct of_device_id adv739x_dt_ids[] = { + { .compatible = "adi,adv7393", }, + { /* sentinel */ } +}; + +static struct i2c_driver adv739x_i2c_driver = { + .driver = { + .name = "mxc_adv739x", + .of_match_table = adv739x_dt_ids, + }, + .probe = adv739x_probe, + .remove = adv739x_remove, + .id_table = adv739x_id, +}; + +static int __init adv739x_i2c_init(void) +{ + return i2c_add_driver(&adv739x_i2c_driver); +} + +static void __exit adv739x_i2c_exit(void) +{ + i2c_del_driver(&adv739x_i2c_driver); +} + +module_init(adv739x_i2c_init); +module_exit(adv739x_i2c_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("ADV739x TV encoder driver"); +MODULE_LICENSE("GPL"); + From a11566c30a4bd474aa0470e9b2642e63f1b9430d Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 15 May 2014 00:12:26 -0700 Subject: [PATCH 0774/1983] net: igb: add i210/i211 support for phy read/write The i210/i211 uses the MDICNFG register for the phy address instead of the MDIC register. Signed-off-by: Tim Harvey --- drivers/net/ethernet/intel/igb/e1000_phy.c | 71 +++++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index ad2b74d95138c1..703050b436a5c7 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -139,7 +139,7 @@ static s32 igb_phy_reset_dsp(struct e1000_hw *hw) s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) { struct e1000_phy_info *phy = &hw->phy; - u32 i, mdic = 0; + u32 i, mdicnfg, mdic = 0; s32 ret_val = 0; if (offset > MAX_PHY_REG_ADDRESS) { @@ -152,11 +152,25 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) * Control register. The MAC will take care of interfacing with the * PHY to retrieve the desired data. */ - mdic = ((offset << E1000_MDIC_REG_SHIFT) | - (phy->addr << E1000_MDIC_PHY_SHIFT) | - (E1000_MDIC_OP_READ)); + switch (hw->mac.type) { + case e1000_i210: + case e1000_i211: + mdicnfg = rd32(E1000_MDICNFG); + mdicnfg &= ~(E1000_MDICNFG_PHY_MASK); + mdicnfg |= (phy->addr << E1000_MDICNFG_PHY_SHIFT); + wr32(E1000_MDICNFG, mdicnfg); + mdic = ((offset << E1000_MDIC_REG_SHIFT) | + (E1000_MDIC_OP_READ)); + break; + default: + mdic = ((offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + break; + } wr32(E1000_MDIC, mdic); + wrfl(); /* Poll the ready bit to see if the MDI read completed * Increasing the time out as testing showed failures with @@ -181,6 +195,18 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) *data = (u16) mdic; out: + switch (hw->mac.type) { + /* restore MDICNFG to have phy's addr */ + case e1000_i210: + case e1000_i211: + mdicnfg = rd32(E1000_MDICNFG); + mdicnfg &= ~(E1000_MDICNFG_PHY_MASK); + mdicnfg |= (hw->phy.addr << E1000_MDICNFG_PHY_SHIFT); + wr32(E1000_MDICNFG, mdicnfg); + break; + default: + break; + } return ret_val; } @@ -195,7 +221,7 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) { struct e1000_phy_info *phy = &hw->phy; - u32 i, mdic = 0; + u32 i, mdicnfg, mdic = 0; s32 ret_val = 0; if (offset > MAX_PHY_REG_ADDRESS) { @@ -208,12 +234,27 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) * Control register. The MAC will take care of interfacing with the * PHY to retrieve the desired data. */ - mdic = (((u32)data) | - (offset << E1000_MDIC_REG_SHIFT) | - (phy->addr << E1000_MDIC_PHY_SHIFT) | - (E1000_MDIC_OP_WRITE)); + switch (hw->mac.type) { + case e1000_i210: + case e1000_i211: + mdicnfg = rd32(E1000_MDICNFG); + mdicnfg &= ~(E1000_MDICNFG_PHY_MASK); + mdicnfg |= (phy->addr << E1000_MDICNFG_PHY_SHIFT); + wr32(E1000_MDICNFG, mdicnfg); + mdic = (((u32)data) | + (offset << E1000_MDIC_REG_SHIFT) | + (E1000_MDIC_OP_WRITE)); + break; + default: + mdic = (((u32)data) | + (offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + break; + } wr32(E1000_MDIC, mdic); + wrfl(); /* Poll the ready bit to see if the MDI read completed * Increasing the time out as testing showed failures with @@ -237,6 +278,18 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) } out: + switch (hw->mac.type) { + /* restore MDICNFG to have phy's addr */ + case e1000_i210: + case e1000_i211: + mdicnfg = rd32(E1000_MDICNFG); + mdicnfg &= ~(E1000_MDICNFG_PHY_MASK); + mdicnfg |= (hw->phy.addr << E1000_MDICNFG_PHY_SHIFT); + wr32(E1000_MDICNFG, mdicnfg); + break; + default: + break; + } return ret_val; } From 95bc3cea15de2b545c78072d82cfab0c5623b3dc Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 15 May 2014 00:29:18 -0700 Subject: [PATCH 0775/1983] net: igb: add phy read/write functions that accept phy addr Add igb_write_reg_gs40g/igb_read_reg_gs40g that can be passed a phy address. The existing igb_write_phy_reg_gs40g/igb_read_phy_reg_gs40g become wrappers to this function. Signed-off-by: Tim Harvey --- drivers/net/ethernet/intel/igb/e1000_82575.c | 4 +- drivers/net/ethernet/intel/igb/e1000_phy.c | 74 ++++++++++++++------ drivers/net/ethernet/intel/igb/e1000_phy.h | 6 +- 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 4fa5c2a77d4998..eadae5974f6829 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -2142,7 +2142,7 @@ static s32 igb_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data) if (ret_val) goto out; - ret_val = igb_read_phy_reg_mdic(hw, offset, data); + ret_val = igb_read_phy_reg_mdic(hw, hw->phy.addr, offset, data); hw->phy.ops.release(hw); @@ -2167,7 +2167,7 @@ static s32 igb_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data) if (ret_val) goto out; - ret_val = igb_write_phy_reg_mdic(hw, offset, data); + ret_val = igb_write_phy_reg_mdic(hw, hw->phy.addr, offset, data); hw->phy.ops.release(hw); diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 703050b436a5c7..11309bd8089954 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -136,9 +136,8 @@ static s32 igb_phy_reset_dsp(struct e1000_hw *hw) * Reads the MDI control regsiter in the PHY at offset and stores the * information read to data. **/ -s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) +s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u8 addr, u32 offset, u16 *data) { - struct e1000_phy_info *phy = &hw->phy; u32 i, mdicnfg, mdic = 0; s32 ret_val = 0; @@ -157,14 +156,14 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) case e1000_i211: mdicnfg = rd32(E1000_MDICNFG); mdicnfg &= ~(E1000_MDICNFG_PHY_MASK); - mdicnfg |= (phy->addr << E1000_MDICNFG_PHY_SHIFT); + mdicnfg |= (addr << E1000_MDICNFG_PHY_SHIFT); wr32(E1000_MDICNFG, mdicnfg); mdic = ((offset << E1000_MDIC_REG_SHIFT) | (E1000_MDIC_OP_READ)); break; default: mdic = ((offset << E1000_MDIC_REG_SHIFT) | - (phy->addr << E1000_MDIC_PHY_SHIFT) | + (addr << E1000_MDIC_PHY_SHIFT) | (E1000_MDIC_OP_READ)); break; } @@ -218,9 +217,8 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) * * Writes data to MDI control register in the PHY at offset. **/ -s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) +s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u8 addr, u32 offset, u16 data) { - struct e1000_phy_info *phy = &hw->phy; u32 i, mdicnfg, mdic = 0; s32 ret_val = 0; @@ -239,7 +237,7 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) case e1000_i211: mdicnfg = rd32(E1000_MDICNFG); mdicnfg &= ~(E1000_MDICNFG_PHY_MASK); - mdicnfg |= (phy->addr << E1000_MDICNFG_PHY_SHIFT); + mdicnfg |= (addr << E1000_MDICNFG_PHY_SHIFT); wr32(E1000_MDICNFG, mdicnfg); mdic = (((u32)data) | (offset << E1000_MDIC_REG_SHIFT) | @@ -248,7 +246,7 @@ s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) default: mdic = (((u32)data) | (offset << E1000_MDIC_REG_SHIFT) | - (phy->addr << E1000_MDIC_PHY_SHIFT) | + (addr << E1000_MDIC_PHY_SHIFT) | (E1000_MDIC_OP_WRITE)); break; } @@ -539,7 +537,7 @@ s32 igb_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) goto out; if (offset > MAX_PHY_MULTI_PAGE_REG) { - ret_val = igb_write_phy_reg_mdic(hw, + ret_val = igb_write_phy_reg_mdic(hw, hw->phy.addr, IGP01E1000_PHY_PAGE_SELECT, (u16)offset); if (ret_val) { @@ -548,8 +546,8 @@ s32 igb_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) } } - ret_val = igb_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = igb_read_phy_reg_mdic(hw, hw->phy.addr, + MAX_PHY_REG_ADDRESS & offset, data); hw->phy.ops.release(hw); @@ -578,7 +576,7 @@ s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) goto out; if (offset > MAX_PHY_MULTI_PAGE_REG) { - ret_val = igb_write_phy_reg_mdic(hw, + ret_val = igb_write_phy_reg_mdic(hw, hw->phy.addr, IGP01E1000_PHY_PAGE_SELECT, (u16)offset); if (ret_val) { @@ -587,8 +585,8 @@ s32 igb_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) } } - ret_val = igb_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = igb_write_phy_reg_mdic(hw, hw->phy.addr, + MAX_PHY_REG_ADDRESS & offset, data); hw->phy.ops.release(hw); @@ -2554,8 +2552,9 @@ s32 igb_get_cable_length_82580(struct e1000_hw *hw) } /** - * igb_write_phy_reg_gs40g - Write GS40G PHY register + * igb_write_reg_gs40g - Write GS40G PHY register * @hw: pointer to the HW structure + * @addr: phy address to write to * @offset: lower half is register offset to write to * upper half is page to use. * @data: data to write at register offset @@ -2563,7 +2562,7 @@ s32 igb_get_cable_length_82580(struct e1000_hw *hw) * Acquires semaphore, if necessary, then writes the data to PHY register * at the offset. Release any acquired semaphores before exiting. **/ -s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) +s32 igb_write_reg_gs40g(struct e1000_hw *hw, u8 addr, u32 offset, u16 data) { s32 ret_val; u16 page = offset >> GS40G_PAGE_SHIFT; @@ -2573,10 +2572,10 @@ s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) if (ret_val) return ret_val; - ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + ret_val = igb_write_phy_reg_mdic(hw, addr, GS40G_PAGE_SELECT, page); if (ret_val) goto release; - ret_val = igb_write_phy_reg_mdic(hw, offset, data); + ret_val = igb_write_phy_reg_mdic(hw, addr, offset, data); release: hw->phy.ops.release(hw); @@ -2584,8 +2583,24 @@ s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) } /** - * igb_read_phy_reg_gs40g - Read GS40G PHY register + * igb_write_phy_reg_gs40g - Write GS40G PHY register + * @hw: pointer to the HW structure + * @offset: lower half is register offset to write to + * upper half is page to use. + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) +{ + return igb_write_reg_gs40g(hw, hw->phy.addr, offset, data); +} + +/** + * igb_read_reg_gs40g - Read GS40G PHY register * @hw: pointer to the HW structure + * @addr: phy address to read from * @offset: lower half is register offset to read to * upper half is page to use. * @data: data to read at register offset @@ -2593,7 +2608,7 @@ s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) * Acquires semaphore, if necessary, then reads the data in the PHY register * at the offset. Release any acquired semaphores before exiting. **/ -s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) +s32 igb_read_reg_gs40g(struct e1000_hw *hw, u8 addr, u32 offset, u16 *data) { s32 ret_val; u16 page = offset >> GS40G_PAGE_SHIFT; @@ -2603,16 +2618,31 @@ s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) if (ret_val) return ret_val; - ret_val = igb_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + ret_val = igb_write_phy_reg_mdic(hw, addr, GS40G_PAGE_SELECT, page); if (ret_val) goto release; - ret_val = igb_read_phy_reg_mdic(hw, offset, data); + ret_val = igb_read_phy_reg_mdic(hw, addr, offset, data); release: hw->phy.ops.release(hw); return ret_val; } +/** + * igb_read_phy_reg_gs40g - Read GS40G PHY register + * @hw: pointer to the HW structure + * @offset: lower half is register offset to read to + * upper half is page to use. + * @data: data to read at register offset + * + * Acquires semaphore, if necessary, then reads the data in the PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return igb_read_reg_gs40g(hw, hw->phy.addr, offset, data); +} + /** * igb_set_master_slave_mode - Setup PHY for Master/slave mode * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index 6a0873f2095a49..428c2f7430793e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -65,8 +65,8 @@ s32 igb_phy_has_link(struct e1000_hw *hw, u32 iterations, void igb_power_up_phy_copper(struct e1000_hw *hw); void igb_power_down_phy_copper(struct e1000_hw *hw); s32 igb_phy_init_script_igp3(struct e1000_hw *hw); -s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); -s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u8 addr, u32 offset, u16 *data); +s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u8 addr, u32 offset, u16 data); s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data); s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data); @@ -77,6 +77,8 @@ s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw); s32 igb_get_cable_length_82580(struct e1000_hw *hw); s32 igb_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_read_reg_gs40g(struct e1000_hw *hw, u8 addr, u32 offset, u16 *data); +s32 igb_write_reg_gs40g(struct e1000_hw *hw, u8 addr, u32 offset, u16 data); s32 igb_check_polarity_m88(struct e1000_hw *hw); /* IGP01E1000 Specific Registers */ From fa6954ebfbd2b09f7d154c3a02695ab4ff2f4df0 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Thu, 15 May 2014 12:36:23 -0700 Subject: [PATCH 0776/1983] net: igb: register mii_bus for SerDes w/ external phy If an i210 is configured for 1000BASE-BX link_mode and has an external phy specified, then register an mii bus using the external phy address as a mask. An i210 hooked to an external standard phy will be configured with a link_mo of SGMII in which case phy ops will be configured and used internall in the igb driver for link status. However, in certain cases one might be using a backplane SerDes connection to something that talks on the mdio bus but is not a standard phy, such as a switch. In this case by registering an mdio bus a phy driver can manage the device. Signed-off-by: Tim Harvey --- drivers/net/ethernet/intel/igb/e1000_82575.c | 15 ++ drivers/net/ethernet/intel/igb/e1000_hw.h | 7 + drivers/net/ethernet/intel/igb/igb_main.c | 166 ++++++++++++++++++- 3 files changed, 183 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index eadae5974f6829..269ef286cb0d34 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -606,13 +606,25 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) switch (link_mode) { case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: hw->phy.media_type = e1000_media_type_internal_serdes; + if (igb_sgmii_uses_mdio_82575(hw)) { + u32 mdicnfg = rd32(E1000_MDICNFG); + mdicnfg &= E1000_MDICNFG_PHY_MASK; + hw->phy.addr = mdicnfg >> E1000_MDICNFG_PHY_SHIFT; + hw_dbg("1000BASE_KX w/ external MDIO device at 0x%x\n", + hw->phy.addr); + } else { + hw_dbg("1000BASE_KX"); + } break; case E1000_CTRL_EXT_LINK_MODE_SGMII: /* Get phy control interface type set (MDIO vs. I2C)*/ if (igb_sgmii_uses_mdio_82575(hw)) { hw->phy.media_type = e1000_media_type_copper; dev_spec->sgmii_active = true; + hw_dbg("SGMII with external MDIO PHY"); break; + } else { + hw_dbg("SGMII with external I2C PHY"); } /* fall through for I2C based SGMII */ case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: @@ -629,8 +641,11 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) hw->phy.media_type = e1000_media_type_copper; dev_spec->sgmii_active = true; } + hw_dbg("SERDES with external SFP"); break; + } else { + hw_dbg("SERDES"); } /* do not change link mode for 100BaseFX */ diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index b79980ad225bd0..91b04bed61a54a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -32,6 +32,7 @@ #include #include #include +#include #include "e1000_regs.h" #include "e1000_defines.h" @@ -553,6 +554,12 @@ struct e1000_hw { struct e1000_mbx_info mbx; struct e1000_host_mng_dhcp_cookie mng_cookie; +#ifdef CONFIG_PHYLIB + /* Phylib and MDIO interface */ + struct mii_bus *mii_bus; + struct phy_device *phy_dev; + phy_interface_t phy_interface; +#endif union { struct e1000_dev_spec_82575 _82575; } dev_spec; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 5f9376511ab111..a4d0330ba03c1c 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -2185,6 +2186,124 @@ static s32 igb_init_i2c(struct igb_adapter *adapter) return status; } +#ifdef CONFIG_PHYLIB +/* + * MMIO/PHYdev support + */ + +static int igb_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct e1000_hw *hw = bus->priv; + u16 out; + int err; + + err = igb_read_reg_gs40g(hw, mii_id, regnum, &out); + if (err) + return err; + return out; +} + +static int igb_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, + u16 val) +{ + struct e1000_hw *hw = bus->priv; + + return igb_write_reg_gs40g(hw, mii_id, regnum, val); +} + +static int igb_enet_mdio_reset(struct mii_bus *bus) +{ + udelay(300); + return 0; +} + +static void igb_enet_mii_link(struct net_device *netdev) +{ +} + +/* Probe the mdio bus for phys and connect them */ +static int igb_enet_mii_probe(struct net_device *netdev) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + struct phy_device *phy_dev = NULL; + int phy_id; + + /* check for attached phy */ + for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { + if (hw->mii_bus->phy_map[phy_id]) { + phy_dev = hw->mii_bus->phy_map[phy_id]; + break; + } + } + if (!phy_dev) { + netdev_err(netdev, "no PHY found\n"); + return -ENODEV; + } + + hw->phy_interface = PHY_INTERFACE_MODE_RGMII; + phy_dev = phy_connect(netdev, dev_name(&phy_dev->dev), + igb_enet_mii_link, hw->phy_interface); + if (IS_ERR(phy_dev)) { + netdev_err(netdev, "could not attach to PHY\n"); + return PTR_ERR(phy_dev); + } + + hw->phy_dev = phy_dev; + netdev_info(netdev, "igb PHY driver [%s] (mii_bus:phy_addr=%s)\n", + hw->phy_dev->drv->name, dev_name(&hw->phy_dev->dev)); + + return 0; +} + +/* Create and register mdio bus */ +static int igb_enet_mii_init(struct pci_dev *pdev) +{ + struct mii_bus *mii_bus; + struct net_device *netdev = pci_get_drvdata(pdev); + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + int err; + + mii_bus = mdiobus_alloc(); + if (mii_bus == NULL) { + err = -ENOMEM; + goto err_out; + } + + mii_bus->name = "igb_enet_mii_bus"; + mii_bus->read = igb_enet_mdio_read; + mii_bus->write = igb_enet_mdio_write; + mii_bus->reset = igb_enet_mdio_reset; + snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + pci_name(pdev), hw->device_id + 1); + mii_bus->priv = hw; + mii_bus->parent = &pdev->dev; + mii_bus->phy_mask = ~(1 << hw->phy.addr); + + err = mdiobus_register(mii_bus); + if (err) { + printk(KERN_ERR "failed to register mii_bus: %d\n", err); + goto err_out_free_mdiobus; + } + hw->mii_bus = mii_bus; + + return 0; + +err_out_free_mdiobus: + mdiobus_free(mii_bus); +err_out: + return err; +} + +static void igb_enet_mii_remove(struct e1000_hw *hw) +{ + if (hw->mii_bus) { + mdiobus_unregister(hw->mii_bus); + mdiobus_free(hw->mii_bus); + } +} +#endif /* CONFIG_PHYLIB */ /** * igb_read_mac_addr_dts - Read mac addres from the device tree @@ -2619,6 +2738,13 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } pm_runtime_put_noidle(&pdev->dev); + +#ifdef CONFIG_PHYLIB + /* create and register the mdio bus if using ext phy */ + if (rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO) + igb_enet_mii_init(pdev); +#endif + return 0; err_register: @@ -2762,6 +2888,10 @@ static void igb_remove(struct pci_dev *pdev) struct e1000_hw *hw = &adapter->hw; pm_runtime_get_noresume(&pdev->dev); +#ifdef CONFIG_PHYLIB + if (rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO) + igb_enet_mii_remove(hw); +#endif #ifdef CONFIG_IGB_HWMON igb_sysfs_exit(adapter); #endif @@ -3066,6 +3196,12 @@ static int __igb_open(struct net_device *netdev, bool resuming) if (!resuming) pm_runtime_put(&pdev->dev); +#ifdef CONFIG_PHYLIB + /* Probe and connect to PHY if using ext phy */ + if (rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO) + igb_enet_mii_probe(netdev); +#endif + /* start the watchdog. */ hw->mac.get_link_status = 1; schedule_work(&adapter->watchdog_task); @@ -7121,21 +7257,41 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) static int igb_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; struct mii_ioctl_data *data = if_mii(ifr); - if (adapter->hw.phy.media_type != e1000_media_type_copper) + if (adapter->hw.phy.media_type != e1000_media_type_copper && + !(rd32(E1000_MDICNFG) & E1000_MDICNFG_EXT_MDIO)) return -EOPNOTSUPP; switch (cmd) { case SIOCGMIIPHY: - data->phy_id = adapter->hw.phy.addr; + data->phy_id = hw->phy.addr; break; case SIOCGMIIREG: - if (igb_read_phy_reg(&adapter->hw, data->reg_num & 0x1F, - &data->val_out)) - return -EIO; + if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) { + if (igb_read_reg_gs40g(hw, data->phy_id, + data->reg_num & 0x1F, + &data->val_out)) + return -EIO; + } else { + if (igb_read_phy_reg(hw, data->reg_num & 0x1F, + &data->val_out)) + return -EIO; + } break; case SIOCSMIIREG: + if (hw->mac.type == e1000_i210 || hw->mac.type == e1000_i211) { + if (igb_write_reg_gs40g(hw, data->phy_id, + data->reg_num & 0x1F, + data->val_in)) + return -EIO; + } else { + if (igb_write_phy_reg(hw, data->reg_num & 0x1F, + data->val_in)) + return -EIO; + } + break; default: return -EOPNOTSUPP; } From 64f1f3a030d522c622fb2fbf5d41b06c5336eab7 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Tue, 3 Feb 2015 15:17:18 -0800 Subject: [PATCH 0777/1983] video: mxc: ldb: use native mode for display timings When calling of_get_videomode OF_USE_NATIVE_MODE should be passed in to obtain the native mode in the case of multiple available timings. Signed-off-by: Tim Harvey --- drivers/video/mxc/ldb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index 4739df7fcca1b8..7cea84ac9db988 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -30,6 +30,7 @@ #include #include #include