diff options
author | Koen Kooi <koen@openembedded.org> | 2009-08-16 11:34:38 +0200 |
---|---|---|
committer | Koen Kooi <koen@openembedded.org> | 2009-08-16 11:34:38 +0200 |
commit | 97aee9aeb0da17f7c3a3c31acf5f8ddd227bd3e2 (patch) | |
tree | c6f9e1aa40a5abfde9d03de2fb4f86e778ef8ffd | |
parent | 2001d352e87a4a143c6dd4bed2d3261937c16eba (diff) |
linux-omap-pm 2.6.29: update to latest git HEAD, sync patches and defconfig with regular linux-omap 2.6.29
50 files changed, 30313 insertions, 48 deletions
diff --git a/recipes/linux/linux-omap-pm-2.6.29/0001-ASoC-Add-support-for-OMAP3-EVM.patch b/recipes/linux/linux-omap-pm-2.6.29/0001-ASoC-Add-support-for-OMAP3-EVM.patch new file mode 100644 index 0000000000..a76e96e444 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/0001-ASoC-Add-support-for-OMAP3-EVM.patch @@ -0,0 +1,206 @@ +From c1dad0b6b434300ae64c902d11611c54c513ea10 Mon Sep 17 00:00:00 2001 +From: Anuj Aggarwal <anuj.aggarwal@ti.com> +Date: Fri, 21 Nov 2008 17:41:03 +0530 +Subject: [PATCH] ASoC: Add support for OMAP3 EVM + +This patch adds ALSA SoC support for OMAP3 EVM using TWL4030 audio codec. + +Signed-off-by: Anuj Aggarwal <anuj.aggarwal@ti.com> +--- + sound/soc/omap/Kconfig | 8 +++ + sound/soc/omap/Makefile | 3 +- + sound/soc/omap/omap3evm.c | 147 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 157 insertions(+), 1 deletions(-) + create mode 100644 sound/soc/omap/omap3evm.c + +diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig +index 0daeee4..deb6ba9 100644 +--- a/sound/soc/omap/Kconfig ++++ b/sound/soc/omap/Kconfig +@@ -22,6 +22,14 @@ config SND_OMAP_SOC_OMAP3_BEAGLE + help + Say Y if you want to add support for SoC audio on the Beagleboard. + ++config SND_OMAP_SOC_OMAP3EVM ++ tristate "SoC Audio support for OMAP3EVM board" ++ depends on SND_OMAP_SOC && MACH_OMAP3EVM ++ select SND_OMAP_SOC_MCBSP ++ select SND_SOC_TWL4030 ++ help ++ Say Y if you want to add support for SoC audio on the omap3evm board. ++ + config SND_OMAP_SOC_OSK5912 + tristate "SoC Audio support for omap osk5912" + depends on SND_OMAP_SOC && MACH_OMAP_OSK +diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile +index 4bae404..ef31c25 100644 +--- a/sound/soc/omap/Makefile ++++ b/sound/soc/omap/Makefile +@@ -10,9 +10,10 @@ snd-soc-n810-objs := n810.o + snd-soc-omap3beagle-objs := omap3beagle.o + snd-soc-osk5912-objs := osk5912.o + snd-soc-overo-objs := overo.o ++snd-soc-omap3evm-objs := omap3evm.o + + obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o + obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o + obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o + obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o +- ++obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o +diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c +new file mode 100644 +index 0000000..570af55 +--- /dev/null ++++ b/sound/soc/omap/omap3evm.c +@@ -0,0 +1,147 @@ ++/* ++ * omap3evm.c -- ALSA SoC support for OMAP3 EVM ++ * ++ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com> ++ * ++ * Based on sound/soc/omap/beagle.c by Steve Sakoman ++ * ++ * Copyright (C) 2008 Texas Instruments, Incorporated ++ * ++ * 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. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, ++ * whether express or implied; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/platform_device.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/soc.h> ++#include <sound/soc-dapm.h> ++ ++#include <asm/mach-types.h> ++#include <mach/hardware.h> ++#include <mach/gpio.h> ++#include <mach/mcbsp.h> ++ ++#include "omap-mcbsp.h" ++#include "omap-pcm.h" ++#include "../codecs/twl4030.h" ++ ++static int omap3evm_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->dai->codec_dai; ++ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; ++ int ret; ++ ++ /* Set codec DAI configuration */ ++ ret = snd_soc_dai_set_fmt(codec_dai, ++ SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBM_CFM); ++ if (ret < 0) { ++ printk(KERN_ERR "can't set codec DAI configuration\n"); ++ return ret; ++ } ++ ++ /* Set cpu DAI configuration */ ++ ret = snd_soc_dai_set_fmt(cpu_dai, ++ SND_SOC_DAIFMT_I2S | ++ SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBM_CFM); ++ if (ret < 0) { ++ printk(KERN_ERR "can't set cpu DAI configuration\n"); ++ return ret; ++ } ++ ++ /* Set the codec system clock for DAC and ADC */ ++ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, ++ SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ printk(KERN_ERR "can't set codec system clock\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static struct snd_soc_ops omap3evm_ops = { ++ .hw_params = omap3evm_hw_params, ++}; ++ ++/* Digital audio interface glue - connects codec <--> CPU */ ++static struct snd_soc_dai_link omap3evm_dai = { ++ .name = "TWL4030", ++ .stream_name = "TWL4030", ++ .cpu_dai = &omap_mcbsp_dai[0], ++ .codec_dai = &twl4030_dai, ++ .ops = &omap3evm_ops, ++}; ++ ++/* Audio machine driver */ ++static struct snd_soc_machine snd_soc_machine_omap3evm = { ++ .name = "omap3evm", ++ .dai_link = &omap3evm_dai, ++ .num_links = 1, ++}; ++ ++/* Audio subsystem */ ++static struct snd_soc_device omap3evm_snd_devdata = { ++ .machine = &snd_soc_machine_omap3evm, ++ .platform = &omap_soc_platform, ++ .codec_dev = &soc_codec_dev_twl4030, ++}; ++ ++static struct platform_device *omap3evm_snd_device; ++ ++static int __init omap3evm_soc_init(void) ++{ ++ int ret; ++ ++ if (!machine_is_omap3evm()) { ++ pr_debug("Not OMAP3 EVM!\n"); ++ return -ENODEV; ++ } ++ pr_info("OMAP3 EVM SoC init\n"); ++ ++ omap3evm_snd_device = platform_device_alloc("soc-audio", -1); ++ if (!omap3evm_snd_device) { ++ printk(KERN_ERR "Platform device allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(omap3evm_snd_device, &omap3evm_snd_devdata); ++ omap3evm_snd_devdata.dev = &omap3evm_snd_device->dev; ++ *(unsigned int *)omap3evm_dai.cpu_dai->private_data = 1; /* McBSP2 */ ++ ++ ret = platform_device_add(omap3evm_snd_device); ++ if (ret) ++ goto err1; ++ ++ return 0; ++ ++err1: ++ printk(KERN_ERR "Unable to add platform device\n"); ++ platform_device_put(omap3evm_snd_device); ++ ++ return ret; ++} ++ ++static void __exit omap3evm_soc_exit(void) ++{ ++ platform_device_unregister(omap3evm_snd_device); ++} ++ ++module_init(omap3evm_soc_init); ++module_exit(omap3evm_soc_exit); ++ ++MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>"); ++MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM"); ++MODULE_LICENSE("GPL"); +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/0001-This-merges-Steve-Kipisz-USB-EHCI-support.-He-star.patch b/recipes/linux/linux-omap-pm-2.6.29/0001-This-merges-Steve-Kipisz-USB-EHCI-support.-He-star.patch new file mode 100644 index 0000000000..d590f8ffb9 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/0001-This-merges-Steve-Kipisz-USB-EHCI-support.-He-star.patch @@ -0,0 +1,146 @@ +From f8f10f496bce396416d7156da876222c6ce8c341 Mon Sep 17 00:00:00 2001 +From: Steven Kipisz <skipisz@beagleboard.org> +Date: Wed, 9 Jan 2009 12:01:11 -0600 +Subject: [PATCH-USB] Omap3 beagleboard: add support for EHCI in revision C1 boards + +Signed-off-by: Jason Kridner <jkridner@beagleboard.org> +--- + arch/arm/mach-omap2/board-omap3beagle.c | 10 +--------- + arch/arm/mach-omap2/usb-ehci.c | 4 +--- + drivers/usb/host/ehci-omap.c | 26 ++++++++++++++++++++++++++ + 3 files changed, 28 insertions(+), 12 deletions(-) + +diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c +index fe97bab..de81153 100644 +--- a/arch/arm/mach-omap2/board-omap3beagle.c ++++ b/arch/arm/mach-omap2/board-omap3beagle.c +@@ -140,15 +140,7 @@ static int beagle_twl_gpio_setup(struct device *dev, + * power switch and overcurrent detect + */ + +- gpio_request(gpio + 1, "EHCI_nOC"); +- gpio_direction_input(gpio + 1); +- +- /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */ +- gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); +- gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1); +- +- /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ +- gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; ++ /* TODO: This needs to be modified to not rely on u-boot */ + + return 0; + } +diff --git a/arch/arm/mach-omap2/usb-ehci.c b/arch/arm/mach-omap2/usb-ehci.c +index 489439d..2c6305b 100644 +--- a/arch/arm/mach-omap2/usb-ehci.c ++++ b/arch/arm/mach-omap2/usb-ehci.c +@@ -152,9 +152,7 @@ static void setup_ehci_io_mux(void) + void __init usb_ehci_init(void) + { + #if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE) +- /* Setup Pin IO MUX for EHCI */ +- if (cpu_is_omap34xx()) +- setup_ehci_io_mux(); ++ /* TODO: Setup Pin IO MUX for EHCI - moved this temporarily to U-boot */ + + if (platform_device_register(&ehci_device) < 0) { + printk(KERN_ERR "Unable to register HS-USB (EHCI) device\n"); + +diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c +index 1b3266c..8472996 100644 +--- a/drivers/usb/host/ehci-omap.c ++++ b/drivers/usb/host/ehci-omap.c +@@ -48,16 +48,26 @@ + * to get the PHY state machine in working state + */ + #define EXTERNAL_PHY_RESET ++#ifdef CONFIG_MACH_OMAP3_BEAGLE ++#define EXT_PHY_RESET_GPIO_PORT2 (147) ++#else + #define EXT_PHY_RESET_GPIO_PORT1 (57) + #define EXT_PHY_RESET_GPIO_PORT2 (61) ++#endif + #define EXT_PHY_RESET_DELAY (10) + ++#define PHY_STP_PULLUP_ENABLE (0x10) ++#define PHY_STP_PULLUP_DISABLE (0x90) ++ ++ + /* ISSUE2: + * USBHOST supports External charge pump PHYs only + * Use the VBUS from Port1 to power VBUS of Port2 externally + * So use Port2 as the working ULPI port + */ ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + #define VBUS_INTERNAL_CHARGEPUMP_HACK ++#endif + + #endif /* CONFIG_OMAP_EHCI_PHY_MODE */ + +@@ -225,14 +235,43 @@ static int omap_start_ehc(struct platform_device *dev, struct usb_hcd *hcd) + + #ifdef EXTERNAL_PHY_RESET + /* Refer: ISSUE1 */ ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + gpio_request(EXT_PHY_RESET_GPIO_PORT1, "USB1 PHY reset"); + gpio_direction_output(EXT_PHY_RESET_GPIO_PORT1, 0); ++#endif + gpio_request(EXT_PHY_RESET_GPIO_PORT2, "USB2 PHY reset"); + gpio_direction_output(EXT_PHY_RESET_GPIO_PORT2, 0); ++ gpio_set_value(EXT_PHY_RESET_GPIO_PORT2, 0); + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(EXT_PHY_RESET_DELAY); + #endif + ++ /* ++ * The PHY register 0x7 - Interface Control register is ++ * configured to disable the integrated STP pull-up resistor ++ * used for interface protection. ++ * ++ * May not need to be here. ++ */ ++ omap_writel((0x7 << EHCI_INSNREG05_ULPI_REGADD_SHIFT) |/* interface reg */ ++ (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |/* Write */ ++ (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |/* Port1 */ ++ (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |/* Start */ ++ (PHY_STP_PULLUP_DISABLE), ++ EHCI_INSNREG05_ULPI); ++ ++ while (!(omap_readl(EHCI_INSNREG05_ULPI) & (1<<EHCI_INSNREG05_ULPI_CONTROL_SHIFT))); ++ ++ /* Force PHY to HS */ ++ omap_writel((0x4 << EHCI_INSNREG05_ULPI_REGADD_SHIFT) |/* function ctrl */ ++ (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) |/* Write */ ++ (1 << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) |/* Port1 */ ++ (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT) |/* Start */ ++ (0x40), ++ EHCI_INSNREG05_ULPI); ++ ++ while (!(omap_readl(EHCI_INSNREG05_ULPI) & (1<<EHCI_INSNREG05_ULPI_CONTROL_SHIFT))); ++ + /* Configure TLL for 60Mhz clk for ULPI */ + ehci_clocks->usbtll_fck_clk = clk_get(&dev->dev, USBHOST_TLL_FCLK); + if (IS_ERR(ehci_clocks->usbtll_fck_clk)) +@@ -307,7 +346,9 @@ static int omap_start_ehc(struct platform_device *dev, struct usb_hcd *hcd) + * Hold the PHY in RESET for enough time till PHY is settled and ready + */ + udelay(EXT_PHY_RESET_DELAY); ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + gpio_set_value(EXT_PHY_RESET_GPIO_PORT1, 1); ++#endif + gpio_set_value(EXT_PHY_RESET_GPIO_PORT2, 1); + #endif + +@@ -393,7 +434,9 @@ static void omap_stop_ehc(struct platform_device *dev, struct usb_hcd *hcd) + + + #ifdef EXTERNAL_PHY_RESET ++#ifndef CONFIG_MACH_OMAP3_BEAGLE + gpio_free(EXT_PHY_RESET_GPIO_PORT1); ++#endif + gpio_free(EXT_PHY_RESET_GPIO_PORT2); + #endif + +-- +1.6.0.4.790.gaa14a diff --git a/recipes/linux/linux-omap-pm-2.6.29/0001-board-ldp-add-regulator-info-to-get-the-microSD-slo.patch b/recipes/linux/linux-omap-pm-2.6.29/0001-board-ldp-add-regulator-info-to-get-the-microSD-slo.patch new file mode 100644 index 0000000000..49045f7bb1 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/0001-board-ldp-add-regulator-info-to-get-the-microSD-slo.patch @@ -0,0 +1,90 @@ +From 7efa7cc5b807cb840c62b5bf54bf47181c9b95a6 Mon Sep 17 00:00:00 2001 +From: Koen Kooi <koen@openembedded.org> +Date: Mon, 30 Mar 2009 15:21:37 +0200 +Subject: [PATCH v2] ARM: OMAP: board-ldp: add regulator info to get the microSD slot working again + +The ldp board was left behind when other boards got updated. The ldp info was copied from the beagleboard board file and s/beagle/ldp/g + +Changes since v1: + * dropped vsim portion since only 4 pins are hooked up + +Signed-off-by: Koen Kooi <koen@beagleboard.org> +--- + arch/arm/mach-omap2/board-ldp.c | 32 ++++++++++++++++++++++++++++++++ + 1 files changed, 32 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c +index 30926b0..19a5c15 100644 +--- a/arch/arm/mach-omap2/board-ldp.c ++++ b/arch/arm/mach-omap2/board-ldp.c +@@ -22,6 +22,7 @@ + #include <linux/clk.h> + #include <linux/spi/spi.h> + #include <linux/spi/ads7846.h> ++#include <linux/regulator/machine.h> + #include <linux/i2c/twl4030.h> + + #include <mach/hardware.h> +@@ -450,7 +451,16 @@ static struct twl4030_script *twl4030_scripts[] __initdata = { + &wrst_script, + }; + ++static const struct twl4030_resconfig ldp_resconfig[] = { ++ /* disable regulators that u-boot left enabled; the ++ * devices' drivers should be managing these. ++ */ ++ { .resource = RES_VMMC1, }, ++ { 0, }, ++}; ++ + static struct twl4030_power_data sdp3430_t2scripts_data __initdata = { ++ .resource_config = ldp_resconfig, + .scripts = twl4030_scripts, + .size = ARRAY_SIZE(twl4030_scripts), + }; +@@ -474,6 +484,25 @@ static struct twl4030_madc_platform_data ldp_madc_data = { + .irq_line = 1, + }; + ++static struct regulator_consumer_supply ldp_vmmc1_supply = { ++ .supply = "vmmc", ++}; ++ ++/* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */ ++static struct regulator_init_data ldp_vmmc1 = { ++ .constraints = { ++ .min_uV = 1850000, ++ .max_uV = 3150000, ++ .valid_modes_mask = REGULATOR_MODE_NORMAL ++ | REGULATOR_MODE_STANDBY, ++ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE ++ | REGULATOR_CHANGE_MODE ++ | REGULATOR_CHANGE_STATUS, ++ }, ++ .num_consumer_supplies = 1, ++ .consumer_supplies = &ldp_vmmc1_supply, ++}; ++ + static struct twl4030_platform_data ldp_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, +@@ -483,6 +512,7 @@ static struct twl4030_platform_data ldp_twldata = { + .madc = &ldp_madc_data, + .usb = &ldp_usb_data, + .power = &sdp3430_t2scripts_data, ++ .vmmc1 = &ldp_vmmc1, + .gpio = &ldp_gpio_data, + .keypad = &ldp_kp_twl4030_data, + }; +@@ -530,6 +560,8 @@ static void __init omap_ldp_init(void) + omap_serial_init(); + usb_musb_init(); + twl4030_mmc_init(mmc); ++ /* link regulators to MMC adapters */ ++ ldp_vmmc1_supply.dev = mmc[0].dev; + } + + static void __init omap_ldp_map_io(void) +-- +1.6.2 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/0001-implement-TIF_RESTORE_SIGMASK-support-and-enable-the.patch b/recipes/linux/linux-omap-pm-2.6.29/0001-implement-TIF_RESTORE_SIGMASK-support-and-enable-the.patch new file mode 100644 index 0000000000..7852f0afdb --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/0001-implement-TIF_RESTORE_SIGMASK-support-and-enable-the.patch @@ -0,0 +1,287 @@ +From 8a7643b09856f4f661403dcedbe0455b3cbeeea9 Mon Sep 17 00:00:00 2001 +From: Steven Newbury <s_j_newbury@yahoo.co.uk> +Date: Fri, 22 May 2009 14:25:40 +0200 +Subject: [PATCH] implement TIF_RESTORE_SIGMASK support and enable the related + syscalls: + +pselect6 +ppoll +epoll_pwait + +Based on http://www.spinics.net/lists/arm-kernel/msg38114.html +--- + arch/arm/include/asm/thread_info.h | 2 + + arch/arm/include/asm/unistd.h | 7 ++- + arch/arm/kernel/calls.S | 6 +- + arch/arm/kernel/signal.c | 90 +++++++++++++++--------------------- + 4 files changed, 46 insertions(+), 59 deletions(-) + +diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h +index 4f88482..2cf0917 100644 +--- a/arch/arm/include/asm/thread_info.h ++++ b/arch/arm/include/asm/thread_info.h +@@ -136,6 +136,7 @@ extern void vfp_sync_state(struct thread_info *thread); + #define TIF_SIGPENDING 0 + #define TIF_NEED_RESCHED 1 + #define TIF_SYSCALL_TRACE 8 ++#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal */ + #define TIF_POLLING_NRFLAG 16 + #define TIF_USING_IWMMXT 17 + #define TIF_MEMDIE 18 +@@ -144,6 +145,7 @@ extern void vfp_sync_state(struct thread_info *thread); + #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) + #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) + #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) ++#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) + #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) + #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) + #define _TIF_FREEZE (1 << TIF_FREEZE) +diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h +index 94cc58e..cd1eaa0 100644 +--- a/arch/arm/include/asm/unistd.h ++++ b/arch/arm/include/asm/unistd.h +@@ -360,8 +360,8 @@ + #define __NR_readlinkat (__NR_SYSCALL_BASE+332) + #define __NR_fchmodat (__NR_SYSCALL_BASE+333) + #define __NR_faccessat (__NR_SYSCALL_BASE+334) +- /* 335 for pselect6 */ +- /* 336 for ppoll */ ++#define __NR_pselect6 (__NR_SYSCALL_BASE+335) ++#define __NR_ppoll (__NR_SYSCALL_BASE+336) + #define __NR_unshare (__NR_SYSCALL_BASE+337) + #define __NR_set_robust_list (__NR_SYSCALL_BASE+338) + #define __NR_get_robust_list (__NR_SYSCALL_BASE+339) +@@ -372,7 +372,7 @@ + #define __NR_vmsplice (__NR_SYSCALL_BASE+343) + #define __NR_move_pages (__NR_SYSCALL_BASE+344) + #define __NR_getcpu (__NR_SYSCALL_BASE+345) +- /* 346 for epoll_pwait */ ++#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346) + #define __NR_kexec_load (__NR_SYSCALL_BASE+347) + #define __NR_utimensat (__NR_SYSCALL_BASE+348) + #define __NR_signalfd (__NR_SYSCALL_BASE+349) +@@ -430,6 +430,7 @@ + #define __ARCH_WANT_SYS_SIGPENDING + #define __ARCH_WANT_SYS_SIGPROCMASK + #define __ARCH_WANT_SYS_RT_SIGACTION ++#define __ARCH_WANT_SYS_RT_SIGSUSPEND + + #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) + #define __ARCH_WANT_SYS_TIME +diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S +index 1680e9e..534000d 100644 +--- a/arch/arm/kernel/calls.S ++++ b/arch/arm/kernel/calls.S +@@ -344,8 +344,8 @@ + CALL(sys_readlinkat) + CALL(sys_fchmodat) + CALL(sys_faccessat) +-/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */ +- CALL(sys_ni_syscall) /* eventually ppoll */ ++/* 335 */ CALL(sys_pselect6) ++ CALL(sys_ppoll) + CALL(sys_unshare) + CALL(sys_set_robust_list) + CALL(sys_get_robust_list) +@@ -355,7 +355,7 @@ + CALL(sys_vmsplice) + CALL(sys_move_pages) + /* 345 */ CALL(sys_getcpu) +- CALL(sys_ni_syscall) /* eventually epoll_pwait */ ++ CALL(sys_epoll_pwait) + CALL(sys_kexec_load) + CALL(sys_utimensat) + CALL(sys_signalfd) +diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c +index 80b8b5c..7645048 100644 +--- a/arch/arm/kernel/signal.c ++++ b/arch/arm/kernel/signal.c +@@ -47,57 +47,23 @@ const unsigned long sigreturn_codes[7] = { + MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN, + }; + +-static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall); ++static void do_signal(struct pt_regs * regs, int syscall); + + /* + * atomically swap in the new signal mask, and wait for a signal. + */ +-asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs) ++asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask) + { +- sigset_t saveset; +- + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); +- saveset = current->blocked; ++ current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +- regs->ARM_r0 = -EINTR; +- +- while (1) { +- current->state = TASK_INTERRUPTIBLE; +- schedule(); +- if (do_signal(&saveset, regs, 0)) +- return regs->ARM_r0; +- } +-} +- +-asmlinkage int +-sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs) +-{ +- sigset_t saveset, newset; +- +- /* XXX: Don't preclude handling different sized sigset_t's. */ +- if (sigsetsize != sizeof(sigset_t)) +- return -EINVAL; +- +- if (copy_from_user(&newset, unewset, sizeof(newset))) +- return -EFAULT; +- sigdelsetmask(&newset, ~_BLOCKABLE); +- +- spin_lock_irq(¤t->sighand->siglock); +- saveset = current->blocked; +- current->blocked = newset; +- recalc_sigpending(); +- spin_unlock_irq(¤t->sighand->siglock); +- regs->ARM_r0 = -EINTR; +- +- while (1) { +- current->state = TASK_INTERRUPTIBLE; +- schedule(); +- if (do_signal(&saveset, regs, 0)) +- return regs->ARM_r0; +- } ++ current->state = TASK_INTERRUPTIBLE; ++ schedule(); ++ set_thread_flag(TIF_RESTORE_SIGMASK); ++ return -ERESTARTNOHAND; + } + + asmlinkage int +@@ -290,7 +256,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs) + + badframe: + force_sig(SIGSEGV, current); +- return 0; ++ return -EFAULT; + } + + asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) +@@ -325,7 +291,7 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) + + badframe: + force_sig(SIGSEGV, current); +- return 0; ++ return -EFAULT; + } + + static int +@@ -541,7 +507,7 @@ static inline void restart_syscall(struct pt_regs *regs) + /* + * OK, we're invoking a handler + */ +-static void ++static int + handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, + struct pt_regs * regs, int syscall) +@@ -592,7 +558,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, + + if (ret != 0) { + force_sigsegv(sig, tsk); +- return; ++ return ret; + } + + /* +@@ -606,6 +572,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, + recalc_sigpending(); + spin_unlock_irq(&tsk->sighand->siglock); + ++ return ret; + } + + /* +@@ -617,11 +584,12 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +-static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) ++static void do_signal(struct pt_regs *regs, int syscall) + { + struct k_sigaction ka; + siginfo_t info; + int signr; ++ sigset_t *oldset; + + /* + * We want the common case to go fast, which +@@ -630,18 +598,29 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) + * if so. + */ + if (!user_mode(regs)) +- return 0; ++ return; + + if (try_to_freeze()) + goto no_signal; + + single_step_clear(current); + ++ if (test_thread_flag(TIF_RESTORE_SIGMASK)) ++ oldset = ¤t->saved_sigmask; ++ else ++ oldset = ¤t->blocked; ++ + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { +- handle_signal(signr, &ka, &info, oldset, regs, syscall); ++ if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) { ++ /* a signal was successfully delivered; the saved ++ * sigmask will have been stored in the signal frame, ++ * and will be restored by sigreturn, so we can simply ++ * clear the TIF_RESTORE_SIGMASK flag */ ++ clear_thread_flag(TIF_RESTORE_SIGMASK); ++ } + single_step_set(current); +- return 1; ++ return; + } + + no_signal: +@@ -665,7 +644,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) + usp = (u32 __user *)regs->ARM_sp; + + /* +- * Either we supports OABI only, or we have ++ * Either we support OABI only, or we have + * EABI with the OABI compat layer enabled. + * In the later case we don't know if user + * space is EABI or not, and if not we must +@@ -695,12 +674,17 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) + } + } + single_step_set(current); +- return 0; ++ /* if there's no signal to deliver, we just put the saved sigmask ++ back. */ ++ if (test_thread_flag(TIF_RESTORE_SIGMASK)) { ++ clear_thread_flag(TIF_RESTORE_SIGMASK); ++ sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); ++ } + } + + asmlinkage void + do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) + { +- if (thread_flags & _TIF_SIGPENDING) +- do_signal(¤t->blocked, regs, syscall); ++ if (thread_flags & (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK)) ++ do_signal(regs, syscall); + } +-- +1.6.2.4 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/ads7846-detection.patch b/recipes/linux/linux-omap-pm-2.6.29/ads7846-detection.patch new file mode 100644 index 0000000000..25a1cb052c --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/ads7846-detection.patch @@ -0,0 +1,41 @@ +diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c +index 2ae5ab8..a68b0a6 100644 +--- a/drivers/input/touchscreen/ads7846.c ++++ b/drivers/input/touchscreen/ads7846.c +@@ -1154,9 +1154,16 @@ static int __devinit ads7846_probe(struct spi_device *spi) + /* take a first sample, leaving nPENIRQ active and vREF off; avoid + * the touchscreen, in case it's not connected. + */ +- (void) ads7846_read12_ser(&spi->dev, ++ err = ads7846_read12_ser(&spi->dev, + READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + ++ /* if sample is all 0's or all 1's then there is no device on spi */ ++ if ( (err == 0x000) || (err == 0xfff)) { ++ dev_info(&spi->dev, "no device detected, test read result was 0x%08X\n", err); ++ err = -ENODEV; ++ goto err_free_irq; ++ } ++ + err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); + if (err) + goto err_remove_hwmon; +@@ -1174,7 +1181,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) + err_free_irq: + free_irq(spi->irq, ts); + err_free_gpio: +- if (ts->gpio_pendown != -1) ++ if (!ts->get_pendown_state && ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); + err_cleanup_filter: + if (ts->filter_cleanup) +@@ -1201,7 +1208,7 @@ static int __devexit ads7846_remove(struct spi_device *spi) + /* suspend left the IRQ disabled */ + enable_irq(ts->spi->irq); + +- if (ts->gpio_pendown != -1) ++ if (!ts->get_pendown_state && ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); + + if (ts->filter_cleanup) + diff --git a/recipes/linux/linux-omap-pm-2.6.29/beagleboard/defconfig b/recipes/linux/linux-omap-pm-2.6.29/beagleboard/defconfig index a992a5994e..e6f44e8b96 100644 --- a/recipes/linux/linux-omap-pm-2.6.29/beagleboard/defconfig +++ b/recipes/linux/linux-omap-pm-2.6.29/beagleboard/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.29-omap1 -# Sat Jun 20 13:15:25 2009 +# Sun Aug 16 11:34:12 2009 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -190,7 +190,7 @@ CONFIG_ARCH_OMAP3=y # CONFIG_OMAP_DEBUG_POWERDOMAIN is not set # CONFIG_OMAP_DEBUG_CLOCKDOMAIN is not set CONFIG_OMAP_SMARTREFLEX=y -# CONFIG_OMAP_SMARTREFLEX_TESTING is not set +CONFIG_OMAP_SMARTREFLEX_TESTING=y CONFIG_OMAP_RESET_CLOCKS=y CONFIG_OMAP_BOOT_TAG=y CONFIG_OMAP_BOOT_REASON=y @@ -198,7 +198,8 @@ CONFIG_OMAP_BOOT_REASON=y # CONFIG_OMAP_GPIO_SWITCH is not set # CONFIG_OMAP_MUX is not set CONFIG_OMAP_MCBSP=y -# CONFIG_OMAP_MBOX_FWK is not set +CONFIG_OMAP_MBOX_FWK=y +CONFIG_OMAP_IOMMU=y # CONFIG_OMAP_MPU_TIMER is not set CONFIG_OMAP_32K_TIMER=y # CONFIG_OMAP3_DEBOBS is not set @@ -311,10 +312,10 @@ CONFIG_CPU_FREQ_TABLE=y CONFIG_CPU_FREQ_DEBUG=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y @@ -1039,8 +1040,44 @@ CONFIG_MACVLAN=m CONFIG_EQUALIZER=m CONFIG_TUN=m CONFIG_VETH=m -# CONFIG_NET_ETHERNET is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +CONFIG_SMSC_PHY=m +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +CONFIG_ENC28J60=y +# CONFIG_ENC28J60_WRITEVERIFY is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set @@ -1294,7 +1331,7 @@ CONFIG_SPI_MASTER=y # # CONFIG_SPI_BITBANG is not set # CONFIG_SPI_GPIO is not set -# CONFIG_SPI_OMAP24XX is not set +CONFIG_SPI_OMAP24XX=y # # SPI Protocol Masters @@ -1476,6 +1513,7 @@ CONFIG_MEDIA_TUNER_MXL5005S=m CONFIG_VIDEO_V4L2=m CONFIG_VIDEO_V4L1=m CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_DMA_SG=m CONFIG_VIDEOBUF_VMALLOC=m CONFIG_VIDEOBUF_DVB=m CONFIG_VIDEO_IR=m @@ -1499,6 +1537,8 @@ CONFIG_VIDEO_VIVI=m # CONFIG_VIDEO_SAA5246A is not set # CONFIG_VIDEO_SAA5249 is not set # CONFIG_VIDEO_AU0828 is not set +CONFIG_VIDEO_OMAP3=m +CONFIG_VIDEO_OMAP34XX_ISP_RESIZER=m # CONFIG_SOC_CAMERA is not set CONFIG_V4L_USB_DRIVERS=y CONFIG_USB_VIDEO_CLASS=m @@ -2156,7 +2196,7 @@ CONFIG_RTC_INTF_DEV=y # # I2C RTC drivers # -# CONFIG_RTC_DRV_DS1307 is not set +CONFIG_RTC_DRV_DS1307=y # CONFIG_RTC_DRV_DS1374 is not set # CONFIG_RTC_DRV_DS1672 is not set # CONFIG_RTC_DRV_MAX6900 is not set @@ -2166,7 +2206,7 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_PCF8563 is not set # CONFIG_RTC_DRV_PCF8583 is not set # CONFIG_RTC_DRV_M41T80 is not set -CONFIG_RTC_DRV_TWL4030=y +CONFIG_RTC_DRV_TWL4030=m # CONFIG_RTC_DRV_S35390A is not set # CONFIG_RTC_DRV_FM3130 is not set # CONFIG_RTC_DRV_RX8581 is not set @@ -2480,7 +2520,7 @@ CONFIG_NLS_ISO8859_8=m CONFIG_NLS_CODEPAGE_1250=m CONFIG_NLS_CODEPAGE_1251=m CONFIG_NLS_ASCII=m -CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=m CONFIG_NLS_ISO8859_3=m CONFIG_NLS_ISO8859_4=m diff --git a/recipes/linux/linux-omap-pm-2.6.29/beagleboard/tincantools-puppy.diff b/recipes/linux/linux-omap-pm-2.6.29/beagleboard/tincantools-puppy.diff new file mode 100644 index 0000000000..c7856731e5 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/beagleboard/tincantools-puppy.diff @@ -0,0 +1,66 @@ +--- /tmp/board-omap3beagle.c 2009-07-01 01:06:44.000000000 +0200 ++++ git/arch/arm/mach-omap2/board-omap3beagle.c 2009-07-01 01:06:50.000000000 +0200 +@@ -125,6 +125,13 @@ + .wires = 8, + .gpio_wp = 29, + }, ++ { ++ .mmc = 2, ++ .wires = 4, ++ .gpio_wp = 141, ++ .gpio_cd = 162, ++ .transceiver = true, ++ }, + {} /* Terminator */ + }; + +@@ -132,6 +139,11 @@ + .supply = "vmmc", + }; + ++static struct regulator_consumer_supply beagle_vmmc2_supply = { ++ .supply = "vmmc", ++}; ++ ++ + static struct regulator_consumer_supply beagle_vsim_supply = { + .supply = "vmmc_aux", + }; +@@ -148,6 +160,7 @@ + + /* link regulators to MMC adapters */ + beagle_vmmc1_supply.dev = mmc[0].dev; ++ beagle_vmmc2_supply.dev = mmc[1].dev; + beagle_vsim_supply.dev = mmc[0].dev; + + /* REVISIT: need ehci-omap hooks for external VBUS +@@ -209,6 +222,21 @@ + .consumer_supplies = &beagle_vmmc1_supply, + }; + ++/* VMMC2 for MMC2 pins CMD, CLK, DAT0..DAT3 (max 100 mA) */ ++static struct regulator_init_data beagle_vmmc2 = { ++ .constraints = { ++ .min_uV = 2700000, ++ .max_uV = 3150000, ++ .valid_modes_mask = REGULATOR_MODE_NORMAL ++ | REGULATOR_MODE_STANDBY, ++ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE ++ | REGULATOR_CHANGE_MODE ++ | REGULATOR_CHANGE_STATUS, ++ }, ++ .num_consumer_supplies = 1, ++ .consumer_supplies = &beagle_vmmc2_supply, ++}; ++ + /* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */ + static struct regulator_init_data beagle_vsim = { + .constraints = { +@@ -284,6 +312,7 @@ + .gpio = &beagle_gpio_data, + .power = &beagle_power_data, + .vmmc1 = &beagle_vmmc1, ++ .vmmc2 = &beagle_vmmc2, + .vsim = &beagle_vsim, + .vdac = &beagle_vdac, + .vpll2 = &beagle_vpll2, diff --git a/recipes/linux/linux-omap-pm-2.6.29/beagleboard/tincantools-zippy.diff b/recipes/linux/linux-omap-pm-2.6.29/beagleboard/tincantools-zippy.diff new file mode 100644 index 0000000000..cab479e5d3 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/beagleboard/tincantools-zippy.diff @@ -0,0 +1,103 @@ +--- /tmp/board-omap3beagle.c 2009-08-13 12:35:01.000000000 +0200 ++++ git/arch/arm/mach-omap2/board-omap3beagle.c 2009-08-13 12:44:00.000000000 +0200 +@@ -21,6 +21,7 @@ + #include <linux/io.h> + #include <linux/leds.h> + #include <linux/gpio.h> ++#include <linux/irq.h> + #include <linux/input.h> + #include <linux/gpio_keys.h> + +@@ -54,6 +55,47 @@ + #define GPMC_CS_SIZE 0x30 + + #define NAND_BLOCK_SIZE SZ_128K ++#if defined(CONFIG_ENC28J60) || defined(CONFIG_ENC28J60_MODULE) ++ ++#include <mach/mcspi.h> ++#include <linux/spi/spi.h> ++ ++#define OMAP3BEAGLE_GPIO_ENC28J60_IRQ 157 ++ ++static struct omap2_mcspi_device_config enc28j60_spi_chip_info = { ++ .turbo_mode = 0, ++ .single_channel = 1, /* 0: slave, 1: master */ ++}; ++ ++static struct spi_board_info omap3beagle_spi_board_info[] __initdata = { ++ { ++ .modalias = "enc28j60", ++ .bus_num = 4, ++ .chip_select = 0, ++ .max_speed_hz = 20000000, ++ .controller_data = &enc28j60_spi_chip_info, ++ }, ++}; ++ ++static void __init omap3beagle_enc28j60_init(void) ++{ ++ if ((gpio_request(OMAP3BEAGLE_GPIO_ENC28J60_IRQ, "ENC28J60_IRQ") == 0) && ++ (gpio_direction_input(OMAP3BEAGLE_GPIO_ENC28J60_IRQ) == 0)) { ++ gpio_export(OMAP3BEAGLE_GPIO_ENC28J60_IRQ, 0); ++ omap3beagle_spi_board_info[0].irq = OMAP_GPIO_IRQ(OMAP3BEAGLE_GPIO_ENC28J60_IRQ); ++ set_irq_type(omap3beagle_spi_board_info[0].irq, IRQ_TYPE_EDGE_FALLING); ++ } else { ++ printk(KERN_ERR "could not obtain gpio for ENC28J60_IRQ\n"); ++ return; ++ } ++ ++ spi_register_board_info(omap3beagle_spi_board_info, ++ ARRAY_SIZE(omap3beagle_spi_board_info)); ++} ++ ++#else ++static inline void __init omap3beagle_enc28j60_init(void) { return; } ++#endif + + static struct mtd_partition omap3beagle_nand_partitions[] = { + /* All the partition sizes are listed in terms of NAND block size */ +@@ -318,7 +360,7 @@ + .vpll2 = &beagle_vpll2, + }; + +-static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = { ++static struct i2c_board_info __initdata beagle_i2c1_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, +@@ -327,10 +369,24 @@ + }, + }; + ++#if defined(CONFIG_RTC_DRV_DS1307) || \ ++ defined(CONFIG_RTC_DRV_DS1307_MODULE) ++ ++static struct i2c_board_info __initdata beagle_i2c2_boardinfo[] = { ++ { ++ I2C_BOARD_INFO("ds1307", 0x68), ++ }, ++}; ++#else ++static struct i2c_board_info __initdata beagle_i2c2_boardinfo[] = {}; ++#endif ++ + static int __init omap3_beagle_i2c_init(void) + { +- omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo, +- ARRAY_SIZE(beagle_i2c_boardinfo)); ++ omap_register_i2c_bus(1, 2600, beagle_i2c1_boardinfo, ++ ARRAY_SIZE(beagle_i2c1_boardinfo)); ++ omap_register_i2c_bus(2, 400, beagle_i2c2_boardinfo, ++ ARRAY_SIZE(beagle_i2c2_boardinfo)); + /* Bus 3 is attached to the DVI port where devices like the pico DLP + * projector don't work reliably with 400kHz */ + omap_register_i2c_bus(3, 100, NULL, 0); +@@ -542,6 +598,8 @@ + + omap_cfg_reg(J25_34XX_GPIO170); + ++ omap3beagle_enc28j60_init(); ++ + usb_musb_init(); + usb_ehci_init(); + omap3beagle_flash_init(); diff --git a/recipes/linux/linux-omap-pm-2.6.29/cache-display-fix.patch b/recipes/linux/linux-omap-pm-2.6.29/cache-display-fix.patch new file mode 100644 index 0000000000..019fd5acf1 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/cache-display-fix.patch @@ -0,0 +1,238 @@ +On Tue, 2008-07-01 at 06:23 +0100, Dirk Behme wrote: +> Catalin Marinas wrote: +> > But, anyway, if you want a patch, Harry is updating it to a recent +> > kernel. +> +> Any news on this? I think there are some people wanting a patch ;) + +See below for a preliminary patch updated to 2.6.26-rc8. Note that I +don't plan to submit it in its current form but clean it up a bit first. + + +Show the cache type of ARMv7 CPUs + +From: Catalin Marinas <catalin.marinas@arm.com> + +Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> +--- + + arch/arm/kernel/setup.c | 137 +++++++++++++++++++++++++++++++++++++++++++++- + include/asm-arm/system.h | 18 ++++++ + 2 files changed, 153 insertions(+), 2 deletions(-) + + +diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c +index 5ae0eb2..0cd238d 100644 +--- a/arch/arm/kernel/setup.c ++++ b/arch/arm/kernel/setup.c +@@ -256,6 +256,24 @@ static const char *proc_arch[] = { + "?(17)", + }; + ++static const char *v7_cache_policy[4] = { ++ "reserved", ++ "AVIVT", ++ "VIPT", ++ "PIPT", ++}; ++ ++static const char *v7_cache_type[8] = { ++ "none", ++ "instruction only", ++ "data only", ++ "separate instruction and data", ++ "unified", ++ "unknown type", ++ "unknown type", ++ "unknown type", ++}; ++ + #define CACHE_TYPE(x) (((x) >> 25) & 15) + #define CACHE_S(x) ((x) & (1 << 24)) + #define CACHE_DSIZE(x) (((x) >> 12) & 4095) /* only if S=1 */ +@@ -266,6 +284,22 @@ static const char *proc_arch[] = { + #define CACHE_M(y) ((y) & (1 << 2)) + #define CACHE_LINE(y) ((y) & 3) + ++#define CACHE_TYPE_V7(x) (((x) >> 14) & 3) ++#define CACHE_UNIFIED(x) ((((x) >> 27) & 7)+1) ++#define CACHE_COHERENT(x) ((((x) >> 24) & 7)+1) ++ ++#define CACHE_ID_LEVEL_MASK 7 ++#define CACHE_ID_LEVEL_BITS 3 ++ ++#define CACHE_LINE_V7(v) ((1 << (((v) & 7)+4))) ++#define CACHE_ASSOC_V7(v) ((((v) >> 3) & ((1<<10)-1))+1) ++#define CACHE_SETS_V7(v) ((((v) >> 13) & ((1<<15)-1))+1) ++#define CACHE_SIZE_V7(v) (CACHE_LINE_V7(v)*CACHE_ASSOC_V7(v)*CACHE_SETS_V7(v)) ++#define CACHE_WA_V7(v) (((v) & (1<<28)) != 0) ++#define CACHE_RA_V7(v) (((v) & (1<<29)) != 0) ++#define CACHE_WB_V7(v) (((v) & (1<<30)) != 0) ++#define CACHE_WT_V7(v) (((v) & (1<<31)) != 0) ++ + static inline void dump_cache(const char *prefix, int cpu, unsigned int cache) + { + unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0); +@@ -279,11 +313,57 @@ static inline void dump_cache(const char *prefix, int cpu, unsigned int cache) + CACHE_LINE(cache))); + } + ++static void dump_v7_cache(const char *type, int cpu, unsigned int level) ++{ ++ unsigned int cachesize; ++ ++ write_extended_cpuid(2,0,0,0,level); /* Set the cache size selection register */ ++ write_extended_cpuid(0,7,5,4,0); /* Prefetch flush to wait for above */ ++ cachesize = read_extended_cpuid(1,0,0,0); ++ ++ printk("CPU%u: %s cache: %d bytes, associativity %d, %d byte lines, %d sets,\n supports%s%s%s%s\n", ++ cpu, type, ++ CACHE_SIZE_V7(cachesize),CACHE_ASSOC_V7(cachesize), ++ CACHE_LINE_V7(cachesize),CACHE_SETS_V7(cachesize), ++ CACHE_WA_V7(cachesize) ? " WA" : "", ++ CACHE_RA_V7(cachesize) ? " RA" : "", ++ CACHE_WB_V7(cachesize) ? " WB" : "", ++ CACHE_WT_V7(cachesize) ? " WT" : ""); ++} ++ + static void __init dump_cpu_info(int cpu) + { + unsigned int info = read_cpuid(CPUID_CACHETYPE); + +- if (info != processor_id) { ++ if (info != processor_id && (info & (1 << 31))) { ++ /* ARMv7 style of cache info register */ ++ unsigned int id = read_extended_cpuid(1,0,0,1); ++ unsigned int level = 0; ++ printk("CPU%u: L1 I %s cache. Caches unified at level %u, coherent at level %u\n", ++ cpu, ++ v7_cache_policy[CACHE_TYPE_V7(info)], ++ CACHE_UNIFIED(id), ++ CACHE_COHERENT(id)); ++ ++ while (id & CACHE_ID_LEVEL_MASK) { ++ printk("CPU%u: Level %u cache is %s\n", ++ cpu, (level >> 1)+1, v7_cache_type[id & CACHE_ID_LEVEL_MASK]); ++ ++ if (id & 1) { ++ /* Dump I at this level */ ++ dump_v7_cache("I", cpu, level | 1); ++ } ++ ++ if (id & (4 | 2)) { ++ /* Dump D or unified at this level */ ++ dump_v7_cache((id & 4) ? "unified" : "D", cpu, level); ++ } ++ ++ /* Next level out */ ++ level += 2; ++ id >>= CACHE_ID_LEVEL_BITS; ++ } ++ } else if (info != processor_id) { + printk("CPU%u: D %s %s cache\n", cpu, cache_is_vivt() ? "VIVT" : "VIPT", + cache_types[CACHE_TYPE(info)]); + if (CACHE_S(info)) { +@@ -916,6 +996,30 @@ c_show_cache(struct seq_file *m, const char *type, unsigned int cache) + CACHE_LINE(cache))); + } + ++static void c_show_v7_cache(struct seq_file *m, const char *type, unsigned int levelselect) ++{ ++ unsigned int cachesize; ++ unsigned int level = (levelselect >> 1) + 1; ++ ++ write_extended_cpuid(2,0,0,0,levelselect); /* Set the cache size selection register */ ++ write_extended_cpuid(0,7,5,4,0); /* Prefetch flush to wait for above */ ++ cachesize = read_extended_cpuid(1,0,0,0); ++ ++ seq_printf(m, "L%u %s size\t\t: %d bytes\n" ++ "L%u %s assoc\t\t: %d\n" ++ "L%u %s line length\t: %d\n" ++ "L%u %s sets\t\t: %d\n" ++ "L%u %s supports\t\t:%s%s%s%s\n", ++ level, type, CACHE_SIZE_V7(cachesize), ++ level, type, CACHE_ASSOC_V7(cachesize), ++ level, type, CACHE_LINE_V7(cachesize), ++ level, type, CACHE_SETS_V7(cachesize), ++ level, type, CACHE_WA_V7(cachesize) ? " WA" : "", ++ CACHE_RA_V7(cachesize) ? " RA" : "", ++ CACHE_WB_V7(cachesize) ? " WB" : "", ++ CACHE_WT_V7(cachesize) ? " WT" : ""); ++} ++ + static int c_show(struct seq_file *m, void *v) + { + int i; +@@ -971,7 +1075,36 @@ static int c_show(struct seq_file *m, void *v) + + { + unsigned int cache_info = read_cpuid(CPUID_CACHETYPE); +- if (cache_info != processor_id) { ++ if (cache_info != processor_id && (cache_info & (1<<31))) { ++ /* V7 style of cache info register */ ++ unsigned int id = read_extended_cpuid(1,0,0,1); ++ unsigned int levelselect = 0; ++ seq_printf(m, "L1 I cache\t:%s\n" ++ "Cache unification level\t: %u\n" ++ "Cache coherency level\t: %u\n", ++ v7_cache_policy[CACHE_TYPE_V7(cache_info)], ++ CACHE_UNIFIED(id), ++ CACHE_COHERENT(id)); ++ ++ while (id & CACHE_ID_LEVEL_MASK) { ++ seq_printf(m, "Level %u cache\t\t: %s\n", ++ (levelselect >> 1)+1, v7_cache_type[id & CACHE_ID_LEVEL_MASK]); ++ ++ if (id & 1) { ++ /* Dump I at this level */ ++ c_show_v7_cache(m, "I", levelselect | 1); ++ } ++ ++ if (id & (4 | 2)) { ++ /* Dump D or unified at this level */ ++ c_show_v7_cache(m, (id & 4) ? "cache" : "D", levelselect); ++ } ++ ++ /* Next level out */ ++ levelselect += 2; ++ id >>= CACHE_ID_LEVEL_BITS; ++ } ++ } else if (cache_info != processor_id) { + seq_printf(m, "Cache type\t: %s\n" + "Cache clean\t: %s\n" + "Cache lockdown\t: %s\n" +diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h +index 514af79..704738e 100644 +--- a/arch/arm/include/asm/system.h ++++ b/arch/arm/include/asm/system.h +@@ -74,6 +74,24 @@ + : "cc"); \ + __val; \ + }) ++#define read_extended_cpuid(op1,op2,op3,op4) \ ++ ({ \ ++ unsigned int __val; \ ++ asm("mrc p15," __stringify(op1) ",%0,c" __stringify(op2)",c" __stringify(op3)"," __stringify(op4) \ ++ : "=r" (__val) \ ++ : \ ++ : "cc"); \ ++ __val; \ ++ }) ++ ++#define write_extended_cpuid(op1,op2,op3,op4,v) \ ++ ({ \ ++ unsigned int __val = v; \ ++ asm("mcr p15," __stringify(op1) ",%0,c" __stringify(op2)",c" __stringify(op3)"," __stringify(op4) \ ++ : \ ++ : "r" (__val) \ ++ : "cc"); \ ++ }) + #else + extern unsigned int processor_id; + #define read_cpuid(reg) (processor_id) + + +-- +Catalin + + diff --git a/recipes/linux/linux-omap-pm-2.6.29/fix-audio-capture.patch b/recipes/linux/linux-omap-pm-2.6.29/fix-audio-capture.patch index 8aaac57b5f..c5ff914757 100644 --- a/recipes/linux/linux-omap-pm-2.6.29/fix-audio-capture.patch +++ b/recipes/linux/linux-omap-pm-2.6.29/fix-audio-capture.patch @@ -14,38 +14,3 @@ index ee2f0d3..8b4aafb 100644 0x00, /* REG_ADCMICSEL (0x8) */ 0x00, /* REG_DIGMIXING (0x9) */ ---- /tmp/Kconfig 2009-06-20 13:13:21.000000000 +0200 -+++ git/sound/soc/omap/Kconfig 2009-06-20 13:14:02.000000000 +0200 -@@ -15,6 +15,14 @@ - help - Say Y if you want to add support for SoC audio on Nokia N810. - -+config SND_OMAP_SOC_OMAP3_BEAGLE -+ tristate "SoC Audio support for OMAP3 Beagle" -+ depends on SND_OMAP_SOC && MACH_OMAP3_BEAGLE -+ select SND_OMAP_SOC_MCBSP -+ select SND_SOC_TWL4030 -+ help -+ Say Y if you want to add support for SoC audio on the Beagleboard. -+ - config SND_OMAP_SOC_OSK5912 - tristate "SoC Audio support for omap osk5912" - depends on SND_OMAP_SOC && MACH_OMAP_OSK ---- /tmp/Makefile 2009-06-23 08:37:40.000000000 +0200 -+++ git/sound/soc/omap/Makefile 2009-06-23 08:38:38.000000000 +0200 -@@ -7,6 +7,7 @@ - - # OMAP Machine Support - snd-soc-n810-objs := n810.o -+snd-soc-omap3beagle-objs := omap3beagle.o - snd-soc-osk5912-objs := osk5912.o - snd-soc-overo-objs := overo.o - snd-soc-omap2evm-objs := omap2evm.o -@@ -14,6 +15,7 @@ - snd-soc-omap3pandora-objs := omap3pandora.o - - obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o -+obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o - obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o - obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o - obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o diff --git a/recipes/linux/linux-omap-pm-2.6.29/fix-unaligned-access.diff b/recipes/linux/linux-omap-pm-2.6.29/fix-unaligned-access.diff new file mode 100644 index 0000000000..c82090f54a --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/fix-unaligned-access.diff @@ -0,0 +1,41 @@ +From: Mans Rullgard <mans@mansr.com> +Date: Sat, 28 Mar 2009 12:54:25 +0000 (+0000) +Subject: NSM: Fix unaligned accesses in nsm_init_private() +X-Git-Url: http://git.mansr.com/?p=linux-omap;a=commitdiff_plain;h=8f2bd6fdde1ebfef57f65b6cf29b29008c23d297 + +NSM: Fix unaligned accesses in nsm_init_private() + +This fixes unaligned accesses in nsm_init_private() when +creating nlm_reboot keys. + +Signed-off-by: Mans Rullgard <mans@mansr.com> +--- + +diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c +index 5e2c4d5..6d5d4a4 100644 +--- a/fs/lockd/mon.c ++++ b/fs/lockd/mon.c +@@ -16,6 +16,8 @@ + #include <linux/sunrpc/svc.h> + #include <linux/lockd/lockd.h> + ++#include <asm/unaligned.h> ++ + #define NLMDBG_FACILITY NLMDBG_MONITOR + #define NSM_PROGRAM 100024 + #define NSM_VERSION 1 +@@ -274,10 +276,12 @@ static void nsm_init_private(struct nsm_handle *nsm) + { + u64 *p = (u64 *)&nsm->sm_priv.data; + struct timespec ts; ++ s64 ns; + + ktime_get_ts(&ts); +- *p++ = timespec_to_ns(&ts); +- *p = (unsigned long)nsm; ++ ns = timespec_to_ns(&ts); ++ put_unaligned(ns, p); ++ put_unaligned((unsigned long)nsm, p + 1); + } + + static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap, diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0001-omap-iommu-tlb-and-pagetable-primitives.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0001-omap-iommu-tlb-and-pagetable-primitives.patch new file mode 100644 index 0000000000..c2c9bc2b62 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0001-omap-iommu-tlb-and-pagetable-primitives.patch @@ -0,0 +1,1226 @@ +From a62a047ed02162573e4bece18ecf8bdd66ccd06b Mon Sep 17 00:00:00 2001 +From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +Date: Mon, 26 Jan 2009 15:13:40 +0200 +Subject: [PATCH] omap iommu: tlb and pagetable primitives + +This patch provides: + +- iotlb_*() : iommu tlb operations +- iopgtable_*() : iommu pagetable(twl) operations +- iommu_*() : the other generic operations + +and the entry points to register and acquire iommu object. + +Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +--- + arch/arm/plat-omap/include/mach/iommu.h | 157 +++++ + arch/arm/plat-omap/iommu.c | 953 +++++++++++++++++++++++++++++++ + arch/arm/plat-omap/iopgtable.h | 72 +++ + 3 files changed, 1182 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/plat-omap/include/mach/iommu.h + create mode 100644 arch/arm/plat-omap/iommu.c + create mode 100644 arch/arm/plat-omap/iopgtable.h + +diff --git a/arch/arm/plat-omap/include/mach/iommu.h b/arch/arm/plat-omap/include/mach/iommu.h +new file mode 100644 +index 0000000..ef04d7a +--- /dev/null ++++ b/arch/arm/plat-omap/include/mach/iommu.h +@@ -0,0 +1,157 @@ ++/* ++ * omap iommu: main structures ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ++ * ++ * 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 __MACH_IOMMU_H ++#define __MACH_IOMMU_H ++ ++struct iotlb_entry { ++ u32 da; ++ u32 pa; ++ u32 pgsz, prsvd, valid; ++ union { ++ u16 ap; ++ struct { ++ u32 endian, elsz, mixed; ++ }; ++ }; ++}; ++ ++struct iommu { ++ const char *name; ++ struct module *owner; ++ struct clk *clk; ++ void __iomem *regbase; ++ struct device *dev; ++ ++ unsigned int refcount; ++ struct mutex iommu_lock; /* global for this whole object */ ++ ++ /* ++ * We don't change iopgd for a situation like pgd for a task, ++ * but share it globally for each iommu. ++ */ ++ u32 *iopgd; ++ spinlock_t page_table_lock; /* protect iopgd */ ++ ++ int nr_tlb_entries; ++ ++ struct list_head mmap; ++ struct mutex mmap_lock; /* protect mmap */ ++ ++ int (*isr)(struct iommu *obj); ++ ++ void *ctx; /* iommu context: registres saved area */ ++}; ++ ++struct cr_regs { ++ union { ++ struct { ++ u16 cam_l; ++ u16 cam_h; ++ }; ++ u32 cam; ++ }; ++ union { ++ struct { ++ u16 ram_l; ++ u16 ram_h; ++ }; ++ u32 ram; ++ }; ++}; ++ ++struct iotlb_lock { ++ short base; ++ short vict; ++}; ++ ++/* architecture specific functions */ ++struct iommu_functions { ++ unsigned long version; ++ ++ int (*enable)(struct iommu *obj); ++ void (*disable)(struct iommu *obj); ++ u32 (*fault_isr)(struct iommu *obj, u32 *ra); ++ ++ void (*tlb_read_cr)(struct iommu *obj, struct cr_regs *cr); ++ void (*tlb_load_cr)(struct iommu *obj, struct cr_regs *cr); ++ ++ struct cr_regs *(*alloc_cr)(struct iommu *obj, struct iotlb_entry *e); ++ int (*cr_valid)(struct cr_regs *cr); ++ u32 (*cr_to_virt)(struct cr_regs *cr); ++ void (*cr_to_e)(struct cr_regs *cr, struct iotlb_entry *e); ++ ssize_t (*dump_cr)(struct iommu *obj, struct cr_regs *cr, char *buf); ++ ++ u32 (*get_pte_attr)(struct iotlb_entry *e); ++ ++ void (*save_ctx)(struct iommu *obj); ++ void (*restore_ctx)(struct iommu *obj); ++ ssize_t (*dump_ctx)(struct iommu *obj, char *buf); ++}; ++ ++struct iommu_platform_data { ++ const char *name; ++ const char *clk_name; ++ const int nr_tlb_entries; ++}; ++ ++#include <mach/iommu2.h> ++ ++/* ++ * utilities for super page(16MB, 1MB, 64KB and 4KB) ++ */ ++ ++#define iopgsz_max(bytes) \ ++ (((bytes) >= SZ_16M) ? SZ_16M : \ ++ ((bytes) >= SZ_1M) ? SZ_1M : \ ++ ((bytes) >= SZ_64K) ? SZ_64K : \ ++ ((bytes) >= SZ_4K) ? SZ_4K : 0) ++ ++#define bytes_to_iopgsz(bytes) \ ++ (((bytes) == SZ_16M) ? MMU_CAM_PGSZ_16M : \ ++ ((bytes) == SZ_1M) ? MMU_CAM_PGSZ_1M : \ ++ ((bytes) == SZ_64K) ? MMU_CAM_PGSZ_64K : \ ++ ((bytes) == SZ_4K) ? MMU_CAM_PGSZ_4K : -1) ++ ++#define iopgsz_to_bytes(iopgsz) \ ++ (((iopgsz) == MMU_CAM_PGSZ_16M) ? SZ_16M : \ ++ ((iopgsz) == MMU_CAM_PGSZ_1M) ? SZ_1M : \ ++ ((iopgsz) == MMU_CAM_PGSZ_64K) ? SZ_64K : \ ++ ((iopgsz) == MMU_CAM_PGSZ_4K) ? SZ_4K : 0) ++ ++#define iopgsz_ok(bytes) (bytes_to_iopgsz(bytes) >= 0) ++ ++/* ++ * global functions ++ */ ++extern u32 iommu_arch_version(void); ++ ++extern int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e); ++extern void flush_iotlb_page(struct iommu *obj, u32 da); ++extern void flush_iotlb_range(struct iommu *obj, u32 start, u32 end); ++extern void flush_iotlb_all(struct iommu *obj); ++ ++ssize_t iotlb_dump_cr(struct iommu *obj, struct cr_regs *cr, char *buf); ++ ++extern int iopgtable_store_entry(struct iommu *obj, struct iotlb_entry *e); ++extern size_t iopgtable_clear_entry(struct iommu *obj, u32 iova); ++ ++extern struct iommu *iommu_get(const char *name); ++extern void iommu_put(struct iommu *obj); ++ ++extern void iommu_save_ctx(struct iommu *obj); ++extern void iommu_restore_ctx(struct iommu *obj); ++ ++extern int install_iommu_arch(const struct iommu_functions *ops); ++extern void uninstall_iommu_arch(const struct iommu_functions *ops); ++ ++#endif /* __MACH_IOMMU_H */ +diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c +new file mode 100644 +index 0000000..e638883 +--- /dev/null ++++ b/arch/arm/plat-omap/iommu.c +@@ -0,0 +1,953 @@ ++/* ++ * omap iommu: tlb and pagetable primitives ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>, ++ * Paul Mundt and Toshihiro Kobayashi ++ * ++ * 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 <linux/err.h> ++#include <linux/module.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/clk.h> ++#include <linux/platform_device.h> ++ ++#include <asm/io.h> ++#include <asm/cacheflush.h> ++ ++#include <mach/clock.h> ++#include <mach/iommu.h> ++ ++#include "iopgtable.h" ++ ++/* accommodate the difference between omap1 and omap2/3 */ ++static const struct iommu_functions *arch_iommu; ++ ++static struct platform_driver omap_iommu_driver; ++static struct kmem_cache *iopte_cachep; ++ ++/** ++ * install_iommu_arch() - Install archtecure specific iommu functions ++ * @ops: a pointer to architecture specific iommu functions ++ * ++ * There are several kind of iommu algorithm(tlb, pagetable) among ++ * omap series. This interface installs such an iommu algorighm. ++ **/ ++int install_iommu_arch(const struct iommu_functions *ops) ++{ ++ if (arch_iommu) ++ return -EBUSY; ++ ++ arch_iommu = ops; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(install_iommu_arch); ++ ++/** ++ * uninstall_iommu_arch() - Uninstall archtecure specific iommu functions ++ * @ops: a pointer to architecture specific iommu functions ++ * ++ * This interface uninstalls the iommu algorighm installed previously. ++ **/ ++void uninstall_iommu_arch(const struct iommu_functions *ops) ++{ ++ if (arch_iommu != ops) ++ pr_err("%s: not your arch\n", __func__); ++ ++ arch_iommu = NULL; ++} ++EXPORT_SYMBOL_GPL(uninstall_iommu_arch); ++ ++/** ++ * iommu_save_ctx() - Save registers for pm off-mode support ++ * @obj: target iommu ++ **/ ++void iommu_save_ctx(struct iommu *obj) ++{ ++ arch_iommu->save_ctx(obj); ++} ++EXPORT_SYMBOL_GPL(iommu_save_ctx); ++ ++/** ++ * iommu_restore_ctx() - Restore registers for pm off-mode support ++ * @obj: target iommu ++ **/ ++void iommu_restore_ctx(struct iommu *obj) ++{ ++ arch_iommu->restore_ctx(obj); ++} ++EXPORT_SYMBOL_GPL(iommu_restore_ctx); ++ ++/** ++ * iommu_arch_version() - Return running iommu arch version ++ **/ ++u32 iommu_arch_version(void) ++{ ++ return arch_iommu->version; ++} ++EXPORT_SYMBOL_GPL(iommu_arch_version); ++ ++static int iommu_enable(struct iommu *obj) ++{ ++ int err; ++ ++ if (!obj) ++ return -EINVAL; ++ ++ clk_enable(obj->clk); ++ ++ err = arch_iommu->enable(obj); ++ ++ clk_disable(obj->clk); ++ return err; ++} ++ ++static void iommu_disable(struct iommu *obj) ++{ ++ if (!obj) ++ return; ++ ++ clk_enable(obj->clk); ++ ++ arch_iommu->disable(obj); ++ ++ clk_disable(obj->clk); ++} ++ ++#ifdef DEBUG ++static ssize_t iommu_dump_ctx(struct iommu *obj, char *buf) ++{ ++ if (!obj || !buf) ++ return -EINVAL; ++ ++ return arch_iommu->dump_ctx(obj, buf); ++} ++#endif ++ ++/* ++ * TLB operations ++ */ ++static inline void iotlb_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e) ++{ ++ BUG_ON(!cr || !e); ++ ++ arch_iommu->cr_to_e(cr, e); ++} ++ ++static inline int iotlb_cr_valid(struct cr_regs *cr) ++{ ++ if (!cr) ++ return -EINVAL; ++ ++ return arch_iommu->cr_valid(cr); ++} ++ ++static inline struct cr_regs *iotlb_alloc_cr(struct iommu *obj, ++ struct iotlb_entry *e) ++{ ++ if (!e) ++ return NULL; ++ ++ return arch_iommu->alloc_cr(obj, e); ++} ++ ++static inline u32 iotlb_cr_to_virt(struct cr_regs *cr) ++{ ++ return arch_iommu->cr_to_virt(cr); ++} ++ ++static u32 get_iopte_attr(struct iotlb_entry *e) ++{ ++ return arch_iommu->get_pte_attr(e); ++} ++ ++static u32 iommu_report_fault(struct iommu *obj, u32 *da) ++{ ++ return arch_iommu->fault_isr(obj, da); ++} ++ ++static void iotlb_lock_get(struct iommu *obj, struct iotlb_lock *l) ++{ ++ u32 val; ++ ++ val = iommu_read_reg(obj, MMU_LOCK); ++ ++ l->base = MMU_LOCK_BASE(val); ++ l->vict = MMU_LOCK_VICT(val); ++ ++ BUG_ON(l->base != 0); /* Currently no preservation is used */ ++} ++ ++static void iotlb_lock_set(struct iommu *obj, struct iotlb_lock *l) ++{ ++ u32 val; ++ ++ BUG_ON(l->base != 0); /* Currently no preservation is used */ ++ ++ val = (l->base << MMU_LOCK_BASE_SHIFT); ++ val |= (l->vict << MMU_LOCK_VICT_SHIFT); ++ ++ iommu_write_reg(obj, val, MMU_LOCK); ++} ++ ++static void iotlb_read_cr(struct iommu *obj, struct cr_regs *cr) ++{ ++ arch_iommu->tlb_read_cr(obj, cr); ++} ++ ++static void iotlb_load_cr(struct iommu *obj, struct cr_regs *cr) ++{ ++ arch_iommu->tlb_load_cr(obj, cr); ++ ++ iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY); ++ iommu_write_reg(obj, 1, MMU_LD_TLB); ++} ++ ++/** ++ * iotlb_dump_cr() - Dump an iommu tlb entry into buf ++ * @obj: target iommu ++ * @cr: contents of cam and ram register ++ * @buf: output buffer ++ **/ ++ssize_t iotlb_dump_cr(struct iommu *obj, struct cr_regs *cr, char *buf) ++{ ++ BUG_ON(!cr || !buf); ++ ++ return arch_iommu->dump_cr(obj, cr, buf); ++} ++EXPORT_SYMBOL_GPL(iotlb_dump_cr); ++ ++/** ++ * load_iotlb_entry() - Set an iommu tlb entry ++ * @obj: target iommu ++ * @e: an iommu tlb entry info ++ **/ ++int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e) ++{ ++ int i; ++ int err = 0; ++ struct iotlb_lock l; ++ struct cr_regs *cr; ++ ++ if (!obj || !obj->nr_tlb_entries || !e) ++ return -EINVAL; ++ ++ clk_enable(obj->clk); ++ ++ for (i = 0; i < obj->nr_tlb_entries; i++) { ++ struct cr_regs tmp; ++ ++ iotlb_lock_get(obj, &l); ++ l.vict = i; ++ iotlb_lock_set(obj, &l); ++ iotlb_read_cr(obj, &tmp); ++ if (!iotlb_cr_valid(&tmp)) ++ break; ++ } ++ ++ if (i == obj->nr_tlb_entries) { ++ dev_dbg(obj->dev, "%s: full: no entry\n", __func__); ++ err = -EBUSY; ++ goto out; ++ } ++ ++ cr = iotlb_alloc_cr(obj, e); ++ if (IS_ERR(cr)) { ++ clk_disable(obj->clk); ++ return PTR_ERR(cr); ++ } ++ ++ iotlb_load_cr(obj, cr); ++ kfree(cr); ++ ++ /* increment victim for next tlb load */ ++ if (++l.vict == obj->nr_tlb_entries) ++ l.vict = 0; ++ iotlb_lock_set(obj, &l); ++out: ++ clk_disable(obj->clk); ++ return err; ++} ++EXPORT_SYMBOL_GPL(load_iotlb_entry); ++ ++/** ++ * flush_iotlb_page() - Clear an iommu tlb entry ++ * @obj: target iommu ++ * @da: iommu device virtual address ++ * ++ * Clear an iommu tlb entry which includes 'da' address. ++ **/ ++void flush_iotlb_page(struct iommu *obj, u32 da) ++{ ++ struct iotlb_lock l; ++ int i; ++ ++ clk_enable(obj->clk); ++ ++ for (i = 0; i < obj->nr_tlb_entries; i++) { ++ struct cr_regs cr; ++ u32 start; ++ size_t bytes; ++ ++ iotlb_lock_get(obj, &l); ++ l.vict = i; ++ iotlb_lock_set(obj, &l); ++ iotlb_read_cr(obj, &cr); ++ if (!iotlb_cr_valid(&cr)) ++ continue; ++ ++ start = iotlb_cr_to_virt(&cr); ++ bytes = iopgsz_to_bytes(cr.cam & 3); ++ ++ if ((start <= da) && (da < start + bytes)) { ++ dev_dbg(obj->dev, "%s: %08x<=%08x(%x)\n", ++ __func__, start, da, bytes); ++ ++ iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY); ++ } ++ } ++ clk_disable(obj->clk); ++ ++ if (i == obj->nr_tlb_entries) ++ dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da); ++} ++EXPORT_SYMBOL_GPL(flush_iotlb_page); ++ ++/** ++ * flush_iotlb_range() - Clear an iommu tlb entries ++ * @obj: target iommu ++ * @start: iommu device virtual address(start) ++ * @end: iommu device virtual address(end) ++ * ++ * Clear an iommu tlb entry which includes 'da' address. ++ **/ ++void flush_iotlb_range(struct iommu *obj, u32 start, u32 end) ++{ ++ u32 da = start; ++ ++ while (da < end) { ++ flush_iotlb_page(obj, da); ++ /* FIXME: Optimize for multiple page size */ ++ da += IOPTE_SIZE; ++ } ++} ++EXPORT_SYMBOL_GPL(flush_iotlb_range); ++ ++/** ++ * flush_iotlb_all() - Clear all iommu tlb entries ++ * @obj: target iommu ++ **/ ++void flush_iotlb_all(struct iommu *obj) ++{ ++ struct iotlb_lock l; ++ ++ clk_enable(obj->clk); ++ ++ l.base = 0; ++ l.vict = 0; ++ iotlb_lock_set(obj, &l); ++ ++ iommu_write_reg(obj, 1, MMU_GFLUSH); ++ ++ clk_disable(obj->clk); ++} ++EXPORT_SYMBOL_GPL(flush_iotlb_all); ++ ++/* ++ * H/W pagetable operations ++ */ ++static void flush_iopgd_range(u32 *first, u32 *last) ++{ ++ /* FIXME: L2 cache should be taken care of if it exists */ ++ do { ++ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd" ++ : : "r" (first)); ++ first += L1_CACHE_BYTES / sizeof(*first); ++ } while (first <= last); ++} ++ ++static void flush_iopte_range(u32 *first, u32 *last) ++{ ++ /* FIXME: L2 cache should be taken care of if it exists */ ++ do { ++ asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte" ++ : : "r" (first)); ++ first += L1_CACHE_BYTES / sizeof(*first); ++ } while (first <= last); ++} ++ ++static void iopte_free(u32 *iopte) ++{ ++ /* Note: freed iopte's must be clean ready for re-use */ ++ kmem_cache_free(iopte_cachep, iopte); ++} ++ ++static u32 *iopte_alloc(struct iommu *obj, u32 *iopgd, u32 da) ++{ ++ u32 *iopte; ++ ++ /* a table has already existed */ ++ if (*iopgd) ++ goto pte_ready; ++ ++ /* ++ * do the allocation outside the page table lock ++ */ ++ spin_unlock(&obj->page_table_lock); ++ iopte = kmem_cache_zalloc(iopte_cachep, GFP_KERNEL); ++ spin_lock(&obj->page_table_lock); ++ ++ if (!*iopgd) { ++ if (!iopte) ++ return ERR_PTR(-ENOMEM); ++ ++ *iopgd = virt_to_phys(iopte) | IOPGD_TABLE; ++ flush_iopgd_range(iopgd, iopgd); ++ ++ dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte); ++ } else { ++ /* We raced, free the reduniovant table */ ++ iopte_free(iopte); ++ } ++ ++pte_ready: ++ iopte = iopte_offset(iopgd, da); ++ ++ dev_vdbg(obj->dev, ++ "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n", ++ __func__, da, iopgd, *iopgd, iopte, *iopte); ++ ++ return iopte; ++} ++ ++static int iopgd_alloc_section(struct iommu *obj, u32 da, u32 pa, u32 prot) ++{ ++ u32 *iopgd = iopgd_offset(obj, da); ++ ++ *iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION; ++ flush_iopgd_range(iopgd, iopgd); ++ return 0; ++} ++ ++static int iopgd_alloc_super(struct iommu *obj, u32 da, u32 pa, u32 prot) ++{ ++ u32 *iopgd = iopgd_offset(obj, da); ++ int i; ++ ++ for (i = 0; i < 16; i++) ++ *(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER; ++ flush_iopgd_range(iopgd, iopgd + 15); ++ return 0; ++} ++ ++static int iopte_alloc_page(struct iommu *obj, u32 da, u32 pa, u32 prot) ++{ ++ u32 *iopgd = iopgd_offset(obj, da); ++ u32 *iopte = iopte_alloc(obj, iopgd, da); ++ ++ if (IS_ERR(iopte)) ++ return PTR_ERR(iopte); ++ ++ *iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL; ++ flush_iopte_range(iopte, iopte); ++ ++ dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n", ++ __func__, da, pa, iopte, *iopte); ++ ++ return 0; ++} ++ ++static int iopte_alloc_large(struct iommu *obj, u32 da, u32 pa, u32 prot) ++{ ++ u32 *iopgd = iopgd_offset(obj, da); ++ u32 *iopte = iopte_alloc(obj, iopgd, da); ++ int i; ++ ++ if (IS_ERR(iopte)) ++ return PTR_ERR(iopte); ++ ++ for (i = 0; i < 16; i++) ++ *(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE; ++ flush_iopte_range(iopte, iopte + 15); ++ return 0; ++} ++ ++static int iopgtable_store_entry_core(struct iommu *obj, struct iotlb_entry *e) ++{ ++ int (*fn)(struct iommu *, u32, u32, u32); ++ u32 prot; ++ int err; ++ ++ if (!obj || !e) ++ return -EINVAL; ++ ++ switch (e->pgsz) { ++ case MMU_CAM_PGSZ_16M: ++ fn = iopgd_alloc_super; ++ break; ++ case MMU_CAM_PGSZ_1M: ++ fn = iopgd_alloc_section; ++ break; ++ case MMU_CAM_PGSZ_64K: ++ fn = iopte_alloc_large; ++ break; ++ case MMU_CAM_PGSZ_4K: ++ fn = iopte_alloc_page; ++ break; ++ default: ++ fn = NULL; ++ BUG(); ++ break; ++ } ++ ++ prot = get_iopte_attr(e); ++ ++ spin_lock(&obj->page_table_lock); ++ err = fn(obj, e->da, e->pa, prot); ++ spin_unlock(&obj->page_table_lock); ++ ++ return err; ++} ++ ++#ifdef DEBUG ++static void dump_tlb_entries(struct iommu *obj) ++{ ++ int i; ++ struct iotlb_lock l; ++ ++ clk_enable(obj->clk); ++ ++ pr_info("%8s %8s\n", "cam:", "ram:"); ++ pr_info("-----------------------------------------\n"); ++ ++ for (i = 0; i < obj->nr_tlb_entries; i++) { ++ struct cr_regs cr; ++ static char buf[4096]; ++ ++ iotlb_lock_get(obj, &l); ++ l.vict = i; ++ iotlb_lock_set(obj, &l); ++ iotlb_read_cr(obj, &cr); ++ if (!iotlb_cr_valid(&cr)) ++ continue; ++ ++ memset(buf, 0, 4096); ++ iotlb_dump_cr(obj, &cr, buf); ++ pr_err("%s", buf); ++ } ++ ++ clk_disable(obj->clk); ++} ++#else ++static inline void dump_tlb_entries(struct iommu *obj) {} ++#endif ++ ++/** ++ * iopgtable_store_entry() - Make an iommu pte entry ++ * @obj: target iommu ++ * @e: an iommu tlb entry info ++ **/ ++int iopgtable_store_entry(struct iommu *obj, struct iotlb_entry *e) ++{ ++ int err; ++ ++ flush_iotlb_page(obj, e->da); ++ err = iopgtable_store_entry_core(obj, e); ++#ifdef USE_IOTLB ++ if (!err) ++ load_iotlb_entry(obj, e); ++#endif ++ return err; ++} ++EXPORT_SYMBOL_GPL(iopgtable_store_entry); ++ ++/** ++ * iopgtable_lookup_entry() - Lookup an iommu pte entry ++ * @obj: target iommu ++ * @da: iommu device virtual address ++ * @ppgd: iommu pgd entry pointer to be returned ++ * @ppte: iommu pte entry pointer to be returned ++ **/ ++void iopgtable_lookup_entry(struct iommu *obj, u32 da, u32 **ppgd, u32 **ppte) ++{ ++ u32 *iopgd, *iopte = NULL; ++ ++ iopgd = iopgd_offset(obj, da); ++ if (!*iopgd) ++ goto out; ++ ++ if (*iopgd & IOPGD_TABLE) ++ iopte = iopte_offset(iopgd, da); ++out: ++ *ppgd = iopgd; ++ *ppte = iopte; ++} ++EXPORT_SYMBOL_GPL(iopgtable_lookup_entry); ++ ++static size_t iopgtable_clear_entry_core(struct iommu *obj, u32 da) ++{ ++ size_t bytes; ++ u32 *iopgd = iopgd_offset(obj, da); ++ int nent = 1; ++ ++ if (!*iopgd) ++ return 0; ++ ++ if (*iopgd & IOPGD_TABLE) { ++ int i; ++ u32 *iopte = iopte_offset(iopgd, da); ++ ++ bytes = IOPTE_SIZE; ++ if (*iopte & IOPTE_LARGE) { ++ nent *= 16; ++ /* rewind to the 1st entry */ ++ iopte = (u32 *)((u32)iopte & IOLARGE_MASK); ++ } ++ bytes *= nent; ++ memset(iopte, 0, nent * sizeof(*iopte)); ++ flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte)); ++ ++ /* ++ * do table walk to check if this table is necessary or not ++ */ ++ iopte = iopte_offset(iopgd, 0); ++ for (i = 0; i < PTRS_PER_IOPTE; i++) ++ if (iopte[i]) ++ goto out; ++ ++ iopte_free(iopte); ++ nent = 1; /* for the next L1 entry */ ++ } else { ++ bytes = IOPGD_SIZE; ++ if (*iopgd & IOPGD_SUPER) { ++ nent *= 16; ++ /* rewind to the 1st entry */ ++ iopgd = (u32 *)((u32)iopgd & IOSUPER_MASK); ++ } ++ bytes *= nent; ++ } ++ memset(iopgd, 0, nent * sizeof(*iopgd)); ++ flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd)); ++out: ++ return bytes; ++} ++ ++/** ++ * iopgtable_clear_entry() - Remove an iommu pte entry ++ * @obj: target iommu ++ * @da: iommu device virtual address ++ **/ ++size_t iopgtable_clear_entry(struct iommu *obj, u32 da) ++{ ++ size_t bytes; ++ ++ spin_lock(&obj->page_table_lock); ++ ++ bytes = iopgtable_clear_entry_core(obj, da); ++ flush_iotlb_page(obj, da); ++ ++ spin_unlock(&obj->page_table_lock); ++ ++ return bytes; ++} ++EXPORT_SYMBOL_GPL(iopgtable_clear_entry); ++ ++static void iopgtable_clear_entry_all(struct iommu *obj) ++{ ++ int i; ++ ++ spin_lock(&obj->page_table_lock); ++ ++ for (i = 0; i < PTRS_PER_IOPGD; i++) { ++ u32 da; ++ u32 *iopgd; ++ ++ da = i << IOPGD_SHIFT; ++ iopgd = iopgd_offset(obj, da); ++ ++ if (!*iopgd) ++ continue; ++ ++ if (*iopgd & IOPGD_TABLE) ++ iopte_free(iopte_offset(iopgd, 0)); ++ ++ *iopgd = 0; ++ flush_iopgd_range(iopgd, iopgd); ++ } ++ ++ flush_iotlb_all(obj); ++ ++ spin_unlock(&obj->page_table_lock); ++} ++ ++/* ++ * Device IOMMU generic operations ++ */ ++static irqreturn_t iommu_fault_handler(int irq, void *data) ++{ ++ u32 stat, da; ++ u32 *iopgd, *iopte; ++ int err = -EIO; ++ struct iommu *obj = data; ++ ++ /* Dynamic loading TLB or PTE */ ++ if (obj->isr) ++ err = obj->isr(obj); ++ ++ if (!err) ++ return IRQ_HANDLED; ++ ++ stat = iommu_report_fault(obj, &da); ++ if (!stat) ++ return IRQ_HANDLED; ++ ++ iopgd = iopgd_offset(obj, da); ++ ++ if (!(*iopgd & IOPGD_TABLE)) { ++ dev_err(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x\n", __func__, ++ da, iopgd, *iopgd); ++ return IRQ_NONE; ++ } ++ ++ iopte = iopte_offset(iopgd, da); ++ ++ dev_err(obj->dev, "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n", ++ __func__, da, iopgd, *iopgd, iopte, *iopte); ++ ++ dump_tlb_entries(obj); ++ ++ return IRQ_NONE; ++} ++ ++static int device_match_by_alias(struct device *dev, void *data) ++{ ++ struct iommu *obj = to_iommu(dev); ++ const char *name = data; ++ ++ pr_debug("%s: %s %s\n", __func__, obj->name, name); ++ ++ return strcmp(obj->name, name) == 0; ++} ++ ++/** ++ * iommu_put() - Get iommu handler ++ * @name: target iommu name ++ **/ ++struct iommu *iommu_get(const char *name) ++{ ++ int err = -ENOMEM; ++ struct device *dev; ++ struct iommu *obj; ++ ++ dev = driver_find_device(&omap_iommu_driver.driver, NULL, (void *)name, ++ device_match_by_alias); ++ if (!dev) ++ return ERR_PTR(-ENODEV); ++ ++ obj = to_iommu(dev); ++ ++ mutex_lock(&obj->iommu_lock); ++ ++ if (obj->refcount++ == 0) { ++ err = iommu_enable(obj); ++ if (err) ++ goto err_enable; ++ flush_iotlb_all(obj); ++ } ++ ++ if (!try_module_get(obj->owner)) ++ goto err_module; ++ ++ mutex_unlock(&obj->iommu_lock); ++ ++ dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); ++ return obj; ++ ++err_module: ++ if (obj->refcount == 1) ++ iommu_disable(obj); ++err_enable: ++ mutex_unlock(&obj->iommu_lock); ++ return ERR_PTR(err); ++} ++EXPORT_SYMBOL_GPL(iommu_get); ++ ++/** ++ * iommu_put() - Put back iommu handler ++ * @obj: target iommu ++ **/ ++void iommu_put(struct iommu *obj) ++{ ++ if (!obj && IS_ERR(obj)) ++ return; ++ ++ mutex_lock(&obj->iommu_lock); ++ ++ if (--obj->refcount == 0) ++ iommu_disable(obj); ++ ++ module_put(obj->owner); ++ ++ mutex_unlock(&obj->iommu_lock); ++ ++ dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name); ++} ++EXPORT_SYMBOL_GPL(iommu_put); ++ ++/* ++ * OMAP Device MMU(IOMMU) detection ++ */ ++static int __devinit omap_iommu_probe(struct platform_device *pdev) ++{ ++ int err = -ENODEV; ++ void *p; ++ int irq; ++ struct iommu *obj; ++ struct resource *res; ++ struct iommu_platform_data *pdata = pdev->dev.platform_data; ++ ++ if (pdev->num_resources != 2) ++ return -EINVAL; ++ ++ obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL); ++ if (!obj) ++ return -ENOMEM; ++ ++ obj->clk = clk_get(&pdev->dev, pdata->clk_name); ++ if (IS_ERR(obj->clk)) ++ goto err_clk; ++ ++ obj->nr_tlb_entries = pdata->nr_tlb_entries; ++ obj->name = pdata->name; ++ obj->dev = &pdev->dev; ++ obj->ctx = (void *)obj + sizeof(*obj); ++ ++ mutex_init(&obj->iommu_lock); ++ mutex_init(&obj->mmap_lock); ++ spin_lock_init(&obj->page_table_lock); ++ INIT_LIST_HEAD(&obj->mmap); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ err = -ENODEV; ++ goto err_mem; ++ } ++ obj->regbase = ioremap(res->start, resource_size(res)); ++ if (!obj->regbase) { ++ err = -ENOMEM; ++ goto err_mem; ++ } ++ ++ res = request_mem_region(res->start, resource_size(res), ++ dev_name(&pdev->dev)); ++ if (!res) { ++ err = -EIO; ++ goto err_mem; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ err = -ENODEV; ++ goto err_irq; ++ } ++ err = request_irq(irq, iommu_fault_handler, IRQF_SHARED, ++ dev_name(&pdev->dev), obj); ++ if (err < 0) ++ goto err_irq; ++ platform_set_drvdata(pdev, obj); ++ ++ p = (void *)__get_free_pages(GFP_KERNEL, get_order(IOPGD_TABLE_SIZE)); ++ if (!p) { ++ err = -ENOMEM; ++ goto err_pgd; ++ } ++ memset(p, 0, IOPGD_TABLE_SIZE); ++ clean_dcache_area(p, IOPGD_TABLE_SIZE); ++ obj->iopgd = p; ++ ++ BUG_ON(!IS_ALIGNED((unsigned long)obj->iopgd, IOPGD_TABLE_SIZE)); ++ ++ dev_info(&pdev->dev, "%s registered\n", obj->name); ++ return 0; ++ ++err_pgd: ++ free_irq(irq, obj); ++err_irq: ++ release_mem_region(res->start, resource_size(res)); ++ iounmap(obj->regbase); ++err_mem: ++ clk_put(obj->clk); ++err_clk: ++ kfree(obj); ++ return err; ++} ++ ++static int __devexit omap_iommu_remove(struct platform_device *pdev) ++{ ++ int irq; ++ struct resource *res; ++ struct iommu *obj = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ iopgtable_clear_entry_all(obj); ++ free_pages((unsigned long)obj->iopgd, get_order(IOPGD_TABLE_SIZE)); ++ ++ irq = platform_get_irq(pdev, 0); ++ free_irq(irq, obj); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++ iounmap(obj->regbase); ++ ++ clk_put(obj->clk); ++ dev_info(&pdev->dev, "%s removed\n", obj->name); ++ kfree(obj); ++ return 0; ++} ++ ++static struct platform_driver omap_iommu_driver = { ++ .probe = omap_iommu_probe, ++ .remove = __devexit_p(omap_iommu_remove), ++ .driver = { ++ .name = "omap-iommu", ++ }, ++}; ++ ++static void iopte_cachep_ctor(void *iopte) ++{ ++ clean_dcache_area(iopte, IOPTE_TABLE_SIZE); ++} ++ ++static int __init omap_iommu_init(void) ++{ ++ struct kmem_cache *p; ++ const unsigned long flags = SLAB_HWCACHE_ALIGN; ++ ++ p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, 0, flags, ++ iopte_cachep_ctor); ++ if (!p) ++ return -ENOMEM; ++ iopte_cachep = p; ++ ++ return platform_driver_register(&omap_iommu_driver); ++} ++module_init(omap_iommu_init); ++ ++static void __exit omap_iommu_exit(void) ++{ ++ kmem_cache_destroy(iopte_cachep); ++ ++ platform_driver_unregister(&omap_iommu_driver); ++} ++module_exit(omap_iommu_exit); ++ ++MODULE_DESCRIPTION("omap iommu: tlb and pagetable primitives"); ++MODULE_ALIAS("platform:omap-iommu"); ++MODULE_AUTHOR("Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi"); ++MODULE_LICENSE("GPL v2"); +diff --git a/arch/arm/plat-omap/iopgtable.h b/arch/arm/plat-omap/iopgtable.h +new file mode 100644 +index 0000000..37dac43 +--- /dev/null ++++ b/arch/arm/plat-omap/iopgtable.h +@@ -0,0 +1,72 @@ ++/* ++ * omap iommu: pagetable definitions ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ++ * ++ * 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 __PLAT_OMAP_IOMMU_H ++#define __PLAT_OMAP_IOMMU_H ++ ++#define IOPGD_SHIFT 20 ++#define IOPGD_SIZE (1 << IOPGD_SHIFT) ++#define IOPGD_MASK (~(IOPGD_SIZE - 1)) ++#define IOSECTION_MASK IOPGD_MASK ++#define PTRS_PER_IOPGD (1 << (32 - IOPGD_SHIFT)) ++#define IOPGD_TABLE_SIZE (PTRS_PER_IOPGD * sizeof(u32)) ++ ++#define IOSUPER_SIZE (IOPGD_SIZE << 4) ++#define IOSUPER_MASK (~(IOSUPER_SIZE - 1)) ++ ++#define IOPTE_SHIFT 12 ++#define IOPTE_SIZE (1 << IOPTE_SHIFT) ++#define IOPTE_MASK (~(IOPTE_SIZE - 1)) ++#define IOPAGE_MASK IOPTE_MASK ++#define PTRS_PER_IOPTE (1 << (IOPGD_SHIFT - IOPTE_SHIFT)) ++#define IOPTE_TABLE_SIZE (PTRS_PER_IOPTE * sizeof(u32)) ++ ++#define IOLARGE_SIZE (IOPTE_SIZE << 4) ++#define IOLARGE_MASK (~(IOLARGE_SIZE - 1)) ++ ++#define IOPGD_TABLE (1 << 0) ++#define IOPGD_SECTION (2 << 0) ++#define IOPGD_SUPER (1 << 18 | 2 << 0) ++ ++#define IOPTE_SMALL (2 << 0) ++#define IOPTE_LARGE (1 << 0) ++ ++#define iopgd_index(da) (((da) >> IOPGD_SHIFT) & (PTRS_PER_IOPGD - 1)) ++#define iopgd_offset(obj, da) ((obj)->iopgd + iopgd_index(da)) ++ ++#define iopte_paddr(iopgd) (*iopgd & ~((1 << 10) - 1)) ++#define iopte_vaddr(iopgd) ((u32 *)phys_to_virt(iopte_paddr(iopgd))) ++ ++#define iopte_index(da) (((da) >> IOPTE_SHIFT) & (PTRS_PER_IOPTE - 1)) ++#define iopte_offset(iopgd, da) (iopte_vaddr(iopgd) + iopte_index(da)) ++ ++static inline u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, ++ u32 flags) ++{ ++ memset(e, 0, sizeof(*e)); ++ ++ e->da = da; ++ e->pa = pa; ++ e->valid = 1; ++ /* FIXME: add OMAP1 support */ ++ e->pgsz = flags & MMU_CAM_PGSZ_MASK; ++ e->endian = flags & MMU_RAM_ENDIAN_MASK; ++ e->elsz = flags & MMU_RAM_ELSZ_MASK; ++ e->mixed = flags & MMU_RAM_MIXED_MASK; ++ ++ return iopgsz_to_bytes(e->pgsz); ++} ++ ++#define to_iommu(dev) \ ++ (struct iommu *)platform_get_drvdata(to_platform_device(dev)) ++ ++#endif /* __PLAT_OMAP_IOMMU_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0002-omap-iommu-omap2-architecture-specific-functions.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0002-omap-iommu-omap2-architecture-specific-functions.patch new file mode 100644 index 0000000000..d5f78dd14e --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0002-omap-iommu-omap2-architecture-specific-functions.patch @@ -0,0 +1,453 @@ +From c79d7959c45f40e47520aa6acd54c19094754787 Mon Sep 17 00:00:00 2001 +From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +Date: Mon, 26 Jan 2009 15:13:45 +0200 +Subject: [PATCH] omap iommu: omap2 architecture specific functions + +The structure 'arch_mmu' accommodates the difference between omap1 and +omap2/3. + +This patch provides omap2/3 specific functions + +Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +--- + arch/arm/mach-omap2/iommu2.c | 326 ++++++++++++++++++++++++++++++ + arch/arm/plat-omap/include/mach/iommu2.h | 94 +++++++++ + 2 files changed, 420 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/mach-omap2/iommu2.c + create mode 100644 arch/arm/plat-omap/include/mach/iommu2.h + +diff --git a/arch/arm/mach-omap2/iommu2.c b/arch/arm/mach-omap2/iommu2.c +new file mode 100644 +index 0000000..88a44f1 +--- /dev/null ++++ b/arch/arm/mach-omap2/iommu2.c +@@ -0,0 +1,326 @@ ++/* ++ * omap iommu: omap2/3 architecture specific functions ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>, ++ * Paul Mundt and Toshihiro Kobayashi ++ * ++ * 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 <linux/err.h> ++#include <linux/device.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/stringify.h> ++ ++#include <asm/io.h> ++ ++#include <mach/iommu.h> ++#include <mach/iommu2.h> ++ ++/* ++ * omap2 architecture specific register bit definitions ++ */ ++#define IOMMU_ARCH_VERSION 0x00000011 ++ ++/* SYSCONF */ ++#define MMU_SYS_IDLE_SHIFT 3 ++#define MMU_SYS_IDLE_FORCE (0 << MMU_SYS_IDLE_SHIFT) ++#define MMU_SYS_IDLE_NONE (1 << MMU_SYS_IDLE_SHIFT) ++#define MMU_SYS_IDLE_SMART (2 << MMU_SYS_IDLE_SHIFT) ++#define MMU_SYS_IDLE_MASK (3 << MMU_SYS_IDLE_SHIFT) ++ ++#define MMU_SYS_SOFTRESET (1 << 1) ++#define MMU_SYS_AUTOIDLE 1 ++ ++/* SYSSTATUS */ ++#define MMU_SYS_RESETDONE 1 ++ ++/* IRQSTATUS & IRQENABLE */ ++#define MMU_IRQ_MULTIHITFAULT (1 << 4) ++#define MMU_IRQ_TABLEWALKFAULT (1 << 3) ++#define MMU_IRQ_EMUMISS (1 << 2) ++#define MMU_IRQ_TRANSLATIONFAULT (1 << 1) ++#define MMU_IRQ_TLBMISS (1 << 0) ++#define MMU_IRQ_MASK \ ++ (MMU_IRQ_MULTIHITFAULT | MMU_IRQ_TABLEWALKFAULT | MMU_IRQ_EMUMISS | \ ++ MMU_IRQ_TRANSLATIONFAULT) ++ ++/* MMU_CNTL */ ++#define MMU_CNTL_SHIFT 1 ++#define MMU_CNTL_MASK (7 << MMU_CNTL_SHIFT) ++#define MMU_CNTL_EML_TLB (1 << 3) ++#define MMU_CNTL_TWL_EN (1 << 2) ++#define MMU_CNTL_MMU_EN (1 << 1) ++ ++#define get_cam_va_mask(pgsz) \ ++ (((pgsz) == MMU_CAM_PGSZ_16M) ? 0xff000000 : \ ++ ((pgsz) == MMU_CAM_PGSZ_1M) ? 0xfff00000 : \ ++ ((pgsz) == MMU_CAM_PGSZ_64K) ? 0xffff0000 : \ ++ ((pgsz) == MMU_CAM_PGSZ_4K) ? 0xfffff000 : 0) ++ ++static int omap2_iommu_enable(struct iommu *obj) ++{ ++ u32 l, pa; ++ unsigned long timeout; ++ ++ if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K)) ++ return -EINVAL; ++ ++ pa = virt_to_phys(obj->iopgd); ++ if (!IS_ALIGNED(pa, SZ_16K)) ++ return -EINVAL; ++ ++ iommu_write_reg(obj, MMU_SYS_SOFTRESET, MMU_SYSCONFIG); ++ ++ timeout = jiffies + msecs_to_jiffies(20); ++ do { ++ l = iommu_read_reg(obj, MMU_SYSSTATUS); ++ if (l & MMU_SYS_RESETDONE) ++ break; ++ } while (time_after(jiffies, timeout)); ++ ++ if (!(l & MMU_SYS_RESETDONE)) { ++ dev_err(obj->dev, "can't take mmu out of reset\n"); ++ return -ENODEV; ++ } ++ ++ l = iommu_read_reg(obj, MMU_REVISION); ++ dev_info(obj->dev, "%s: version %d.%d\n", obj->name, ++ (l >> 4) & 0xf, l & 0xf); ++ ++ l = iommu_read_reg(obj, MMU_SYSCONFIG); ++ l &= ~MMU_SYS_IDLE_MASK; ++ l |= (MMU_SYS_IDLE_SMART | MMU_SYS_AUTOIDLE); ++ iommu_write_reg(obj, l, MMU_SYSCONFIG); ++ ++ iommu_write_reg(obj, MMU_IRQ_MASK, MMU_IRQENABLE); ++ iommu_write_reg(obj, pa, MMU_TTB); ++ ++ l = iommu_read_reg(obj, MMU_CNTL); ++ l &= ~MMU_CNTL_MASK; ++ l |= (MMU_CNTL_MMU_EN | MMU_CNTL_TWL_EN); ++ iommu_write_reg(obj, l, MMU_CNTL); ++ ++ return 0; ++} ++ ++static void omap2_iommu_disable(struct iommu *obj) ++{ ++ u32 l = iommu_read_reg(obj, MMU_CNTL); ++ ++ l &= ~MMU_CNTL_MASK; ++ iommu_write_reg(obj, l, MMU_CNTL); ++ iommu_write_reg(obj, MMU_SYS_IDLE_FORCE, MMU_SYSCONFIG); ++ ++ dev_dbg(obj->dev, "%s is shutting down\n", obj->name); ++} ++ ++static u32 omap2_iommu_fault_isr(struct iommu *obj, u32 *ra) ++{ ++ int i; ++ u32 stat, da; ++ const char *err_msg[] = { ++ "tlb miss", ++ "translation fault", ++ "emulation miss", ++ "table walk fault", ++ "multi hit fault", ++ }; ++ ++ stat = iommu_read_reg(obj, MMU_IRQSTATUS); ++ stat &= MMU_IRQ_MASK; ++ if (!stat) ++ return 0; ++ ++ da = iommu_read_reg(obj, MMU_FAULT_AD); ++ *ra = da; ++ ++ dev_err(obj->dev, "%s:\tda:%08x ", __func__, da); ++ ++ for (i = 0; i < ARRAY_SIZE(err_msg); i++) { ++ if (stat & (1 << i)) ++ printk("%s ", err_msg[i]); ++ } ++ printk("\n"); ++ ++ iommu_write_reg(obj, stat, MMU_IRQSTATUS); ++ return stat; ++} ++ ++static void omap2_tlb_read_cr(struct iommu *obj, struct cr_regs *cr) ++{ ++ cr->cam = iommu_read_reg(obj, MMU_READ_CAM); ++ cr->ram = iommu_read_reg(obj, MMU_READ_RAM); ++} ++ ++static void omap2_tlb_load_cr(struct iommu *obj, struct cr_regs *cr) ++{ ++ iommu_write_reg(obj, cr->cam | MMU_CAM_V, MMU_CAM); ++ iommu_write_reg(obj, cr->ram, MMU_RAM); ++} ++ ++static u32 omap2_cr_to_virt(struct cr_regs *cr) ++{ ++ u32 page_size = cr->cam & MMU_CAM_PGSZ_MASK; ++ u32 mask = get_cam_va_mask(cr->cam & page_size); ++ ++ return cr->cam & mask; ++} ++ ++static struct cr_regs *omap2_alloc_cr(struct iommu *obj, struct iotlb_entry *e) ++{ ++ struct cr_regs *cr; ++ ++ if (e->da & ~(get_cam_va_mask(e->pgsz))) { ++ dev_err(obj->dev, "%s:\twrong alignment: %08x\n", __func__, ++ e->da); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ cr = kmalloc(sizeof(*cr), GFP_KERNEL); ++ if (!cr) ++ return ERR_PTR(-ENOMEM); ++ ++ cr->cam = (e->da & MMU_CAM_VATAG_MASK) | e->prsvd | e->pgsz; ++ cr->ram = e->pa | e->endian | e->elsz | e->mixed; ++ ++ return cr; ++} ++ ++static inline int omap2_cr_valid(struct cr_regs *cr) ++{ ++ return cr->cam & MMU_CAM_V; ++} ++ ++static u32 omap2_get_pte_attr(struct iotlb_entry *e) ++{ ++ u32 attr; ++ ++ attr = e->mixed << 5; ++ attr |= e->endian; ++ attr |= e->elsz >> 3; ++ attr <<= ((e->pgsz & MMU_CAM_PGSZ_4K) ? 0 : 6); ++ ++ return attr; ++} ++ ++static ssize_t omap2_dump_cr(struct iommu *obj, struct cr_regs *cr, char *buf) ++{ ++ char *p = buf; ++ ++ /* FIXME: Need more detail analysis of cam/ram */ ++ p += sprintf(p, "%08x %08x\n", cr->cam, cr->ram); ++ ++ return p - buf; ++} ++ ++#define pr_reg(name) \ ++ p += sprintf(p, "%20s: %08x\n", \ ++ __stringify(name), iommu_read_reg(obj, MMU_##name)); ++ ++static ssize_t omap2_iommu_dump_ctx(struct iommu *obj, char *buf) ++{ ++ char *p = buf; ++ ++ pr_reg(REVISION); ++ pr_reg(SYSCONFIG); ++ pr_reg(SYSSTATUS); ++ pr_reg(IRQSTATUS); ++ pr_reg(IRQENABLE); ++ pr_reg(WALKING_ST); ++ pr_reg(CNTL); ++ pr_reg(FAULT_AD); ++ pr_reg(TTB); ++ pr_reg(LOCK); ++ pr_reg(LD_TLB); ++ pr_reg(CAM); ++ pr_reg(RAM); ++ pr_reg(GFLUSH); ++ pr_reg(FLUSH_ENTRY); ++ pr_reg(READ_CAM); ++ pr_reg(READ_RAM); ++ pr_reg(EMU_FAULT_AD); ++ ++ return p - buf; ++} ++ ++static void omap2_iommu_save_ctx(struct iommu *obj) ++{ ++ int i; ++ u32 *p = obj->ctx; ++ ++ for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) { ++ p[i] = iommu_read_reg(obj, i * sizeof(u32)); ++ dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i, p[i]); ++ } ++ ++ BUG_ON(p[0] != IOMMU_ARCH_VERSION); ++} ++ ++static void omap2_iommu_restore_ctx(struct iommu *obj) ++{ ++ int i; ++ u32 *p = obj->ctx; ++ ++ for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) { ++ iommu_write_reg(obj, p[i], i * sizeof(u32)); ++ dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i, p[i]); ++ } ++ ++ BUG_ON(p[0] != IOMMU_ARCH_VERSION); ++} ++ ++static void omap2_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e) ++{ ++ e->da = cr->cam & MMU_CAM_VATAG_MASK; ++ e->pa = cr->ram & MMU_RAM_PADDR_MASK; ++ e->valid = cr->cam & MMU_CAM_V; ++ e->pgsz = cr->cam & MMU_CAM_PGSZ_MASK; ++ e->endian = cr->ram & MMU_RAM_ENDIAN_MASK; ++ e->elsz = cr->ram & MMU_RAM_ELSZ_MASK; ++ e->mixed = cr->ram & MMU_RAM_MIXED; ++} ++ ++static const struct iommu_functions omap2_iommu_ops = { ++ .version = IOMMU_ARCH_VERSION, ++ ++ .enable = omap2_iommu_enable, ++ .disable = omap2_iommu_disable, ++ .fault_isr = omap2_iommu_fault_isr, ++ ++ .tlb_read_cr = omap2_tlb_read_cr, ++ .tlb_load_cr = omap2_tlb_load_cr, ++ ++ .cr_to_e = omap2_cr_to_e, ++ .cr_to_virt = omap2_cr_to_virt, ++ .alloc_cr = omap2_alloc_cr, ++ .cr_valid = omap2_cr_valid, ++ .dump_cr = omap2_dump_cr, ++ ++ .get_pte_attr = omap2_get_pte_attr, ++ ++ .save_ctx = omap2_iommu_save_ctx, ++ .restore_ctx = omap2_iommu_restore_ctx, ++ .dump_ctx = omap2_iommu_dump_ctx, ++}; ++ ++static int __init omap2_iommu_init(void) ++{ ++ return install_iommu_arch(&omap2_iommu_ops); ++} ++module_init(omap2_iommu_init); ++ ++static void __exit omap2_iommu_exit(void) ++{ ++ uninstall_iommu_arch(&omap2_iommu_ops); ++} ++module_exit(omap2_iommu_exit); ++ ++MODULE_AUTHOR("Hiroshi DOYU, Paul Mundt and Toshihiro Kobayashi"); ++MODULE_DESCRIPTION("omap iommu: omap2/3 architecture specific functions"); ++MODULE_LICENSE("GPL v2"); +diff --git a/arch/arm/plat-omap/include/mach/iommu2.h b/arch/arm/plat-omap/include/mach/iommu2.h +new file mode 100644 +index 0000000..d746047 +--- /dev/null ++++ b/arch/arm/plat-omap/include/mach/iommu2.h +@@ -0,0 +1,94 @@ ++/* ++ * omap iommu: omap2 architecture specific definitions ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ++ * ++ * 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 __MACH_IOMMU2_H ++#define __MACH_IOMMU2_H ++ ++/* ++ * MMU Register offsets ++ */ ++#define MMU_REVISION 0x00 ++#define MMU_SYSCONFIG 0x10 ++#define MMU_SYSSTATUS 0x14 ++#define MMU_IRQSTATUS 0x18 ++#define MMU_IRQENABLE 0x1c ++#define MMU_WALKING_ST 0x40 ++#define MMU_CNTL 0x44 ++#define MMU_FAULT_AD 0x48 ++#define MMU_TTB 0x4c ++#define MMU_LOCK 0x50 ++#define MMU_LD_TLB 0x54 ++#define MMU_CAM 0x58 ++#define MMU_RAM 0x5c ++#define MMU_GFLUSH 0x60 ++#define MMU_FLUSH_ENTRY 0x64 ++#define MMU_READ_CAM 0x68 ++#define MMU_READ_RAM 0x6c ++#define MMU_EMU_FAULT_AD 0x70 ++ ++#define MMU_REG_SIZE 256 ++ ++/* ++ * MMU Register bit definitions ++ */ ++#define MMU_LOCK_BASE_SHIFT 10 ++#define MMU_LOCK_BASE_MASK (0x1f << MMU_LOCK_BASE_SHIFT) ++#define MMU_LOCK_BASE(x) \ ++ ((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT) ++ ++#define MMU_LOCK_VICT_SHIFT 4 ++#define MMU_LOCK_VICT_MASK (0x1f << MMU_LOCK_VICT_SHIFT) ++#define MMU_LOCK_VICT(x) \ ++ ((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT) ++ ++#define MMU_CAM_VATAG_SHIFT 12 ++#define MMU_CAM_VATAG_MASK \ ++ ((~0UL >> MMU_CAM_VATAG_SHIFT) << MMU_CAM_VATAG_SHIFT) ++#define MMU_CAM_P (1 << 3) ++#define MMU_CAM_V (1 << 2) ++#define MMU_CAM_PGSZ_MASK 3 ++#define MMU_CAM_PGSZ_1M (0 << 0) ++#define MMU_CAM_PGSZ_64K (1 << 0) ++#define MMU_CAM_PGSZ_4K (2 << 0) ++#define MMU_CAM_PGSZ_16M (3 << 0) ++ ++#define MMU_RAM_PADDR_SHIFT 12 ++#define MMU_RAM_PADDR_MASK \ ++ ((~0UL >> MMU_RAM_PADDR_SHIFT) << MMU_RAM_PADDR_SHIFT) ++#define MMU_RAM_ENDIAN_SHIFT 9 ++#define MMU_RAM_ENDIAN_MASK (1 << MMU_RAM_ENDIAN_SHIFT) ++#define MMU_RAM_ENDIAN_BIG (1 << MMU_RAM_ENDIAN_SHIFT) ++#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT) ++#define MMU_RAM_ELSZ_SHIFT 7 ++#define MMU_RAM_ELSZ_MASK (3 << MMU_RAM_ELSZ_SHIFT) ++#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT) ++#define MMU_RAM_ELSZ_16 (1 << MMU_RAM_ELSZ_SHIFT) ++#define MMU_RAM_ELSZ_32 (2 << MMU_RAM_ELSZ_SHIFT) ++#define MMU_RAM_ELSZ_NONE (3 << MMU_RAM_ELSZ_SHIFT) ++#define MMU_RAM_MIXED_SHIFT 6 ++#define MMU_RAM_MIXED_MASK (1 << MMU_RAM_MIXED_SHIFT) ++#define MMU_RAM_MIXED MMU_RAM_MIXED_MASK ++ ++/* ++ * register accessors ++ */ ++static inline u32 iommu_read_reg(struct iommu *obj, size_t offs) ++{ ++ return __raw_readl(obj->regbase + offs); ++} ++ ++static inline void iommu_write_reg(struct iommu *obj, u32 val, size_t offs) ++{ ++ __raw_writel(val, obj->regbase + offs); ++} ++ ++#endif /* __MACH_IOMMU2_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0003-omap-iommu-omap3-iommu-device-registration.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0003-omap-iommu-omap3-iommu-device-registration.patch new file mode 100644 index 0000000000..2954c47872 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0003-omap-iommu-omap3-iommu-device-registration.patch @@ -0,0 +1,124 @@ +From 6a84082597dd322713c5d5951530e3eecb878ad4 Mon Sep 17 00:00:00 2001 +From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +Date: Wed, 28 Jan 2009 21:32:04 +0200 +Subject: [PATCH] omap iommu: omap3 iommu device registration + +Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +--- + arch/arm/mach-omap2/omap3-iommu.c | 104 +++++++++++++++++++++++++++++++++++++ + 1 files changed, 104 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/mach-omap2/omap3-iommu.c + +diff --git a/arch/arm/mach-omap2/omap3-iommu.c b/arch/arm/mach-omap2/omap3-iommu.c +new file mode 100644 +index 0000000..97481cc +--- /dev/null ++++ b/arch/arm/mach-omap2/omap3-iommu.c +@@ -0,0 +1,104 @@ ++/* ++ * omap iommu: omap3 device registration ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ++ * ++ * 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 <linux/platform_device.h> ++#include <linux/io.h> ++ ++#include <mach/iommu.h> ++ ++#define OMAP3_MMU1_BASE 0x480bd400 ++#define OMAP3_MMU2_BASE 0x5d000000 ++#define OMAP3_MMU1_IRQ 24 ++#define OMAP3_MMU2_IRQ 28 ++ ++static struct resource omap3_iommu_res[] = { ++ { /* Camera ISP MMU */ ++ .start = OMAP3_MMU1_BASE, ++ .end = OMAP3_MMU1_BASE + MMU_REG_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = OMAP3_MMU1_IRQ, ++ .flags = IORESOURCE_IRQ, ++ }, ++ { /* IVA2.2 MMU */ ++ .start = OMAP3_MMU2_BASE, ++ .end = OMAP3_MMU2_BASE + MMU_REG_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = OMAP3_MMU2_IRQ, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++#define NR_IOMMU_RES (ARRAY_SIZE(omap3_iommu_res) / 2) ++ ++static const struct iommu_platform_data omap3_iommu_pdata[] __initconst = { ++ { ++ .name = "isp", ++ .nr_tlb_entries = 8, ++ .clk_name = "cam_ick", ++ }, ++ { ++ .name = "iva2", ++ .nr_tlb_entries = 32, ++ .clk_name = "iva2_ck", ++ }, ++}; ++#define NR_IOMMU_DEVICES ARRAY_SIZE(omap3_iommu_pdata) ++ ++static struct platform_device *omap3_iommu_pdev[NR_IOMMU_DEVICES]; ++ ++static int __init omap3_iommu_init(void) ++{ ++ int i, err; ++ ++ for (i = 0; i < NR_IOMMU_DEVICES; i++) { ++ struct platform_device *pdev; ++ ++ pdev = platform_device_alloc("omap-iommu", i + 1); ++ if (!pdev) ++ goto err_out; ++ err = platform_device_add_resources(pdev, ++ &omap3_iommu_res[2 * i], NR_IOMMU_RES); ++ if (err) ++ goto err_out; ++ err = platform_device_add_data(pdev, &omap3_iommu_pdata[i], ++ sizeof(omap3_iommu_pdata[0])); ++ if (err) ++ goto err_out; ++ err = platform_device_add(pdev); ++ if (err) ++ goto err_out; ++ omap3_iommu_pdev[i] = pdev; ++ } ++ return 0; ++ ++err_out: ++ while (i--) ++ platform_device_put(omap3_iommu_pdev[i]); ++ return err; ++} ++module_init(omap3_iommu_init); ++ ++static void __exit omap3_iommu_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < NR_IOMMU_DEVICES; i++) ++ platform_device_unregister(omap3_iommu_pdev[i]); ++} ++module_exit(omap3_iommu_exit); ++ ++MODULE_AUTHOR("Hiroshi DOYU"); ++MODULE_DESCRIPTION("omap iommu: omap3 device registration"); ++MODULE_LICENSE("GPL v2"); +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch new file mode 100644 index 0000000000..945778b943 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch @@ -0,0 +1,1083 @@ +From 07365182b998af3dc2b79e822b8e21a3f50262c4 Mon Sep 17 00:00:00 2001 +From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +Date: Wed, 28 Jan 2009 21:32:08 +0200 +Subject: [PATCH] omap iommu: simple virtual address space management + +This patch provides a device drivers, which has a omap iommu, with +address mapping APIs between device virtual address(iommu), physical +address and MPU virtual address. + +There are 4 possible patterns for iommu virtual address(iova/da) mapping. + + |iova/ mapping iommu_ page + | da pa va (d)-(p)-(v) function type + --------------------------------------------------------------------------- + 1 | c c c 1 - 1 - 1 _kmap() / _kunmap() s + 2 | c c,a c 1 - 1 - 1 _kmalloc()/ _kfree() s + 3 | c d c 1 - n - 1 _vmap() / _vunmap() s + 4 | c d,a c 1 - n - 1 _vmalloc()/ _vfree() n* + + 'iova': device iommu virtual address + 'da': alias of 'iova' + 'pa': physical address + 'va': mpu virtual address + + 'c': contiguous memory area + 'd': dicontiguous memory area + 'a': anonymous memory allocation + '()': optional feature + + 'n': a normal page(4KB) size is used. + 's': multiple iommu superpage(16MB, 1MB, 64KB, 4KB) size is used. + + '*': not yet, but feasible. + +Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +--- + arch/arm/include/asm/io.h | 6 + + arch/arm/mm/ioremap.c | 11 + + arch/arm/plat-omap/include/mach/iovmm.h | 94 ++++ + arch/arm/plat-omap/iovmm.c | 891 +++++++++++++++++++++++++++++++ + 4 files changed, 1002 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/plat-omap/include/mach/iovmm.h + create mode 100644 arch/arm/plat-omap/iovmm.c + +diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h +index d2a59cf..cbdadfe 100644 +--- a/arch/arm/include/asm/io.h ++++ b/arch/arm/include/asm/io.h +@@ -75,6 +75,12 @@ extern void __iomem * __arm_ioremap(unsigned long, size_t, unsigned int); + extern void __iounmap(volatile void __iomem *addr); + + /* ++ * external interface to remap single page with appropriate type ++ */ ++extern int ioremap_page(unsigned long virt, unsigned long phys, ++ unsigned int mtype); ++ ++/* + * Bad read/write accesses... + */ + extern void __readwrite_bug(const char *fn); +diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c +index 9f88dd3..8441351 100644 +--- a/arch/arm/mm/ioremap.c ++++ b/arch/arm/mm/ioremap.c +@@ -110,6 +110,17 @@ static int remap_area_pages(unsigned long start, unsigned long pfn, + return err; + } + ++int ioremap_page(unsigned long virt, unsigned long phys, unsigned int mtype) ++{ ++ const struct mem_type *type; ++ ++ type = get_mem_type(mtype); ++ if (!type) ++ return -EINVAL; ++ ++ return remap_area_pages(virt, __phys_to_pfn(phys), PAGE_SIZE, type); ++} ++EXPORT_SYMBOL(ioremap_page); + + void __check_kvm_seq(struct mm_struct *mm) + { +diff --git a/arch/arm/plat-omap/include/mach/iovmm.h b/arch/arm/plat-omap/include/mach/iovmm.h +new file mode 100644 +index 0000000..bdc7ce5 +--- /dev/null ++++ b/arch/arm/plat-omap/include/mach/iovmm.h +@@ -0,0 +1,94 @@ ++/* ++ * omap iommu: simple virtual address space management ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ++ * ++ * 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 __IOMMU_MMAP_H ++#define __IOMMU_MMAP_H ++ ++struct iovm_struct { ++ struct iommu *iommu; /* iommu object which this belongs to */ ++ u32 da_start; /* area definition */ ++ u32 da_end; ++ u32 flags; /* IOVMF_: see below */ ++ struct list_head list; /* linked in ascending order */ ++ const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */ ++ void *va; /* mpu side mapped address */ ++}; ++ ++/* ++ * IOVMF_FLAGS: attribute for iommu virtual memory area(iovma) ++ * ++ * lower 16 bit is used for h/w and upper 16 bit is for s/w. ++ */ ++#define IOVMF_SW_SHIFT 16 ++#define IOVMF_HW_SIZE (1 << IOVMF_SW_SHIFT) ++#define IOVMF_HW_MASK (IOVMF_HW_SIZE - 1) ++#define IOVMF_SW_MASK (~IOVMF_HW_MASK)UL ++ ++/* ++ * iovma: h/w flags derived from cam and ram attribute ++ */ ++#define IOVMF_CAM_MASK (~((1 << 10) - 1)) ++#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK) ++ ++#define IOVMF_PGSZ_MASK (3 << 0) ++#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M ++#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K ++#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K ++#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M ++ ++#define IOVMF_ENDIAN_MASK (1 << 9) ++#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG ++#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE ++ ++#define IOVMF_ELSZ_MASK (3 << 7) ++#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8 ++#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16 ++#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32 ++#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE ++ ++#define IOVMF_MIXED_MASK (1 << 6) ++#define IOVMF_MIXED MMU_RAM_MIXED ++ ++/* ++ * iovma: s/w flags, used for mapping and umapping internally. ++ */ ++#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT) ++#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT) ++#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT) ++ ++/* "superpages" is supported just with physically linear pages */ ++#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT)) ++#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT)) ++#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT)) ++ ++#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT)) ++#define IOVMF_DA_ANON (2 << (4 + IOVMF_SW_SHIFT)) ++#define IOVMF_DA_MASK (3 << (4 + IOVMF_SW_SHIFT)) ++ ++ ++extern struct iovm_struct *find_iovm_area(struct iommu *obj, u32 da); ++extern u32 iommu_vmap(struct iommu *obj, u32 da, ++ const struct sg_table *sgt, u32 flags); ++extern struct sg_table *iommu_vunmap(struct iommu *obj, u32 da); ++extern u32 iommu_vmalloc(struct iommu *obj, u32 da, size_t bytes, ++ u32 flags); ++extern void iommu_vfree(struct iommu *obj, const u32 da); ++extern u32 iommu_kmap(struct iommu *obj, u32 da, u32 pa, size_t bytes, ++ u32 flags); ++extern void iommu_kunmap(struct iommu *obj, u32 da); ++extern u32 iommu_kmalloc(struct iommu *obj, u32 da, size_t bytes, ++ u32 flags); ++extern void iommu_kfree(struct iommu *obj, u32 da); ++ ++extern void *da_to_va(struct iommu *obj, u32 da); ++ ++#endif /* __IOMMU_MMAP_H */ +diff --git a/arch/arm/plat-omap/iovmm.c b/arch/arm/plat-omap/iovmm.c +new file mode 100644 +index 0000000..6726d10 +--- /dev/null ++++ b/arch/arm/plat-omap/iovmm.c +@@ -0,0 +1,891 @@ ++/* ++ * omap iommu: simple virtual address space management ++ * ++ * Copyright (C) 2008-2009 Nokia Corporation ++ * ++ * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> ++ * ++ * 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 <linux/err.h> ++#include <linux/vmalloc.h> ++#include <linux/device.h> ++#include <linux/scatterlist.h> ++ ++#include <asm/io.h> ++#include <asm/cacheflush.h> ++ ++#include <mach/iommu.h> ++#include <mach/iovmm.h> ++ ++#include "iopgtable.h" ++ ++/* ++ * A device driver needs to create address mappings between: ++ * ++ * - iommu/device address ++ * - physical address ++ * - mpu virtual address ++ * ++ * There are 4 possible patterns for them: ++ * ++ * |iova/ mapping iommu_ page ++ * | da pa va (d)-(p)-(v) function type ++ * --------------------------------------------------------------------------- ++ * 1 | c c c 1 - 1 - 1 _kmap() / _kunmap() s ++ * 2 | c c,a c 1 - 1 - 1 _kmalloc()/ _kfree() s ++ * 3 | c d c 1 - n - 1 _vmap() / _vunmap() s ++ * 4 | c d,a c 1 - n - 1 _vmalloc()/ _vfree() n* ++ * ++ * ++ * 'iova': device iommu virtual address ++ * 'da': alias of 'iova' ++ * 'pa': physical address ++ * 'va': mpu virtual address ++ * ++ * 'c': contiguous memory area ++ * 'd': dicontiguous memory area ++ * 'a': anonymous memory allocation ++ * '()': optional feature ++ * ++ * 'n': a normal page(4KB) size is used. ++ * 's': multiple iommu superpage(16MB, 1MB, 64KB, 4KB) size is used. ++ * ++ * '*': not yet, but feasible. ++ */ ++ ++static struct kmem_cache *iovm_area_cachep; ++ ++/* return total bytes of sg buffers */ ++static size_t sgtable_len(const struct sg_table *sgt) ++{ ++ unsigned int i, total = 0; ++ struct scatterlist *sg; ++ ++ if (!sgt) ++ return 0; ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ size_t bytes; ++ ++ bytes = sg_dma_len(sg); ++ ++ if (!iopgsz_ok(bytes)) { ++ pr_err("%s: sg[%d] not iommu pagesize(%x)\n", ++ __func__, i, bytes); ++ return 0; ++ } ++ ++ total += bytes; ++ } ++ ++ return total; ++} ++#define sgtable_ok(x) (!!sgtable_len(x)) ++ ++/* ++ * calculate the optimal number sg elements from total bytes based on ++ * iommu superpages ++ */ ++static unsigned int sgtable_nents(size_t bytes) ++{ ++ int i; ++ unsigned int nr_entries; ++ const unsigned long pagesize[] = { SZ_16M, SZ_1M, SZ_64K, SZ_4K, }; ++ ++ if (!IS_ALIGNED(bytes, PAGE_SIZE)) { ++ pr_err("%s: wrong size %08x\n", __func__, bytes); ++ return 0; ++ } ++ ++ nr_entries = 0; ++ for (i = 0; i < ARRAY_SIZE(pagesize); i++) { ++ if (bytes >= pagesize[i]) { ++ nr_entries += (bytes / pagesize[i]); ++ bytes %= pagesize[i]; ++ } ++ } ++ BUG_ON(bytes); ++ ++ return nr_entries; ++} ++ ++/* allocate and initialize sg_table header(a kind of 'superblock') */ ++static struct sg_table *sgtable_alloc(const size_t bytes, u32 flags) ++{ ++ unsigned int nr_entries; ++ int err; ++ struct sg_table *sgt; ++ ++ if (!bytes) ++ return ERR_PTR(-EINVAL); ++ ++ if (!IS_ALIGNED(bytes, PAGE_SIZE)) ++ return ERR_PTR(-EINVAL); ++ ++ /* FIXME: IOVMF_DA_FIXED should support 'superpages' */ ++ if ((flags & IOVMF_LINEAR) && (flags & IOVMF_DA_ANON)) { ++ nr_entries = sgtable_nents(bytes); ++ if (!nr_entries) ++ return ERR_PTR(-EINVAL); ++ } else ++ nr_entries = bytes / PAGE_SIZE; ++ ++ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); ++ if (!sgt) ++ return ERR_PTR(-ENOMEM); ++ ++ err = sg_alloc_table(sgt, nr_entries, GFP_KERNEL); ++ if (err) ++ return ERR_PTR(err); ++ ++ pr_debug("%s: sgt:%p(%d entries)\n", __func__, sgt, nr_entries); ++ ++ return sgt; ++} ++ ++/* free sg_table header(a kind of superblock) */ ++static void sgtable_free(struct sg_table *sgt) ++{ ++ if (!sgt) ++ return; ++ ++ sg_free_table(sgt); ++ kfree(sgt); ++ ++ pr_debug("%s: sgt:%p\n", __func__, sgt); ++} ++ ++/* map 'sglist' to a contiguous mpu virtual area and return 'va' */ ++static void *vmap_sg(const struct sg_table *sgt) ++{ ++ u32 va; ++ size_t total; ++ unsigned int i; ++ struct scatterlist *sg; ++ struct vm_struct *new; ++ ++ total = sgtable_len(sgt); ++ if (!total) ++ return ERR_PTR(-EINVAL); ++ ++ new = __get_vm_area(total, VM_IOREMAP, VMALLOC_START, VMALLOC_END); ++ if (!new) ++ return ERR_PTR(-ENOMEM); ++ va = (u32)new->addr; ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ size_t bytes; ++ u32 pa; ++ int err; ++ ++ pa = sg_phys(sg); ++ bytes = sg_dma_len(sg); ++ ++ BUG_ON(bytes != PAGE_SIZE); ++ ++ err = ioremap_page(va, pa, MT_DEVICE); ++ if (err) ++ goto err_out; ++ ++ va += bytes; ++ } ++ ++ flush_cache_vmap(new->addr, total); ++ return new->addr; ++ ++err_out: ++ WARN_ON(1); /* FIXME: cleanup some mpu mappings */ ++ vunmap(new->addr); ++ return ERR_PTR(-EAGAIN); ++} ++ ++static inline void vunmap_sg(const void *va) ++{ ++ vunmap(va); ++} ++ ++static struct iovm_struct *__find_iovm_area(struct iommu *obj, const u32 da) ++{ ++ struct iovm_struct *tmp; ++ ++ list_for_each_entry(tmp, &obj->mmap, list) { ++ if ((da >= tmp->da_start) && (da < tmp->da_end)) { ++ size_t len; ++ ++ len = tmp->da_end - tmp->da_start; ++ ++ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", ++ __func__, tmp->da_start, da, tmp->da_end, len, ++ tmp->flags); ++ ++ return tmp; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * find_iovm_area - find iovma which includes @da ++ * @da: iommu device virtual address ++ * ++ * Find the existing iovma starting at @da ++ */ ++struct iovm_struct *find_iovm_area(struct iommu *obj, u32 da) ++{ ++ struct iovm_struct *area; ++ ++ mutex_lock(&obj->mmap_lock); ++ area = __find_iovm_area(obj, da); ++ mutex_unlock(&obj->mmap_lock); ++ ++ return area; ++} ++EXPORT_SYMBOL_GPL(find_iovm_area); ++ ++/* ++ * This finds the hole(area) which fits the requested address and len ++ * in iovmas mmap, and returns the new allocated iovma. ++ */ ++static struct iovm_struct *alloc_iovm_area(struct iommu *obj, u32 da, ++ size_t bytes, u32 flags) ++{ ++ struct iovm_struct *new, *tmp; ++ u32 start, prev_end, alignement; ++ ++ if (!obj || !bytes) ++ return ERR_PTR(-EINVAL); ++ ++ start = da; ++ alignement = PAGE_SIZE; ++ ++ if (flags & IOVMF_DA_ANON) { ++ /* ++ * Reserve the first page for NULL ++ */ ++ start = PAGE_SIZE; ++ if (flags & IOVMF_LINEAR) ++ alignement = iopgsz_max(bytes); ++ start = roundup(start, alignement); ++ } ++ ++ tmp = NULL; ++ if (list_empty(&obj->mmap)) ++ goto found; ++ ++ prev_end = 0; ++ list_for_each_entry(tmp, &obj->mmap, list) { ++ ++ if ((prev_end <= start) && (start + bytes < tmp->da_start)) ++ goto found; ++ ++ if (flags & IOVMF_DA_ANON) ++ start = roundup(tmp->da_end, alignement); ++ ++ prev_end = tmp->da_end; ++ } ++ ++ if ((start >= prev_end) && (ULONG_MAX - start >= bytes)) ++ goto found; ++ ++ dev_dbg(obj->dev, "%s: no space to fit %08x(%x) flags: %08x\n", ++ __func__, da, bytes, flags); ++ ++ return ERR_PTR(-EINVAL); ++ ++found: ++ new = kmem_cache_zalloc(iovm_area_cachep, GFP_KERNEL); ++ if (!new) ++ return ERR_PTR(-ENOMEM); ++ ++ new->iommu = obj; ++ new->da_start = start; ++ new->da_end = start + bytes; ++ new->flags = flags; ++ ++ /* ++ * keep ascending order of iovmas ++ */ ++ if (tmp) ++ list_add_tail(&new->list, &tmp->list); ++ else ++ list_add(&new->list, &obj->mmap); ++ ++ dev_dbg(obj->dev, "%s: found %08x-%08x-%08x(%x) %08x\n", ++ __func__, new->da_start, start, new->da_end, bytes, flags); ++ ++ return new; ++} ++ ++static void free_iovm_area(struct iommu *obj, struct iovm_struct *area) ++{ ++ size_t bytes; ++ ++ BUG_ON(!obj || !area); ++ ++ bytes = area->da_end - area->da_start; ++ ++ dev_dbg(obj->dev, "%s: %08x-%08x(%x) %08x\n", ++ __func__, area->da_start, area->da_end, bytes, area->flags); ++ ++ list_del(&area->list); ++ kmem_cache_free(iovm_area_cachep, area); ++} ++ ++/** ++ * da_to_va - convert (d) to (v) ++ * @obj: objective iommu ++ * @da: iommu device virtual address ++ * @va: mpu virtual address ++ * ++ * Returns mpu virtual addr which corresponds to a given device virtual addr ++ */ ++void *da_to_va(struct iommu *obj, u32 da) ++{ ++ void *va = NULL; ++ struct iovm_struct *area; ++ ++ mutex_lock(&obj->mmap_lock); ++ ++ area = __find_iovm_area(obj, da); ++ if (!area) { ++ dev_warn(obj->dev, "%s: no da area(%08x)\n", __func__, da); ++ goto out; ++ } ++ va = area->va; ++ mutex_unlock(&obj->mmap_lock); ++out: ++ return va; ++} ++EXPORT_SYMBOL_GPL(da_to_va); ++ ++static void sgtable_fill_vmalloc(struct sg_table *sgt, void *_va) ++{ ++ unsigned int i; ++ struct scatterlist *sg; ++ void *va = _va; ++ void *va_end; ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ struct page *pg; ++ const size_t bytes = PAGE_SIZE; ++ ++ /* ++ * iommu 'superpage' isn't supported with 'iommu_vmalloc()' ++ */ ++ pg = vmalloc_to_page(va); ++ BUG_ON(!pg); ++ sg_set_page(sg, pg, bytes, 0); ++ ++ va += bytes; ++ } ++ ++ va_end = _va + PAGE_SIZE * i; ++ flush_cache_vmap(_va, va_end); ++} ++ ++static inline void sgtable_drain_vmalloc(struct sg_table *sgt) ++{ ++ /* ++ * Actually this is not necessary at all, just exists for ++ * consistency of the code readibility. ++ */ ++ BUG_ON(!sgt); ++} ++ ++static void sgtable_fill_kmalloc(struct sg_table *sgt, u32 pa, size_t len) ++{ ++ unsigned int i; ++ struct scatterlist *sg; ++ void *va; ++ ++ va = phys_to_virt(pa); ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ size_t bytes; ++ ++ bytes = iopgsz_max(len); ++ ++ BUG_ON(!iopgsz_ok(bytes)); ++ ++ sg_set_buf(sg, phys_to_virt(pa), bytes); ++ /* ++ * 'pa' is cotinuous(linear). ++ */ ++ pa += bytes; ++ len -= bytes; ++ } ++ BUG_ON(len); ++ ++ clean_dcache_area(va, len); ++} ++ ++static inline void sgtable_drain_kmalloc(struct sg_table *sgt) ++{ ++ /* ++ * Actually this is not necessary at all, just exists for ++ * consistency of the code readibility ++ */ ++ BUG_ON(!sgt); ++} ++ ++/* create 'da' <-> 'pa' mapping from 'sgt' */ ++static int map_iovm_area(struct iommu *obj, struct iovm_struct *new, ++ const struct sg_table *sgt, u32 flags) ++{ ++ int err; ++ unsigned int i, j; ++ struct scatterlist *sg; ++ u32 da = new->da_start; ++ ++ if (!obj || !new || !sgt) ++ return -EINVAL; ++ ++ BUG_ON(!sgtable_ok(sgt)); ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) { ++ u32 pa; ++ int pgsz; ++ size_t bytes; ++ struct iotlb_entry e; ++ ++ pa = sg_phys(sg); ++ bytes = sg_dma_len(sg); ++ ++ flags &= ~IOVMF_PGSZ_MASK; ++ pgsz = bytes_to_iopgsz(bytes); ++ if (pgsz < 0) ++ goto err_out; ++ flags |= pgsz; ++ ++ pr_debug("%s: [%d] %08x %08x(%x)\n", __func__, ++ i, da, pa, bytes); ++ ++ iotlb_init_entry(&e, da, pa, flags); ++ err = iopgtable_store_entry(obj, &e); ++ if (err) ++ goto err_out; ++ ++ da += bytes; ++ } ++ return 0; ++ ++err_out: ++ da = new->da_start; ++ ++ for_each_sg(sgt->sgl, sg, i, j) { ++ size_t bytes; ++ ++ bytes = iopgtable_clear_entry(obj, da); ++ ++ BUG_ON(!iopgsz_ok(bytes)); ++ ++ da += bytes; ++ } ++ return err; ++} ++ ++/* release 'da' <-> 'pa' mapping */ ++static void unmap_iovm_area(struct iommu *obj, struct iovm_struct *area) ++{ ++ u32 start; ++ size_t total = area->da_end - area->da_start; ++ ++ BUG_ON((!total) || !IS_ALIGNED(total, PAGE_SIZE)); ++ ++ start = area->da_start; ++ while (total > 0) { ++ size_t bytes; ++ ++ bytes = iopgtable_clear_entry(obj, start); ++ if (bytes == 0) ++ bytes = PAGE_SIZE; ++ else ++ dev_dbg(obj->dev, "%s: unmap %08x(%x) %08x\n", ++ __func__, start, bytes, area->flags); ++ ++ BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE)); ++ ++ total -= bytes; ++ start += bytes; ++ } ++ BUG_ON(total); ++} ++ ++/* template function for all unmapping */ ++static struct sg_table *unmap_vm_area(struct iommu *obj, const u32 da, ++ void (*fn)(const void *), u32 flags) ++{ ++ struct sg_table *sgt = NULL; ++ struct iovm_struct *area; ++ ++ BUG_ON(in_interrupt()); ++ ++ if (!IS_ALIGNED(da, PAGE_SIZE)) { ++ dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da); ++ return NULL; ++ } ++ ++ mutex_lock(&obj->mmap_lock); ++ ++ area = __find_iovm_area(obj, da); ++ if (!area) { ++ dev_err(obj->dev, "%s: no da area(%08x)\n", __func__, da); ++ goto out; ++ } ++ ++ if ((area->flags & flags) != flags) { ++ dev_err(obj->dev, "%s: wrong flags(%08x)\n", __func__, ++ area->flags); ++ goto out; ++ } ++ sgt = (struct sg_table *)area->sgt; ++ ++ unmap_iovm_area(obj, area); ++ ++ fn(area->va); ++ ++ dev_dbg(obj->dev, "%s: %08x-%08x-%08x(%x) %08x\n", __func__, ++ area->da_start, da, area->da_end, ++ area->da_end - area->da_start, area->flags); ++ ++ free_iovm_area(obj, area); ++out: ++ mutex_unlock(&obj->mmap_lock); ++ ++ return sgt; ++} ++ ++static u32 map_iommu_region(struct iommu *obj, u32 da, ++ const struct sg_table *sgt, void *va, size_t bytes, u32 flags) ++{ ++ int err = -ENOMEM; ++ struct iovm_struct *new; ++ ++ mutex_lock(&obj->mmap_lock); ++ ++ new = alloc_iovm_area(obj, da, bytes, flags); ++ if (IS_ERR(new)) { ++ err = PTR_ERR(new); ++ goto err_alloc_iovma; ++ } ++ new->va = va; ++ new->sgt = sgt; ++ ++ if (map_iovm_area(obj, new, sgt, new->flags)) ++ goto err_map; ++ ++ mutex_unlock(&obj->mmap_lock); ++ ++ dev_dbg(obj->dev, "%s: da:%08x(%x) flags:%08x va:%p\n", ++ __func__, new->da_start, bytes, new->flags, va); ++ ++ return new->da_start; ++ ++err_map: ++ free_iovm_area(obj, new); ++err_alloc_iovma: ++ mutex_unlock(&obj->mmap_lock); ++ return err; ++} ++ ++static inline u32 __iommu_vmap(struct iommu *obj, u32 da, ++ const struct sg_table *sgt, void *va, size_t bytes, u32 flags) ++{ ++ return map_iommu_region(obj, da, sgt, va, bytes, flags); ++} ++ ++/** ++ * iommu_vmap - (d)-(p)-(v) address mapper ++ * @obj: objective iommu ++ * @sgt: address of scatter gather table ++ * @flags: iovma and page property ++ * ++ * Creates 1-n-1 mapping with given @sgt and returns @da. ++ * All @sgt element must be io page size aligned. ++ */ ++u32 iommu_vmap(struct iommu *obj, u32 da, const struct sg_table *sgt, ++ u32 flags) ++{ ++ size_t bytes; ++ void *va; ++ ++ if (!obj || !obj->dev || !sgt) ++ return -EINVAL; ++ ++ bytes = sgtable_len(sgt); ++ if (!bytes) ++ return -EINVAL; ++ bytes = PAGE_ALIGN(bytes); ++ ++ va = vmap_sg(sgt); ++ if (IS_ERR(va)) ++ return PTR_ERR(va); ++ ++ flags &= IOVMF_HW_MASK; ++ flags |= IOVMF_DISCONT; ++ flags |= IOVMF_MMIO; ++ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); ++ ++ da = __iommu_vmap(obj, da, sgt, va, bytes, flags); ++ if (IS_ERR_VALUE(da)) ++ vunmap_sg(va); ++ ++ return da; ++} ++EXPORT_SYMBOL_GPL(iommu_vmap); ++ ++/** ++ * iommu_vunmap - release virtual mapping obtained by 'iommu_vmap()' ++ * @obj: objective iommu ++ * @da: iommu device virtual address ++ * ++ * Free the iommu virtually contiguous memory area starting at ++ * @da, which was returned by 'iommu_vmap()'. ++ */ ++struct sg_table *iommu_vunmap(struct iommu *obj, u32 da) ++{ ++ struct sg_table *sgt; ++ /* ++ * 'sgt' is allocated before 'iommu_vmalloc()' is called. ++ * Just returns 'sgt' to the caller to free ++ */ ++ sgt = unmap_vm_area(obj, da, vunmap_sg, IOVMF_DISCONT | IOVMF_MMIO); ++ if (!sgt) ++ dev_err(obj->dev, "%s: No sgt\n", __func__); ++ return sgt; ++} ++EXPORT_SYMBOL_GPL(iommu_vunmap); ++ ++/** ++ * iommu_vmalloc - (d)-(p)-(v) address allocator and mapper ++ * @obj: objective iommu ++ * @da: contiguous iommu virtual memory ++ * @bytes: allocation size ++ * @flags: iovma and page property ++ * ++ * Allocate @bytes linearly and creates 1-n-1 mapping and returns ++ * @da again, which might be adjusted if 'IOVMF_DA_ANON' is set. ++ */ ++u32 iommu_vmalloc(struct iommu *obj, u32 da, size_t bytes, u32 flags) ++{ ++ void *va; ++ struct sg_table *sgt; ++ ++ if (!obj || !obj->dev || !bytes) ++ return -EINVAL; ++ ++ bytes = PAGE_ALIGN(bytes); ++ ++ va = vmalloc(bytes); ++ if (!va) ++ return -ENOMEM; ++ ++ sgt = sgtable_alloc(bytes, flags); ++ if (IS_ERR(sgt)) { ++ da = PTR_ERR(sgt); ++ goto err_sgt_alloc; ++ } ++ sgtable_fill_vmalloc(sgt, va); ++ ++ flags &= IOVMF_HW_MASK; ++ flags |= IOVMF_DISCONT; ++ flags |= IOVMF_ALLOC; ++ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); ++ ++ da = __iommu_vmap(obj, da, sgt, va, bytes, flags); ++ if (IS_ERR_VALUE(da)) ++ goto err_iommu_vmap; ++ ++ return da; ++ ++err_iommu_vmap: ++ sgtable_drain_vmalloc(sgt); ++ sgtable_free(sgt); ++err_sgt_alloc: ++ vfree(va); ++ return da; ++} ++EXPORT_SYMBOL_GPL(iommu_vmalloc); ++ ++/** ++ * iommu_vfree - release memory allocated by 'iommu_vmalloc()' ++ * @obj: objective iommu ++ * @da: iommu device virtual address ++ * ++ * Frees the iommu virtually continuous memory area starting at ++ * @da, as obtained from 'iommu_vmalloc()'. ++ */ ++void iommu_vfree(struct iommu *obj, const u32 da) ++{ ++ struct sg_table *sgt; ++ ++ sgt = unmap_vm_area(obj, da, vfree, IOVMF_DISCONT | IOVMF_ALLOC); ++ if (!sgt) ++ dev_err(obj->dev, "%s: No sgt\n", __func__); ++ sgtable_free(sgt); ++} ++EXPORT_SYMBOL_GPL(iommu_vfree); ++ ++static u32 __iommu_kmap(struct iommu *obj, u32 da, u32 pa, void *va, ++ size_t bytes, u32 flags) ++{ ++ struct sg_table *sgt; ++ ++ sgt = sgtable_alloc(bytes, flags); ++ if (IS_ERR(sgt)) ++ return PTR_ERR(sgt); ++ ++ sgtable_fill_kmalloc(sgt, pa, bytes); ++ ++ da = map_iommu_region(obj, da, sgt, va, bytes, flags); ++ if (IS_ERR_VALUE(da)) { ++ sgtable_drain_kmalloc(sgt); ++ sgtable_free(sgt); ++ } ++ ++ return da; ++} ++ ++/** ++ * iommu_kmap - (d)-(p)-(v) address mapper ++ * @obj: objective iommu ++ * @da: contiguous iommu virtual memory ++ * @pa: contiguous physical memory ++ * @flags: iovma and page property ++ * ++ * Creates 1-1-1 mapping and returns @da again, which can be ++ * adjusted if 'IOVMF_DA_ANON' is set. ++ */ ++u32 iommu_kmap(struct iommu *obj, u32 da, u32 pa, size_t bytes, ++ u32 flags) ++{ ++ void *va; ++ ++ if (!obj || !obj->dev || !bytes) ++ return -EINVAL; ++ ++ bytes = PAGE_ALIGN(bytes); ++ ++ va = ioremap(pa, bytes); ++ if (!va) ++ return -ENOMEM; ++ ++ flags &= IOVMF_HW_MASK; ++ flags |= IOVMF_LINEAR; ++ flags |= IOVMF_MMIO; ++ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); ++ ++ da = __iommu_kmap(obj, da, pa, va, bytes, flags); ++ if (IS_ERR_VALUE(da)) ++ iounmap(va); ++ ++ return da; ++} ++EXPORT_SYMBOL_GPL(iommu_kmap); ++ ++/** ++ * iommu_kunmap - release virtual mapping obtained by 'iommu_kmap()' ++ * @obj: objective iommu ++ * @da: iommu device virtual address ++ * ++ * Frees the iommu virtually contiguous memory area starting at ++ * @da, which was passed to and was returned by'iommu_kmap()'. ++ */ ++void iommu_kunmap(struct iommu *obj, u32 da) ++{ ++ struct sg_table *sgt; ++ ++ sgt = unmap_vm_area(obj, da, __iounmap, IOVMF_LINEAR | IOVMF_MMIO); ++ if (!sgt) ++ dev_err(obj->dev, "%s: No sgt\n", __func__); ++ sgtable_free(sgt); ++} ++EXPORT_SYMBOL_GPL(iommu_kunmap); ++ ++/** ++ * iommu_kmalloc - (d)-(p)-(v) address allocator and mapper ++ * @obj: objective iommu ++ * @da: contiguous iommu virtual memory ++ * @bytes: bytes for allocation ++ * @flags: iovma and page property ++ * ++ * Allocate @bytes linearly and creates 1-1-1 mapping and returns ++ * @da again, which might be adjusted if 'IOVMF_DA_ANON' is set. ++ */ ++u32 iommu_kmalloc(struct iommu *obj, u32 da, size_t bytes, u32 flags) ++{ ++ void *va; ++ u32 pa; ++ ++ if (!obj || !obj->dev || !bytes) ++ return -EINVAL; ++ ++ bytes = PAGE_ALIGN(bytes); ++ ++ va = kmalloc(bytes, GFP_KERNEL | GFP_DMA); ++ if (!va) ++ return -ENOMEM; ++ pa = virt_to_phys(va); ++ ++ flags &= IOVMF_HW_MASK; ++ flags |= IOVMF_LINEAR; ++ flags |= IOVMF_ALLOC; ++ flags |= (da ? IOVMF_DA_FIXED : IOVMF_DA_ANON); ++ ++ da = __iommu_kmap(obj, da, pa, va, bytes, flags); ++ if (IS_ERR_VALUE(da)) ++ kfree(va); ++ ++ return da; ++} ++EXPORT_SYMBOL_GPL(iommu_kmalloc); ++ ++/** ++ * iommu_kfree - release virtual mapping obtained by 'iommu_kmalloc()' ++ * @obj: objective iommu ++ * @da: iommu device virtual address ++ * ++ * Frees the iommu virtually contiguous memory area starting at ++ * @da, which was passed to and was returned by'iommu_kmalloc()'. ++ */ ++void iommu_kfree(struct iommu *obj, u32 da) ++{ ++ struct sg_table *sgt; ++ ++ sgt = unmap_vm_area(obj, da, kfree, IOVMF_LINEAR | IOVMF_ALLOC); ++ if (!sgt) ++ dev_err(obj->dev, "%s: No sgt\n", __func__); ++ sgtable_free(sgt); ++} ++EXPORT_SYMBOL_GPL(iommu_kfree); ++ ++ ++static int __init iovmm_init(void) ++{ ++ const unsigned long flags = SLAB_HWCACHE_ALIGN; ++ struct kmem_cache *p; ++ ++ p = kmem_cache_create("iovm_area_cache", sizeof(struct iovm_struct), 0, ++ flags, NULL); ++ if (!p) ++ return -ENOMEM; ++ iovm_area_cachep = p; ++ ++ return 0; ++} ++module_init(iovmm_init); ++ ++static void __exit iovmm_exit(void) ++{ ++ kmem_cache_destroy(iovm_area_cachep); ++} ++module_exit(iovmm_exit); ++ ++MODULE_DESCRIPTION("omap iommu: simple virtual address space management"); ++MODULE_AUTHOR("Hiroshi DOYU <Hiroshi.DOYU@nokia.com>"); ++MODULE_LICENSE("GPL v2"); +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0005-omap-iommu-entries-for-Kconfig-and-Makefile.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0005-omap-iommu-entries-for-Kconfig-and-Makefile.patch new file mode 100644 index 0000000000..c0f9e4d9ac --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0005-omap-iommu-entries-for-Kconfig-and-Makefile.patch @@ -0,0 +1,45 @@ +From 7de046a6a8446358001c38ad1d0b2b829ca0c98c Mon Sep 17 00:00:00 2001 +From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +Date: Wed, 28 Jan 2009 21:32:08 +0200 +Subject: [PATCH] omap iommu: entries for Kconfig and Makefile + +Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +--- + arch/arm/plat-omap/Kconfig | 8 ++++++++ + arch/arm/plat-omap/Makefile | 1 + + 2 files changed, 9 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig +index b16ae76..2090bb5 100644 +--- a/arch/arm/plat-omap/Kconfig ++++ b/arch/arm/plat-omap/Kconfig +@@ -176,6 +176,14 @@ config OMAP_MBOX_FWK + Say Y here if you want to use OMAP Mailbox framework support for + DSP, IVA1.0 and IVA2 in OMAP1/2/3. + ++config OMAP_IOMMU ++ tristate "IOMMU support" ++ depends on ARCH_OMAP ++ default n ++ help ++ Say Y here if you want to use OMAP IOMMU support for IVA2 and ++ Camera in OMAP3. ++ + choice + prompt "System timer" + default OMAP_MPU_TIMER +diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile +index 3ebc09e..aa8f6df 100644 +--- a/arch/arm/plat-omap/Makefile ++++ b/arch/arm/plat-omap/Makefile +@@ -13,6 +13,7 @@ obj- := + obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o + + obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o ++obj-$(CONFIG_OMAP_IOMMU) += iommu.o iovmm.o + + obj-$(CONFIG_CPU_FREQ) += cpu-omap.o + obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0006-omap-iommu-Don-t-try-BUG_ON-in_interrupt.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0006-omap-iommu-Don-t-try-BUG_ON-in_interrupt.patch new file mode 100644 index 0000000000..54a7abfe85 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0006-omap-iommu-Don-t-try-BUG_ON-in_interrupt.patch @@ -0,0 +1,26 @@ +From b03f695e25bbdaa95a2cc87e15ee8592e7ca128d Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Feb 2009 18:01:29 +0200 +Subject: [PATCH] omap iommu: Don't try BUG_ON(in_interrupt()) + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + arch/arm/plat-omap/iovmm.c | 2 -- + 1 files changed, 0 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/plat-omap/iovmm.c b/arch/arm/plat-omap/iovmm.c +index 6726d10..bdfbb09 100644 +--- a/arch/arm/plat-omap/iovmm.c ++++ b/arch/arm/plat-omap/iovmm.c +@@ -523,8 +523,6 @@ static struct sg_table *unmap_vm_area(struct iommu *obj, const u32 da, + struct sg_table *sgt = NULL; + struct iovm_struct *area; + +- BUG_ON(in_interrupt()); +- + if (!IS_ALIGNED(da, PAGE_SIZE)) { + dev_err(obj->dev, "%s: alignment err(%08x)\n", __func__, da); + return NULL; +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0007-omap-iommu-We-support-chained-scatterlists-probabl.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0007-omap-iommu-We-support-chained-scatterlists-probabl.patch new file mode 100644 index 0000000000..d8ad0eb0b7 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0007-omap-iommu-We-support-chained-scatterlists-probabl.patch @@ -0,0 +1,24 @@ +From 24f984f784cae1a4515fe1be8db1ac24cdf51e84 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Feb 2009 18:37:41 +0200 +Subject: [PATCH] omap iommu: We support chained scatterlists, probably. :) + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + arch/arm/include/asm/scatterlist.h | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/include/asm/scatterlist.h b/arch/arm/include/asm/scatterlist.h +index ca0a37d..393f8b8 100644 +--- a/arch/arm/include/asm/scatterlist.h ++++ b/arch/arm/include/asm/scatterlist.h +@@ -24,4 +24,6 @@ struct scatterlist { + #define sg_dma_address(sg) ((sg)->dma_address) + #define sg_dma_len(sg) ((sg)->length) + ++#define ARCH_HAS_SG_CHAIN ++ + #endif /* _ASMARM_SCATTERLIST_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0008-omap2-iommu-entries-for-Kconfig-and-Makefile.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0008-omap2-iommu-entries-for-Kconfig-and-Makefile.patch new file mode 100644 index 0000000000..298e797c37 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/iommu/0008-omap2-iommu-entries-for-Kconfig-and-Makefile.patch @@ -0,0 +1,29 @@ +From 3c65ff4a684d3e0f4d9c59e731975408452c3743 Mon Sep 17 00:00:00 2001 +From: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +Date: Wed, 28 Jan 2009 21:32:09 +0200 +Subject: [PATCH] omap2 iommu: entries for Kconfig and Makefile + +Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> +--- + arch/arm/mach-omap2/Makefile | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile +index b44bb78..33b5aa8 100644 +--- a/arch/arm/mach-omap2/Makefile ++++ b/arch/arm/mach-omap2/Makefile +@@ -38,6 +38,11 @@ obj-$(CONFIG_ARCH_OMAP3) += clock34xx.o + obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o + mailbox_mach-objs := mailbox.o + ++iommu-y += iommu2.o ++iommu-$(CONFIG_ARCH_OMAP3) += omap3-iommu.o ++ ++obj-$(CONFIG_OMAP_IOMMU) += $(iommu-y) ++ + # Specific board support + obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o + obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o board-h4-mmc.o +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0001-omap3isp-Add-ISP-main-driver-and-register-definitio.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0001-omap3isp-Add-ISP-main-driver-and-register-definitio.patch new file mode 100644 index 0000000000..e6e07d8afc --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0001-omap3isp-Add-ISP-main-driver-and-register-definitio.patch @@ -0,0 +1,4625 @@ +From 77c99cd863b906c803c3dec08753c19bf9b67882 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add ISP main driver and register definitions + +TODO: + +- Release resoures in isp_probe() if something fails. + +- Implement a sensible generic interface so that the ISP can offer a + v4l2_subdev (like the v4l2-int-device slaves) interface towards the + camera driver. + +- Handle CSI1 and CSI2 error cases (currently unhandled?). + +- Fix H3A / HIST interrupt enabling / disabling. + +- Clean up the private ioctls. + +- Handle SBL overflows somehow. + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/Makefile | 2 + + drivers/media/video/isp/Makefile | 12 + + drivers/media/video/isp/isp.c | 2547 ++++++++++++++++++++++++++++++++++++++ + drivers/media/video/isp/isp.h | 318 +++++ + drivers/media/video/isp/ispreg.h | 1674 +++++++++++++++++++++++++ + 5 files changed, 4553 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/Makefile + create mode 100644 drivers/media/video/isp/isp.c + create mode 100644 drivers/media/video/isp/isp.h + create mode 100644 drivers/media/video/isp/ispreg.h + +diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile +index 72f6d03..e654270 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -106,6 +106,8 @@ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o + obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o + obj-$(CONFIG_VIDEO_OV7670) += ov7670.o + ++obj-y += isp/ ++ + obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o + + obj-$(CONFIG_USB_DABUSB) += dabusb.o +diff --git a/drivers/media/video/isp/Makefile b/drivers/media/video/isp/Makefile +new file mode 100644 +index 0000000..f14d617 +--- /dev/null ++++ b/drivers/media/video/isp/Makefile +@@ -0,0 +1,12 @@ ++# Makefile for OMAP3 ISP driver ++ ++ifdef CONFIG_ARCH_OMAP3410 ++isp-mod-objs += \ ++ isp.o ispccdc.o ++else ++isp-mod-objs += \ ++ isp.o ispccdc.o ispmmu.o \ ++ isppreview.o ispresizer.o isph3a.o isphist.o isp_af.o ispcsi2.o ++endif ++ ++obj-$(CONFIG_VIDEO_OMAP3) += isp-mod.o +diff --git a/drivers/media/video/isp/isp.c b/drivers/media/video/isp/isp.c +new file mode 100644 +index 0000000..54c839b +--- /dev/null ++++ b/drivers/media/video/isp/isp.c +@@ -0,0 +1,2547 @@ ++/* ++ * isp.c ++ * ++ * Driver Library for ISP Control module in TI's OMAP3 Camera ISP ++ * ISP interface and IRQ related APIs are defined here. ++ * ++ * Copyright (C) 2009 Texas Instruments. ++ * Copyright (C) 2009 Nokia. ++ * ++ * Contributors: ++ * Sameer Venkatraman <sameerv@ti.com> ++ * Mohit Jalori <mjalori@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * Toni Leinonen <toni.leinonen@nokia.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <asm/cacheflush.h> ++ ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/clk.h> ++#include <linux/dma-mapping.h> ++#include <linux/vmalloc.h> ++#include <linux/platform_device.h> ++ ++#include "isp.h" ++#include "ispmmu.h" ++#include "ispreg.h" ++#include "ispccdc.h" ++#include "isph3a.h" ++#include "isphist.h" ++#include "isp_af.h" ++#include "isppreview.h" ++#include "ispresizer.h" ++#include "ispcsi2.h" ++ ++static struct isp_device *omap3isp; ++ ++static int isp_try_size(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output); ++ ++static void isp_save_ctx(void); ++ ++static void isp_restore_ctx(void); ++ ++static void isp_buf_init(void); ++ ++/* List of image formats supported via OMAP ISP */ ++const static struct v4l2_fmtdesc isp_formats[] = { ++ { ++ .description = "UYVY, packed", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ }, ++ { ++ .description = "YUYV (YUV 4:2:2), packed", ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ }, ++ { ++ .description = "Bayer10 (GrR/BGb)", ++ .pixelformat = V4L2_PIX_FMT_SGRBG10, ++ }, ++}; ++ ++/* ISP Crop capabilities */ ++static struct v4l2_rect ispcroprect; ++static struct v4l2_rect cur_rect; ++ ++/** ++ * struct vcontrol - Video control structure. ++ * @qc: V4L2 Query control structure. ++ * @current_value: Current value of the control. ++ */ ++static struct vcontrol { ++ struct v4l2_queryctrl qc; ++ int current_value; ++} video_control[] = { ++ { ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .minimum = ISPPRV_BRIGHT_LOW, ++ .maximum = ISPPRV_BRIGHT_HIGH, ++ .step = ISPPRV_BRIGHT_STEP, ++ .default_value = ISPPRV_BRIGHT_DEF, ++ }, ++ .current_value = ISPPRV_BRIGHT_DEF, ++ }, ++ { ++ { ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .minimum = ISPPRV_CONTRAST_LOW, ++ .maximum = ISPPRV_CONTRAST_HIGH, ++ .step = ISPPRV_CONTRAST_STEP, ++ .default_value = ISPPRV_CONTRAST_DEF, ++ }, ++ .current_value = ISPPRV_CONTRAST_DEF, ++ }, ++ { ++ { ++ .id = V4L2_CID_COLORFX, ++ .type = V4L2_CTRL_TYPE_MENU, ++ .name = "Color Effects", ++ .minimum = V4L2_COLORFX_NONE, ++ .maximum = V4L2_COLORFX_SEPIA, ++ .step = 1, ++ .default_value = V4L2_COLORFX_NONE, ++ }, ++ .current_value = V4L2_COLORFX_NONE, ++ } ++}; ++ ++static struct v4l2_querymenu video_menu[] = { ++ { ++ .id = V4L2_CID_COLORFX, ++ .index = 0, ++ .name = "None", ++ }, ++ { ++ .id = V4L2_CID_COLORFX, ++ .index = 1, ++ .name = "B&W", ++ }, ++ { ++ .id = V4L2_CID_COLORFX, ++ .index = 2, ++ .name = "Sepia", ++ }, ++}; ++ ++struct isp_buf { ++ dma_addr_t isp_addr; ++ void (*complete)(struct videobuf_buffer *vb, void *priv); ++ struct videobuf_buffer *vb; ++ void *priv; ++ u32 vb_state; ++}; ++ ++#define ISP_BUFS_IS_FULL(bufs) \ ++ (((bufs)->queue + 1) % NUM_BUFS == (bufs)->done) ++#define ISP_BUFS_IS_EMPTY(bufs) ((bufs)->queue == (bufs)->done) ++#define ISP_BUFS_IS_LAST(bufs) \ ++ ((bufs)->queue == ((bufs)->done + 1) % NUM_BUFS) ++#define ISP_BUFS_QUEUED(bufs) \ ++ ((((bufs)->done - (bufs)->queue + NUM_BUFS)) % NUM_BUFS) ++#define ISP_BUF_DONE(bufs) ((bufs)->buf + (bufs)->done) ++#define ISP_BUF_NEXT_DONE(bufs) \ ++ ((bufs)->buf + ((bufs)->done + 1) % NUM_BUFS) ++#define ISP_BUF_QUEUE(bufs) ((bufs)->buf + (bufs)->queue) ++#define ISP_BUF_MARK_DONE(bufs) \ ++ (bufs)->done = ((bufs)->done + 1) % NUM_BUFS; ++#define ISP_BUF_MARK_QUEUED(bufs) \ ++ (bufs)->queue = ((bufs)->queue + 1) % NUM_BUFS; ++ ++struct isp_bufs { ++ dma_addr_t isp_addr_capture[VIDEO_MAX_FRAME]; ++ spinlock_t lock; /* For handling current buffer */ ++ /* queue full: (ispsg.queue + 1) % NUM_BUFS == ispsg.done ++ queue empty: ispsg.queue == ispsg.done */ ++ struct isp_buf buf[NUM_BUFS]; ++ /* Next slot to queue a buffer. */ ++ int queue; ++ /* Buffer that is being processed. */ ++ int done; ++ /* Wait for this many hs_vs before anything else. */ ++ int wait_hs_vs; ++}; ++ ++/** ++ * struct ispirq - Structure for containing callbacks to be called in ISP ISR. ++ * @isp_callbk: Array which stores callback functions, indexed by the type of ++ * callback (8 possible types). ++ * @isp_callbk_arg1: Pointer to array containing pointers to the first argument ++ * to be passed to the requested callback function. ++ * @isp_callbk_arg2: Pointer to array containing pointers to the second ++ * argument to be passed to the requested callback function. ++ * ++ * This structure is used to contain all the callback functions related for ++ * each callback type (CBK_CCDC_VD0, CBK_CCDC_VD1, CBK_PREV_DONE, ++ * CBK_RESZ_DONE, CBK_MMU_ERR, CBK_H3A_AWB_DONE, CBK_HIST_DONE, CBK_HS_VS, ++ * CBK_LSC_ISR). ++ */ ++struct isp_irq { ++ isp_callback_t isp_callbk[CBK_END]; ++ isp_vbq_callback_ptr isp_callbk_arg1[CBK_END]; ++ void *isp_callbk_arg2[CBK_END]; ++}; ++ ++/** ++ * struct ispmodule - Structure for storing ISP sub-module information. ++ * @isp_pipeline: Bit mask for submodules enabled within the ISP. ++ * @applyCrop: Flag to do a crop operation when video buffer queue ISR is done ++ * @pix: Structure containing the format and layout of the output image. ++ * @ccdc_input_width: ISP CCDC module input image width. ++ * @ccdc_input_height: ISP CCDC module input image height. ++ * @ccdc_output_width: ISP CCDC module output image width. ++ * @ccdc_output_height: ISP CCDC module output image height. ++ * @preview_input_width: ISP Preview module input image width. ++ * @preview_input_height: ISP Preview module input image height. ++ * @preview_output_width: ISP Preview module output image width. ++ * @preview_output_height: ISP Preview module output image height. ++ * @resizer_input_width: ISP Resizer module input image width. ++ * @resizer_input_height: ISP Resizer module input image height. ++ * @resizer_output_width: ISP Resizer module output image width. ++ * @resizer_output_height: ISP Resizer module output image height. ++ */ ++struct isp_module { ++ unsigned int isp_pipeline; ++ int applyCrop; ++ struct v4l2_pix_format pix; ++ unsigned int ccdc_input_width; ++ unsigned int ccdc_input_height; ++ unsigned int ccdc_output_width; ++ unsigned int ccdc_output_height; ++ unsigned int preview_input_width; ++ unsigned int preview_input_height; ++ unsigned int preview_output_width; ++ unsigned int preview_output_height; ++ unsigned int resizer_input_width; ++ unsigned int resizer_input_height; ++ unsigned int resizer_output_width; ++ unsigned int resizer_output_height; ++}; ++ ++#define RAW_CAPTURE(isp) \ ++ (!((isp)->module.isp_pipeline & OMAP_ISP_PREVIEW)) ++ ++/** ++ * struct isp - Structure for storing ISP Control module information ++ * @lock: Spinlock to sync between isr and processes. ++ * @isp_mutex: Semaphore used to get access to the ISP. ++ * @ref_count: Reference counter. ++ * @cam_ick: Pointer to ISP Interface clock. ++ * @cam_fck: Pointer to ISP Functional clock. ++ * ++ * This structure is used to store the OMAP ISP Control Information. ++ */ ++static struct isp { ++ spinlock_t lock; /* For handling registered ISP callbacks */ ++ struct mutex isp_mutex; /* For handling ref_count field */ ++ int ref_count; ++ struct clk *cam_ick; ++ struct clk *cam_mclk; ++ struct clk *csi2_fck; ++ struct isp_interface_config *config; ++ dma_addr_t tmp_buf; ++ size_t tmp_buf_size; ++ unsigned long tmp_buf_offset; ++ struct isp_bufs bufs; ++ struct isp_irq irq; ++ struct isp_module module; ++} isp_obj; ++ ++/* Structure for saving/restoring ISP module registers */ ++static struct isp_reg isp_reg_list[] = { ++ {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_GRESET_LENGTH, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_REPLAY, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_FRAME, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_DELAY, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_STRB_DELAY, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_SHUT_DELAY, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_LENGTH, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_STRB_LENGTH, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_SHUT_LENGTH, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF_SYSCONFIG, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF_IRQENABLE, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_CTRL, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_CTRL, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_START, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_START, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_END, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_END, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_WINDOWSIZE, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_WINDOWSIZE, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_THRESHOLD, 0}, ++ {OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_THRESHOLD, 0}, ++ {0, ISP_TOK_TERM, 0} ++}; ++ ++u32 isp_reg_readl(enum isp_mem_resources isp_mmio_range, u32 reg_offset) ++{ ++ return __raw_readl(omap3isp->mmio_base[isp_mmio_range] + reg_offset); ++} ++EXPORT_SYMBOL(isp_reg_readl); ++ ++void isp_reg_writel(u32 reg_value, enum isp_mem_resources isp_mmio_range, ++ u32 reg_offset) ++{ ++ __raw_writel(reg_value, ++ omap3isp->mmio_base[isp_mmio_range] + reg_offset); ++} ++EXPORT_SYMBOL(isp_reg_writel); ++ ++/* ++ * ++ * V4L2 Handling ++ * ++ */ ++ ++/** ++ * find_vctrl - Returns the index of the ctrl array of the requested ctrl ID. ++ * @id: Requested control ID. ++ * ++ * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of ++ * domain. ++ **/ ++static int find_vctrl(int id) ++{ ++ int i; ++ ++ if (id < V4L2_CID_BASE) ++ return -EDOM; ++ ++ for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--) ++ if (video_control[i].qc.id == id) ++ break; ++ ++ if (i < 0) ++ i = -EINVAL; ++ ++ return i; ++} ++ ++static int find_next_vctrl(int id) ++{ ++ int i; ++ u32 best = (u32)-1; ++ ++ for (i = 0; i < ARRAY_SIZE(video_control); i++) { ++ if (video_control[i].qc.id > id && ++ (best == (u32)-1 || ++ video_control[i].qc.id < ++ video_control[best].qc.id)) { ++ best = i; ++ } ++ } ++ ++ if (best == (u32)-1) ++ return -EINVAL; ++ ++ return best; ++} ++ ++/** ++ * find_vmenu - Returns index of the menu array of the requested ctrl option. ++ * @id: Requested control ID. ++ * @index: Requested menu option index. ++ * ++ * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of ++ * domain. ++ **/ ++static int find_vmenu(int id, int index) ++{ ++ int i; ++ ++ if (id < V4L2_CID_BASE) ++ return -EDOM; ++ ++ for (i = (ARRAY_SIZE(video_menu) - 1); i >= 0; i--) { ++ if (video_menu[i].id != id || video_menu[i].index != index) ++ continue; ++ return i; ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * isp_release_resources - Free ISP submodules ++ **/ ++static void isp_release_resources(void) ++{ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_CCDC) ++ ispccdc_free(); ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW) ++ isppreview_free(); ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) ++ ispresizer_free(); ++ return; ++} ++ ++static int isp_wait(int (*busy)(void), int wait_for_busy, int max_wait) ++{ ++ int wait = 0; ++ ++ if (max_wait == 0) ++ max_wait = 10000; /* 10 ms */ ++ ++ while ((wait_for_busy && !busy()) ++ || (!wait_for_busy && busy())) { ++ rmb(); ++ udelay(1); ++ wait++; ++ if (wait > max_wait) { ++ printk(KERN_ALERT "%s: wait is too much\n", __func__); ++ return -EBUSY; ++ } ++ } ++ DPRINTK_ISPCTRL(KERN_ALERT "%s: wait %d\n", __func__, wait); ++ ++ return 0; ++} ++ ++static int ispccdc_sbl_wait_idle(int max_wait) ++{ ++ return isp_wait(ispccdc_sbl_busy, 0, max_wait); ++} ++ ++static void isp_enable_interrupts(int is_raw) ++{ ++ isp_reg_writel(-1, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ | ++ IRQ0ENABLE_HS_VS_IRQ | ++ IRQ0ENABLE_CCDC_VD0_IRQ | ++ IRQ0ENABLE_CCDC_VD1_IRQ); ++ ++ if (is_raw) ++ return; ++ ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_PRV_DONE_IRQ | ++ IRQ0ENABLE_RSZ_DONE_IRQ); ++ ++ return; ++} ++ ++static void isp_disable_interrupts(void) ++{ ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~(IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ | ++ IRQ0ENABLE_HS_VS_IRQ | ++ IRQ0ENABLE_CCDC_VD0_IRQ | ++ IRQ0ENABLE_CCDC_VD1_IRQ | ++ IRQ0ENABLE_PRV_DONE_IRQ | ++ IRQ0ENABLE_RSZ_DONE_IRQ)); ++} ++ ++/** ++ * isp_set_callback - Sets the callback for the ISP module done events. ++ * @type: Type of the event for which callback is requested. ++ * @callback: Method to be called as callback in the ISR context. ++ * @arg1: First argument to be passed when callback is called in ISR. ++ * @arg2: Second argument to be passed when callback is called in ISR. ++ * ++ * This function sets a callback function for a done event in the ISP ++ * module, and enables the corresponding interrupt. ++ **/ ++int isp_set_callback(enum isp_callback_type type, isp_callback_t callback, ++ isp_vbq_callback_ptr arg1, ++ void *arg2) ++{ ++ unsigned long irqflags = 0; ++ ++ if (callback == NULL) { ++ DPRINTK_ISPCTRL("ISP_ERR : Null Callback\n"); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&isp_obj.lock, irqflags); ++ isp_obj.irq.isp_callbk[type] = callback; ++ isp_obj.irq.isp_callbk_arg1[type] = arg1; ++ isp_obj.irq.isp_callbk_arg2[type] = arg2; ++ spin_unlock_irqrestore(&isp_obj.lock, irqflags); ++ ++ switch (type) { ++ case CBK_H3A_AWB_DONE: ++ isp_reg_writel(IRQ0ENABLE_H3A_AWB_DONE_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_H3A_AWB_DONE_IRQ); ++ break; ++ case CBK_H3A_AF_DONE: ++ isp_reg_writel(IRQ0ENABLE_H3A_AF_DONE_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_H3A_AF_DONE_IRQ); ++ break; ++ case CBK_HIST_DONE: ++ isp_reg_writel(IRQ0ENABLE_HIST_DONE_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_HIST_DONE_IRQ); ++ break; ++ case CBK_PREV_DONE: ++ isp_reg_writel(IRQ0ENABLE_PRV_DONE_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_PRV_DONE_IRQ); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_set_callback); ++ ++/** ++ * isp_unset_callback - Clears the callback for the ISP module done events. ++ * @type: Type of the event for which callback to be cleared. ++ * ++ * This function clears a callback function for a done event in the ISP ++ * module, and disables the corresponding interrupt. ++ **/ ++int isp_unset_callback(enum isp_callback_type type) ++{ ++ unsigned long irqflags = 0; ++ ++ spin_lock_irqsave(&isp_obj.lock, irqflags); ++ isp_obj.irq.isp_callbk[type] = NULL; ++ isp_obj.irq.isp_callbk_arg1[type] = NULL; ++ isp_obj.irq.isp_callbk_arg2[type] = NULL; ++ spin_unlock_irqrestore(&isp_obj.lock, irqflags); ++ ++ switch (type) { ++ case CBK_H3A_AWB_DONE: ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~IRQ0ENABLE_H3A_AWB_DONE_IRQ); ++ break; ++ case CBK_H3A_AF_DONE: ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~IRQ0ENABLE_H3A_AF_DONE_IRQ); ++ break; ++ case CBK_HIST_DONE: ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~IRQ0ENABLE_HIST_DONE_IRQ); ++ break; ++ case CBK_CSIA: ++ isp_csi2_irq_set(0); ++ break; ++ case CBK_CSIB: ++ isp_reg_writel(IRQ0ENABLE_CSIB_IRQ, OMAP3_ISP_IOMEM_MAIN, ++ ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_CSIB_IRQ); ++ break; ++ case CBK_PREV_DONE: ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~IRQ0ENABLE_PRV_DONE_IRQ); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_unset_callback); ++ ++/** ++ * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. ++ * @xclk: Desired frequency of the clock in Hz. ++ * @xclksel: XCLK to configure (0 = A, 1 = B). ++ * ++ * Configures the specified MCLK divisor in the ISP timing control register ++ * (TCTRL_CTRL) to generate the desired xclk clock value. ++ * ++ * Divisor = CM_CAM_MCLK_HZ / xclk ++ * ++ * Returns the final frequency that is actually being generated ++ **/ ++u32 isp_set_xclk(u32 xclk, u8 xclksel) ++{ ++ u32 divisor; ++ u32 currentxclk; ++ ++ if (xclk >= CM_CAM_MCLK_HZ) { ++ divisor = ISPTCTRL_CTRL_DIV_BYPASS; ++ currentxclk = CM_CAM_MCLK_HZ; ++ } else if (xclk >= 2) { ++ divisor = CM_CAM_MCLK_HZ / xclk; ++ if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) ++ divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; ++ currentxclk = CM_CAM_MCLK_HZ / divisor; ++ } else { ++ divisor = xclk; ++ currentxclk = 0; ++ } ++ ++ switch (xclksel) { ++ case 0: ++ isp_reg_and_or(OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, ++ ~ISPTCTRL_CTRL_DIVA_MASK, ++ divisor << ISPTCTRL_CTRL_DIVA_SHIFT); ++ DPRINTK_ISPCTRL("isp_set_xclk(): cam_xclka set to %d Hz\n", ++ currentxclk); ++ break; ++ case 1: ++ isp_reg_and_or(OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, ++ ~ISPTCTRL_CTRL_DIVB_MASK, ++ divisor << ISPTCTRL_CTRL_DIVB_SHIFT); ++ DPRINTK_ISPCTRL("isp_set_xclk(): cam_xclkb set to %d Hz\n", ++ currentxclk); ++ break; ++ default: ++ DPRINTK_ISPCTRL("ISP_ERR: isp_set_xclk(): Invalid requested " ++ "xclk. Must be 0 (A) or 1 (B)." ++ "\n"); ++ return -EINVAL; ++ } ++ ++ return currentxclk; ++} ++EXPORT_SYMBOL(isp_set_xclk); ++ ++/** ++ * isp_power_settings - Sysconfig settings, for Power Management. ++ * @isp_sysconfig: Structure containing the power settings for ISP to configure ++ * ++ * Sets the power settings for the ISP, and SBL bus. ++ **/ ++static void isp_power_settings(int idle) ++{ ++ if (idle) { ++ isp_reg_writel(ISP_SYSCONFIG_AUTOIDLE | ++ (ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY << ++ ISP_SYSCONFIG_MIDLEMODE_SHIFT), ++ OMAP3_ISP_IOMEM_MAIN, ++ ISP_SYSCONFIG); ++ if (omap_rev() == OMAP3430_REV_ES1_0) { ++ isp_reg_writel(ISPCSI1_AUTOIDLE | ++ (ISPCSI1_MIDLEMODE_SMARTSTANDBY << ++ ISPCSI1_MIDLEMODE_SHIFT), ++ OMAP3_ISP_IOMEM_CSI2A, ++ ISP_CSIA_SYSCONFIG); ++ isp_reg_writel(ISPCSI1_AUTOIDLE | ++ (ISPCSI1_MIDLEMODE_SMARTSTANDBY << ++ ISPCSI1_MIDLEMODE_SHIFT), ++ OMAP3_ISP_IOMEM_CCP2, ++ ISP_CSIB_SYSCONFIG); ++ } ++ isp_reg_writel(ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, ++ ISP_CTRL); ++ ++ } else { ++ isp_reg_writel(ISP_SYSCONFIG_AUTOIDLE | ++ (ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY << ++ ISP_SYSCONFIG_MIDLEMODE_SHIFT), ++ OMAP3_ISP_IOMEM_MAIN, ++ ISP_SYSCONFIG); ++ if (omap_rev() == OMAP3430_REV_ES1_0) { ++ isp_reg_writel(ISPCSI1_AUTOIDLE | ++ (ISPCSI1_MIDLEMODE_FORCESTANDBY << ++ ISPCSI1_MIDLEMODE_SHIFT), ++ OMAP3_ISP_IOMEM_CSI2A, ++ ISP_CSIA_SYSCONFIG); ++ ++ isp_reg_writel(ISPCSI1_AUTOIDLE | ++ (ISPCSI1_MIDLEMODE_FORCESTANDBY << ++ ISPCSI1_MIDLEMODE_SHIFT), ++ OMAP3_ISP_IOMEM_CCP2, ++ ISP_CSIB_SYSCONFIG); ++ } ++ ++ isp_reg_writel(ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, ++ ISP_CTRL); ++ } ++} ++ ++#define BIT_SET(var, shift, mask, val) \ ++ do { \ ++ var = (var & ~(mask << shift)) \ ++ | (val << shift); \ ++ } while (0) ++ ++static int isp_init_csi(struct isp_interface_config *config) ++{ ++ u32 i = 0, val, reg; ++ int format; ++ ++ switch (config->u.csi.format) { ++ case V4L2_PIX_FMT_SGRBG10: ++ format = 0x16; /* RAW10+VP */ ++ break; ++ case V4L2_PIX_FMT_SGRBG10DPCM8: ++ format = 0x12; /* RAW8+DPCM10+VP */ ++ break; ++ default: ++ printk(KERN_ERR "isp_init_csi: bad csi format\n"); ++ return -EINVAL; ++ } ++ ++ /* Reset the CSI and wait for reset to complete */ ++ isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSCONFIG) | ++ BIT(1), ++ OMAP3_ISP_IOMEM_CCP2, ++ ISPCSI1_SYSCONFIG); ++ while (!(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSSTATUS) & ++ BIT(0))) { ++ udelay(10); ++ if (i++ > 10) ++ break; ++ } ++ if (!(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSSTATUS) & ++ BIT(0))) { ++ printk(KERN_WARNING ++ "omap3_isp: timeout waiting for csi reset\n"); ++ } ++ ++ /* ISPCSI1_CTRL */ ++ val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL); ++ val &= ~BIT(11); /* Enable VP only off -> ++ extract embedded data to interconnect */ ++ BIT_SET(val, 8, 0x3, config->u.csi.vpclk); /* Video port clock */ ++/* val |= BIT(3); */ /* Wait for FEC before disabling interface */ ++ val |= BIT(2); /* I/O cell output is parallel ++ (no effect, but errata says should be enabled ++ for class 1/2) */ ++ val |= BIT(12); /* VP clock polarity to falling edge ++ (needed or bad picture!) */ ++ ++ /* Data/strobe physical layer */ ++ BIT_SET(val, 1, 1, config->u.csi.signalling); ++ BIT_SET(val, 10, 1, config->u.csi.strobe_clock_inv); ++ val |= BIT(4); /* Magic bit to enable CSI1 and strobe mode */ ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL); ++ ++ /* ISPCSI1_LCx_CTRL logical channel #0 */ ++ reg = ISPCSI1_LCx_CTRL(0); /* reg = ISPCSI1_CTRL1; */ ++ val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, reg); ++ /* Format = RAW10+VP or RAW8+DPCM10+VP*/ ++ BIT_SET(val, 3, 0x1f, format); ++ /* Enable setting of frame regions of interest */ ++ BIT_SET(val, 1, 1, 1); ++ BIT_SET(val, 2, 1, config->u.csi.crc); ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, reg); ++ ++ /* ISPCSI1_DAT_START for logical channel #0 */ ++ reg = ISPCSI1_LCx_DAT_START(0); /* reg = ISPCSI1_DAT_START; */ ++ val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, reg); ++ BIT_SET(val, 16, 0xfff, config->u.csi.data_start); ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, reg); ++ ++ /* ISPCSI1_DAT_SIZE for logical channel #0 */ ++ reg = ISPCSI1_LCx_DAT_SIZE(0); /* reg = ISPCSI1_DAT_SIZE; */ ++ val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, reg); ++ BIT_SET(val, 16, 0xfff, config->u.csi.data_size); ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, reg); ++ ++ /* Clear status bits for logical channel #0 */ ++ isp_reg_writel(0xFFF & ~BIT(6), OMAP3_ISP_IOMEM_CCP2, ++ ISPCSI1_LC01_IRQSTATUS); ++ ++ /* Enable CSI1 */ ++ val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL); ++ val |= BIT(0) | BIT(4); ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL); ++ ++ if (!(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL) & BIT(4))) { ++ printk(KERN_WARNING "OMAP3 CSI1 bus not available\n"); ++ if (config->u.csi.signalling) /* Strobe mode requires CSI1 */ ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/** ++ * isp_configure_interface - Configures ISP Control I/F related parameters. ++ * @config: Pointer to structure containing the desired configuration for the ++ * ISP. ++ * ++ * Configures ISP control register (ISP_CTRL) with the values specified inside ++ * the config structure. Controls: ++ * - Selection of parallel or serial input to the preview hardware. ++ * - Data lane shifter. ++ * - Pixel clock polarity. ++ * - 8 to 16-bit bridge at the input of CCDC module. ++ * - HS or VS synchronization signal detection ++ **/ ++int isp_configure_interface(struct isp_interface_config *config) ++{ ++ u32 ispctrl_val = isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); ++ int r; ++ ++ isp_obj.config = config; ++ ++ ispctrl_val &= ISPCTRL_SHIFT_MASK; ++ ispctrl_val |= config->dataline_shift << ISPCTRL_SHIFT_SHIFT; ++ ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; ++ ++ ispctrl_val &= ISPCTRL_PAR_SER_CLK_SEL_MASK; ++ ++ isp_buf_init(); ++ ++ switch (config->ccdc_par_ser) { ++ case ISP_PARLL: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; ++ ispctrl_val |= config->u.par.par_clk_pol ++ << ISPCTRL_PAR_CLK_POL_SHIFT; ++ ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_BENDIAN; ++ ispctrl_val |= config->u.par.par_bridge ++ << ISPCTRL_PAR_BRIDGE_SHIFT; ++ break; ++ case ISP_CSIA: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; ++ ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_BENDIAN; ++ ++ isp_csi2_ctx_config_format(0, config->u.csi.format); ++ isp_csi2_ctx_update(0, false); ++ ++ if (config->u.csi.crc) ++ isp_csi2_ctrl_config_ecc_enable(true); ++ ++ isp_csi2_ctrl_config_vp_out_ctrl(config->u.csi.vpclk); ++ isp_csi2_ctrl_config_vp_only_enable(true); ++ isp_csi2_ctrl_config_vp_clk_enable(true); ++ isp_csi2_ctrl_update(false); ++ ++ isp_csi2_irq_complexio1_set(1); ++ isp_csi2_irq_status_set(1); ++ isp_csi2_irq_set(1); ++ ++ isp_csi2_enable(1); ++ mdelay(3); ++ break; ++ case ISP_CSIB: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; ++ r = isp_init_csi(config); ++ if (r) ++ return r; ++ break; ++ case ISP_NONE: ++ return 0; ++ default: ++ return -EINVAL; ++ } ++ ++ ispctrl_val &= ~ISPCTRL_SYNC_DETECT_VSRISE; ++ ispctrl_val |= config->hsvs_syncdetect; ++ ++ isp_reg_writel(ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); ++ ++ /* Set sensor specific fields in CCDC and Previewer module.*/ ++ isppreview_set_skip(config->prev_sph, config->prev_slv); ++ ispccdc_set_wenlog(config->wenlog); ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_configure_interface); ++ ++static int isp_buf_process(struct isp_bufs *bufs); ++ ++/** ++ * omap34xx_isp_isr - Interrupt Service Routine for Camera ISP module. ++ * @irq: Not used currently. ++ * @ispirq_disp: Pointer to the object that is passed while request_irq is ++ * called. This is the isp_obj.irq object containing info on the ++ * callback. ++ * ++ * Handles the corresponding callback if plugged in. ++ * ++ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the ++ * IRQ wasn't handled. ++ **/ ++static irqreturn_t omap34xx_isp_isr(int irq, void *_isp) ++{ ++ struct isp *isp = _isp; ++ struct isp_irq *irqdis = &isp->irq; ++ struct isp_bufs *bufs = &isp->bufs; ++ unsigned long flags; ++ u32 irqstatus = 0; ++ unsigned long irqflags = 0; ++ int wait_hs_vs = 0; ++ ++ irqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_writel(irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ ++ spin_lock_irqsave(&bufs->lock, flags); ++ wait_hs_vs = bufs->wait_hs_vs; ++ if (irqstatus & HS_VS && bufs->wait_hs_vs) ++ bufs->wait_hs_vs--; ++ spin_unlock_irqrestore(&bufs->lock, flags); ++ ++ spin_lock_irqsave(&isp_obj.lock, irqflags); ++ /* ++ * We need to wait for the first HS_VS interrupt from CCDC. ++ * Otherwise our frame (and everything else) might be bad. ++ */ ++ if (wait_hs_vs) ++ goto out_ignore_buff; ++ ++ if (irqstatus & CCDC_VD0) { ++ if (RAW_CAPTURE(&isp_obj)) ++ isp_buf_process(bufs); ++ if (!ispccdc_busy()) ++ ispccdc_config_shadow_registers(); ++ } ++ ++ if (irqstatus & PREV_DONE) { ++ if (irqdis->isp_callbk[CBK_PREV_DONE]) ++ irqdis->isp_callbk[CBK_PREV_DONE]( ++ PREV_DONE, ++ irqdis->isp_callbk_arg1[CBK_PREV_DONE], ++ irqdis->isp_callbk_arg2[CBK_PREV_DONE]); ++ else if (!RAW_CAPTURE(&isp_obj) && !ispresizer_busy()) { ++ if (isp_obj.module.applyCrop) { ++ ispresizer_applycrop(); ++ if (!ispresizer_busy()) ++ isp_obj.module.applyCrop = 0; ++ } ++ if (!isppreview_busy()) { ++ ispresizer_enable(1); ++ if (isppreview_busy()) { ++ /* FIXME: locking! */ ++ ISP_BUF_DONE(bufs)->vb_state = ++ VIDEOBUF_ERROR; ++ printk(KERN_ERR "%s: can't stop" ++ " preview\n", __func__); ++ } ++ } ++ if (!isppreview_busy()) ++ isppreview_config_shadow_registers(); ++ if (!isppreview_busy()) ++ isph3a_update_wb(); ++ } ++ } ++ ++ if (irqstatus & RESZ_DONE) { ++ if (!RAW_CAPTURE(&isp_obj)) { ++ if (!ispresizer_busy()) ++ ispresizer_config_shadow_registers(); ++ isp_buf_process(bufs); ++ } ++ } ++ ++ if (irqstatus & H3A_AWB_DONE) { ++ if (irqdis->isp_callbk[CBK_H3A_AWB_DONE]) ++ irqdis->isp_callbk[CBK_H3A_AWB_DONE]( ++ H3A_AWB_DONE, ++ irqdis->isp_callbk_arg1[CBK_H3A_AWB_DONE], ++ irqdis->isp_callbk_arg2[CBK_H3A_AWB_DONE]); ++ } ++ ++ if (irqstatus & HIST_DONE) { ++ if (irqdis->isp_callbk[CBK_HIST_DONE]) ++ irqdis->isp_callbk[CBK_HIST_DONE]( ++ HIST_DONE, ++ irqdis->isp_callbk_arg1[CBK_HIST_DONE], ++ irqdis->isp_callbk_arg2[CBK_HIST_DONE]); ++ } ++ ++ if (irqstatus & H3A_AF_DONE) { ++ if (irqdis->isp_callbk[CBK_H3A_AF_DONE]) ++ irqdis->isp_callbk[CBK_H3A_AF_DONE]( ++ H3A_AF_DONE, ++ irqdis->isp_callbk_arg1[CBK_H3A_AF_DONE], ++ irqdis->isp_callbk_arg2[CBK_H3A_AF_DONE]); ++ } ++ ++ ++out_ignore_buff: ++ if (irqstatus & LSC_PRE_ERR) { ++ struct isp_buf *buf = ISP_BUF_DONE(bufs); ++ /* Mark buffer faulty. */ ++ buf->vb_state = VIDEOBUF_ERROR; ++ ispccdc_lsc_error_handler(); ++ printk(KERN_ERR "%s: lsc prefetch error\n", __func__); ++ } ++ ++ if (irqstatus & CSIA) { ++ struct isp_buf *buf = ISP_BUF_DONE(bufs); ++ isp_csi2_isr(); ++ buf->vb_state = VIDEOBUF_ERROR; ++ } ++ ++ if (irqstatus & IRQ0STATUS_CSIB_IRQ) { ++ u32 ispcsi1_irqstatus; ++ ++ ispcsi1_irqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ++ ISPCSI1_LC01_IRQSTATUS); ++ DPRINTK_ISPCTRL("%x\n", ispcsi1_irqstatus); ++ } ++ ++ if (irqdis->isp_callbk[CBK_CATCHALL]) { ++ irqdis->isp_callbk[CBK_CATCHALL]( ++ irqstatus, ++ irqdis->isp_callbk_arg1[CBK_CATCHALL], ++ irqdis->isp_callbk_arg2[CBK_CATCHALL]); ++ } ++ ++ spin_unlock_irqrestore(&isp_obj.lock, irqflags); ++ ++#if 1 ++ { ++ static const struct { ++ int num; ++ char *name; ++ } bits[] = { ++ { 31, "HS_VS_IRQ" }, ++ { 30, "SEC_ERR_IRQ" }, ++ { 29, "OCP_ERR_IRQ" }, ++ { 28, "MMU_ERR_IRQ" }, ++ { 27, "res27" }, ++ { 26, "res26" }, ++ { 25, "OVF_IRQ" }, ++ { 24, "RSZ_DONE_IRQ" }, ++ { 23, "res23" }, ++ { 22, "res22" }, ++ { 21, "CBUFF_IRQ" }, ++ { 20, "PRV_DONE_IRQ" }, ++ { 19, "CCDC_LSC_PREFETCH_ERROR" }, ++ { 18, "CCDC_LSC_PREFETCH_COMPLETED" }, ++ { 17, "CCDC_LSC_DONE" }, ++ { 16, "HIST_DONE_IRQ" }, ++ { 15, "res15" }, ++ { 14, "res14" }, ++ { 13, "H3A_AWB_DONE_IRQ" }, ++ { 12, "H3A_AF_DONE_IRQ" }, ++ { 11, "CCDC_ERR_IRQ" }, ++ { 10, "CCDC_VD2_IRQ" }, ++ { 9, "CCDC_VD1_IRQ" }, ++ { 8, "CCDC_VD0_IRQ" }, ++ { 7, "res7" }, ++ { 6, "res6" }, ++ { 5, "res5" }, ++ { 4, "CSIB_IRQ" }, ++ { 3, "CSIB_LCM_IRQ" }, ++ { 2, "res2" }, ++ { 1, "res1" }, ++ { 0, "CSIA_IRQ" }, ++ }; ++ int i; ++ for (i = 0; i < ARRAY_SIZE(bits); i++) { ++ if ((1 << bits[i].num) & irqstatus) ++ DPRINTK_ISPCTRL("%s ", bits[i].name); ++ } ++ DPRINTK_ISPCTRL("\n"); ++ } ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++/* Device name, needed for resource tracking layer */ ++struct device_driver camera_drv = { ++ .name = "camera" ++}; ++ ++struct device camera_dev = { ++ .driver = &camera_drv, ++}; ++ ++/** ++ * isp_tmp_buf_free - To free allocated 10MB memory ++ * ++ **/ ++static void isp_tmp_buf_free(void) ++{ ++ if (isp_obj.tmp_buf) { ++ ispmmu_vfree(isp_obj.tmp_buf); ++ isp_obj.tmp_buf = 0; ++ isp_obj.tmp_buf_size = 0; ++ } ++} ++ ++/** ++ * isp_tmp_buf_alloc - To allocate a 10MB memory ++ * ++ **/ ++static u32 isp_tmp_buf_alloc(size_t size) ++{ ++ isp_tmp_buf_free(); ++ ++ printk(KERN_INFO "%s: allocating %d bytes\n", __func__, size); ++ ++ isp_obj.tmp_buf = ispmmu_vmalloc(size); ++ if (IS_ERR((void *)isp_obj.tmp_buf)) { ++ printk(KERN_ERR "ispmmu_vmap mapping failed "); ++ return -ENOMEM; ++ } ++ isp_obj.tmp_buf_size = size; ++ ++ isppreview_set_outaddr(isp_obj.tmp_buf); ++ ispresizer_set_inaddr(isp_obj.tmp_buf); ++ ++ return 0; ++} ++ ++/** ++ * isp_start - Starts ISP submodule ++ * ++ * Start the needed isp components assuming these components ++ * are configured correctly. ++ **/ ++void isp_start(void) ++{ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW ++ && is_isppreview_enabled()) ++ isppreview_enable(1); ++ ++ return; ++} ++EXPORT_SYMBOL(isp_start); ++ ++#define ISP_STATISTICS_BUSY \ ++ () ++#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) ++static int __isp_disable_modules(int suspend) ++{ ++ unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; ++ int reset = 0; ++ ++ /* ++ * We need to stop all the modules after CCDC first or they'll ++ * never stop since they may not get a full frame from CCDC. ++ */ ++ if (suspend) { ++ isp_af_suspend(); ++ isph3a_aewb_suspend(); ++ isp_hist_suspend(); ++ isppreview_suspend(); ++ ispresizer_suspend(); ++ } else { ++ isp_af_enable(0); ++ isph3a_aewb_enable(0); ++ isp_hist_enable(0); ++ isppreview_enable(0); ++ ispresizer_enable(0); ++ } ++ ++ timeout = jiffies + ISP_STOP_TIMEOUT; ++ while (isp_af_busy() ++ || isph3a_aewb_busy() ++ || isp_hist_busy() ++ || isppreview_busy() ++ || ispresizer_busy()) { ++ if (time_after(jiffies, timeout)) { ++ printk(KERN_ERR "%s: can't stop non-ccdc modules\n", ++ __func__); ++ reset = 1; ++ break; ++ } ++ msleep(1); ++ } ++ ++ /* Let's stop CCDC now. */ ++ if (suspend) ++ /* This function supends lsc too */ ++ ispccdc_suspend(); ++ else { ++ ispccdc_enable_lsc(0); ++ ispccdc_enable(0); ++ } ++ ++ timeout = jiffies + ISP_STOP_TIMEOUT; ++ while (ispccdc_busy()) { ++ if (time_after(jiffies, timeout)) { ++ printk(KERN_ERR "%s: can't stop ccdc\n", __func__); ++ reset = 1; ++ break; ++ } ++ msleep(1); ++ } ++ ++ return reset; ++} ++ ++static int isp_stop_modules(void) ++{ ++ return __isp_disable_modules(0); ++} ++ ++static int isp_suspend_modules(void) ++{ ++ return __isp_disable_modules(1); ++} ++ ++static void isp_resume_modules(void) ++{ ++ ispresizer_resume(); ++ isppreview_resume(); ++ isp_hist_resume(); ++ isph3a_aewb_resume(); ++ isp_af_resume(); ++ ispccdc_resume(); ++} ++ ++static void isp_reset(void) ++{ ++ unsigned long timeout = 0; ++ ++ isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) ++ | ISP_SYSCONFIG_SOFTRESET, ++ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); ++ while (!(isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_SYSSTATUS) & 0x1)) { ++ if (timeout++ > 10000) { ++ printk(KERN_ALERT "%s: cannot reset ISP\n", __func__); ++ break; ++ } ++ udelay(1); ++ } ++} ++ ++/** ++ * isp_stop - Stops isp submodules ++ **/ ++void isp_stop() ++{ ++ int reset; ++ ++ isp_disable_interrupts(); ++ reset = isp_stop_modules(); ++ isp_buf_init(); ++ if (!reset) ++ return; ++ ++ isp_save_ctx(); ++ isp_reset(); ++ isp_restore_ctx(); ++} ++EXPORT_SYMBOL(isp_stop); ++ ++static void isp_set_buf(struct isp_buf *buf) ++{ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER ++ && is_ispresizer_enabled()) ++ ispresizer_set_outaddr(buf->isp_addr); ++ else if (isp_obj.module.isp_pipeline & OMAP_ISP_CCDC) ++ ispccdc_set_outaddr(buf->isp_addr); ++ ++} ++ ++/** ++ * isp_calc_pipeline - Sets pipeline depending of input and output pixel format ++ * @pix_input: Pointer to V4L2 pixel format structure for input image. ++ * @pix_output: Pointer to V4L2 pixel format structure for output image. ++ **/ ++static u32 isp_calc_pipeline(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output) ++{ ++ isp_release_resources(); ++ if ((pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10 ++ || pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8) ++ && pix_output->pixelformat != V4L2_PIX_FMT_SGRBG10) { ++ isp_obj.module.isp_pipeline = ++ OMAP_ISP_CCDC | OMAP_ISP_PREVIEW | OMAP_ISP_RESIZER; ++ ispccdc_request(); ++ isppreview_request(); ++ ispresizer_request(); ++ ispccdc_config_datapath(CCDC_RAW, CCDC_OTHERS_VP); ++ isppreview_config_datapath(PRV_RAW_CCDC, PREVIEW_MEM); ++ ispresizer_config_datapath(RSZ_MEM_YUV); ++ } else { ++ isp_obj.module.isp_pipeline = OMAP_ISP_CCDC; ++ ispccdc_request(); ++ if (pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10 ++ || pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8) ++ ispccdc_config_datapath(CCDC_RAW, CCDC_OTHERS_VP_MEM); ++ else ++ ispccdc_config_datapath(CCDC_YUV_SYNC, ++ CCDC_OTHERS_MEM); ++ } ++ return 0; ++} ++ ++/** ++ * isp_config_pipeline - Configures the image size and ycpos for ISP submodules ++ * @pix_input: Pointer to V4L2 pixel format structure for input image. ++ * @pix_output: Pointer to V4L2 pixel format structure for output image. ++ * ++ * The configuration of ycpos depends on the output pixel format for both the ++ * Preview and Resizer submodules. ++ **/ ++static void isp_config_pipeline(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output) ++{ ++ ispccdc_config_size(isp_obj.module.ccdc_input_width, ++ isp_obj.module.ccdc_input_height, ++ isp_obj.module.ccdc_output_width, ++ isp_obj.module.ccdc_output_height); ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW) { ++ isppreview_config_size(isp_obj.module.preview_input_width, ++ isp_obj.module.preview_input_height, ++ isp_obj.module.preview_output_width, ++ isp_obj.module.preview_output_height); ++ } ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) { ++ ispresizer_config_size(isp_obj.module.resizer_input_width, ++ isp_obj.module.resizer_input_height, ++ isp_obj.module.resizer_output_width, ++ isp_obj.module.resizer_output_height); ++ } ++ ++ if (pix_output->pixelformat == V4L2_PIX_FMT_UYVY) { ++ isppreview_config_ycpos(YCPOS_YCrYCb); ++ if (is_ispresizer_enabled()) ++ ispresizer_config_ycpos(0); ++ } else { ++ isppreview_config_ycpos(YCPOS_CrYCbY); ++ if (is_ispresizer_enabled()) ++ ispresizer_config_ycpos(1); ++ } ++ ++ return; ++} ++ ++static void isp_buf_init(void) ++{ ++ struct isp_bufs *bufs = &isp_obj.bufs; ++ int sg; ++ ++ bufs->queue = 0; ++ bufs->done = 0; ++ bufs->wait_hs_vs = isp_obj.config->wait_hs_vs; ++ for (sg = 0; sg < NUM_BUFS; sg++) { ++ bufs->buf[sg].complete = NULL; ++ bufs->buf[sg].vb = NULL; ++ bufs->buf[sg].priv = NULL; ++ } ++} ++ ++/** ++ * isp_vbq_sync - Walks the pages table and flushes the cache for ++ * each page. ++ **/ ++static int isp_vbq_sync(struct videobuf_buffer *vb, int when) ++{ ++ flush_cache_all(); ++ ++ return 0; ++} ++ ++static int isp_buf_process(struct isp_bufs *bufs) ++{ ++ struct isp_buf *buf = NULL; ++ unsigned long flags; ++ int last; ++ ++ spin_lock_irqsave(&bufs->lock, flags); ++ ++ if (ISP_BUFS_IS_EMPTY(bufs)) ++ goto out; ++ ++ if (RAW_CAPTURE(&isp_obj) && ispccdc_sbl_wait_idle(1000)) { ++ printk(KERN_ERR "ccdc %d won't become idle!\n", ++ RAW_CAPTURE(&isp_obj)); ++ goto out; ++ } ++ ++ /* We had at least one buffer in queue. */ ++ buf = ISP_BUF_DONE(bufs); ++ last = ISP_BUFS_IS_LAST(bufs); ++ ++ if (!last) { ++ /* Set new buffer address. */ ++ isp_set_buf(ISP_BUF_NEXT_DONE(bufs)); ++ } else { ++ /* Tell ISP not to write any of our buffers. */ ++ isp_disable_interrupts(); ++ if (RAW_CAPTURE(&isp_obj)) ++ ispccdc_enable(0); ++ else ++ ispresizer_enable(0); ++ /* ++ * We must wait for the HS_VS since before that the ++ * CCDC may trigger interrupts even if it's not ++ * receiving a frame. ++ */ ++ bufs->wait_hs_vs = isp_obj.config->wait_hs_vs; ++ } ++ if ((RAW_CAPTURE(&isp_obj) && ispccdc_busy()) ++ || (!RAW_CAPTURE(&isp_obj) && ispresizer_busy())) { ++ /* ++ * Next buffer available: for the transfer to succeed, the ++ * CCDC (RAW capture) or resizer (YUV capture) must be idle ++ * for the duration of transfer setup. Bad things happen ++ * otherwise! ++ * ++ * Next buffer not available: if we fail to stop the ++ * ISP the buffer is probably going to be bad. ++ */ ++ /* Mark this buffer faulty. */ ++ buf->vb_state = VIDEOBUF_ERROR; ++ /* Mark next faulty, too, in case we have one. */ ++ if (!last) { ++ ISP_BUF_NEXT_DONE(bufs)->vb_state = ++ VIDEOBUF_ERROR; ++ printk(KERN_ALERT "OUCH!!!\n"); ++ } else { ++ printk(KERN_ALERT "Ouch!\n"); ++ } ++ } ++ ++ /* Mark the current buffer as done. */ ++ ISP_BUF_MARK_DONE(bufs); ++ ++ DPRINTK_ISPCTRL(KERN_ALERT "%s: finish %d mmu %p\n", __func__, ++ (bufs->done - 1 + NUM_BUFS) % NUM_BUFS, ++ (bufs->buf+((bufs->done - 1 + NUM_BUFS) ++ % NUM_BUFS))->isp_addr); ++ ++out: ++ spin_unlock_irqrestore(&bufs->lock, flags); ++ ++ if (buf != NULL) { ++ /* ++ * We want to dequeue a buffer from the video buffer ++ * queue. Let's do it! ++ */ ++ isp_vbq_sync(buf->vb, DMA_FROM_DEVICE); ++ buf->vb->state = buf->vb_state; ++ buf->complete(buf->vb, buf->priv); ++ } ++ ++ return 0; ++} ++ ++int isp_buf_queue(struct videobuf_buffer *vb, ++ void (*complete)(struct videobuf_buffer *vb, void *priv), ++ void *priv) ++{ ++ unsigned long flags; ++ struct isp_buf *buf; ++ struct videobuf_dmabuf *dma = videobuf_to_dma(vb); ++ const struct scatterlist *sglist = dma->sglist; ++ struct isp_bufs *bufs = &isp_obj.bufs; ++ int sglen = dma->sglen; ++ ++ BUG_ON(sglen < 0 || !sglist); ++ ++ isp_vbq_sync(vb, DMA_TO_DEVICE); ++ ++ spin_lock_irqsave(&bufs->lock, flags); ++ ++ BUG_ON(ISP_BUFS_IS_FULL(bufs)); ++ ++ buf = ISP_BUF_QUEUE(bufs); ++ ++ buf->isp_addr = bufs->isp_addr_capture[vb->i]; ++ buf->complete = complete; ++ buf->vb = vb; ++ buf->priv = priv; ++ buf->vb_state = VIDEOBUF_DONE; ++ ++ if (ISP_BUFS_IS_EMPTY(bufs)) { ++ isp_enable_interrupts(RAW_CAPTURE(&isp_obj)); ++ isp_set_buf(buf); ++ ispccdc_enable(1); ++ isp_start(); ++ } ++ ++ ISP_BUF_MARK_QUEUED(bufs); ++ ++ spin_unlock_irqrestore(&bufs->lock, flags); ++ ++ DPRINTK_ISPCTRL(KERN_ALERT "%s: queue %d vb %d, mmu %p\n", __func__, ++ (bufs->queue - 1 + NUM_BUFS) % NUM_BUFS, vb->i, ++ buf->isp_addr); ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_buf_queue); ++ ++int isp_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, ++ unsigned int *size) ++{ ++ int rval = 0; ++ size_t tmp_size = PAGE_ALIGN(isp_obj.module.preview_output_width ++ * isp_obj.module.preview_output_height ++ * ISP_BYTES_PER_PIXEL); ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW ++ && isp_obj.tmp_buf_size < tmp_size) ++ rval = isp_tmp_buf_alloc(tmp_size); ++ ++ return rval; ++} ++EXPORT_SYMBOL(isp_vbq_setup); ++ ++/** ++ * isp_vbq_prepare - Videobuffer queue prepare. ++ * @vbq: Pointer to videobuf_queue structure. ++ * @vb: Pointer to videobuf_buffer structure. ++ * @field: Requested Field order for the videobuffer. ++ * ++ * Returns 0 if successful, or -EIO if the ispmmu was unable to map a ++ * scatter-gather linked list data space. ++ **/ ++int isp_vbq_prepare(struct videobuf_queue *vbq, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ unsigned int isp_addr; ++ struct videobuf_dmabuf *vdma; ++ struct isp_bufs *bufs = &isp_obj.bufs; ++ ++ int err = 0; ++ ++ vdma = videobuf_to_dma(vb); ++ ++ isp_addr = ispmmu_vmap(vdma->sglist, vdma->sglen); ++ ++ if (IS_ERR_VALUE(isp_addr)) ++ err = -EIO; ++ else ++ bufs->isp_addr_capture[vb->i] = isp_addr; ++ ++ return err; ++} ++EXPORT_SYMBOL(isp_vbq_prepare); ++ ++/** ++ * isp_vbq_release - Videobuffer queue release. ++ * @vbq: Pointer to videobuf_queue structure. ++ * @vb: Pointer to videobuf_buffer structure. ++ **/ ++void isp_vbq_release(struct videobuf_queue *vbq, struct videobuf_buffer *vb) ++{ ++ struct isp_bufs *bufs = &isp_obj.bufs; ++ ++ ispmmu_vunmap(bufs->isp_addr_capture[vb->i]); ++ bufs->isp_addr_capture[vb->i] = (dma_addr_t)NULL; ++ return; ++} ++EXPORT_SYMBOL(isp_vbq_release); ++ ++/** ++ * isp_queryctrl - Query V4L2 control from existing controls in ISP. ++ * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled. ++ * ++ * Returns 0 if successful, or -EINVAL if not found in ISP. ++ **/ ++int isp_queryctrl(struct v4l2_queryctrl *a) ++{ ++ int i; ++ ++ if (a->id & V4L2_CTRL_FLAG_NEXT_CTRL) { ++ a->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; ++ i = find_next_vctrl(a->id); ++ } else { ++ i = find_vctrl(a->id); ++ } ++ ++ if (i < 0) ++ return -EINVAL; ++ ++ *a = video_control[i].qc; ++ return 0; ++} ++EXPORT_SYMBOL(isp_queryctrl); ++ ++/** ++ * isp_queryctrl - Query V4L2 control from existing controls in ISP. ++ * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled. ++ * ++ * Returns 0 if successful, or -EINVAL if not found in ISP. ++ **/ ++int isp_querymenu(struct v4l2_querymenu *a) ++{ ++ int i; ++ ++ i = find_vmenu(a->id, a->index); ++ ++ if (i < 0) ++ return -EINVAL; ++ ++ *a = video_menu[i]; ++ return 0; ++} ++EXPORT_SYMBOL(isp_querymenu); ++ ++/** ++ * isp_g_ctrl - Gets value of the desired V4L2 control. ++ * @a: V4L2 control to read actual value from. ++ * ++ * Return 0 if successful, or -EINVAL if chosen control is not found. ++ **/ ++int isp_g_ctrl(struct v4l2_control *a) ++{ ++ u8 current_value; ++ int rval = 0; ++ ++ if (!isp_obj.ref_count) ++ return -EINVAL; ++ ++ switch (a->id) { ++ case V4L2_CID_BRIGHTNESS: ++ isppreview_query_brightness(¤t_value); ++ a->value = current_value / ISPPRV_BRIGHT_UNITS; ++ break; ++ case V4L2_CID_CONTRAST: ++ isppreview_query_contrast(¤t_value); ++ a->value = current_value / ISPPRV_CONTRAST_UNITS; ++ break; ++ case V4L2_CID_COLORFX: ++ isppreview_get_color(¤t_value); ++ a->value = current_value; ++ break; ++ default: ++ rval = -EINVAL; ++ break; ++ } ++ ++ return rval; ++} ++EXPORT_SYMBOL(isp_g_ctrl); ++ ++/** ++ * isp_s_ctrl - Sets value of the desired V4L2 control. ++ * @a: V4L2 control to read actual value from. ++ * ++ * Return 0 if successful, -EINVAL if chosen control is not found or value ++ * is out of bounds, -EFAULT if copy_from_user or copy_to_user operation fails ++ * from camera abstraction layer related controls or the transfered user space ++ * pointer via the value field is not set properly. ++ **/ ++int isp_s_ctrl(struct v4l2_control *a) ++{ ++ int rval = 0; ++ u8 new_value = a->value; ++ ++ if (!isp_obj.ref_count) ++ return -EINVAL; ++ ++ switch (a->id) { ++ case V4L2_CID_BRIGHTNESS: ++ if (new_value > ISPPRV_BRIGHT_HIGH) ++ rval = -EINVAL; ++ else ++ isppreview_update_brightness(&new_value); ++ break; ++ case V4L2_CID_CONTRAST: ++ if (new_value > ISPPRV_CONTRAST_HIGH) ++ rval = -EINVAL; ++ else ++ isppreview_update_contrast(&new_value); ++ break; ++ case V4L2_CID_COLORFX: ++ if (new_value > V4L2_COLORFX_SEPIA) ++ rval = -EINVAL; ++ else ++ isppreview_set_color(&new_value); ++ break; ++ default: ++ rval = -EINVAL; ++ break; ++ } ++ ++ return rval; ++} ++EXPORT_SYMBOL(isp_s_ctrl); ++ ++/** ++ * isp_handle_private - Handle all private ioctls for isp module. ++ * @cmd: ioctl cmd value ++ * @arg: ioctl arg value ++ * ++ * Return 0 if successful, -EINVAL if chosen cmd value is not handled or value ++ * is out of bounds, -EFAULT if ioctl arg value is not valid. ++ * Function simply routes the input ioctl cmd id to the appropriate handler in ++ * the isp module. ++ **/ ++int isp_handle_private(int cmd, void *arg) ++{ ++ int rval = 0; ++ ++ if (!isp_obj.ref_count) ++ return -EINVAL; ++ ++ switch (cmd) { ++ case VIDIOC_PRIVATE_ISP_CCDC_CFG: ++ rval = omap34xx_isp_ccdc_config(arg); ++ break; ++ case VIDIOC_PRIVATE_ISP_PRV_CFG: ++ rval = omap34xx_isp_preview_config(arg); ++ break; ++ case VIDIOC_PRIVATE_ISP_AEWB_CFG: { ++ struct isph3a_aewb_config *params; ++ params = (struct isph3a_aewb_config *)arg; ++ rval = isph3a_aewb_configure(params); ++ } ++ break; ++ case VIDIOC_PRIVATE_ISP_AEWB_REQ: { ++ struct isph3a_aewb_data *data; ++ data = (struct isph3a_aewb_data *)arg; ++ rval = isph3a_aewb_request_statistics(data); ++ } ++ break; ++ case VIDIOC_PRIVATE_ISP_HIST_CFG: { ++ struct isp_hist_config *params; ++ params = (struct isp_hist_config *)arg; ++ rval = isp_hist_configure(params); ++ } ++ break; ++ case VIDIOC_PRIVATE_ISP_HIST_REQ: { ++ struct isp_hist_data *data; ++ data = (struct isp_hist_data *)arg; ++ rval = isp_hist_request_statistics(data); ++ } ++ break; ++ case VIDIOC_PRIVATE_ISP_AF_CFG: { ++ struct af_configuration *params; ++ params = (struct af_configuration *)arg; ++ rval = isp_af_configure(params); ++ } ++ break; ++ case VIDIOC_PRIVATE_ISP_AF_REQ: { ++ struct isp_af_data *data; ++ data = (struct isp_af_data *)arg; ++ rval = isp_af_request_statistics(data); ++ } ++ break; ++ default: ++ rval = -EINVAL; ++ break; ++ } ++ return rval; ++} ++EXPORT_SYMBOL(isp_handle_private); ++ ++/** ++ * isp_enum_fmt_cap - Gets more information of chosen format index and type ++ * @f: Pointer to structure containing index and type of format to read from. ++ * ++ * Returns 0 if successful, or -EINVAL if format index or format type is ++ * invalid. ++ **/ ++int isp_enum_fmt_cap(struct v4l2_fmtdesc *f) ++{ ++ int index = f->index; ++ enum v4l2_buf_type type = f->type; ++ int rval = -EINVAL; ++ ++ if (index >= NUM_ISP_CAPTURE_FORMATS) ++ goto err; ++ ++ memset(f, 0, sizeof(*f)); ++ f->index = index; ++ f->type = type; ++ ++ switch (f->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ rval = 0; ++ break; ++ default: ++ goto err; ++ } ++ ++ f->flags = isp_formats[index].flags; ++ strncpy(f->description, isp_formats[index].description, ++ sizeof(f->description)); ++ f->pixelformat = isp_formats[index].pixelformat; ++err: ++ return rval; ++} ++EXPORT_SYMBOL(isp_enum_fmt_cap); ++ ++/** ++ * isp_g_fmt_cap - Gets current output image format. ++ * @f: Pointer to V4L2 format structure to be filled with current output format ++ **/ ++void isp_g_fmt_cap(struct v4l2_pix_format *pix) ++{ ++ *pix = isp_obj.module.pix; ++ return; ++} ++EXPORT_SYMBOL(isp_g_fmt_cap); ++ ++/** ++ * isp_s_fmt_cap - Sets I/O formats and crop and configures pipeline in ISP ++ * @f: Pointer to V4L2 format structure to be filled with current output format ++ * ++ * Returns 0 if successful, or return value of either isp_try_size or ++ * isp_try_fmt if there is an error. ++ **/ ++int isp_s_fmt_cap(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output) ++{ ++ int crop_scaling_w = 0, crop_scaling_h = 0; ++ int rval = 0; ++ ++ if (!isp_obj.ref_count) ++ return -EINVAL; ++ ++ rval = isp_calc_pipeline(pix_input, pix_output); ++ if (rval) ++ goto out; ++ ++ rval = isp_try_size(pix_input, pix_output); ++ if (rval) ++ goto out; ++ ++ rval = isp_try_fmt(pix_input, pix_output); ++ if (rval) ++ goto out; ++ ++ if (ispcroprect.width != pix_output->width) { ++ crop_scaling_w = 1; ++ ispcroprect.left = 0; ++ ispcroprect.width = pix_output->width; ++ } ++ ++ if (ispcroprect.height != pix_output->height) { ++ crop_scaling_h = 1; ++ ispcroprect.top = 0; ++ ispcroprect.height = pix_output->height; ++ } ++ ++ isp_config_pipeline(pix_input, pix_output); ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER ++ && (crop_scaling_h || crop_scaling_w)) ++ isp_config_crop(pix_output); ++ ++out: ++ return rval; ++} ++EXPORT_SYMBOL(isp_s_fmt_cap); ++ ++/** ++ * isp_config_crop - Configures crop parameters in isp resizer. ++ * @croppix: Pointer to V4L2 pixel format structure containing crop parameters ++ **/ ++void isp_config_crop(struct v4l2_pix_format *croppix) ++{ ++ u8 crop_scaling_w; ++ u8 crop_scaling_h; ++ unsigned long org_left, num_pix, new_top; ++ ++ struct v4l2_pix_format *pix = croppix; ++ ++ crop_scaling_w = (isp_obj.module.preview_output_width * 10) / ++ pix->width; ++ crop_scaling_h = (isp_obj.module.preview_output_height * 10) / ++ pix->height; ++ ++ cur_rect.left = (ispcroprect.left * crop_scaling_w) / 10; ++ cur_rect.top = (ispcroprect.top * crop_scaling_h) / 10; ++ cur_rect.width = (ispcroprect.width * crop_scaling_w) / 10; ++ cur_rect.height = (ispcroprect.height * crop_scaling_h) / 10; ++ ++ org_left = cur_rect.left; ++ while (((int)cur_rect.left & 0xFFFFFFF0) != (int)cur_rect.left) ++ (int)cur_rect.left--; ++ ++ num_pix = org_left - cur_rect.left; ++ new_top = (int)(num_pix * 3) / 4; ++ cur_rect.top = cur_rect.top - new_top; ++ cur_rect.height = (2 * new_top) + cur_rect.height; ++ ++ cur_rect.width = cur_rect.width + (2 * num_pix); ++ while (((int)cur_rect.width & 0xFFFFFFF0) != (int)cur_rect.width) ++ (int)cur_rect.width--; ++ ++ isp_obj.tmp_buf_offset = ++ cur_rect.left * 2 + ++ isp_obj.module.preview_output_width * 2 * cur_rect.top; ++ ++ ispresizer_trycrop(cur_rect.left, cur_rect.top, cur_rect.width, ++ cur_rect.height, ++ isp_obj.module.resizer_output_width, ++ isp_obj.module.resizer_output_height); ++ ++ return; ++} ++EXPORT_SYMBOL(isp_config_crop); ++ ++/** ++ * isp_g_crop - Gets crop rectangle size and position. ++ * @a: Pointer to V4L2 crop structure to be filled. ++ * ++ * Always returns 0. ++ **/ ++int isp_g_crop(struct v4l2_crop *a) ++{ ++ struct v4l2_crop *crop = a; ++ ++ crop->c = ispcroprect; ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_g_crop); ++ ++/** ++ * isp_s_crop - Sets crop rectangle size and position and queues crop operation ++ * @a: Pointer to V4L2 crop structure with desired parameters. ++ * @pix: Pointer to V4L2 pixel format structure with desired parameters. ++ * ++ * Returns 0 if successful, or -EINVAL if crop parameters are out of bounds. ++ **/ ++int isp_s_crop(struct v4l2_crop *a, struct v4l2_pix_format *pix) ++{ ++ struct v4l2_crop *crop = a; ++ int rval = 0; ++ ++ if (!isp_obj.ref_count) ++ return -EINVAL; ++ ++ if (crop->c.left < 0) ++ crop->c.left = 0; ++ if (crop->c.width < 0) ++ crop->c.width = 0; ++ if (crop->c.top < 0) ++ crop->c.top = 0; ++ if (crop->c.height < 0) ++ crop->c.height = 0; ++ ++ if (crop->c.left >= pix->width) ++ crop->c.left = pix->width - 1; ++ if (crop->c.top >= pix->height) ++ crop->c.top = pix->height - 1; ++ ++ if (crop->c.left + crop->c.width > pix->width) ++ crop->c.width = pix->width - crop->c.left; ++ if (crop->c.top + crop->c.height > pix->height) ++ crop->c.height = pix->height - crop->c.top; ++ ++ ispcroprect.left = crop->c.left; ++ ispcroprect.top = crop->c.top; ++ ispcroprect.width = crop->c.width; ++ ispcroprect.height = crop->c.height; ++ ++ isp_config_crop(pix); ++ ++ isp_obj.module.applyCrop = 1; ++ ++ return rval; ++} ++EXPORT_SYMBOL(isp_s_crop); ++ ++/** ++ * isp_try_fmt_cap - Tries desired input/output image formats ++ * @pix_input: Pointer to V4L2 pixel format structure for input image. ++ * @pix_output: Pointer to V4L2 pixel format structure for output image. ++ * ++ * Returns 0 if successful, or return value of either isp_try_size or ++ * isp_try_fmt if there is an error. ++ **/ ++int isp_try_fmt_cap(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output) ++{ ++ int rval = 0; ++ ++ rval = isp_calc_pipeline(pix_input, pix_output); ++ if (rval) ++ goto out; ++ ++ rval = isp_try_size(pix_input, pix_output); ++ if (rval) ++ goto out; ++ ++ rval = isp_try_fmt(pix_input, pix_output); ++ if (rval) ++ goto out; ++ ++out: ++ return rval; ++} ++EXPORT_SYMBOL(isp_try_fmt_cap); ++ ++/** ++ * isp_try_size - Tries size configuration for I/O images of each ISP submodule ++ * @pix_input: Pointer to V4L2 pixel format structure for input image. ++ * @pix_output: Pointer to V4L2 pixel format structure for output image. ++ * ++ * Returns 0 if successful, or return value of ispccdc_try_size, ++ * isppreview_try_size, or ispresizer_try_size (depending on the pipeline ++ * configuration) if there is an error. ++ **/ ++static int isp_try_size(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output) ++{ ++ int rval = 0; ++ ++ if (pix_output->width <= ISPRSZ_MIN_OUTPUT ++ || pix_output->height <= ISPRSZ_MIN_OUTPUT) ++ return -EINVAL; ++ ++ if (pix_output->width >= ISPRSZ_MAX_OUTPUT ++ || pix_output->height > ISPRSZ_MAX_OUTPUT) ++ return -EINVAL; ++ ++ isp_obj.module.ccdc_input_width = pix_input->width; ++ isp_obj.module.ccdc_input_height = pix_input->height; ++ isp_obj.module.resizer_output_width = pix_output->width; ++ isp_obj.module.resizer_output_height = pix_output->height; ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_CCDC) { ++ rval = ispccdc_try_size(isp_obj.module.ccdc_input_width, ++ isp_obj.module.ccdc_input_height, ++ &isp_obj.module.ccdc_output_width, ++ &isp_obj.module.ccdc_output_height); ++ if (rval) { ++ printk(KERN_ERR "ISP_ERR: The dimensions %dx%d are not" ++ " supported\n", pix_input->width, ++ pix_input->height); ++ return rval; ++ } ++ pix_output->width = isp_obj.module.ccdc_output_width; ++ pix_output->height = isp_obj.module.ccdc_output_height; ++ } ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW) { ++ isp_obj.module.preview_input_width = ++ isp_obj.module.ccdc_output_width; ++ isp_obj.module.preview_input_height = ++ isp_obj.module.ccdc_output_height; ++ rval = isppreview_try_size( ++ isp_obj.module.preview_input_width, ++ isp_obj.module.preview_input_height, ++ &isp_obj.module.preview_output_width, ++ &isp_obj.module.preview_output_height); ++ if (rval) { ++ printk(KERN_ERR "ISP_ERR: The dimensions %dx%d are not" ++ " supported\n", pix_input->width, ++ pix_input->height); ++ return rval; ++ } ++ pix_output->width = isp_obj.module.preview_output_width; ++ pix_output->height = isp_obj.module.preview_output_height; ++ } ++ ++ if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) { ++ isp_obj.module.resizer_input_width = ++ isp_obj.module.preview_output_width; ++ isp_obj.module.resizer_input_height = ++ isp_obj.module.preview_output_height; ++ rval = ispresizer_try_size( ++ &isp_obj.module.resizer_input_width, ++ &isp_obj.module.resizer_input_height, ++ &isp_obj.module.resizer_output_width, ++ &isp_obj.module.resizer_output_height); ++ if (rval) { ++ printk(KERN_ERR "ISP_ERR: The dimensions %dx%d are not" ++ " supported\n", pix_input->width, ++ pix_input->height); ++ return rval; ++ } ++ pix_output->width = isp_obj.module.resizer_output_width; ++ pix_output->height = isp_obj.module.resizer_output_height; ++ } ++ ++ return rval; ++} ++ ++/** ++ * isp_try_fmt - Validates input/output format parameters. ++ * @pix_input: Pointer to V4L2 pixel format structure for input image. ++ * @pix_output: Pointer to V4L2 pixel format structure for output image. ++ * ++ * Always returns 0. ++ **/ ++int isp_try_fmt(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output) ++{ ++ int ifmt; ++ ++ for (ifmt = 0; ifmt < NUM_ISP_CAPTURE_FORMATS; ifmt++) { ++ if (pix_output->pixelformat == isp_formats[ifmt].pixelformat) ++ break; ++ } ++ if (ifmt == NUM_ISP_CAPTURE_FORMATS) ++ ifmt = 1; ++ pix_output->pixelformat = isp_formats[ifmt].pixelformat; ++ pix_output->field = V4L2_FIELD_NONE; ++ pix_output->bytesperline = pix_output->width * ISP_BYTES_PER_PIXEL; ++ pix_output->sizeimage = ++ PAGE_ALIGN(pix_output->bytesperline * pix_output->height); ++ pix_output->priv = 0; ++ switch (pix_output->pixelformat) { ++ case V4L2_PIX_FMT_YUYV: ++ case V4L2_PIX_FMT_UYVY: ++ pix_output->colorspace = V4L2_COLORSPACE_JPEG; ++ break; ++ default: ++ pix_output->colorspace = V4L2_COLORSPACE_SRGB; ++ } ++ ++ isp_obj.module.pix.pixelformat = pix_output->pixelformat; ++ isp_obj.module.pix.width = pix_output->width; ++ isp_obj.module.pix.height = pix_output->height; ++ isp_obj.module.pix.field = pix_output->field; ++ isp_obj.module.pix.bytesperline = pix_output->bytesperline; ++ isp_obj.module.pix.sizeimage = pix_output->sizeimage; ++ isp_obj.module.pix.priv = pix_output->priv; ++ isp_obj.module.pix.colorspace = pix_output->colorspace; ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_try_fmt); ++ ++/** ++ * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. ++ * ++ * Routine for saving the context of each module in the ISP. ++ * CCDC, HIST, H3A, PREV, RESZ and MMU. ++ **/ ++static void isp_save_ctx(void) ++{ ++ isp_save_context(isp_reg_list); ++ ispccdc_save_context(); ++ ispmmu_save_context(); ++ isphist_save_context(); ++ isph3a_save_context(); ++ isppreview_save_context(); ++ ispresizer_save_context(); ++} ++ ++/** ++ * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. ++ * ++ * Routine for restoring the context of each module in the ISP. ++ * CCDC, HIST, H3A, PREV, RESZ and MMU. ++ **/ ++static void isp_restore_ctx(void) ++{ ++ isp_restore_context(isp_reg_list); ++ ispccdc_restore_context(); ++ ispmmu_restore_context(); ++ isphist_restore_context(); ++ isph3a_restore_context(); ++ isppreview_restore_context(); ++ ispresizer_restore_context(); ++} ++ ++static int isp_enable_clocks(void) ++{ ++ int r; ++ ++ r = clk_enable(isp_obj.cam_ick); ++ if (r) { ++ DPRINTK_ISPCTRL("ISP_ERR: clk_en for ick failed\n"); ++ goto out_clk_enable_ick; ++ } ++ r = clk_enable(isp_obj.cam_mclk); ++ if (r) { ++ DPRINTK_ISPCTRL("ISP_ERR: clk_en for mclk failed\n"); ++ goto out_clk_enable_mclk; ++ } ++ r = clk_enable(isp_obj.csi2_fck); ++ if (r) { ++ DPRINTK_ISPCTRL("ISP_ERR: clk_en for csi2_fclk" ++ " failed\n"); ++ goto out_clk_enable_csi2_fclk; ++ } ++ return 0; ++ ++out_clk_enable_csi2_fclk: ++ clk_disable(isp_obj.cam_mclk); ++out_clk_enable_mclk: ++ clk_disable(isp_obj.cam_ick); ++out_clk_enable_ick: ++ return r; ++} ++ ++static void isp_disable_clocks(void) ++{ ++ clk_disable(isp_obj.cam_ick); ++ clk_disable(isp_obj.cam_mclk); ++ clk_disable(isp_obj.csi2_fck); ++} ++ ++/** ++ * isp_get - Adquires the ISP resource. ++ * ++ * Initializes the clocks for the first acquire. ++ **/ ++int isp_get(void) ++{ ++ static int has_context; ++ int ret_err = 0; ++ ++ if (omap3isp == NULL) ++ return -EBUSY; ++ ++ DPRINTK_ISPCTRL("isp_get: old %d\n", isp_obj.ref_count); ++ mutex_lock(&(isp_obj.isp_mutex)); ++ if (isp_obj.ref_count == 0) { ++ ret_err = isp_enable_clocks(); ++ if (ret_err) ++ goto out_err; ++ /* We don't want to restore context before saving it! */ ++ if (has_context) ++ isp_restore_ctx(); ++ else ++ has_context = 1; ++ } else { ++ mutex_unlock(&isp_obj.isp_mutex); ++ return -EBUSY; ++ } ++ isp_obj.ref_count++; ++ mutex_unlock(&(isp_obj.isp_mutex)); ++ ++ DPRINTK_ISPCTRL("isp_get: new %d\n", isp_obj.ref_count); ++ return isp_obj.ref_count; ++ ++out_err: ++ mutex_unlock(&(isp_obj.isp_mutex)); ++ return ret_err; ++} ++EXPORT_SYMBOL(isp_get); ++ ++/** ++ * isp_put - Releases the ISP resource. ++ * ++ * Releases the clocks also for the last release. ++ **/ ++int isp_put(void) ++{ ++ if (omap3isp == NULL) ++ return -EBUSY; ++ ++ DPRINTK_ISPCTRL("isp_put: old %d\n", isp_obj.ref_count); ++ mutex_lock(&(isp_obj.isp_mutex)); ++ if (isp_obj.ref_count) { ++ if (--isp_obj.ref_count == 0) { ++ isp_save_ctx(); ++ isp_tmp_buf_free(); ++ isp_release_resources(); ++ isp_obj.module.isp_pipeline = 0; ++ isp_disable_clocks(); ++ memset(&ispcroprect, 0, sizeof(ispcroprect)); ++ memset(&cur_rect, 0, sizeof(cur_rect)); ++ } ++ } ++ mutex_unlock(&(isp_obj.isp_mutex)); ++ DPRINTK_ISPCTRL("isp_put: new %d\n", isp_obj.ref_count); ++ return isp_obj.ref_count; ++} ++EXPORT_SYMBOL(isp_put); ++ ++/** ++ * isp_save_context - Saves the values of the ISP module registers. ++ * @reg_list: Structure containing pairs of register address and value to ++ * modify on OMAP. ++ **/ ++void isp_save_context(struct isp_reg *reg_list) ++{ ++ struct isp_reg *next = reg_list; ++ ++ for (; next->reg != ISP_TOK_TERM; next++) ++ next->val = isp_reg_readl(next->mmio_range, next->reg); ++} ++EXPORT_SYMBOL(isp_save_context); ++ ++/** ++ * isp_restore_context - Restores the values of the ISP module registers. ++ * @reg_list: Structure containing pairs of register address and value to ++ * modify on OMAP. ++ **/ ++void isp_restore_context(struct isp_reg *reg_list) ++{ ++ struct isp_reg *next = reg_list; ++ ++ for (; next->reg != ISP_TOK_TERM; next++) ++ isp_reg_writel(next->val, next->mmio_range, next->reg); ++} ++EXPORT_SYMBOL(isp_restore_context); ++ ++static int isp_remove(struct platform_device *pdev) ++{ ++ struct isp_device *isp = platform_get_drvdata(pdev); ++ int i; ++ ++ isp_csi2_cleanup(); ++ isp_af_exit(); ++ isp_resizer_cleanup(); ++ isp_preview_cleanup(); ++ ispmmu_cleanup(); ++ isph3a_aewb_cleanup(); ++ isp_hist_cleanup(); ++ isp_ccdc_cleanup(); ++ ++ if (!isp) ++ return 0; ++ ++ clk_put(isp_obj.cam_ick); ++ clk_put(isp_obj.cam_mclk); ++ clk_put(isp_obj.csi2_fck); ++ ++ free_irq(isp->irq, &isp_obj); ++ ++ for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) { ++ if (isp->mmio_base[i]) { ++ iounmap((void *)isp->mmio_base[i]); ++ isp->mmio_base[i] = 0; ++ } ++ ++ if (isp->mmio_base_phys[i]) { ++ release_mem_region(isp->mmio_base_phys[i], ++ isp->mmio_size[i]); ++ isp->mmio_base_phys[i] = 0; ++ } ++ } ++ ++ omap3isp = NULL; ++ ++ kfree(isp); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int isp_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ int reset; ++ ++ mutex_lock(&(isp_obj.isp_mutex)); ++ DPRINTK_ISPCTRL("isp_suspend: starting\n"); ++ if (isp_obj.ref_count == 0) ++ goto out; ++ ++ isp_disable_interrupts(); ++ reset = isp_suspend_modules(); ++ isp_save_ctx(); ++ if (reset) ++ isp_reset(); ++ ++ isp_disable_clocks(); ++ ++out: ++ DPRINTK_ISPCTRL("isp_suspend: done\n"); ++ mutex_unlock(&(isp_obj.isp_mutex)); ++ return 0; ++} ++ ++static int isp_resume(struct platform_device *pdev) ++{ ++ int ret_err = 0; ++ ++ DPRINTK_ISPCTRL("isp_resume: starting\n"); ++ ++ if (omap3isp == NULL) ++ goto out; ++ ++ if (isp_obj.ref_count >= 0) { ++ ret_err = isp_enable_clocks(); ++ if (ret_err) ++ goto out; ++ isp_restore_ctx(); ++ isp_resume_modules(); ++ isp_enable_interrupts(RAW_CAPTURE(&isp_obj)); ++ isp_start(); ++ } ++ ++out: ++ DPRINTK_ISPCTRL("isp_resume: done \n"); ++ return ret_err; ++} ++ ++#else ++ ++#define isp_suspend NULL ++#define isp_resume NULL ++ ++#endif /* CONFIG_PM */ ++ ++ ++static int isp_probe(struct platform_device *pdev) ++{ ++ struct isp_device *isp; ++ int ret_err = 0; ++ int i; ++ ++ isp = kzalloc(sizeof(*isp), GFP_KERNEL); ++ if (!isp) { ++ dev_err(&pdev->dev, "could not allocate memory\n"); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, isp); ++ ++ isp->dev = &pdev->dev; ++ ++ for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) { ++ struct resource *mem; ++ /* request the mem region for the camera registers */ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ if (!mem) { ++ dev_err(isp->dev, "no mem resource?\n"); ++ return -ENODEV; ++ } ++ ++ if (!request_mem_region(mem->start, mem->end - mem->start + 1, ++ pdev->name)) { ++ dev_err(isp->dev, ++ "cannot reserve camera register I/O region\n"); ++ return -ENODEV; ++ ++ } ++ isp->mmio_base_phys[i] = mem->start; ++ isp->mmio_size[i] = mem->end - mem->start + 1; ++ ++ /* map the region */ ++ isp->mmio_base[i] = (unsigned long) ++ ioremap_nocache(isp->mmio_base_phys[i], ++ isp->mmio_size[i]); ++ if (!isp->mmio_base[i]) { ++ dev_err(isp->dev, ++ "cannot map camera register I/O region\n"); ++ return -ENODEV; ++ } ++ } ++ ++ isp->irq = platform_get_irq(pdev, 0); ++ if (isp->irq <= 0) { ++ dev_err(isp->dev, "no irq for camera?\n"); ++ return -ENODEV; ++ } ++ ++ isp_obj.cam_ick = clk_get(&camera_dev, "cam_ick"); ++ if (IS_ERR(isp_obj.cam_ick)) { ++ DPRINTK_ISPCTRL("ISP_ERR: clk_get for " ++ "cam_ick failed\n"); ++ return PTR_ERR(isp_obj.cam_ick); ++ } ++ isp_obj.cam_mclk = clk_get(&camera_dev, "cam_mclk"); ++ if (IS_ERR(isp_obj.cam_mclk)) { ++ DPRINTK_ISPCTRL("ISP_ERR: clk_get for " ++ "cam_mclk failed\n"); ++ ret_err = PTR_ERR(isp_obj.cam_mclk); ++ goto out_clk_get_mclk; ++ } ++ isp_obj.csi2_fck = clk_get(&camera_dev, "csi2_96m_fck"); ++ if (IS_ERR(isp_obj.csi2_fck)) { ++ DPRINTK_ISPCTRL("ISP_ERR: clk_get for csi2_fclk" ++ " failed\n"); ++ ret_err = PTR_ERR(isp_obj.csi2_fck); ++ goto out_clk_get_csi2_fclk; ++ } ++ ++ if (request_irq(isp->irq, omap34xx_isp_isr, IRQF_SHARED, ++ "Omap 3 Camera ISP", &isp_obj)) { ++ DPRINTK_ISPCTRL("Could not install ISR\n"); ++ ret_err = -EINVAL; ++ goto out_request_irq; ++ } ++ ++ isp_obj.ref_count = 0; ++ ++ mutex_init(&(isp_obj.isp_mutex)); ++ spin_lock_init(&isp_obj.lock); ++ spin_lock_init(&isp_obj.bufs.lock); ++ ++ omap3isp = isp; ++ ++ ret_err = ispmmu_init(); ++ if (ret_err) ++ goto out_ispmmu_init; ++ ++ isp_ccdc_init(); ++ isp_hist_init(); ++ isph3a_aewb_init(); ++ isp_preview_init(); ++ isp_resizer_init(); ++ isp_af_init(); ++ isp_csi2_init(); ++ ++ isp_get(); ++ isp_power_settings(1); ++ isp_put(); ++ ++ isph3a_notify(1); ++ isp_af_notify(1); ++ ++ return 0; ++ ++out_ispmmu_init: ++ omap3isp = NULL; ++ free_irq(isp->irq, &isp_obj); ++out_request_irq: ++ clk_put(isp_obj.csi2_fck); ++out_clk_get_csi2_fclk: ++ clk_put(isp_obj.cam_mclk); ++out_clk_get_mclk: ++ clk_put(isp_obj.cam_ick); ++ ++ return ret_err; ++} ++ ++static struct platform_driver omap3isp_driver = { ++ .probe = isp_probe, ++ .remove = isp_remove, ++ .suspend = isp_suspend, ++ .resume = isp_resume, ++ .driver = { ++ .name = "omap3isp", ++ }, ++}; ++ ++/** ++ * isp_init - ISP module initialization. ++ **/ ++static int __init isp_init(void) ++{ ++ return platform_driver_register(&omap3isp_driver); ++} ++ ++/** ++ * isp_cleanup - ISP module cleanup. ++ **/ ++static void __exit isp_cleanup(void) ++{ ++ platform_driver_unregister(&omap3isp_driver); ++} ++ ++/** ++ * isp_print_status - Prints the values of the ISP Control Module registers ++ * ++ * Also prints other debug information stored in the ISP module structure. ++ **/ ++void isp_print_status(void) ++{ ++ if (!is_ispctrl_debug_enabled()) ++ return; ++ ++ DPRINTK_ISPCTRL("###ISP_CTRL=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); ++ DPRINTK_ISPCTRL("###ISP_TCTRL_CTRL=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL)); ++ DPRINTK_ISPCTRL("###ISP_SYSCONFIG=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)); ++ DPRINTK_ISPCTRL("###ISP_SYSSTATUS=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_SYSSTATUS)); ++ DPRINTK_ISPCTRL("###ISP_IRQ0ENABLE=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE)); ++ DPRINTK_ISPCTRL("###ISP_IRQ0STATUS=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); ++} ++EXPORT_SYMBOL(isp_print_status); ++ ++module_init(isp_init); ++module_exit(isp_cleanup); ++ ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_DESCRIPTION("ISP Control Module Library"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/video/isp/isp.h b/drivers/media/video/isp/isp.h +new file mode 100644 +index 0000000..55c98a9 +--- /dev/null ++++ b/drivers/media/video/isp/isp.h +@@ -0,0 +1,318 @@ ++/* ++ * isp.h ++ * ++ * Top level public header file for ISP Control module in ++ * TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments. ++ * Copyright (C) 2009 Nokia. ++ * ++ * Contributors: ++ * Sameer Venkatraman <sameerv@ti.com> ++ * Mohit Jalori <mjalori@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_TOP_H ++#define OMAP_ISP_TOP_H ++#include <mach/cpu.h> ++#include <media/videobuf-dma-sg.h> ++#include <linux/videodev2.h> ++#define OMAP_ISP_CCDC (1 << 0) ++#define OMAP_ISP_PREVIEW (1 << 1) ++#define OMAP_ISP_RESIZER (1 << 2) ++#define OMAP_ISP_AEWB (1 << 3) ++#define OMAP_ISP_AF (1 << 4) ++#define OMAP_ISP_HIST (1 << 5) ++ ++#define ISP_TOK_TERM 0xFFFFFFFF /* ++ * terminating token for ISP ++ * modules reg list ++ */ ++#define NUM_BUFS VIDEO_MAX_FRAME ++ ++#ifndef CONFIG_ARCH_OMAP3410 ++#define USE_ISP_PREVIEW ++#define USE_ISP_RESZ ++#define is_isppreview_enabled() 1 ++#define is_ispresizer_enabled() 1 ++#else ++#define is_isppreview_enabled() 0 ++#define is_ispresizer_enabled() 0 ++#endif ++ ++#define ISP_BYTES_PER_PIXEL 2 ++#define NUM_ISP_CAPTURE_FORMATS (sizeof(isp_formats) / \ ++ sizeof(isp_formats[0])) ++typedef int (*isp_vbq_callback_ptr) (struct videobuf_buffer *vb); ++typedef void (*isp_callback_t) (unsigned long status, ++ isp_vbq_callback_ptr arg1, void *arg2); ++ ++enum isp_mem_resources { ++ OMAP3_ISP_IOMEM_MAIN, ++ OMAP3_ISP_IOMEM_CBUFF, ++ OMAP3_ISP_IOMEM_CCP2, ++ OMAP3_ISP_IOMEM_CCDC, ++ OMAP3_ISP_IOMEM_HIST, ++ OMAP3_ISP_IOMEM_H3A, ++ OMAP3_ISP_IOMEM_PREV, ++ OMAP3_ISP_IOMEM_RESZ, ++ OMAP3_ISP_IOMEM_SBL, ++ OMAP3_ISP_IOMEM_CSI2A, ++ OMAP3_ISP_IOMEM_CSI2PHY ++}; ++ ++struct isp_device { ++ struct device *dev; ++ ++ /*** platform HW resources ***/ ++ unsigned int irq; ++ ++#define mmio_base_main mmio_base[OMAP3_ISP_IOMEM_MAIN] ++#define mmio_cbuff_main mmio_base[OMAP3_ISP_IOMEM_CBUFF] ++#define mmio_ccp2_main mmio_base[OMAP3_ISP_IOMEM_CCP2] ++#define mmio_ccdc_main mmio_base[OMAP3_ISP_IOMEM_CCDC] ++#define mmio_hist_main mmio_base[OMAP3_ISP_IOMEM_HIST] ++#define mmio_h3a_main mmio_base[OMAP3_ISP_IOMEM_H3A] ++#define mmio_prev_main mmio_base[OMAP3_ISP_IOMEM_PREV] ++#define mmio_resz_main mmio_base[OMAP3_ISP_IOMEM_RESZ] ++#define mmio_sbl_main mmio_base[OMAP3_ISP_IOMEM_SBL] ++#define mmio_csi2_main mmio_base[OMAP3_ISP_IOMEM_CSI2A] ++#define mmio_csi2phy_main mmio_base[OMAP3_ISP_IOMEM_CSI2PHY] ++ unsigned long mmio_base[OMAP3_ISP_IOMEM_CSI2PHY + 1]; ++ unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_CSI2PHY + 1]; ++ unsigned long mmio_size[OMAP3_ISP_IOMEM_CSI2PHY + 1]; ++}; ++ ++enum isp_interface_type { ++ ISP_PARLL = 1, ++ ISP_CSIA = 2, ++ ISP_CSIB = 4, ++ ISP_NONE = 8 /* memory input to preview / resizer */ ++}; ++ ++enum isp_irqevents { ++ CSIA = 0x01, ++ CSIB = 0x10, ++ CCDC_VD0 = 0x100, ++ CCDC_VD1 = 0x200, ++ CCDC_VD2 = 0x400, ++ CCDC_ERR = 0x800, ++ H3A_AWB_DONE = 0x2000, ++ H3A_AF_DONE = 0x1000, ++ HIST_DONE = 0x10000, ++ PREV_DONE = 0x100000, ++ LSC_DONE = 0x20000, ++ LSC_PRE_COMP = 0x40000, ++ LSC_PRE_ERR = 0x80000, ++ RESZ_DONE = 0x1000000, ++ SBL_OVF = 0x2000000, ++ MMU_ERR = 0x10000000, ++ OCP_ERR = 0x20000000, ++ HS_VS = 0x80000000 ++}; ++ ++enum isp_callback_type { ++ CBK_CCDC_VD0, ++ CBK_CCDC_VD1, ++ CBK_PREV_DONE, ++ CBK_RESZ_DONE, ++ CBK_MMU_ERR, ++ CBK_H3A_AWB_DONE, ++ CBK_HIST_DONE, ++ CBK_HS_VS, ++ CBK_LSC_ISR, ++ CBK_H3A_AF_DONE, ++ CBK_CATCHALL, ++ CBK_CSIA, ++ CBK_CSIB, ++ CBK_END, ++}; ++ ++/** ++ * struct isp_reg - Structure for ISP register values. ++ * @reg: 32-bit Register address. ++ * @val: 32-bit Register value. ++ */ ++struct isp_reg { ++ enum isp_mem_resources mmio_range; ++ u32 reg; ++ u32 val; ++}; ++ ++/** ++ * struct isp_interface_config - ISP interface configuration. ++ * @ccdc_par_ser: ISP interface type. 0 - Parallel, 1 - CSIA, 2 - CSIB to CCDC. ++ * @par_bridge: CCDC Bridge input control. Parallel interface. ++ * 0 - Disable, 1 - Enable, first byte->cam_d(bits 7 to 0) ++ * 2 - Enable, first byte -> cam_d(bits 15 to 8) ++ * @par_clk_pol: Pixel clock polarity on the parallel interface. ++ * 0 - Non Inverted, 1 - Inverted ++ * @dataline_shift: Data lane shifter. ++ * 0 - No Shift, 1 - CAMEXT[13 to 2]->CAM[11 to 0] ++ * 2 - CAMEXT[13 to 4]->CAM[9 to 0] ++ * 3 - CAMEXT[13 to 6]->CAM[7 to 0] ++ * @hsvs_syncdetect: HS or VS synchronization signal detection. ++ * 0 - HS Falling, 1 - HS rising ++ * 2 - VS falling, 3 - VS rising ++ * @strobe: Strobe related parameter. ++ * @prestrobe: PreStrobe related parameter. ++ * @shutter: Shutter related parameter. ++ * @hskip: Horizontal Start Pixel performed in Preview module. ++ * @vskip: Vertical Start Line performed in Preview module. ++ * @wenlog: Store the value for the sensor specific wenlog field. ++ * @wait_hs_vs: Wait for this many hs_vs before anything else in the beginning. ++ */ ++struct isp_interface_config { ++ enum isp_interface_type ccdc_par_ser; ++ u8 dataline_shift; ++ u32 hsvs_syncdetect; ++ int strobe; ++ int prestrobe; ++ int shutter; ++ u32 prev_sph; ++ u32 prev_slv; ++ u32 wenlog; ++ int wait_hs_vs; ++ union { ++ struct par { ++ unsigned par_bridge:2; ++ unsigned par_clk_pol:1; ++ } par; ++ struct csi { ++ unsigned crc:1; ++ unsigned mode:1; ++ unsigned edge:1; ++ unsigned signalling:1; ++ unsigned strobe_clock_inv:1; ++ unsigned vs_edge:1; ++ unsigned channel:3; ++ unsigned vpclk:2; /* Video port output clock */ ++ unsigned int data_start; ++ unsigned int data_size; ++ u32 format; /* V4L2_PIX_FMT_* */ ++ } csi; ++ } u; ++}; ++ ++u32 isp_reg_readl(enum isp_mem_resources isp_mmio_range, u32 reg_offset); ++ ++void isp_reg_writel(u32 reg_value, enum isp_mem_resources isp_mmio_range, ++ u32 reg_offset); ++ ++static inline void isp_reg_and(enum isp_mem_resources mmio_range, u32 reg, ++ u32 and_bits) ++{ ++ u32 v = isp_reg_readl(mmio_range, reg); ++ ++ isp_reg_writel(v & and_bits, mmio_range, reg); ++} ++ ++static inline void isp_reg_or(enum isp_mem_resources mmio_range, u32 reg, ++ u32 or_bits) ++{ ++ u32 v = isp_reg_readl(mmio_range, reg); ++ ++ isp_reg_writel(v | or_bits, mmio_range, reg); ++} ++ ++static inline void isp_reg_and_or(enum isp_mem_resources mmio_range, u32 reg, ++ u32 and_bits, u32 or_bits) ++{ ++ u32 v = isp_reg_readl(mmio_range, reg); ++ ++ isp_reg_writel((v & and_bits) | or_bits, mmio_range, reg); ++} ++ ++void isp_start(void); ++ ++void isp_stop(void); ++ ++int isp_buf_queue(struct videobuf_buffer *vb, ++ void (*complete)(struct videobuf_buffer *vb, void *priv), ++ void *priv); ++ ++int isp_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, ++ unsigned int *size); ++ ++int isp_vbq_prepare(struct videobuf_queue *vbq, struct videobuf_buffer *vb, ++ enum v4l2_field field); ++ ++void isp_vbq_release(struct videobuf_queue *vbq, struct videobuf_buffer *vb); ++ ++int isp_set_callback(enum isp_callback_type type, isp_callback_t callback, ++ isp_vbq_callback_ptr arg1, void *arg2); ++ ++int isp_unset_callback(enum isp_callback_type type); ++ ++u32 isp_set_xclk(u32 xclk, u8 xclksel); ++ ++int isp_configure_interface(struct isp_interface_config *config); ++ ++int isp_get(void); ++ ++int isp_put(void); ++ ++int isp_queryctrl(struct v4l2_queryctrl *a); ++ ++int isp_querymenu(struct v4l2_querymenu *a); ++ ++int isp_g_ctrl(struct v4l2_control *a); ++ ++int isp_s_ctrl(struct v4l2_control *a); ++ ++int isp_enum_fmt_cap(struct v4l2_fmtdesc *f); ++ ++int isp_try_fmt_cap(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output); ++ ++void isp_g_fmt_cap(struct v4l2_pix_format *pix); ++ ++int isp_s_fmt_cap(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output); ++ ++int isp_g_crop(struct v4l2_crop *a); ++ ++int isp_s_crop(struct v4l2_crop *a, struct v4l2_pix_format *pix); ++ ++void isp_config_crop(struct v4l2_pix_format *pix); ++ ++int isp_try_fmt(struct v4l2_pix_format *pix_input, ++ struct v4l2_pix_format *pix_output); ++ ++int isp_handle_private(int cmd, void *arg); ++ ++void isp_save_context(struct isp_reg *); ++ ++void isp_restore_context(struct isp_reg *); ++ ++void isp_print_status(void); ++ ++int __init isp_ccdc_init(void); ++int __init isp_hist_init(void); ++int __init isph3a_aewb_init(void); ++int __init isp_preview_init(void); ++int __init isp_resizer_init(void); ++int __init isp_af_init(void); ++int __init isp_csi2_init(void); ++ ++void isp_ccdc_cleanup(void); ++void isp_hist_cleanup(void); ++void isph3a_aewb_cleanup(void); ++void isp_preview_cleanup(void); ++void isp_hist_cleanup(void); ++void isp_resizer_cleanup(void); ++void isp_af_exit(void); ++void isp_csi2_cleanup(void); ++ ++#endif /* OMAP_ISP_TOP_H */ +diff --git a/drivers/media/video/isp/ispreg.h b/drivers/media/video/isp/ispreg.h +new file mode 100644 +index 0000000..4f8e1ef +--- /dev/null ++++ b/drivers/media/video/isp/ispreg.h +@@ -0,0 +1,1674 @@ ++/* ++ * ispreg.h ++ * ++ * Header file for all the ISP module in TI's OMAP3 Camera ISP. ++ * It has the OMAP HW register definitions. ++ * ++ * Copyright (C) 2009 Texas Instruments. ++ * Copyright (C) 2009 Nokia. ++ * ++ * Contributors: ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * Thara Gopinath <thara@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef __ISPREG_H__ ++#define __ISPREG_H__ ++ ++#include <mach/omap34xx.h> ++ ++/* Note: Uncomment below defines as needed for enabling module specific debug ++ * messages ++ */ ++ ++/* ++ #define OMAP_ISPCTRL_DEBUG ++ #define OMAP_ISPCCDC_DEBUG ++ #define OMAP_ISPPREV_DEBUG ++ #define OMAP_ISPRESZ_DEBUG ++ #define OMAP_ISPMMU_DEBUG ++ #define OMAP_ISPH3A_DEBUG ++ #define OMAP_ISP_AF_DEBUG ++ #define OMAP_ISPHIST_DEBUG ++*/ ++ ++#ifdef OMAP_ISPCTRL_DEBUG ++#define DPRINTK_ISPCTRL(format, ...) \ ++ printk(KERN_INFO "ISPCTRL: " format, ## __VA_ARGS__) ++#define is_ispctrl_debug_enabled() 1 ++#else ++#define DPRINTK_ISPCTRL(format, ...) ++#define is_ispctrl_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISPCCDC_DEBUG ++#define DPRINTK_ISPCCDC(format, ...) \ ++ printk(KERN_INFO "ISPCCDC: " format, ## __VA_ARGS__) ++#define is_ispccdc_debug_enabled() 1 ++#else ++#define DPRINTK_ISPCCDC(format, ...) ++#define is_ispccdc_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISPPREV_DEBUG ++#define DPRINTK_ISPPREV(format, ...) \ ++ printk(KERN_INFO "ISPPREV: " format, ## __VA_ARGS__) ++#define is_ispprev_debug_enabled() 1 ++#else ++#define DPRINTK_ISPPREV(format, ...) ++#define is_ispprev_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISPRESZ_DEBUG ++#define DPRINTK_ISPRESZ(format, ...) \ ++ printk(KERN_INFO "ISPRESZ: " format, ## __VA_ARGS__) ++#define is_ispresz_debug_enabled() 1 ++#else ++#define DPRINTK_ISPRESZ(format, ...) ++#define is_ispresz_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISPMMU_DEBUG ++#define DPRINTK_ISPMMU(format, ...) \ ++ printk(KERN_INFO "ISPMMU: " format, ## __VA_ARGS__) ++#define is_ispmmu_debug_enabled() 1 ++#else ++#define DPRINTK_ISPMMU(format, ...) ++#define is_ispmmu_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISPH3A_DEBUG ++#define DPRINTK_ISPH3A(format, ...) \ ++ printk(KERN_INFO "ISPH3A: " format, ## __VA_ARGS__) ++#define is_isph3a_debug_enabled() 1 ++#else ++#define DPRINTK_ISPH3A(format, ...) ++#define is_isph3a_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISP_AF_DEBUG ++#define DPRINTK_ISP_AF(format, ...) \ ++ printk(KERN_INFO "ISP_AF: " format, ## __VA_ARGS__) ++#define is_isp_af_debug_enabled() 1 ++#else ++#define DPRINTK_ISP_AF(format, ...) ++#define is_isp_af_debug_enabled() 0 ++#endif ++ ++#ifdef OMAP_ISPHIST_DEBUG ++#define DPRINTK_ISPHIST(format, ...) \ ++ printk(KERN_INFO "ISPHIST: " format, ## __VA_ARGS__) ++#define is_isphist_debug_enabled() 1 ++#else ++#define DPRINTK_ISPHIST(format, ...) ++#define is_isphist_debug_enabled() 0 ++#endif ++ ++#define ISP_32B_BOUNDARY_BUF 0xFFFFFFE0 ++#define ISP_32B_BOUNDARY_OFFSET 0x0000FFE0 ++ ++#define CM_CAM_MCLK_HZ 216000000 ++ ++/* ISP Submodules offset */ ++ ++#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE ++#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CBUFF_REG_OFFSET 0x0100 ++#define OMAP3ISP_CBUFF_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CBUFF_REG_OFFSET) ++#define OMAP3ISP_CBUFF_REG(offset) (OMAP3ISP_CBUFF_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CCP2_REG_OFFSET 0x0400 ++#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CCP2_REG_OFFSET) ++#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CCDC_REG_OFFSET 0x0600 ++#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CCDC_REG_OFFSET) ++#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) ++ ++#define OMAP3ISP_HIST_REG_OFFSET 0x0A00 ++#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_HIST_REG_OFFSET) ++#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) ++ ++#define OMAP3ISP_H3A_REG_OFFSET 0x0C00 ++#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_H3A_REG_OFFSET) ++#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) ++ ++#define OMAP3ISP_PREV_REG_OFFSET 0x0E00 ++#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_PREV_REG_OFFSET) ++#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) ++ ++#define OMAP3ISP_RESZ_REG_OFFSET 0x1000 ++#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_RESZ_REG_OFFSET) ++#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) ++ ++#define OMAP3ISP_SBL_REG_OFFSET 0x1200 ++#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_SBL_REG_OFFSET) ++#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) ++ ++#define OMAP3ISP_MMU_REG_OFFSET 0x1400 ++#define OMAP3ISP_MMU_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_MMU_REG_OFFSET) ++#define OMAP3ISP_MMU_REG(offset) (OMAP3ISP_MMU_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSI2A_REG_OFFSET 0x1800 ++#define OMAP3ISP_CSI2A_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSI2A_REG_OFFSET) ++#define OMAP3ISP_CSI2A_REG(offset) (OMAP3ISP_CSI2A_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSI2PHY_REG_OFFSET 0x1970 ++#define OMAP3ISP_CSI2PHY_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSI2PHY_REG_OFFSET) ++#define OMAP3ISP_CSI2PHY_REG(offset) (OMAP3ISP_CSI2PHY_REG_BASE + (offset)) ++ ++/* ISP module register offset */ ++ ++#define ISP_REVISION (0x000) ++#define ISP_SYSCONFIG (0x004) ++#define ISP_SYSSTATUS (0x008) ++#define ISP_IRQ0ENABLE (0x00C) ++#define ISP_IRQ0STATUS (0x010) ++#define ISP_IRQ1ENABLE (0x014) ++#define ISP_IRQ1STATUS (0x018) ++#define ISP_TCTRL_GRESET_LENGTH (0x030) ++#define ISP_TCTRL_PSTRB_REPLAY (0x034) ++#define ISP_CTRL (0x040) ++#define ISP_SECURE (0x044) ++#define ISP_TCTRL_CTRL (0x050) ++#define ISP_TCTRL_FRAME (0x054) ++#define ISP_TCTRL_PSTRB_DELAY (0x058) ++#define ISP_TCTRL_STRB_DELAY (0x05C) ++#define ISP_TCTRL_SHUT_DELAY (0x060) ++#define ISP_TCTRL_PSTRB_LENGTH (0x064) ++#define ISP_TCTRL_STRB_LENGTH (0x068) ++#define ISP_TCTRL_SHUT_LENGTH (0x06C) ++#define ISP_PING_PONG_ADDR (0x070) ++#define ISP_PING_PONG_MEM_RANGE (0x074) ++#define ISP_PING_PONG_BUF_SIZE (0x078) ++ ++/* CSI1 receiver registers (ES2.0) */ ++#define ISPCSI1_REVISION (0x000) ++#define ISPCSI1_SYSCONFIG (0x004) ++#define ISPCSI1_SYSSTATUS (0x008) ++#define ISPCSI1_LC01_IRQENABLE (0x00C) ++#define ISPCSI1_LC01_IRQSTATUS (0x010) ++#define ISPCSI1_LC23_IRQENABLE (0x014) ++#define ISPCSI1_LC23_IRQSTATUS (0x018) ++#define ISPCSI1_LCM_IRQENABLE (0x02C) ++#define ISPCSI1_LCM_IRQSTATUS (0x030) ++#define ISPCSI1_CTRL (0x040) ++#define ISPCSI1_DBG (0x044) ++#define ISPCSI1_GNQ (0x048) ++#define ISPCSI1_LCx_CTRL(x) ((0x050)+0x30*(x)) ++#define ISPCSI1_LCx_CODE(x) ((0x054)+0x30*(x)) ++#define ISPCSI1_LCx_STAT_START(x) ((0x058)+0x30*(x)) ++#define ISPCSI1_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x)) ++#define ISPCSI1_LCx_SOF_ADDR(x) ((0x060)+0x30*(x)) ++#define ISPCSI1_LCx_EOF_ADDR(x) ((0x064)+0x30*(x)) ++#define ISPCSI1_LCx_DAT_START(x) ((0x068)+0x30*(x)) ++#define ISPCSI1_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x)) ++#define ISPCSI1_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x)) ++#define ISPCSI1_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x)) ++#define ISPCSI1_LCx_DAT_OFST(x) ((0x078)+0x30*(x)) ++#define ISPCSI1_LCM_CTRL (0x1D0) ++#define ISPCSI1_LCM_VSIZE (0x1D4) ++#define ISPCSI1_LCM_HSIZE (0x1D8) ++#define ISPCSI1_LCM_PREFETCH (0x1DC) ++#define ISPCSI1_LCM_SRC_ADDR (0x1E0) ++#define ISPCSI1_LCM_SRC_OFST (0x1E4) ++#define ISPCSI1_LCM_DST_ADDR (0x1E8) ++#define ISPCSI1_LCM_DST_OFST (0x1EC) ++#define ISP_CSIB_SYSCONFIG ISPCSI1_SYSCONFIG ++#define ISP_CSIA_SYSCONFIG ISPCSI2_SYSCONFIG ++ ++/* ISP_CBUFF Registers */ ++ ++#define ISP_CBUFF_SYSCONFIG (0x010) ++#define ISP_CBUFF_IRQENABLE (0x01C) ++ ++#define ISP_CBUFF0_CTRL (0x020) ++#define ISP_CBUFF1_CTRL (0x024) ++ ++#define ISP_CBUFF0_START (0x040) ++#define ISP_CBUFF1_START (0x044) ++ ++#define ISP_CBUFF0_END (0x050) ++#define ISP_CBUFF1_END (0x054) ++ ++#define ISP_CBUFF0_WINDOWSIZE (0x060) ++#define ISP_CBUFF1_WINDOWSIZE (0x064) ++ ++#define ISP_CBUFF0_THRESHOLD (0x070) ++#define ISP_CBUFF1_THRESHOLD (0x074) ++ ++/* CCDC module register offset */ ++ ++#define ISPCCDC_PID (0x000) ++#define ISPCCDC_PCR (0x004) ++#define ISPCCDC_SYN_MODE (0x008) ++#define ISPCCDC_HD_VD_WID (0x00C) ++#define ISPCCDC_PIX_LINES (0x010) ++#define ISPCCDC_HORZ_INFO (0x014) ++#define ISPCCDC_VERT_START (0x018) ++#define ISPCCDC_VERT_LINES (0x01C) ++#define ISPCCDC_CULLING (0x020) ++#define ISPCCDC_HSIZE_OFF (0x024) ++#define ISPCCDC_SDOFST (0x028) ++#define ISPCCDC_SDR_ADDR (0x02C) ++#define ISPCCDC_CLAMP (0x030) ++#define ISPCCDC_DCSUB (0x034) ++#define ISPCCDC_COLPTN (0x038) ++#define ISPCCDC_BLKCMP (0x03C) ++#define ISPCCDC_FPC (0x040) ++#define ISPCCDC_FPC_ADDR (0x044) ++#define ISPCCDC_VDINT (0x048) ++#define ISPCCDC_ALAW (0x04C) ++#define ISPCCDC_REC656IF (0x050) ++#define ISPCCDC_CFG (0x054) ++#define ISPCCDC_FMTCFG (0x058) ++#define ISPCCDC_FMT_HORZ (0x05C) ++#define ISPCCDC_FMT_VERT (0x060) ++#define ISPCCDC_FMT_ADDR0 (0x064) ++#define ISPCCDC_FMT_ADDR1 (0x068) ++#define ISPCCDC_FMT_ADDR2 (0x06C) ++#define ISPCCDC_FMT_ADDR3 (0x070) ++#define ISPCCDC_FMT_ADDR4 (0x074) ++#define ISPCCDC_FMT_ADDR5 (0x078) ++#define ISPCCDC_FMT_ADDR6 (0x07C) ++#define ISPCCDC_FMT_ADDR7 (0x080) ++#define ISPCCDC_PRGEVEN0 (0x084) ++#define ISPCCDC_PRGEVEN1 (0x088) ++#define ISPCCDC_PRGODD0 (0x08C) ++#define ISPCCDC_PRGODD1 (0x090) ++#define ISPCCDC_VP_OUT (0x094) ++ ++#define ISPCCDC_LSC_CONFIG (0x098) ++#define ISPCCDC_LSC_INITIAL (0x09C) ++#define ISPCCDC_LSC_TABLE_BASE (0x0A0) ++#define ISPCCDC_LSC_TABLE_OFFSET (0x0A4) ++ ++/* SBL */ ++#define ISPSBL_CCDC_WR_0 (0x028) ++#define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21) ++#define ISPSBL_CCDC_WR_1 (0x02C) ++#define ISPSBL_CCDC_WR_2 (0x030) ++#define ISPSBL_CCDC_WR_3 (0x034) ++ ++/* Histogram registers */ ++#define ISPHIST_PID (0x000) ++#define ISPHIST_PCR (0x004) ++#define ISPHIST_CNT (0x008) ++#define ISPHIST_WB_GAIN (0x00C) ++#define ISPHIST_R0_HORZ (0x010) ++#define ISPHIST_R0_VERT (0x014) ++#define ISPHIST_R1_HORZ (0x018) ++#define ISPHIST_R1_VERT (0x01C) ++#define ISPHIST_R2_HORZ (0x020) ++#define ISPHIST_R2_VERT (0x024) ++#define ISPHIST_R3_HORZ (0x028) ++#define ISPHIST_R3_VERT (0x02C) ++#define ISPHIST_ADDR (0x030) ++#define ISPHIST_DATA (0x034) ++#define ISPHIST_RADD (0x038) ++#define ISPHIST_RADD_OFF (0x03C) ++#define ISPHIST_H_V_INFO (0x040) ++ ++/* H3A module registers */ ++#define ISPH3A_PID (0x000) ++#define ISPH3A_PCR (0x004) ++#define ISPH3A_AEWWIN1 (0x04C) ++#define ISPH3A_AEWINSTART (0x050) ++#define ISPH3A_AEWINBLK (0x054) ++#define ISPH3A_AEWSUBWIN (0x058) ++#define ISPH3A_AEWBUFST (0x05C) ++#define ISPH3A_AFPAX1 (0x008) ++#define ISPH3A_AFPAX2 (0x00C) ++#define ISPH3A_AFPAXSTART (0x010) ++#define ISPH3A_AFIIRSH (0x014) ++#define ISPH3A_AFBUFST (0x018) ++#define ISPH3A_AFCOEF010 (0x01C) ++#define ISPH3A_AFCOEF032 (0x020) ++#define ISPH3A_AFCOEF054 (0x024) ++#define ISPH3A_AFCOEF076 (0x028) ++#define ISPH3A_AFCOEF098 (0x02C) ++#define ISPH3A_AFCOEF0010 (0x030) ++#define ISPH3A_AFCOEF110 (0x034) ++#define ISPH3A_AFCOEF132 (0x038) ++#define ISPH3A_AFCOEF154 (0x03C) ++#define ISPH3A_AFCOEF176 (0x040) ++#define ISPH3A_AFCOEF198 (0x044) ++#define ISPH3A_AFCOEF1010 (0x048) ++ ++#define ISPPRV_PCR (0x004) ++#define ISPPRV_HORZ_INFO (0x008) ++#define ISPPRV_VERT_INFO (0x00C) ++#define ISPPRV_RSDR_ADDR (0x010) ++#define ISPPRV_RADR_OFFSET (0x014) ++#define ISPPRV_DSDR_ADDR (0x018) ++#define ISPPRV_DRKF_OFFSET (0x01C) ++#define ISPPRV_WSDR_ADDR (0x020) ++#define ISPPRV_WADD_OFFSET (0x024) ++#define ISPPRV_AVE (0x028) ++#define ISPPRV_HMED (0x02C) ++#define ISPPRV_NF (0x030) ++#define ISPPRV_WB_DGAIN (0x034) ++#define ISPPRV_WBGAIN (0x038) ++#define ISPPRV_WBSEL (0x03C) ++#define ISPPRV_CFA (0x040) ++#define ISPPRV_BLKADJOFF (0x044) ++#define ISPPRV_RGB_MAT1 (0x048) ++#define ISPPRV_RGB_MAT2 (0x04C) ++#define ISPPRV_RGB_MAT3 (0x050) ++#define ISPPRV_RGB_MAT4 (0x054) ++#define ISPPRV_RGB_MAT5 (0x058) ++#define ISPPRV_RGB_OFF1 (0x05C) ++#define ISPPRV_RGB_OFF2 (0x060) ++#define ISPPRV_CSC0 (0x064) ++#define ISPPRV_CSC1 (0x068) ++#define ISPPRV_CSC2 (0x06C) ++#define ISPPRV_CSC_OFFSET (0x070) ++#define ISPPRV_CNT_BRT (0x074) ++#define ISPPRV_CSUP (0x078) ++#define ISPPRV_SETUP_YC (0x07C) ++#define ISPPRV_SET_TBL_ADDR (0x080) ++#define ISPPRV_SET_TBL_DATA (0x084) ++#define ISPPRV_CDC_THR0 (0x090) ++#define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4)) ++#define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2) ++#define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3) ++ ++#define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000 ++#define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400 ++#define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800 ++#define ISPPRV_NF_TABLE_ADDR 0x0C00 ++#define ISPPRV_YENH_TABLE_ADDR 0x1000 ++#define ISPPRV_CFA_TABLE_ADDR 0x1400 ++ ++#define ISPPRV_MAXOUTPUT_WIDTH 1280 ++#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 ++#define ISPRSZ_MIN_OUTPUT 64 ++#define ISPRSZ_MAX_OUTPUT 3312 ++ ++/* Resizer module register offset */ ++#define ISPRSZ_PID (0x000) ++#define ISPRSZ_PCR (0x004) ++#define ISPRSZ_CNT (0x008) ++#define ISPRSZ_OUT_SIZE (0x00C) ++#define ISPRSZ_IN_START (0x010) ++#define ISPRSZ_IN_SIZE (0x014) ++#define ISPRSZ_SDR_INADD (0x018) ++#define ISPRSZ_SDR_INOFF (0x01C) ++#define ISPRSZ_SDR_OUTADD (0x020) ++#define ISPRSZ_SDR_OUTOFF (0x024) ++#define ISPRSZ_HFILT10 (0x028) ++#define ISPRSZ_HFILT32 (0x02C) ++#define ISPRSZ_HFILT54 (0x030) ++#define ISPRSZ_HFILT76 (0x034) ++#define ISPRSZ_HFILT98 (0x038) ++#define ISPRSZ_HFILT1110 (0x03C) ++#define ISPRSZ_HFILT1312 (0x040) ++#define ISPRSZ_HFILT1514 (0x044) ++#define ISPRSZ_HFILT1716 (0x048) ++#define ISPRSZ_HFILT1918 (0x04C) ++#define ISPRSZ_HFILT2120 (0x050) ++#define ISPRSZ_HFILT2322 (0x054) ++#define ISPRSZ_HFILT2524 (0x058) ++#define ISPRSZ_HFILT2726 (0x05C) ++#define ISPRSZ_HFILT2928 (0x060) ++#define ISPRSZ_HFILT3130 (0x064) ++#define ISPRSZ_VFILT10 (0x068) ++#define ISPRSZ_VFILT32 (0x06C) ++#define ISPRSZ_VFILT54 (0x070) ++#define ISPRSZ_VFILT76 (0x074) ++#define ISPRSZ_VFILT98 (0x078) ++#define ISPRSZ_VFILT1110 (0x07C) ++#define ISPRSZ_VFILT1312 (0x080) ++#define ISPRSZ_VFILT1514 (0x084) ++#define ISPRSZ_VFILT1716 (0x088) ++#define ISPRSZ_VFILT1918 (0x08C) ++#define ISPRSZ_VFILT2120 (0x090) ++#define ISPRSZ_VFILT2322 (0x094) ++#define ISPRSZ_VFILT2524 (0x098) ++#define ISPRSZ_VFILT2726 (0x09C) ++#define ISPRSZ_VFILT2928 (0x0A0) ++#define ISPRSZ_VFILT3130 (0x0A4) ++#define ISPRSZ_YENH (0x0A8) ++ ++/* MMU module registers */ ++#define ISPMMU_REVISION (0x000) ++#define ISPMMU_SYSCONFIG (0x010) ++#define ISPMMU_SYSSTATUS (0x014) ++#define ISPMMU_IRQSTATUS (0x018) ++#define ISPMMU_IRQENABLE (0x01C) ++#define ISPMMU_WALKING_ST (0x040) ++#define ISPMMU_CNTL (0x044) ++#define ISPMMU_FAULT_AD (0x048) ++#define ISPMMU_TTB (0x04C) ++#define ISPMMU_LOCK (0x050) ++#define ISPMMU_LD_TLB (0x054) ++#define ISPMMU_CAM (0x058) ++#define ISPMMU_RAM (0x05C) ++#define ISPMMU_GFLUSH (0x060) ++#define ISPMMU_FLUSH_ENTRY (0x064) ++#define ISPMMU_READ_CAM (0x068) ++#define ISPMMU_READ_RAM (0x06c) ++#define ISPMMU_EMU_FAULT_AD (0x070) ++ ++#define ISP_INT_CLR 0xFF113F11 ++#define ISPPRV_PCR_EN 1 ++#define ISPPRV_PCR_BUSY (1 << 1) ++#define ISPPRV_PCR_SOURCE (1 << 2) ++#define ISPPRV_PCR_ONESHOT (1 << 3) ++#define ISPPRV_PCR_WIDTH (1 << 4) ++#define ISPPRV_PCR_INVALAW (1 << 5) ++#define ISPPRV_PCR_DRKFEN (1 << 6) ++#define ISPPRV_PCR_DRKFCAP (1 << 7) ++#define ISPPRV_PCR_HMEDEN (1 << 8) ++#define ISPPRV_PCR_NFEN (1 << 9) ++#define ISPPRV_PCR_CFAEN (1 << 10) ++#define ISPPRV_PCR_CFAFMT_SHIFT 11 ++#define ISPPRV_PCR_CFAFMT_MASK 0x7800 ++#define ISPPRV_PCR_CFAFMT_BAYER (0 << 11) ++#define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11) ++#define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11) ++#define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11) ++#define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11) ++#define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11) ++#define ISPPRV_PCR_YNENHEN (1 << 15) ++#define ISPPRV_PCR_SUPEN (1 << 16) ++#define ISPPRV_PCR_YCPOS_SHIFT 17 ++#define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17) ++#define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17) ++#define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17) ++#define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17) ++#define ISPPRV_PCR_RSZPORT (1 << 19) ++#define ISPPRV_PCR_SDRPORT (1 << 20) ++#define ISPPRV_PCR_SCOMP_EN (1 << 21) ++#define ISPPRV_PCR_SCOMP_SFT_SHIFT (22) ++#define ISPPRV_PCR_SCOMP_SFT_MASK (~(7 << 22)) ++#define ISPPRV_PCR_GAMMA_BYPASS (1 << 26) ++#define ISPPRV_PCR_DCOREN (1 << 27) ++#define ISPPRV_PCR_DCCOUP (1 << 28) ++#define ISPPRV_PCR_DRK_FAIL (1 << 31) ++ ++#define ISPPRV_HORZ_INFO_EPH_SHIFT 0 ++#define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff ++#define ISPPRV_HORZ_INFO_SPH_SHIFT 16 ++#define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0 ++ ++#define ISPPRV_VERT_INFO_ELV_SHIFT 0 ++#define ISPPRV_VERT_INFO_ELV_MASK 0x3fff ++#define ISPPRV_VERT_INFO_SLV_SHIFT 16 ++#define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0 ++ ++#define ISPPRV_AVE_EVENDIST_SHIFT 2 ++#define ISPPRV_AVE_EVENDIST_1 0x0 ++#define ISPPRV_AVE_EVENDIST_2 0x1 ++#define ISPPRV_AVE_EVENDIST_3 0x2 ++#define ISPPRV_AVE_EVENDIST_4 0x3 ++#define ISPPRV_AVE_ODDDIST_SHIFT 4 ++#define ISPPRV_AVE_ODDDIST_1 0x0 ++#define ISPPRV_AVE_ODDDIST_2 0x1 ++#define ISPPRV_AVE_ODDDIST_3 0x2 ++#define ISPPRV_AVE_ODDDIST_4 0x3 ++ ++#define ISPPRV_HMED_THRESHOLD_SHIFT 0 ++#define ISPPRV_HMED_EVENDIST (1 << 8) ++#define ISPPRV_HMED_ODDDIST (1 << 9) ++ ++#define ISPPRV_WBGAIN_COEF0_SHIFT 0 ++#define ISPPRV_WBGAIN_COEF1_SHIFT 8 ++#define ISPPRV_WBGAIN_COEF2_SHIFT 16 ++#define ISPPRV_WBGAIN_COEF3_SHIFT 24 ++ ++#define ISPPRV_WBSEL_COEF0 0x0 ++#define ISPPRV_WBSEL_COEF1 0x1 ++#define ISPPRV_WBSEL_COEF2 0x2 ++#define ISPPRV_WBSEL_COEF3 0x3 ++ ++#define ISPPRV_WBSEL_N0_0_SHIFT 0 ++#define ISPPRV_WBSEL_N0_1_SHIFT 2 ++#define ISPPRV_WBSEL_N0_2_SHIFT 4 ++#define ISPPRV_WBSEL_N0_3_SHIFT 6 ++#define ISPPRV_WBSEL_N1_0_SHIFT 8 ++#define ISPPRV_WBSEL_N1_1_SHIFT 10 ++#define ISPPRV_WBSEL_N1_2_SHIFT 12 ++#define ISPPRV_WBSEL_N1_3_SHIFT 14 ++#define ISPPRV_WBSEL_N2_0_SHIFT 16 ++#define ISPPRV_WBSEL_N2_1_SHIFT 18 ++#define ISPPRV_WBSEL_N2_2_SHIFT 20 ++#define ISPPRV_WBSEL_N2_3_SHIFT 22 ++#define ISPPRV_WBSEL_N3_0_SHIFT 24 ++#define ISPPRV_WBSEL_N3_1_SHIFT 26 ++#define ISPPRV_WBSEL_N3_2_SHIFT 28 ++#define ISPPRV_WBSEL_N3_3_SHIFT 30 ++ ++#define ISPPRV_CFA_GRADTH_HOR_SHIFT 0 ++#define ISPPRV_CFA_GRADTH_VER_SHIFT 8 ++ ++#define ISPPRV_BLKADJOFF_B_SHIFT 0 ++#define ISPPRV_BLKADJOFF_G_SHIFT 8 ++#define ISPPRV_BLKADJOFF_R_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0 ++#define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0 ++#define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0 ++#define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0 ++#define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0 ++ ++#define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0 ++#define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16 ++ ++#define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0 ++ ++#define ISPPRV_CSC0_RY_SHIFT 0 ++#define ISPPRV_CSC0_GY_SHIFT 10 ++#define ISPPRV_CSC0_BY_SHIFT 20 ++ ++#define ISPPRV_CSC1_RCB_SHIFT 0 ++#define ISPPRV_CSC1_GCB_SHIFT 10 ++#define ISPPRV_CSC1_BCB_SHIFT 20 ++ ++#define ISPPRV_CSC2_RCR_SHIFT 0 ++#define ISPPRV_CSC2_GCR_SHIFT 10 ++#define ISPPRV_CSC2_BCR_SHIFT 20 ++ ++#define ISPPRV_CSC_OFFSET_CR_SHIFT 0 ++#define ISPPRV_CSC_OFFSET_CB_SHIFT 8 ++#define ISPPRV_CSC_OFFSET_Y_SHIFT 16 ++ ++#define ISPPRV_CNT_BRT_BRT_SHIFT 0 ++#define ISPPRV_CNT_BRT_CNT_SHIFT 8 ++ ++#define ISPPRV_CONTRAST_MAX 0x10 ++#define ISPPRV_CONTRAST_MIN 0xFF ++#define ISPPRV_BRIGHT_MIN 0x00 ++#define ISPPRV_BRIGHT_MAX 0xFF ++ ++#define ISPPRV_CSUP_CSUPG_SHIFT 0 ++#define ISPPRV_CSUP_THRES_SHIFT 8 ++#define ISPPRV_CSUP_HPYF_SHIFT 16 ++ ++#define ISPPRV_SETUP_YC_MINC_SHIFT 0 ++#define ISPPRV_SETUP_YC_MAXC_SHIFT 8 ++#define ISPPRV_SETUP_YC_MINY_SHIFT 16 ++#define ISPPRV_SETUP_YC_MAXY_SHIFT 24 ++#define ISPPRV_YC_MAX 0xFF ++#define ISPPRV_YC_MIN 0x0 ++ ++/* Define bit fields within selected registers */ ++#define ISP_REVISION_SHIFT 0 ++ ++#define ISP_SYSCONFIG_AUTOIDLE 0 ++#define ISP_SYSCONFIG_SOFTRESET (1 << 1) ++#define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12 ++#define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0 ++#define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1 ++#define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2 ++ ++#define ISP_SYSSTATUS_RESETDONE 0 ++ ++#define IRQ0ENABLE_CSIA_IRQ 1 ++#define IRQ0ENABLE_CSIA_LC1_IRQ (1 << 1) ++#define IRQ0ENABLE_CSIA_LC2_IRQ (1 << 2) ++#define IRQ0ENABLE_CSIA_LC3_IRQ (1 << 3) ++#define IRQ0ENABLE_CSIB_IRQ (1 << 4) ++#define IRQ0ENABLE_CSIB_LC1_IRQ (1 << 5) ++#define IRQ0ENABLE_CSIB_LC2_IRQ (1 << 6) ++#define IRQ0ENABLE_CSIB_LC3_IRQ (1 << 7) ++#define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8) ++#define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9) ++#define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10) ++#define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11) ++#define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12) ++#define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13) ++#define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16) ++#define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17) ++#define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18) ++#define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19) ++#define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20) ++#define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24) ++#define IRQ0ENABLE_OVF_IRQ (1 << 25) ++#define IRQ0ENABLE_PING_IRQ (1 << 26) ++#define IRQ0ENABLE_PONG_IRQ (1 << 27) ++#define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28) ++#define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29) ++#define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30) ++#define IRQ0ENABLE_HS_VS_IRQ (1 << 31) ++ ++#define IRQ0STATUS_CSIA_IRQ 1 ++#define IRQ0STATUS_CSIA_LC1_IRQ (1 << 1) ++#define IRQ0STATUS_CSIA_LC2_IRQ (1 << 2) ++#define IRQ0STATUS_CSIA_LC3_IRQ (1 << 3) ++#define IRQ0STATUS_CSIB_IRQ (1 << 4) ++#define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5) ++#define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6) ++#define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7) ++#define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8) ++#define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9) ++#define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10) ++#define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11) ++#define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12) ++#define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13) ++#define IRQ0STATUS_HIST_DONE_IRQ (1 << 16) ++#define IRQ0STATUS_PRV_DONE_IRQ (1 << 20) ++#define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24) ++#define IRQ0STATUS_OVF_IRQ (1 << 25) ++#define IRQ0STATUS_PING_IRQ (1 << 26) ++#define IRQ0STATUS_PONG_IRQ (1 << 27) ++#define IRQ0STATUS_MMU_ERR_IRQ (1 << 28) ++#define IRQ0STATUS_OCP_ERR_IRQ (1 << 29) ++#define IRQ0STATUS_SEC_ERR_IRQ (1 << 30) ++#define IRQ0STATUS_HS_VS_IRQ (1 << 31) ++ ++#define TCTRL_GRESET_LEN 0 ++ ++#define TCTRL_PSTRB_REPLAY_DELAY 0 ++#define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25 ++ ++#define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0 ++#define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1 ++#define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2 ++#define ISPCTRL_PAR_SER_CLK_SEL_MASK 0xFFFFFFFC ++ ++#define ISPCTRL_PAR_BRIDGE_SHIFT 2 ++#define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2) ++#define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2) ++#define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2) ++ ++#define ISPCTRL_PAR_CLK_POL_SHIFT 4 ++#define ISPCTRL_PAR_CLK_POL_INV (1 << 4) ++#define ISPCTRL_PING_PONG_EN (1 << 5) ++#define ISPCTRL_SHIFT_SHIFT 6 ++#define ISPCTRL_SHIFT_0 (0x0 << 6) ++#define ISPCTRL_SHIFT_2 (0x1 << 6) ++#define ISPCTRL_SHIFT_4 (0x2 << 6) ++#define ISPCTRL_SHIFT_MASK (~(0x3 << 6)) ++ ++#define ISPCTRL_CCDC_CLK_EN (1 << 8) ++#define ISPCTRL_SCMP_CLK_EN (1 << 9) ++#define ISPCTRL_H3A_CLK_EN (1 << 10) ++#define ISPCTRL_HIST_CLK_EN (1 << 11) ++#define ISPCTRL_PREV_CLK_EN (1 << 12) ++#define ISPCTRL_RSZ_CLK_EN (1 << 13) ++#define ISPCTRL_SYNC_DETECT_SHIFT 14 ++#define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) ++ ++#define ISPCTRL_CCDC_RAM_EN (1 << 16) ++#define ISPCTRL_PREV_RAM_EN (1 << 17) ++#define ISPCTRL_SBL_RD_RAM_EN (1 << 18) ++#define ISPCTRL_SBL_WR1_RAM_EN (1 << 19) ++#define ISPCTRL_SBL_WR0_RAM_EN (1 << 20) ++#define ISPCTRL_SBL_AUTOIDLE (1 << 21) ++#define ISPCTRL_SBL_SHARED_RPORTB (1 << 28) ++#define ISPCTRL_JPEG_FLUSH (1 << 30) ++#define ISPCTRL_CCDC_FLUSH (1 << 31) ++ ++#define ISPSECURE_SECUREMODE 0 ++ ++#define ISPTCTRL_CTRL_DIV_LOW 0x0 ++#define ISPTCTRL_CTRL_DIV_HIGH 0x1 ++#define ISPTCTRL_CTRL_DIV_BYPASS 0x1F ++ ++#define ISPTCTRL_CTRL_DIVA_SHIFT 0 ++#define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT) ++ ++#define ISPTCTRL_CTRL_DIVB_SHIFT 5 ++#define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT) ++ ++#define ISPTCTRL_CTRL_DIVC_SHIFT 10 ++#define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10) ++ ++#define ISPTCTRL_CTRL_SHUTEN (1 << 21) ++#define ISPTCTRL_CTRL_PSTRBEN (1 << 22) ++#define ISPTCTRL_CTRL_STRBEN (1 << 23) ++#define ISPTCTRL_CTRL_SHUTPOL (1 << 24) ++#define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26) ++ ++#define ISPTCTRL_CTRL_INSEL_SHIFT 27 ++#define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27) ++#define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27) ++#define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27) ++ ++#define ISPTCTRL_CTRL_GRESETEn (1 << 29) ++#define ISPTCTRL_CTRL_GRESETPOL (1 << 30) ++#define ISPTCTRL_CTRL_GRESETDIR (1 << 31) ++ ++#define ISPTCTRL_FRAME_SHUT_SHIFT 0 ++#define ISPTCTRL_FRAME_PSTRB_SHIFT 6 ++#define ISPTCTRL_FRAME_STRB_SHIFT 12 ++ ++#define ISPCCDC_PID_PREV_SHIFT 0 ++#define ISPCCDC_PID_CID_SHIFT 8 ++#define ISPCCDC_PID_TID_SHIFT 16 ++ ++#define ISPCCDC_PCR_EN 1 ++#define ISPCCDC_PCR_BUSY (1 << 1) ++ ++#define ISPCCDC_SYN_MODE_VDHDOUT 0x1 ++#define ISPCCDC_SYN_MODE_FLDOUT (1 << 1) ++#define ISPCCDC_SYN_MODE_VDPOL (1 << 2) ++#define ISPCCDC_SYN_MODE_HDPOL (1 << 3) ++#define ISPCCDC_SYN_MODE_FLDPOL (1 << 4) ++#define ISPCCDC_SYN_MODE_EXWEN (1 << 5) ++#define ISPCCDC_SYN_MODE_DATAPOL (1 << 6) ++#define ISPCCDC_SYN_MODE_FLDMODE (1 << 7) ++#define ISPCCDC_SYN_MODE_DATSIZ_MASK 0xFFFFF8FF ++#define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8) ++#define ISPCCDC_SYN_MODE_PACK8 (1 << 11) ++#define ISPCCDC_SYN_MODE_INPMOD_MASK 0xFFFFCFFF ++#define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12) ++#define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12) ++#define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12) ++#define ISPCCDC_SYN_MODE_LPF (1 << 14) ++#define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15) ++#define ISPCCDC_SYN_MODE_VDHDEN (1 << 16) ++#define ISPCCDC_SYN_MODE_WEN (1 << 17) ++#define ISPCCDC_SYN_MODE_VP2SDR (1 << 18) ++#define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19) ++ ++#define ISPCCDC_HD_VD_WID_VDW_SHIFT 0 ++#define ISPCCDC_HD_VD_WID_HDW_SHIFT 16 ++ ++#define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0 ++#define ISPCCDC_PIX_LINES_PPLN_SHIFT 16 ++ ++#define ISPCCDC_HORZ_INFO_NPH_SHIFT 0 ++#define ISPCCDC_HORZ_INFO_NPH_MASK 0xFFFF8000 ++#define ISPCCDC_HORZ_INFO_SPH_MASK 0x1000FFFF ++#define ISPCCDC_HORZ_INFO_SPH_SHIFT 16 ++ ++#define ISPCCDC_VERT_START_SLV0_SHIFT 16 ++#define ISPCCDC_VERT_START_SLV0_MASK 0x1000FFFF ++#define ISPCCDC_VERT_START_SLV1_SHIFT 0 ++ ++#define ISPCCDC_VERT_LINES_NLV_MASK 0xFFFF8000 ++#define ISPCCDC_VERT_LINES_NLV_SHIFT 0 ++ ++#define ISPCCDC_CULLING_CULV_SHIFT 0 ++#define ISPCCDC_CULLING_CULHODD_SHIFT 16 ++#define ISPCCDC_CULLING_CULHEVN_SHIFT 24 ++ ++#define ISPCCDC_HSIZE_OFF_SHIFT 0 ++ ++#define ISPCCDC_SDOFST_FINV (1 << 14) ++#define ISPCCDC_SDOFST_FOFST_1L 0 ++#define ISPCCDC_SDOFST_FOFST_4L (3 << 12) ++#define ISPCCDC_SDOFST_LOFST3_SHIFT 0 ++#define ISPCCDC_SDOFST_LOFST2_SHIFT 3 ++#define ISPCCDC_SDOFST_LOFST1_SHIFT 6 ++#define ISPCCDC_SDOFST_LOFST0_SHIFT 9 ++#define EVENEVEN 1 ++#define ODDEVEN 2 ++#define EVENODD 3 ++#define ODDODD 4 ++ ++#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 ++#define ISPCCDC_CLAMP_OBST_SHIFT 10 ++#define ISPCCDC_CLAMP_OBSLN_SHIFT 25 ++#define ISPCCDC_CLAMP_OBSLEN_SHIFT 28 ++#define ISPCCDC_CLAMP_CLAMPEN (1 << 31) ++ ++#define ISPCCDC_COLPTN_R_Ye 0x0 ++#define ISPCCDC_COLPTN_Gr_Cy 0x1 ++#define ISPCCDC_COLPTN_Gb_G 0x2 ++#define ISPCCDC_COLPTN_B_Mg 0x3 ++#define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0 ++#define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2 ++#define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4 ++#define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6 ++#define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8 ++#define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10 ++#define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12 ++#define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14 ++#define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16 ++#define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18 ++#define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20 ++#define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22 ++#define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24 ++#define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26 ++#define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28 ++#define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30 ++ ++#define ISPCCDC_BLKCMP_B_MG_SHIFT 0 ++#define ISPCCDC_BLKCMP_GB_G_SHIFT 8 ++#define ISPCCDC_BLKCMP_GR_CY_SHIFT 16 ++#define ISPCCDC_BLKCMP_R_YE_SHIFT 24 ++ ++#define ISPCCDC_FPC_FPNUM_SHIFT 0 ++#define ISPCCDC_FPC_FPCEN (1 << 15) ++#define ISPCCDC_FPC_FPERR (1 << 16) ++ ++#define ISPCCDC_VDINT_1_SHIFT 0 ++#define ISPCCDC_VDINT_0_SHIFT 16 ++#define ISPCCDC_VDINT_0_MASK 0x7FFF ++#define ISPCCDC_VDINT_1_MASK 0x7FFF ++ ++#define ISPCCDC_ALAW_GWDI_SHIFT 0 ++#define ISPCCDC_ALAW_CCDTBL (1 << 3) ++ ++#define ISPCCDC_REC656IF_R656ON 1 ++#define ISPCCDC_REC656IF_ECCFVH (1 << 1) ++ ++#define ISPCCDC_CFG_BW656 (1 << 5) ++#define ISPCCDC_CFG_FIDMD_SHIFT 6 ++#define ISPCCDC_CFG_WENLOG (1 << 8) ++#define ISPCCDC_CFG_WENLOG_AND (0 << 8) ++#define ISPCCDC_CFG_WENLOG_OR (1 << 8) ++#define ISPCCDC_CFG_Y8POS (1 << 11) ++#define ISPCCDC_CFG_BSWD (1 << 12) ++#define ISPCCDC_CFG_MSBINVI (1 << 13) ++#define ISPCCDC_CFG_VDLC (1 << 15) ++ ++#define ISPCCDC_FMTCFG_FMTEN 0x1 ++#define ISPCCDC_FMTCFG_LNALT (1 << 1) ++#define ISPCCDC_FMTCFG_LNUM_SHIFT 2 ++#define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4 ++#define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8 ++#define ISPCCDC_FMTCFG_VPIN_MASK 0xFFFF8000 ++#define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12) ++#define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12) ++#define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12) ++#define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12) ++#define ISPCCDC_FMTCFG_VPEN (1 << 15) ++ ++#define ISPCCDC_FMTCF_VPIF_FRQ_MASK 0xFFF8FFFF ++#define ISPCCDC_FMTCF_VPIF_FRQ_BY2 (0x0 << 16) ++#define ISPCCDC_FMTCF_VPIF_FRQ_BY3 (0x1 << 16) ++#define ISPCCDC_FMTCF_VPIF_FRQ_BY4 (0x2 << 16) ++#define ISPCCDC_FMTCF_VPIF_FRQ_BY5 (0x3 << 16) ++#define ISPCCDC_FMTCF_VPIF_FRQ_BY6 (0x4 << 16) ++ ++#define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0 ++#define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16 ++ ++#define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0 ++#define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16 ++ ++#define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF0000 ++#define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF ++ ++#define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1FFF0000 ++#define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x1FFF ++ ++#define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0 ++#define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4 ++#define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17 ++ ++#define ISPRSZ_PID_PREV_SHIFT 0 ++#define ISPRSZ_PID_CID_SHIFT 8 ++#define ISPRSZ_PID_TID_SHIFT 16 ++ ++#define ISPRSZ_PCR_ENABLE 0x5 ++#define ISPRSZ_PCR_BUSY (1 << 1) ++ ++#define ISPRSZ_CNT_HRSZ_SHIFT 0 ++#define ISPRSZ_CNT_HRSZ_MASK 0x3FF ++#define ISPRSZ_CNT_VRSZ_SHIFT 10 ++#define ISPRSZ_CNT_VRSZ_MASK 0xFFC00 ++#define ISPRSZ_CNT_HSTPH_SHIFT 20 ++#define ISPRSZ_CNT_HSTPH_MASK 0x700000 ++#define ISPRSZ_CNT_VSTPH_SHIFT 23 ++#define ISPRSZ_CNT_VSTPH_MASK 0x3800000 ++#define ISPRSZ_CNT_CBILIN_MASK 0x20000000 ++#define ISPRSZ_CNT_INPTYP_MASK 0x08000000 ++#define ISPRSZ_CNT_PIXFMT_MASK 0x04000000 ++#define ISPRSZ_CNT_YCPOS (1 << 26) ++#define ISPRSZ_CNT_INPTYP (1 << 27) ++#define ISPRSZ_CNT_INPSRC (1 << 28) ++#define ISPRSZ_CNT_CBILIN (1 << 29) ++ ++#define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0 ++#define ISPRSZ_OUT_SIZE_HORZ_MASK 0x7FF ++#define ISPRSZ_OUT_SIZE_VERT_SHIFT 16 ++#define ISPRSZ_OUT_SIZE_VERT_MASK 0x7FF0000 ++ ++ ++#define ISPRSZ_IN_START_HORZ_ST_SHIFT 0 ++#define ISPRSZ_IN_START_HORZ_ST_MASK 0x1FFF ++#define ISPRSZ_IN_START_VERT_ST_SHIFT 16 ++#define ISPRSZ_IN_START_VERT_ST_MASK 0x1FFF0000 ++ ++ ++#define ISPRSZ_IN_SIZE_HORZ_SHIFT 0 ++#define ISPRSZ_IN_SIZE_HORZ_MASK 0x1FFF ++#define ISPRSZ_IN_SIZE_VERT_SHIFT 16 ++#define ISPRSZ_IN_SIZE_VERT_MASK 0x1FFF0000 ++ ++#define ISPRSZ_SDR_INADD_ADDR_SHIFT 0 ++#define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF ++ ++#define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0 ++#define ISPRSZ_SDR_INOFF_OFFSET_MASK 0xFFFF ++ ++#define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0 ++#define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF ++ ++ ++#define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0 ++#define ISPRSZ_SDR_OUTOFF_OFFSET_MASK 0xFFFF ++ ++#define ISPRSZ_HFILT10_COEF0_SHIFT 0 ++#define ISPRSZ_HFILT10_COEF0_MASK 0x3FF ++#define ISPRSZ_HFILT10_COEF1_SHIFT 16 ++#define ISPRSZ_HFILT10_COEF1_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT32_COEF2_SHIFT 0 ++#define ISPRSZ_HFILT32_COEF2_MASK 0x3FF ++#define ISPRSZ_HFILT32_COEF3_SHIFT 16 ++#define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT54_COEF4_SHIFT 0 ++#define ISPRSZ_HFILT54_COEF4_MASK 0x3FF ++#define ISPRSZ_HFILT54_COEF5_SHIFT 16 ++#define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT76_COEFF6_SHIFT 0 ++#define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF ++#define ISPRSZ_HFILT76_COEFF7_SHIFT 16 ++#define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT98_COEFF8_SHIFT 0 ++#define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF ++#define ISPRSZ_HFILT98_COEFF9_SHIFT 16 ++#define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1110_COEF10_SHIFT 0 ++#define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF ++#define ISPRSZ_HFILT1110_COEF11_SHIFT 16 ++#define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1312_COEFF12_SHIFT 0 ++#define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF ++#define ISPRSZ_HFILT1312_COEFF13_SHIFT 16 ++#define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1514_COEFF14_SHIFT 0 ++#define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF ++#define ISPRSZ_HFILT1514_COEFF15_SHIFT 16 ++#define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1716_COEF16_SHIFT 0 ++#define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF ++#define ISPRSZ_HFILT1716_COEF17_SHIFT 16 ++#define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1918_COEF18_SHIFT 0 ++#define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF ++#define ISPRSZ_HFILT1918_COEF19_SHIFT 16 ++#define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2120_COEF20_SHIFT 0 ++#define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF ++#define ISPRSZ_HFILT2120_COEF21_SHIFT 16 ++#define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2322_COEF22_SHIFT 0 ++#define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF ++#define ISPRSZ_HFILT2322_COEF23_SHIFT 16 ++#define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2524_COEF24_SHIFT 0 ++#define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF ++#define ISPRSZ_HFILT2524_COEF25_SHIFT 16 ++#define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2726_COEF26_SHIFT 0 ++#define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF ++#define ISPRSZ_HFILT2726_COEF27_SHIFT 16 ++#define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2928_COEF28_SHIFT 0 ++#define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF ++#define ISPRSZ_HFILT2928_COEF29_SHIFT 16 ++#define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT3130_COEF30_SHIFT 0 ++#define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF ++#define ISPRSZ_HFILT3130_COEF31_SHIFT 16 ++#define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT10_COEF0_SHIFT 0 ++#define ISPRSZ_VFILT10_COEF0_MASK 0x3FF ++#define ISPRSZ_VFILT10_COEF1_SHIFT 16 ++#define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT32_COEF2_SHIFT 0 ++#define ISPRSZ_VFILT32_COEF2_MASK 0x3FF ++#define ISPRSZ_VFILT32_COEF3_SHIFT 16 ++#define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT54_COEF4_SHIFT 0 ++#define ISPRSZ_VFILT54_COEF4_MASK 0x3FF ++#define ISPRSZ_VFILT54_COEF5_SHIFT 16 ++#define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT76_COEFF6_SHIFT 0 ++#define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF ++#define ISPRSZ_VFILT76_COEFF7_SHIFT 16 ++#define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT98_COEFF8_SHIFT 0 ++#define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF ++#define ISPRSZ_VFILT98_COEFF9_SHIFT 16 ++#define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1110_COEF10_SHIFT 0 ++#define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF ++#define ISPRSZ_VFILT1110_COEF11_SHIFT 16 ++#define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1312_COEFF12_SHIFT 0 ++#define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF ++#define ISPRSZ_VFILT1312_COEFF13_SHIFT 16 ++#define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1514_COEFF14_SHIFT 0 ++#define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF ++#define ISPRSZ_VFILT1514_COEFF15_SHIFT 16 ++#define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1716_COEF16_SHIFT 0 ++#define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF ++#define ISPRSZ_VFILT1716_COEF17_SHIFT 16 ++#define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1918_COEF18_SHIFT 0 ++#define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF ++#define ISPRSZ_VFILT1918_COEF19_SHIFT 16 ++#define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2120_COEF20_SHIFT 0 ++#define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF ++#define ISPRSZ_VFILT2120_COEF21_SHIFT 16 ++#define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2322_COEF22_SHIFT 0 ++#define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF ++#define ISPRSZ_VFILT2322_COEF23_SHIFT 16 ++#define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2524_COEF24_SHIFT 0 ++#define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF ++#define ISPRSZ_VFILT2524_COEF25_SHIFT 16 ++#define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2726_COEF26_SHIFT 0 ++#define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF ++#define ISPRSZ_VFILT2726_COEF27_SHIFT 16 ++#define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2928_COEF28_SHIFT 0 ++#define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF ++#define ISPRSZ_VFILT2928_COEF29_SHIFT 16 ++#define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT3130_COEF30_SHIFT 0 ++#define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF ++#define ISPRSZ_VFILT3130_COEF31_SHIFT 16 ++#define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000 ++ ++#define ISPRSZ_YENH_CORE_SHIFT 0 ++#define ISPRSZ_YENH_CORE_MASK 0xFF ++#define ISPRSZ_YENH_SLOP_SHIFT 8 ++#define ISPRSZ_YENH_SLOP_MASK 0xF00 ++#define ISPRSZ_YENH_GAIN_SHIFT 12 ++#define ISPRSZ_YENH_GAIN_MASK 0xF000 ++#define ISPRSZ_YENH_ALGO_SHIFT 16 ++#define ISPRSZ_YENH_ALGO_MASK 0x30000 ++ ++#define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1 ++#define ISPH3A_PCR_AF_MED_TH_SHIFT 3 ++#define ISPH3A_PCR_AF_RGBPOS_SHIFT 11 ++#define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22 ++#define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000 ++#define ISPH3A_PCR_BUSYAF (1 << 15) ++#define ISPH3A_PCR_BUSYAEAWB (1 << 18) ++ ++#define ISPH3A_AEWWIN1_WINHC_SHIFT 0 ++#define ISPH3A_AEWWIN1_WINHC_MASK 0x3F ++#define ISPH3A_AEWWIN1_WINVC_SHIFT 6 ++#define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0 ++#define ISPH3A_AEWWIN1_WINW_SHIFT 13 ++#define ISPH3A_AEWWIN1_WINW_MASK 0xFE000 ++#define ISPH3A_AEWWIN1_WINH_SHIFT 24 ++#define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000 ++ ++#define ISPH3A_AEWINSTART_WINSH_SHIFT 0 ++#define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF ++#define ISPH3A_AEWINSTART_WINSV_SHIFT 16 ++#define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000 ++ ++#define ISPH3A_AEWINBLK_WINH_SHIFT 0 ++#define ISPH3A_AEWINBLK_WINH_MASK 0x7F ++#define ISPH3A_AEWINBLK_WINSV_SHIFT 16 ++#define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000 ++ ++#define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0 ++#define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F ++#define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8 ++#define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00 ++ ++#define ISPHIST_PCR_ENABLE_SHIFT 0 ++#define ISPHIST_PCR_ENABLE_MASK 0x01 ++#define ISPHIST_PCR_BUSY 0x02 ++ ++#define ISPHIST_CNT_DATASIZE_SHIFT 8 ++#define ISPHIST_CNT_DATASIZE_MASK 0x0100 ++#define ISPHIST_CNT_CLEAR_SHIFT 7 ++#define ISPHIST_CNT_CLEAR_MASK 0x080 ++#define ISPHIST_CNT_CFA_SHIFT 6 ++#define ISPHIST_CNT_CFA_MASK 0x040 ++#define ISPHIST_CNT_BINS_SHIFT 4 ++#define ISPHIST_CNT_BINS_MASK 0x030 ++#define ISPHIST_CNT_SOURCE_SHIFT 3 ++#define ISPHIST_CNT_SOURCE_MASK 0x08 ++#define ISPHIST_CNT_SHIFT_SHIFT 0 ++#define ISPHIST_CNT_SHIFT_MASK 0x07 ++ ++#define ISPHIST_WB_GAIN_WG00_SHIFT 24 ++#define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000 ++#define ISPHIST_WB_GAIN_WG01_SHIFT 16 ++#define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000 ++#define ISPHIST_WB_GAIN_WG02_SHIFT 8 ++#define ISPHIST_WB_GAIN_WG02_MASK 0xFF00 ++#define ISPHIST_WB_GAIN_WG03_SHIFT 0 ++#define ISPHIST_WB_GAIN_WG03_MASK 0xFF ++ ++#define ISPHIST_REGHORIZ_HSTART_SHIFT 16 /* ++ * REGION 0 to 3 HORZ ++ * and VERT ++ */ ++#define ISPHIST_REGHORIZ_HSTART_MASK 0x3FFF0000 ++#define ISPHIST_REGHORIZ_HEND_SHIFT 0 ++#define ISPHIST_REGHORIZ_HEND_MASK 0x3FFF ++#define ISPHIST_REGVERT_VSTART_SHIFT 16 ++#define ISPHIST_REGVERT_VSTART_MASK 0x3FFF0000 ++#define ISPHIST_REGVERT_VEND_SHIFT 0 ++#define ISPHIST_REGVERT_VEND_MASK 0x3FFF ++ ++#define ISPHIST_REGHORIZ_MASK 0x3FFF3FFF ++#define ISPHIST_REGVERT_MASK 0x3FFF3FFF ++ ++#define ISPHIST_ADDR_SHIFT 0 ++#define ISPHIST_ADDR_MASK 0x3FF ++ ++#define ISPHIST_DATA_SHIFT 0 ++#define ISPHIST_DATA_MASK 0xFFFFF ++ ++#define ISPHIST_RADD_SHIFT 0 ++#define ISPHIST_RADD_MASK 0xFFFFFFFF ++ ++#define ISPHIST_RADD_OFF_SHIFT 0 ++#define ISPHIST_RADD_OFF_MASK 0xFFFF ++ ++#define ISPHIST_HV_INFO_HSIZE_SHIFT 16 ++#define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000 ++#define ISPHIST_HV_INFO_VSIZE_SHIFT 0 ++#define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF ++ ++#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF ++ ++#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700 ++#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8 ++#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800 ++#define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12 ++#define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE ++#define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1 ++#define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6) ++ ++#define ISPCCDC_LSC_INITIAL_X_MASK 0x3F ++#define ISPCCDC_LSC_INITIAL_X_SHIFT 0 ++#define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000 ++#define ISPCCDC_LSC_INITIAL_Y_SHIFT 16 ++ ++#define ISPMMU_REVISION_REV_MINOR_MASK 0xF ++#define ISPMMU_REVISION_REV_MAJOR_SHIFT 0x4 ++ ++#define IRQENABLE_MULTIHITFAULT (1<<4) ++#define IRQENABLE_TWFAULT (1<<3) ++#define IRQENABLE_EMUMISS (1<<2) ++#define IRQENABLE_TRANSLNFAULT (1<<1) ++#define IRQENABLE_TLBMISS (1) ++ ++#define ISPMMU_MMUCNTL_MMU_EN (1<<1) ++#define ISPMMU_MMUCNTL_TWL_EN (1<<2) ++#define ISPMMU_MMUCNTL_EMUTLBUPDATE (1<<3) ++#define ISPMMU_AUTOIDLE 0x1 ++#define ISPMMU_SIDLEMODE_FORCEIDLE 0 ++#define ISPMMU_SIDLEMODE_NOIDLE 1 ++#define ISPMMU_SIDLEMODE_SMARTIDLE 2 ++#define ISPMMU_SIDLEMODE_SHIFT 3 ++ ++#define ISPCSI1_AUTOIDLE 0x1 ++#define ISPCSI1_MIDLEMODE_SHIFT 12 ++#define ISPCSI1_MIDLEMODE_FORCESTANDBY 0x0 ++#define ISPCSI1_MIDLEMODE_NOSTANDBY 0x1 ++#define ISPCSI1_MIDLEMODE_SMARTSTANDBY 0x2 ++ ++/* CSI2 receiver registers (ES2.0) */ ++#define ISPCSI2_REVISION (0x000) ++#define ISPCSI2_SYSCONFIG (0x010) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \ ++ (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \ ++ (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \ ++ (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \ ++ (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT 1 ++#define ISPCSI2_SYSCONFIG_SOFT_RESET_MASK \ ++ (0x1 << ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT) ++#define ISPCSI2_SYSCONFIG_SOFT_RESET_NORMAL \ ++ (0x0 << ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT) ++#define ISPCSI2_SYSCONFIG_SOFT_RESET_RESET \ ++ (0x1 << ISPCSI2_SYSCONFIG_SOFT_RESET_SHIFT) ++#define ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT 0 ++#define ISPCSI2_SYSCONFIG_AUTO_IDLE_MASK \ ++ (0x1 << ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT) ++#define ISPCSI2_SYSCONFIG_AUTO_IDLE_FREE \ ++ (0x0 << ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT) ++#define ISPCSI2_SYSCONFIG_AUTO_IDLE_AUTO \ ++ (0x1 << ISPCSI2_SYSCONFIG_AUTO_IDLE_SHIFT) ++#define ISPCSI2_SYSSTATUS (0x014) ++#define ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT 0 ++#define ISPCSI2_SYSSTATUS_RESET_DONE_MASK \ ++ (0x1 << ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT) ++#define ISPCSI2_SYSSTATUS_RESET_DONE_ONGOING \ ++ (0x0 << ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT) ++#define ISPCSI2_SYSSTATUS_RESET_DONE_DONE \ ++ (0x1 << ISPCSI2_SYSSTATUS_RESET_DONE_SHIFT) ++#define ISPCSI2_IRQSTATUS (0x018) ++#define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14) ++#define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13) ++#define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12) ++#define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11) ++#define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10) ++#define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9) ++#define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8) ++#define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n)) ++ ++#define ISPCSI2_IRQENABLE (0x01C) ++#define ISPCSI2_CTRL (0x040) ++#define ISPCSI2_CTRL_VP_CLK_EN_SHIFT 15 ++#define ISPCSI2_CTRL_VP_CLK_EN_MASK (0x1 << ISPCSI2_CTRL_VP_CLK_EN_SHIFT) ++#define ISPCSI2_CTRL_VP_CLK_EN_DISABLE (0x0 << ISPCSI2_CTRL_VP_CLK_EN_SHIFT) ++#define ISPCSI2_CTRL_VP_CLK_EN_ENABLE (0x1 << ISPCSI2_CTRL_VP_CLK_EN_SHIFT) ++ ++#define ISPCSI2_CTRL_VP_ONLY_EN_SHIFT 11 ++#define ISPCSI2_CTRL_VP_ONLY_EN_MASK (0x1 << ISPCSI2_CTRL_VP_ONLY_EN_SHIFT) ++#define ISPCSI2_CTRL_VP_ONLY_EN_DISABLE (0x0 << ISPCSI2_CTRL_VP_ONLY_EN_SHIFT) ++#define ISPCSI2_CTRL_VP_ONLY_EN_ENABLE (0x1 << ISPCSI2_CTRL_VP_ONLY_EN_SHIFT) ++ ++#define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8 ++#define ISPCSI2_CTRL_VP_OUT_CTRL_MASK (0x3 << \ ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) ++#define ISPCSI2_CTRL_VP_OUT_CTRL_DISABLE (0x0 << \ ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) ++#define ISPCSI2_CTRL_VP_OUT_CTRL_DIV2 (0x1 << \ ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) ++#define ISPCSI2_CTRL_VP_OUT_CTRL_DIV3 (0x2 << \ ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) ++#define ISPCSI2_CTRL_VP_OUT_CTRL_DIV4 (0x3 << \ ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) ++ ++#define ISPCSI2_CTRL_DBG_EN_SHIFT 7 ++#define ISPCSI2_CTRL_DBG_EN_MASK (0x1 << ISPCSI2_CTRL_DBG_EN_SHIFT) ++#define ISPCSI2_CTRL_DBG_EN_DISABLE (0x0 << ISPCSI2_CTRL_DBG_EN_SHIFT) ++#define ISPCSI2_CTRL_DBG_EN_ENABLE (0x1 << ISPCSI2_CTRL_DBG_EN_SHIFT) ++ ++#define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5 ++#define ISPCSI2_CTRL_BURST_SIZE_MASK (0x3 << \ ++ ISPCSI2_CTRL_BURST_SIZE_SHIFT) ++#define ISPCSI2_CTRL_BURST_SIZE_MYSTERY_VAL (0x2 << \ ++ ISPCSI2_CTRL_BURST_SIZE_SHIFT) ++ ++#define ISPCSI2_CTRL_FRAME_SHIFT 3 ++#define ISPCSI2_CTRL_FRAME_MASK (0x1 << ISPCSI2_CTRL_FRAME_SHIFT) ++#define ISPCSI2_CTRL_FRAME_DISABLE_IMM (0x0 << ISPCSI2_CTRL_FRAME_SHIFT) ++#define ISPCSI2_CTRL_FRAME_DISABLE_FEC (0x1 << ISPCSI2_CTRL_FRAME_SHIFT) ++ ++#define ISPCSI2_CTRL_ECC_EN_SHIFT 2 ++#define ISPCSI2_CTRL_ECC_EN_MASK (0x1 << ISPCSI2_CTRL_ECC_EN_SHIFT) ++#define ISPCSI2_CTRL_ECC_EN_DISABLE (0x0 << ISPCSI2_CTRL_ECC_EN_SHIFT) ++#define ISPCSI2_CTRL_ECC_EN_ENABLE (0x1 << ISPCSI2_CTRL_ECC_EN_SHIFT) ++ ++#define ISPCSI2_CTRL_SECURE_SHIFT 1 ++#define ISPCSI2_CTRL_SECURE_MASK (0x1 << ISPCSI2_CTRL_SECURE_SHIFT) ++#define ISPCSI2_CTRL_SECURE_DISABLE (0x0 << ISPCSI2_CTRL_SECURE_SHIFT) ++#define ISPCSI2_CTRL_SECURE_ENABLE (0x1 << ISPCSI2_CTRL_SECURE_SHIFT) ++ ++#define ISPCSI2_CTRL_IF_EN_SHIFT 0 ++#define ISPCSI2_CTRL_IF_EN_MASK (0x1 << ISPCSI2_CTRL_IF_EN_SHIFT) ++#define ISPCSI2_CTRL_IF_EN_DISABLE (0x0 << ISPCSI2_CTRL_IF_EN_SHIFT) ++#define ISPCSI2_CTRL_IF_EN_ENABLE (0x1 << ISPCSI2_CTRL_IF_EN_SHIFT) ++ ++#define ISPCSI2_DBG_H (0x044) ++#define ISPCSI2_GNQ (0x048) ++#define ISPCSI2_COMPLEXIO_CFG1 (0x050) ++#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT 29 ++#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_MASK \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_ONGOING \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_DONE \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_RESET_DONE_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT 27 ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_MASK \ ++ (0x3 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_OFF \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ON \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ULPW \ ++ (0x2 << ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT 25 ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_MASK \ ++ (0x3 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_OFF \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ON \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ULPW \ ++ (0x2 << ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT 24 ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_MASK \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_DISABLE \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_ENABLE \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_SHIFT) ++ ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n) (3 + ((n) * 4)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_MASK(n) \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_PN(n) \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POL_NP(n) \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(n)) ++ ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n) ((n) * 4) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_MASK(n) \ ++ (0x7 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_NC(n) \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_1(n) \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_2(n) \ ++ (0x2 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_3(n) \ ++ (0x3 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_4(n) \ ++ (0x4 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_5(n) \ ++ (0x5 << ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(n)) ++ ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT 3 ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_MASK \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_PN \ ++ (0x0 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_NP \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT) ++ ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT 0 ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_MASK \ ++ (0x7 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_1 \ ++ (0x1 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_2 \ ++ (0x2 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_3 \ ++ (0x3 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_4 \ ++ (0x4 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_5 \ ++ (0x5 << ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT) ++ ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS (0x054) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEALLULPMEXIT (1 << 26) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEALLULPMENTER (1 << 25) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM5 (1 << 24) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM4 (1 << 23) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM3 (1 << 22) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM2 (1 << 21) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_STATEULPM1 (1 << 20) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL5 (1 << 19) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL4 (1 << 18) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL3 (1 << 17) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL2 (1 << 16) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRCONTROL1 (1 << 15) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC5 (1 << 14) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC4 (1 << 13) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC3 (1 << 12) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC2 (1 << 11) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRESC1 (1 << 10) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS5 (1 << 4) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS4 (1 << 3) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS3 (1 << 2) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS2 (1 << 1) ++#define ISPCSI2_COMPLEXIO1_IRQSTATUS_ERRSOTHS1 1 ++ ++#define ISPCSI2_SHORT_PACKET (0x05C) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE (0x060) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMEXIT (1 << 26) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMENTER (1 << 25) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM5 (1 << 24) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM4 (1 << 23) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM3 (1 << 22) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM2 (1 << 21) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM1 (1 << 20) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL5 (1 << 19) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL4 (1 << 18) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL3 (1 << 17) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL2 (1 << 16) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL1 (1 << 15) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC5 (1 << 14) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC4 (1 << 13) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC3 (1 << 12) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC2 (1 << 11) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC1 (1 << 10) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS5 (1 << 9) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS4 (1 << 8) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS3 (1 << 7) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS2 (1 << 6) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS1 (1 << 5) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS5 (1 << 4) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS4 (1 << 3) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS3 (1 << 2) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS2 (1 << 1) ++#define ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS1 1 ++#define ISPCSI2_DBG_P (0x068) ++#define ISPCSI2_TIMING (0x06C) ++ ++ ++#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n) \ ++ ((16 * ((n) - 1)) + 15) ++#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_MASK(n) \ ++ (0x1 << ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_DISABLE(n) \ ++ (0x0 << ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_FORCE_RX_MODE_IO_ENABLE(n) \ ++ (0x1 << ISPCSI2_TIMING_FORCE_RX_MODE_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n) ((16 * ((n) - 1)) + 14) ++#define ISPCSI2_TIMING_STOP_STATE_X16_IO_MASK(n) \ ++ (0x1 << ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_X16_IO_DISABLE(n) \ ++ (0x0 << ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_X16_IO_ENABLE(n) \ ++ (0x1 << ISPCSI2_TIMING_STOP_STATE_X16_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n) ((16 * ((n) - 1)) + 13) ++#define ISPCSI2_TIMING_STOP_STATE_X4_IO_MASK(n) \ ++ (0x1 << ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_X4_IO_DISABLE(n) \ ++ (0x0 << ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_X4_IO_ENABLE(n) \ ++ (0x1 << ISPCSI2_TIMING_STOP_STATE_X4_IO_SHIFT(n)) ++#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1)) ++#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \ ++ (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n)) ++ ++#define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n)) ++#define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8 ++#define ISPCSI2_CTX_CTRL1_COUNT_MASK (0xFF << \ ++ ISPCSI2_CTX_CTRL1_COUNT_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT 7 ++#define ISPCSI2_CTX_CTRL1_EOF_EN_MASK \ ++ (0x1 << ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOF_EN_DISABLE \ ++ (0x0 << ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOF_EN_ENABLE \ ++ (0x1 << ISPCSI2_CTX_CTRL1_EOF_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT 6 ++#define ISPCSI2_CTX_CTRL1_EOL_EN_MASK \ ++ (0x1 << ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOL_EN_DISABLE \ ++ (0x0 << ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOL_EN_ENABLE \ ++ (0x1 << ISPCSI2_CTX_CTRL1_EOL_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_CS_EN_SHIFT 5 ++#define ISPCSI2_CTX_CTRL1_CS_EN_MASK \ ++ (0x1 << ISPCSI2_CTX_CTRL1_CS_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_CS_EN_DISABLE \ ++ (0x0 << ISPCSI2_CTX_CTRL1_CS_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_CS_EN_ENABLE \ ++ (0x1 << ISPCSI2_CTX_CTRL1_CS_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT 4 ++#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_MASK \ ++ (0x1 << ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_DISABLE \ ++ (0x0 << ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_ENABLE \ ++ (0x1 << ISPCSI2_CTX_CTRL1_COUNT_UNLOCK_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_PING_PONG_SHIFT 3 ++#define ISPCSI2_CTX_CTRL1_PING_PONG_MASK \ ++ (0x1 << ISPCSI2_CTX_CTRL1_PING_PONG_SHIFT) ++#define ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT 0 ++#define ISPCSI2_CTX_CTRL1_CTX_EN_MASK \ ++ (0x1 << ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_CTX_EN_DISABLE \ ++ (0x0 << ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT) ++#define ISPCSI2_CTX_CTRL1_CTX_EN_ENABLE \ ++ (0x1 << ISPCSI2_CTX_CTRL1_CTX_EN_SHIFT) ++ ++#define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n)) ++#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 ++#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \ ++ (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT) ++#define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0 ++#define ISPCSI2_CTX_CTRL2_FORMAT_MASK (0x3FF << \ ++ ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) ++ ++#define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n)) ++#define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 5 ++#define ISPCSI2_CTX_DAT_OFST_OFST_MASK (0x7FF << \ ++ ISPCSI2_CTX_DAT_OFST_OFST_SHIFT) ++ ++#define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07C) + 0x20 * (n)) ++#define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n)) ++#define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n)) ++#define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8) ++#define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7) ++#define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6) ++#define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5) ++#define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3) ++#define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2) ++#define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1) ++#define ISPCSI2_CTX_IRQENABLE_FS_IRQ 1 ++#define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n)) ++#define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8) ++#define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7) ++#define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6) ++#define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5) ++#define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3) ++#define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2) ++#define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1) ++#define ISPCSI2_CTX_IRQSTATUS_FS_IRQ 1 ++ ++#define ISPCSI2_CTX_CTRL3(n) ((0x08C) + 0x20 * (n)) ++#define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5 ++#define ISPCSI2_CTX_CTRL3_ALPHA_MASK (0x3FFF << \ ++ ISPCSI2_CTX_CTRL3_ALPHA_SHIFT) ++ ++#define ISPCSI2PHY_CFG0 (0x000) ++#define ISPCSI2PHY_CFG0_THS_TERM_SHIFT 8 ++#define ISPCSI2PHY_CFG0_THS_TERM_MASK \ ++ (0xFF << ISPCSI2PHY_CFG0_THS_TERM_SHIFT) ++#define ISPCSI2PHY_CFG0_THS_TERM_RESETVAL \ ++ (0x04 << ISPCSI2PHY_CFG0_THS_TERM_SHIFT) ++#define ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT 0 ++#define ISPCSI2PHY_CFG0_THS_SETTLE_MASK \ ++ (0xFF << ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT) ++#define ISPCSI2PHY_CFG0_THS_SETTLE_RESETVAL \ ++ (0x27 << ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT) ++#define ISPCSI2PHY_CFG1 (0x004) ++#define ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT 18 ++#define ISPCSI2PHY_CFG1_TCLK_TERM_MASK \ ++ (0x7F << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) ++#define ISPCSI2PHY_CFG1_TCLK_TERM__RESETVAL \ ++ (0x00 << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) ++#define ISPCSI2PHY_CFG1_RESERVED1_SHIFT 10 ++#define ISPCSI2PHY_CFG1_RESERVED1_MASK \ ++ (0xFF << ISPCSI2PHY_CFG1_RESERVED1_SHIFT) ++#define ISPCSI2PHY_CFG1_RESERVED1__RESETVAL \ ++ (0xB8 << ISPCSI2PHY_CFG1_RESERVED1_SHIFT) ++#define ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT 8 ++#define ISPCSI2PHY_CFG1_TCLK_MISS_MASK \ ++ (0x3 << ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT) ++#define ISPCSI2PHY_CFG1_TCLK_MISS__RESETVAL \ ++ (0x1 << ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT) ++#define ISPCSI2PHY_CFG1_TCLK_SETTLE_SHIFT 0 ++#define ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK \ ++ (0xFF << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) ++#define ISPCSI2PHY_CFG1_TCLK_SETTLE__RESETVAL \ ++ (0x0E << ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT) ++#define ISPCSI2PHY_CFG1__RESETVAL (ISPCSI2PHY_CFG1_TCLK_TERM__RESETVAL | \ ++ ISPCSI2PHY_CFG1_RESERVED1__RESETVAL | \ ++ ISPCSI2PHY_CFG1_TCLK_MISS__RESETVAL | \ ++ ISPCSI2PHY_CFG1_TCLK_SETTLE__RESETVAL) ++#define ISPCSI2PHY_CFG1__EDITABLE_MASK (ISPCSI2PHY_CFG1_TCLK_TERM_MASK | \ ++ ISPCSI2PHY_CFG1_RESERVED1_MASK | \ ++ ISPCSI2PHY_CFG1_TCLK_MISS_MASK | \ ++ ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK) ++ ++#endif /* __ISPREG_H__ */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0002-omap3isp-Add-ISP-MMU-wrapper.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0002-omap3isp-Add-ISP-MMU-wrapper.patch new file mode 100644 index 0000000000..cfca26723a --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0002-omap3isp-Add-ISP-MMU-wrapper.patch @@ -0,0 +1,209 @@ +From 731527a7dc26533a878c7c5f36fc148fdcaa21b8 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add ISP MMU wrapper + +TODO: + +- The ISP driver should start using the IOMMU directly without this wrapper. + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/isp/ispmmu.c | 141 ++++++++++++++++++++++++++++++++++++++ + drivers/media/video/isp/ispmmu.h | 36 ++++++++++ + 2 files changed, 177 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/ispmmu.c + create mode 100644 drivers/media/video/isp/ispmmu.h + +diff --git a/drivers/media/video/isp/ispmmu.c b/drivers/media/video/isp/ispmmu.c +new file mode 100644 +index 0000000..f872c71 +--- /dev/null ++++ b/drivers/media/video/isp/ispmmu.c +@@ -0,0 +1,141 @@ ++/* ++ * omap iommu wrapper for TI's OMAP3430 Camera ISP ++ * ++ * Copyright (C) 2008--2009 Nokia. ++ * ++ * Contributors: ++ * Hiroshi Doyu <hiroshi.doyu@nokia.com> ++ * Sakari Ailus <sakari.ailus@nokia.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <linux/module.h> ++ ++#include "ispmmu.h" ++#include "isp.h" ++ ++#include <mach/iommu.h> ++#include <mach/iovmm.h> ++ ++#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) ++ ++static struct iommu *isp_iommu; ++ ++dma_addr_t ispmmu_vmalloc(size_t bytes) ++{ ++ return (dma_addr_t)iommu_vmalloc(isp_iommu, 0, bytes, IOMMU_FLAG); ++} ++ ++void ispmmu_vfree(const dma_addr_t da) ++{ ++ iommu_vfree(isp_iommu, (u32)da); ++} ++ ++dma_addr_t ispmmu_kmap(u32 pa, int size) ++{ ++ void *da; ++ ++ da = (void *)iommu_kmap(isp_iommu, 0, pa, size, IOMMU_FLAG); ++ if (IS_ERR(da)) ++ return PTR_ERR(da); ++ ++ return (dma_addr_t)da; ++} ++ ++void ispmmu_kunmap(dma_addr_t da) ++{ ++ iommu_kunmap(isp_iommu, (u32)da); ++} ++ ++dma_addr_t ispmmu_vmap(const struct scatterlist *sglist, ++ int sglen) ++{ ++ int err; ++ void *da; ++ struct sg_table *sgt; ++ unsigned int i; ++ struct scatterlist *sg, *src = (struct scatterlist *)sglist; ++ ++ /* ++ * convert isp sglist to iommu sgt ++ * FIXME: should be fixed in the upper layer? ++ */ ++ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); ++ if (!sgt) ++ return -ENOMEM; ++ err = sg_alloc_table(sgt, sglen, GFP_KERNEL); ++ if (err) ++ goto err_sg_alloc; ++ ++ for_each_sg(sgt->sgl, sg, sgt->nents, i) ++ sg_set_buf(sg, phys_to_virt(sg_dma_address(src + i)), ++ sg_dma_len(src + i)); ++ ++ da = (void *)iommu_vmap(isp_iommu, 0, sgt, IOMMU_FLAG); ++ if (IS_ERR(da)) ++ goto err_vmap; ++ ++ return (dma_addr_t)da; ++ ++err_vmap: ++ sg_free_table(sgt); ++err_sg_alloc: ++ kfree(sgt); ++ return -ENOMEM; ++} ++EXPORT_SYMBOL_GPL(ispmmu_vmap); ++ ++void ispmmu_vunmap(dma_addr_t da) ++{ ++ struct sg_table *sgt; ++ ++ sgt = iommu_vunmap(isp_iommu, (u32)da); ++ if (!sgt) ++ return; ++ sg_free_table(sgt); ++ kfree(sgt); ++} ++EXPORT_SYMBOL_GPL(ispmmu_vunmap); ++ ++void ispmmu_save_context(void) ++{ ++ if (isp_iommu) ++ iommu_save_ctx(isp_iommu); ++} ++ ++void ispmmu_restore_context(void) ++{ ++ if (isp_iommu) ++ iommu_restore_ctx(isp_iommu); ++} ++ ++int __init ispmmu_init(void) ++{ ++ int err = 0; ++ ++ isp_get(); ++ isp_iommu = iommu_get("isp"); ++ if (IS_ERR(isp_iommu)) { ++ err = PTR_ERR(isp_iommu); ++ isp_iommu = NULL; ++ } ++ isp_put(); ++ ++ return err; ++} ++ ++void ispmmu_cleanup(void) ++{ ++ isp_get(); ++ if (isp_iommu) ++ iommu_put(isp_iommu); ++ isp_put(); ++ isp_iommu = NULL; ++} +diff --git a/drivers/media/video/isp/ispmmu.h b/drivers/media/video/isp/ispmmu.h +new file mode 100644 +index 0000000..0bc5bcb +--- /dev/null ++++ b/drivers/media/video/isp/ispmmu.h +@@ -0,0 +1,36 @@ ++/* ++ * omap iommu wrapper for TI's OMAP3430 Camera ISP ++ * ++ * Copyright (C) 2008--2009 Nokia. ++ * ++ * Contributors: ++ * Hiroshi Doyu <hiroshi.doyu@nokia.com> ++ * Sakari Ailus <sakari.ailus@nokia.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_MMU_H ++#define OMAP_ISP_MMU_H ++ ++#include <linux/err.h> ++#include <linux/scatterlist.h> ++ ++dma_addr_t ispmmu_vmalloc(size_t bytes); ++void ispmmu_vfree(const dma_addr_t da); ++dma_addr_t ispmmu_kmap(u32 pa, int size); ++void ispmmu_kunmap(dma_addr_t da); ++dma_addr_t ispmmu_vmap(const struct scatterlist *sglist, int sglen); ++void ispmmu_vunmap(dma_addr_t da); ++void ispmmu_save_context(void); ++void ispmmu_restore_context(void); ++int ispmmu_init(void); ++void ispmmu_cleanup(void); ++ ++#endif /* OMAP_ISP_MMU_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0003-omap3isp-Add-userspace-header.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0003-omap3isp-Add-userspace-header.patch new file mode 100644 index 0000000000..66c171f544 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0003-omap3isp-Add-userspace-header.patch @@ -0,0 +1,696 @@ +From 98ca1ef8c6e2561989aeef981131cf5077eab1d1 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add userspace header + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + arch/arm/plat-omap/include/mach/isp_user.h | 676 ++++++++++++++++++++++++++++ + 1 files changed, 676 insertions(+), 0 deletions(-) + create mode 100644 arch/arm/plat-omap/include/mach/isp_user.h + +diff --git a/arch/arm/plat-omap/include/mach/isp_user.h b/arch/arm/plat-omap/include/mach/isp_user.h +new file mode 100644 +index 0000000..b819e26 +--- /dev/null ++++ b/arch/arm/plat-omap/include/mach/isp_user.h +@@ -0,0 +1,676 @@ ++/* ++ * isp_user.h ++ * ++ * Include file for OMAP ISP module in TI's OMAP3. ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Mohit Jalori <mjalori@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_USER_H ++#define OMAP_ISP_USER_H ++ ++/* ISP Private IOCTLs */ ++#define VIDIOC_PRIVATE_ISP_CCDC_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct ispccdc_update_config) ++#define VIDIOC_PRIVATE_ISP_PRV_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct ispprv_update_config) ++#define VIDIOC_PRIVATE_ISP_AEWB_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct isph3a_aewb_config) ++#define VIDIOC_PRIVATE_ISP_AEWB_REQ \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct isph3a_aewb_data) ++#define VIDIOC_PRIVATE_ISP_HIST_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct isp_hist_config) ++#define VIDIOC_PRIVATE_ISP_HIST_REQ \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 7, struct isp_hist_data) ++#define VIDIOC_PRIVATE_ISP_AF_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct af_configuration) ++#define VIDIOC_PRIVATE_ISP_AF_REQ \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 9, struct isp_af_data) ++ ++/* AE/AWB related structures and flags*/ ++ ++/* Flags for update field */ ++#define REQUEST_STATISTICS (1 << 0) ++#define SET_COLOR_GAINS (1 << 1) ++#define SET_DIGITAL_GAIN (1 << 2) ++#define SET_EXPOSURE (1 << 3) ++#define SET_ANALOG_GAIN (1 << 4) ++ ++#define MAX_FRAME_COUNT 0x0FFF ++#define MAX_FUTURE_FRAMES 10 ++ ++#define MAX_SATURATION_LIM 1023 ++#define MIN_WIN_H 2 ++#define MAX_WIN_H 256 ++#define MIN_WIN_W 6 ++#define MAX_WIN_W 256 ++#define MAX_WINVC 128 ++#define MAX_WINHC 36 ++#define MAX_WINSTART 4095 ++#define MIN_SUB_INC 2 ++#define MAX_SUB_INC 32 ++ ++/* Range Constants */ ++#define AF_IIRSH_MIN 0 ++#define AF_IIRSH_MAX 4094 ++#define AF_PAXEL_HORIZONTAL_COUNT_MIN 0 ++#define AF_PAXEL_HORIZONTAL_COUNT_MAX 35 ++#define AF_PAXEL_VERTICAL_COUNT_MIN 0 ++#define AF_PAXEL_VERTICAL_COUNT_MAX 127 ++#define AF_PAXEL_INCREMENT_MIN 0 ++#define AF_PAXEL_INCREMENT_MAX 14 ++#define AF_PAXEL_HEIGHT_MIN 0 ++#define AF_PAXEL_HEIGHT_MAX 127 ++#define AF_PAXEL_WIDTH_MIN 0 ++#define AF_PAXEL_WIDTH_MAX 127 ++#define AF_PAXEL_HZSTART_MIN 2 ++#define AF_PAXEL_HZSTART_MAX 4094 ++ ++#define AF_PAXEL_VTSTART_MIN 0 ++#define AF_PAXEL_VTSTART_MAX 4095 ++#define AF_THRESHOLD_MAX 255 ++#define AF_COEF_MAX 4095 ++#define AF_PAXEL_SIZE 48 ++ ++/** ++ * struct isph3a_aewb_config - AE AWB configuration reset values. ++ * saturation_limit: Saturation limit. ++ * @win_height: Window Height. Range 2 - 256, even values only. ++ * @win_width: Window Width. Range 6 - 256, even values only. ++ * @ver_win_count: Vertical Window Count. Range 1 - 128. ++ * @hor_win_count: Horizontal Window Count. Range 1 - 36. ++ * @ver_win_start: Vertical Window Start. Range 0 - 4095. ++ * @hor_win_start: Horizontal Window Start. Range 0 - 4095. ++ * @blk_ver_win_start: Black Vertical Windows Start. Range 0 - 4095. ++ * @blk_win_height: Black Window Height. Range 2 - 256, even values only. ++ * @subsample_ver_inc: Subsample Vertical points increment Range 2 - 32, even ++ * values only. ++ * @subsample_hor_inc: Subsample Horizontal points increment Range 2 - 32, even ++ * values only. ++ * @alaw_enable: AEW ALAW EN flag. ++ * @aewb_enable: AE AWB stats generation EN flag. ++ */ ++struct isph3a_aewb_config { ++ __u16 saturation_limit; ++ __u16 win_height; ++ __u16 win_width; ++ __u16 ver_win_count; ++ __u16 hor_win_count; ++ __u16 ver_win_start; ++ __u16 hor_win_start; ++ __u16 blk_ver_win_start; ++ __u16 blk_win_height; ++ __u16 subsample_ver_inc; ++ __u16 subsample_hor_inc; ++ __u8 alaw_enable; ++ __u8 aewb_enable; ++}; ++ ++/** ++ * struct isph3a_aewb_data - Structure of data sent to or received from user ++ * @h3a_aewb_statistics_buf: Pointer to pass to user. ++ * @shutter: Shutter speed. ++ * @gain: Sensor analog Gain. ++ * @shutter_cap: Shutter speed for capture. ++ * @gain_cap: Sensor Gain for capture. ++ * @dgain: White balance digital gain. ++ * @wb_gain_b: White balance color gain blue. ++ * @wb_gain_r: White balance color gain red. ++ * @wb_gain_gb: White balance color gain green blue. ++ * @wb_gain_gr: White balance color gain green red. ++ * @frame_number: Frame number of requested stats. ++ * @curr_frame: Current frame number being processed. ++ * @update: Bitwise flags to update parameters. ++ * @ts: Timestamp of returned framestats. ++ * @field_count: Sequence number of returned framestats. ++ */ ++struct isph3a_aewb_data { ++ void *h3a_aewb_statistics_buf; ++ __u32 shutter; ++ __u16 gain; ++ __u32 shutter_cap; ++ __u16 gain_cap; ++ __u16 dgain; ++ __u16 wb_gain_b; ++ __u16 wb_gain_r; ++ __u16 wb_gain_gb; ++ __u16 wb_gain_gr; ++ __u16 frame_number; ++ __u16 curr_frame; ++ __u8 update; ++ struct timeval ts; ++ __u32 config_counter; ++ unsigned long field_count; ++}; ++ ++ ++/* Histogram related structs */ ++/* Flags for number of bins */ ++#define BINS_32 0x0 ++#define BINS_64 0x1 ++#define BINS_128 0x2 ++#define BINS_256 0x3 ++ ++struct isp_hist_config { ++ __u8 hist_source; /* CCDC or Memory */ ++ __u8 input_bit_width; /* Needed o know the size per pixel */ ++ __u8 hist_frames; /* Num of frames to be processed and ++ * accumulated ++ */ ++ __u8 hist_h_v_info; /* frame-input width and height if source is ++ * memory ++ */ ++ __u16 hist_radd; /* frame-input address in memory */ ++ __u16 hist_radd_off; /* line-offset for frame-input */ ++ __u16 hist_bins; /* number of bins: 32, 64, 128, or 256 */ ++ __u16 wb_gain_R; /* White Balance Field-to-Pattern Assignments */ ++ __u16 wb_gain_RG; /* White Balance Field-to-Pattern Assignments */ ++ __u16 wb_gain_B; /* White Balance Field-to-Pattern Assignments */ ++ __u16 wb_gain_BG; /* White Balance Field-to-Pattern Assignments */ ++ __u8 num_regions; /* number of regions to be configured */ ++ __u16 reg0_hor; /* Region 0 size and position */ ++ __u16 reg0_ver; /* Region 0 size and position */ ++ __u16 reg1_hor; /* Region 1 size and position */ ++ __u16 reg1_ver; /* Region 1 size and position */ ++ __u16 reg2_hor; /* Region 2 size and position */ ++ __u16 reg2_ver; /* Region 2 size and position */ ++ __u16 reg3_hor; /* Region 3 size and position */ ++ __u16 reg3_ver; /* Region 3 size and position */ ++}; ++ ++struct isp_hist_data { ++ __u32 *hist_statistics_buf; /* Pointer to pass to user */ ++}; ++ ++/* Auto Focus related structs */ ++ ++#define AF_NUMBER_OF_COEF 11 ++ ++/* Flags for update field */ ++#define REQUEST_STATISTICS (1 << 0) ++#define LENS_DESIRED_POSITION (1 << 1) ++#define LENS_CURRENT_POSITION (1 << 2) ++ ++/** ++ * struct isp_af_xtrastats - Extra statistics related to AF generated stats. ++ * @ts: Timestamp when the frame gets delivered to the user. ++ * @field_count: Field count of the frame delivered to the user. ++ * @lens_position: Lens position when the stats are being generated. ++ */ ++struct isp_af_xtrastats { ++ struct timeval ts; ++ unsigned long field_count; ++ __u16 lens_position; /* deprecated */ ++}; ++ ++/** ++ * struct isp_af_data - AF statistics data to transfer between driver and user. ++ * @af_statistics_buf: Pointer to pass to user. ++ * @lens_current_position: Read value of lens absolute position. ++ * @desired_lens_direction: Lens desired location. ++ * @update: Bitwise flags to update parameters. ++ * @frame_number: Data for which frame is desired/given. ++ * @curr_frame: Current frame number being processed by AF module. ++ * @xtrastats: Extra statistics structure. ++ */ ++struct isp_af_data { ++ void *af_statistics_buf; ++ __u16 lens_current_position; /* deprecated */ ++ __u16 desired_lens_direction; /* deprecated */ ++ __u16 update; ++ __u16 frame_number; ++ __u16 curr_frame; ++ __u32 config_counter; ++ struct isp_af_xtrastats xtrastats; ++}; ++ ++/* enum used for status of specific feature */ ++enum af_alaw_enable { ++ H3A_AF_ALAW_DISABLE = 0, ++ H3A_AF_ALAW_ENABLE = 1 ++}; ++ ++enum af_hmf_enable { ++ H3A_AF_HMF_DISABLE = 0, ++ H3A_AF_HMF_ENABLE = 1 ++}; ++ ++enum af_config_flag { ++ H3A_AF_CFG_DISABLE = 0, ++ H3A_AF_CFG_ENABLE = 1 ++}; ++ ++enum af_mode { ++ ACCUMULATOR_SUMMED = 0, ++ ACCUMULATOR_PEAK = 1 ++}; ++ ++/* Red, Green, and blue pixel location in the AF windows */ ++enum rgbpos { ++ GR_GB_BAYER = 0, /* GR and GB as Bayer pattern */ ++ RG_GB_BAYER = 1, /* RG and GB as Bayer pattern */ ++ GR_BG_BAYER = 2, /* GR and BG as Bayer pattern */ ++ RG_BG_BAYER = 3, /* RG and BG as Bayer pattern */ ++ GG_RB_CUSTOM = 4, /* GG and RB as custom pattern */ ++ RB_GG_CUSTOM = 5 /* RB and GG as custom pattern */ ++}; ++ ++/* Contains the information regarding the Horizontal Median Filter */ ++struct af_hmf { ++ enum af_hmf_enable enable; /* Status of Horizontal Median Filter */ ++ unsigned int threshold; /* Threshhold Value for Horizontal Median ++ * Filter ++ */ ++}; ++ ++/* Contains the information regarding the IIR Filters */ ++struct af_iir { ++ unsigned int hz_start_pos; /* IIR Start Register Value */ ++ int coeff_set0[AF_NUMBER_OF_COEF]; /* ++ * IIR Filter Coefficient for ++ * Set 0 ++ */ ++ int coeff_set1[AF_NUMBER_OF_COEF]; /* ++ * IIR Filter Coefficient for ++ * Set 1 ++ */ ++}; ++ ++/* Contains the information regarding the Paxels Structure in AF Engine */ ++struct af_paxel { ++ unsigned int width; /* Width of the Paxel */ ++ unsigned int height; /* Height of the Paxel */ ++ unsigned int hz_start; /* Horizontal Start Position */ ++ unsigned int vt_start; /* Vertical Start Position */ ++ unsigned int hz_cnt; /* Horizontal Count */ ++ unsigned int vt_cnt; /* vertical Count */ ++ unsigned int line_incr; /* Line Increment */ ++}; ++/* Contains the parameters required for hardware set up of AF Engine */ ++struct af_configuration { ++ enum af_alaw_enable alaw_enable; /*ALWAW status */ ++ struct af_hmf hmf_config; /*HMF configurations */ ++ enum rgbpos rgb_pos; /*RGB Positions */ ++ struct af_iir iir_config; /*IIR filter configurations */ ++ struct af_paxel paxel_config; /*Paxel parameters */ ++ enum af_mode mode; /*Accumulator mode */ ++ enum af_config_flag af_config; /*Flag indicates Engine is configured */ ++}; ++ ++/* ISP CCDC structs */ ++ ++/* Abstraction layer CCDC configurations */ ++#define ISP_ABS_CCDC_ALAW (1 << 0) ++#define ISP_ABS_CCDC_LPF (1 << 1) ++#define ISP_ABS_CCDC_BLCLAMP (1 << 2) ++#define ISP_ABS_CCDC_BCOMP (1 << 3) ++#define ISP_ABS_CCDC_FPC (1 << 4) ++#define ISP_ABS_CCDC_CULL (1 << 5) ++#define ISP_ABS_CCDC_COLPTN (1 << 6) ++#define ISP_ABS_CCDC_CONFIG_LSC (1 << 7) ++#define ISP_ABS_TBL_LSC (1 << 8) ++ ++#define RGB_MAX 3 ++ ++/* Enumeration constants for Alaw input width */ ++enum alaw_ipwidth { ++ ALAW_BIT12_3 = 0x3, ++ ALAW_BIT11_2 = 0x4, ++ ALAW_BIT10_1 = 0x5, ++ ALAW_BIT9_0 = 0x6 ++}; ++ ++/* Enumeration constants for Video Port */ ++enum vpin { ++ BIT12_3 = 3, ++ BIT11_2 = 4, ++ BIT10_1 = 5, ++ BIT9_0 = 6 ++}; ++ ++enum vpif_freq { ++ PIXCLKBY2, ++ PIXCLKBY3_5, ++ PIXCLKBY4_5, ++ PIXCLKBY5_5, ++ PIXCLKBY6_5 ++}; ++ ++/** ++ * struct ispccdc_lsc_config - Structure for LSC configuration. ++ * @offset: Table Offset of the gain table. ++ * @gain_mode_n: Vertical dimension of a paxel in LSC configuration. ++ * @gain_mode_m: Horizontal dimension of a paxel in LSC configuration. ++ * @gain_format: Gain table format. ++ * @fmtsph: Start pixel horizontal from start of the HS sync pulse. ++ * @fmtlnh: Number of pixels in horizontal direction to use for the data ++ * reformatter. ++ * @fmtslv: Start line from start of VS sync pulse for the data reformatter. ++ * @fmtlnv: Number of lines in vertical direction for the data reformatter. ++ * @initial_x: X position, in pixels, of the first active pixel in reference ++ * to the first active paxel. Must be an even number. ++ * @initial_y: Y position, in pixels, of the first active pixel in reference ++ * to the first active paxel. Must be an even number. ++ * @size: Size of LSC gain table. Filled when loaded from userspace. ++ */ ++struct ispccdc_lsc_config { ++ __u16 offset; ++ __u8 gain_mode_n; ++ __u8 gain_mode_m; ++ __u8 gain_format; ++ __u16 fmtsph; ++ __u16 fmtlnh; ++ __u16 fmtslv; ++ __u16 fmtlnv; ++ __u8 initial_x; ++ __u8 initial_y; ++ __u32 size; ++}; ++ ++/** ++ * struct ispccdc_bclamp - Structure for Optical & Digital black clamp subtract ++ * @obgain: Optical black average gain. ++ * @obstpixel: Start Pixel w.r.t. HS pulse in Optical black sample. ++ * @oblines: Optical Black Sample lines. ++ * @oblen: Optical Black Sample Length. ++ * @dcsubval: Digital Black Clamp subtract value. ++ */ ++struct ispccdc_bclamp { ++ __u8 obgain; ++ __u8 obstpixel; ++ __u8 oblines; ++ __u8 oblen; ++ __u16 dcsubval; ++}; ++ ++/** ++ * ispccdc_fpc - Structure for FPC ++ * @fpnum: Number of faulty pixels to be corrected in the frame. ++ * @fpcaddr: Memory address of the FPC Table ++ */ ++struct ispccdc_fpc { ++ __u16 fpnum; ++ __u32 fpcaddr; ++}; ++ ++/** ++ * ispccdc_blcomp - Structure for Black Level Compensation parameters. ++ * @b_mg: B/Mg pixels. 2's complement. -128 to +127. ++ * @gb_g: Gb/G pixels. 2's complement. -128 to +127. ++ * @gr_cy: Gr/Cy pixels. 2's complement. -128 to +127. ++ * @r_ye: R/Ye pixels. 2's complement. -128 to +127. ++ */ ++struct ispccdc_blcomp { ++ __u8 b_mg; ++ __u8 gb_g; ++ __u8 gr_cy; ++ __u8 r_ye; ++}; ++ ++/** ++ * struct ispccdc_vp - Structure for Video Port parameters ++ * @bitshift_sel: Video port input select. 3 - bits 12-3, 4 - bits 11-2, ++ * 5 - bits 10-1, 6 - bits 9-0. ++ * @freq_sel: Video port data ready frequency. 1 - 1/3.5, 2 - 1/4.5, ++ * 3 - 1/5.5, 4 - 1/6.5. ++ */ ++struct ispccdc_vp { ++ enum vpin bitshift_sel; ++ enum vpif_freq freq_sel; ++}; ++ ++/** ++ * ispccdc_culling - Structure for Culling parameters. ++ * @v_pattern: Vertical culling pattern. ++ * @h_odd: Horizontal Culling pattern for odd lines. ++ * @h_even: Horizontal Culling pattern for even lines. ++ */ ++struct ispccdc_culling { ++ __u8 v_pattern; ++ __u16 h_odd; ++ __u16 h_even; ++}; ++ ++/** ++ * ispccdc_update_config - Structure for CCDC configuration. ++ * @update: Specifies which CCDC registers should be updated. ++ * @flag: Specifies which CCDC functions should be enabled. ++ * @alawip: Enable/Disable A-Law compression. ++ * @bclamp: Black clamp control register. ++ * @blcomp: Black level compensation value for RGrGbB Pixels. 2's complement. ++ * @fpc: Number of faulty pixels corrected in the frame, address of FPC table. ++ * @cull: Cull control register. ++ * @colptn: Color pattern of the sensor. ++ * @lsc: Pointer to LSC gain table. ++ */ ++struct ispccdc_update_config { ++ __u16 update; ++ __u16 flag; ++ enum alaw_ipwidth alawip; ++ struct ispccdc_bclamp *bclamp; ++ struct ispccdc_blcomp *blcomp; ++ struct ispccdc_fpc *fpc; ++ struct ispccdc_lsc_config *lsc_cfg; ++ struct ispccdc_culling *cull; ++ __u32 colptn; ++ __u8 *lsc; ++}; ++ ++/* Preview configuration */ ++ ++/*Abstraction layer preview configurations*/ ++#define ISP_ABS_PREV_LUMAENH (1 << 0) ++#define ISP_ABS_PREV_INVALAW (1 << 1) ++#define ISP_ABS_PREV_HRZ_MED (1 << 2) ++#define ISP_ABS_PREV_CFA (1 << 3) ++#define ISP_ABS_PREV_CHROMA_SUPP (1 << 4) ++#define ISP_ABS_PREV_WB (1 << 5) ++#define ISP_ABS_PREV_BLKADJ (1 << 6) ++#define ISP_ABS_PREV_RGB2RGB (1 << 7) ++#define ISP_ABS_PREV_COLOR_CONV (1 << 8) ++#define ISP_ABS_PREV_YC_LIMIT (1 << 9) ++#define ISP_ABS_PREV_DEFECT_COR (1 << 10) ++#define ISP_ABS_PREV_GAMMABYPASS (1 << 11) ++#define ISP_ABS_TBL_NF (1 << 12) ++#define ISP_ABS_TBL_REDGAMMA (1 << 13) ++#define ISP_ABS_TBL_GREENGAMMA (1 << 14) ++#define ISP_ABS_TBL_BLUEGAMMA (1 << 15) ++ ++#define ISPPRV_NF_TBL_SIZE 64 ++#define ISPPRV_CFA_TBL_SIZE 576 ++#define ISPPRV_GAMMA_TBL_SIZE 1024 ++#define ISPPRV_YENH_TBL_SIZE 128 ++ ++/** ++ * struct ispprev_hmed - Structure for Horizontal Median Filter. ++ * @odddist: Distance between consecutive pixels of same color in the odd line. ++ * @evendist: Distance between consecutive pixels of same color in the even ++ * line. ++ * @thres: Horizontal median filter threshold. ++ */ ++struct ispprev_hmed { ++ __u8 odddist; ++ __u8 evendist; ++ __u8 thres; ++}; ++ ++/* ++ * Enumeration for CFA Formats supported by preview ++ */ ++enum cfa_fmt { ++ CFAFMT_BAYER, CFAFMT_SONYVGA, CFAFMT_RGBFOVEON, ++ CFAFMT_DNSPL, CFAFMT_HONEYCOMB, CFAFMT_RRGGBBFOVEON ++}; ++ ++/** ++ * struct ispprev_cfa - Structure for CFA Inpterpolation. ++ * @cfafmt: CFA Format Enum value supported by preview. ++ * @cfa_gradthrs_vert: CFA Gradient Threshold - Vertical. ++ * @cfa_gradthrs_horz: CFA Gradient Threshold - Horizontal. ++ * @cfa_table: Pointer to the CFA table. ++ */ ++struct ispprev_cfa { ++ enum cfa_fmt cfafmt; ++ __u8 cfa_gradthrs_vert; ++ __u8 cfa_gradthrs_horz; ++ __u32 *cfa_table; ++}; ++ ++/** ++ * struct ispprev_csup - Structure for Chrominance Suppression. ++ * @gain: Gain. ++ * @thres: Threshold. ++ * @hypf_en: Flag to enable/disable the High Pass Filter. ++ */ ++struct ispprev_csup { ++ __u8 gain; ++ __u8 thres; ++ __u8 hypf_en; ++}; ++ ++/** ++ * struct ispprev_wbal - Structure for White Balance. ++ * @dgain: Digital gain (U10Q8). ++ * @coef3: White balance gain - COEF 3 (U8Q5). ++ * @coef2: White balance gain - COEF 2 (U8Q5). ++ * @coef1: White balance gain - COEF 1 (U8Q5). ++ * @coef0: White balance gain - COEF 0 (U8Q5). ++ */ ++struct ispprev_wbal { ++ __u16 dgain; ++ __u8 coef3; ++ __u8 coef2; ++ __u8 coef1; ++ __u8 coef0; ++}; ++ ++/** ++ * struct ispprev_blkadj - Structure for Black Adjustment. ++ * @red: Black level offset adjustment for Red in 2's complement format ++ * @green: Black level offset adjustment for Green in 2's complement format ++ * @blue: Black level offset adjustment for Blue in 2's complement format ++ */ ++struct ispprev_blkadj { ++ /*Black level offset adjustment for Red in 2's complement format */ ++ __u8 red; ++ /*Black level offset adjustment for Green in 2's complement format */ ++ __u8 green; ++ /* Black level offset adjustment for Blue in 2's complement format */ ++ __u8 blue; ++}; ++ ++/** ++ * struct ispprev_rgbtorgb - Structure for RGB to RGB Blending. ++ * @matrix: Blending values(S12Q8 format) ++ * [RR] [GR] [BR] ++ * [RG] [GG] [BG] ++ * [RB] [GB] [BB] ++ * @offset: Blending offset value for R,G,B in 2's complement integer format. ++ */ ++struct ispprev_rgbtorgb { ++ __u16 matrix[3][3]; ++ __u16 offset[3]; ++}; ++ ++/** ++ * struct ispprev_csc - Structure for Color Space Conversion from RGB-YCbYCr ++ * @matrix: Color space conversion coefficients(S10Q8) ++ * [CSCRY] [CSCGY] [CSCBY] ++ * [CSCRCB] [CSCGCB] [CSCBCB] ++ * [CSCRCR] [CSCGCR] [CSCBCR] ++ * @offset: CSC offset values for Y offset, CB offset and CR offset respectively ++ */ ++struct ispprev_csc { ++ __u16 matrix[RGB_MAX][RGB_MAX]; ++ __s16 offset[RGB_MAX]; ++}; ++ ++/** ++ * struct ispprev_yclimit - Structure for Y, C Value Limit. ++ * @minC: Minimum C value ++ * @maxC: Maximum C value ++ * @minY: Minimum Y value ++ * @maxY: Maximum Y value ++ */ ++struct ispprev_yclimit { ++ __u8 minC; ++ __u8 maxC; ++ __u8 minY; ++ __u8 maxY; ++}; ++ ++/** ++ * struct ispprev_dcor - Structure for Defect correction. ++ * @couplet_mode_en: Flag to enable or disable the couplet dc Correction in NF ++ * @detect_correct: Thresholds for correction bit 0:10 detect 16:25 correct ++ */ ++struct ispprev_dcor { ++ __u8 couplet_mode_en; ++ __u32 detect_correct[4]; ++}; ++ ++/** ++ * struct ispprev_nf - Structure for Noise Filter ++ * @spread: Spread value to be used in Noise Filter ++ * @table: Pointer to the Noise Filter table ++ */ ++struct ispprev_nf { ++ __u8 spread; ++ __u32 table[ISPPRV_NF_TBL_SIZE]; ++}; ++ ++/** ++ * struct ispprv_update_config - Structure for Preview Configuration (user). ++ * @update: Specifies which ISP Preview registers should be updated. ++ * @flag: Specifies which ISP Preview functions should be enabled. ++ * @yen: Pointer to luma enhancement table. ++ * @shading_shift: 3bit value of shift used in shading compensation. ++ * @prev_hmed: Pointer to structure containing the odd and even distance. ++ * between the pixels in the image along with the filter threshold. ++ * @prev_cfa: Pointer to structure containing the CFA interpolation table, CFA. ++ * format in the image, vertical and horizontal gradient threshold. ++ * @csup: Pointer to Structure for Chrominance Suppression coefficients. ++ * @prev_wbal: Pointer to structure for White Balance. ++ * @prev_blkadj: Pointer to structure for Black Adjustment. ++ * @rgb2rgb: Pointer to structure for RGB to RGB Blending. ++ * @prev_csc: Pointer to structure for Color Space Conversion from RGB-YCbYCr. ++ * @yclimit: Pointer to structure for Y, C Value Limit. ++ * @prev_dcor: Pointer to structure for defect correction. ++ * @prev_nf: Pointer to structure for Noise Filter ++ * @red_gamma: Pointer to red gamma correction table. ++ * @green_gamma: Pointer to green gamma correction table. ++ * @blue_gamma: Pointer to blue gamma correction table. ++ */ ++struct ispprv_update_config { ++ __u16 update; ++ __u16 flag; ++ void *yen; ++ __u32 shading_shift; ++ struct ispprev_hmed *prev_hmed; ++ struct ispprev_cfa *prev_cfa; ++ struct ispprev_csup *csup; ++ struct ispprev_wbal *prev_wbal; ++ struct ispprev_blkadj *prev_blkadj; ++ struct ispprev_rgbtorgb *rgb2rgb; ++ struct ispprev_csc *prev_csc; ++ struct ispprev_yclimit *yclimit; ++ struct ispprev_dcor *prev_dcor; ++ struct ispprev_nf *prev_nf; ++ __u32 *red_gamma; ++ __u32 *green_gamma; ++ __u32 *blue_gamma; ++}; ++ ++#endif /* OMAP_ISP_USER_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0004-omap3isp-Add-ISP-frontend-CCDC.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0004-omap3isp-Add-ISP-frontend-CCDC.patch new file mode 100644 index 0000000000..4a161729fd --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0004-omap3isp-Add-ISP-frontend-CCDC.patch @@ -0,0 +1,1875 @@ +From 9ea796fe5383a6961125a6a18185a901fe8627d7 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add ISP frontend (CCDC) + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/isp/ispccdc.c | 1638 +++++++++++++++++++++++++++++++++++++ + drivers/media/video/isp/ispccdc.h | 209 +++++ + 2 files changed, 1847 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/ispccdc.c + create mode 100644 drivers/media/video/isp/ispccdc.h + +diff --git a/drivers/media/video/isp/ispccdc.c b/drivers/media/video/isp/ispccdc.c +new file mode 100644 +index 0000000..2574ea2 +--- /dev/null ++++ b/drivers/media/video/isp/ispccdc.c +@@ -0,0 +1,1638 @@ ++/* ++ * ispccdc.c ++ * ++ * Driver Library for CCDC module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Senthilvadivu Guruswamy <svadivu@ti.com> ++ * Pallavi Kulkarni <p-kulkarni@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <linux/mutex.h> ++#include <linux/module.h> ++#include <linux/uaccess.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispccdc.h" ++#include "ispmmu.h" ++ ++#define LSC_TABLE_INIT_SIZE 50052 ++ ++static u32 *fpc_table_add; ++static unsigned long fpc_table_add_m; ++ ++/** ++ * struct isp_ccdc - Structure for the CCDC module to store its own information ++ * @ccdc_inuse: Flag to determine if CCDC has been reserved or not (0 or 1). ++ * @ccdcout_w: CCDC output width. ++ * @ccdcout_h: CCDC output height. ++ * @ccdcin_w: CCDC input width. ++ * @ccdcin_h: CCDC input height. ++ * @ccdcin_woffset: CCDC input horizontal offset. ++ * @ccdcin_hoffset: CCDC input vertical offset. ++ * @crop_w: Crop width. ++ * @crop_h: Crop weight. ++ * @ccdc_inpfmt: CCDC input format. ++ * @ccdc_outfmt: CCDC output format. ++ * @vpout_en: Video port output enable. ++ * @wen: Data write enable. ++ * @exwen: External data write enable. ++ * @refmt_en: Reformatter enable. ++ * @ccdcslave: CCDC slave mode enable. ++ * @syncif_ipmod: Image ++ * @obclamp_en: Data input format. ++ * @mutexlock: Mutex used to get access to the CCDC. ++ */ ++static struct isp_ccdc { ++ u8 ccdc_inuse; ++ u32 ccdcout_w; ++ u32 ccdcout_h; ++ u32 ccdcin_w; ++ u32 ccdcin_h; ++ u32 ccdcin_woffset; ++ u32 ccdcin_hoffset; ++ u32 crop_w; ++ u32 crop_h; ++ u8 ccdc_inpfmt; ++ u8 ccdc_outfmt; ++ u8 vpout_en; ++ u8 wen; ++ u8 exwen; ++ u8 refmt_en; ++ u8 ccdcslave; ++ u8 syncif_ipmod; ++ u8 obclamp_en; ++ u8 pm_state; ++ u8 lsc_enable; ++ int lsc_state; ++ struct mutex mutexlock; /* For checking/modifying ccdc_inuse */ ++ u32 wenlog; ++} ispccdc_obj; ++ ++static struct ispccdc_lsc_config lsc_config; ++static u8 *lsc_gain_table; ++static unsigned long lsc_ispmmu_addr; ++static int lsc_initialized; ++static u8 *lsc_gain_table_tmp; ++ ++/* Structure for saving/restoring CCDC module registers*/ ++static struct isp_reg ispccdc_reg_list[] = { ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HD_VD_WID, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PIX_LINES, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CULLING, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR0, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR1, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR2, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR3, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR4, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR5, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR6, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR7, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN0, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN1, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD0, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD1, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_INITIAL, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE, 0}, ++ {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_OFFSET, 0}, ++ {0, ISP_TOK_TERM, 0} ++}; ++ ++/** ++ * omap34xx_isp_ccdc_config - Sets CCDC configuration from userspace ++ * @userspace_add: Structure containing CCDC configuration sent from userspace. ++ * ++ * Returns 0 if successful, -EINVAL if the pointer to the configuration ++ * structure is null, or the copy_from_user function fails to copy user space ++ * memory to kernel space memory. ++ **/ ++int omap34xx_isp_ccdc_config(void *userspace_add) ++{ ++ struct ispccdc_bclamp bclamp_t; ++ struct ispccdc_blcomp blcomp_t; ++ struct ispccdc_fpc fpc_t; ++ struct ispccdc_culling cull_t; ++ struct ispccdc_update_config *ccdc_struct; ++ ++ if (userspace_add == NULL) ++ return -EINVAL; ++ ++ ccdc_struct = userspace_add; ++ ++ if (ISP_ABS_CCDC_ALAW & ccdc_struct->flag) { ++ if (ISP_ABS_CCDC_ALAW & ccdc_struct->update) ++ ispccdc_config_alaw(ccdc_struct->alawip); ++ ispccdc_enable_alaw(1); ++ } else if (ISP_ABS_CCDC_ALAW & ccdc_struct->update) ++ ispccdc_enable_alaw(0); ++ ++ if (ISP_ABS_CCDC_LPF & ccdc_struct->flag) ++ ispccdc_enable_lpf(1); ++ else ++ ispccdc_enable_lpf(0); ++ ++ if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->flag) { ++ if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->update) { ++ if (copy_from_user(&bclamp_t, (struct ispccdc_bclamp *) ++ ccdc_struct->bclamp, ++ sizeof(struct ispccdc_bclamp))) ++ goto copy_from_user_err; ++ ++ ispccdc_enable_black_clamp(1); ++ ispccdc_config_black_clamp(bclamp_t); ++ } else ++ ispccdc_enable_black_clamp(1); ++ } else { ++ if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->update) { ++ if (copy_from_user(&bclamp_t, (struct ispccdc_bclamp *) ++ ccdc_struct->bclamp, ++ sizeof(struct ispccdc_bclamp))) ++ goto copy_from_user_err; ++ ++ ispccdc_enable_black_clamp(0); ++ ispccdc_config_black_clamp(bclamp_t); ++ } ++ } ++ ++ if (ISP_ABS_CCDC_BCOMP & ccdc_struct->update) { ++ if (copy_from_user(&blcomp_t, (struct ispccdc_blcomp *) ++ ccdc_struct->blcomp, ++ sizeof(blcomp_t))) ++ goto copy_from_user_err; ++ ++ ispccdc_config_black_comp(blcomp_t); ++ } ++ ++ if (ISP_ABS_CCDC_FPC & ccdc_struct->flag) { ++ if (ISP_ABS_CCDC_FPC & ccdc_struct->update) { ++ if (copy_from_user(&fpc_t, (struct ispccdc_fpc *) ++ ccdc_struct->fpc, ++ sizeof(fpc_t))) ++ goto copy_from_user_err; ++ fpc_table_add = kmalloc(64 + fpc_t.fpnum * 4, ++ GFP_KERNEL | GFP_DMA); ++ if (!fpc_table_add) { ++ printk(KERN_ERR "Cannot allocate memory for" ++ " FPC table"); ++ return -ENOMEM; ++ } ++ while (((unsigned long)fpc_table_add & 0xFFFFFFC0) ++ != (unsigned long)fpc_table_add) ++ fpc_table_add++; ++ ++ fpc_table_add_m = ispmmu_kmap(virt_to_phys ++ (fpc_table_add), ++ fpc_t.fpnum * 4); ++ ++ if (copy_from_user(fpc_table_add, (u32 *)fpc_t.fpcaddr, ++ fpc_t.fpnum * 4)) ++ goto copy_from_user_err; ++ ++ fpc_t.fpcaddr = fpc_table_add_m; ++ ispccdc_config_fpc(fpc_t); ++ } ++ ispccdc_enable_fpc(1); ++ } else if (ISP_ABS_CCDC_FPC & ccdc_struct->update) ++ ispccdc_enable_fpc(0); ++ ++ if (ISP_ABS_CCDC_CULL & ccdc_struct->update) { ++ if (copy_from_user(&cull_t, (struct ispccdc_culling *) ++ ccdc_struct->cull, ++ sizeof(cull_t))) ++ goto copy_from_user_err; ++ ispccdc_config_culling(cull_t); ++ } ++ ++ if (is_isplsc_activated()) { ++ if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->flag) { ++ if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->update) { ++ if (copy_from_user( ++ &lsc_config, ++ (struct ispccdc_lsc_config *) ++ ccdc_struct->lsc_cfg, ++ sizeof(struct ispccdc_lsc_config))) ++ goto copy_from_user_err; ++ ispccdc_config_lsc(&lsc_config); ++ } ++ ispccdc_enable_lsc(1); ++ } else if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->update) { ++ ispccdc_enable_lsc(0); ++ } ++ if (ISP_ABS_TBL_LSC & ccdc_struct->update) { ++ if (copy_from_user(lsc_gain_table, ++ ccdc_struct->lsc, lsc_config.size)) ++ goto copy_from_user_err; ++ ispccdc_load_lsc(lsc_gain_table, lsc_config.size); ++ } ++ } ++ ++ if (ISP_ABS_CCDC_COLPTN & ccdc_struct->update) ++ ispccdc_config_imgattr(ccdc_struct->colptn); ++ ++ return 0; ++ ++copy_from_user_err: ++ printk(KERN_ERR "CCDC Config:Copy From User Error"); ++ return -EINVAL ; ++} ++EXPORT_SYMBOL(omap34xx_isp_ccdc_config); ++ ++/** ++ * Set the value to be used for CCDC_CFG.WENLOG. ++ * w - Value of wenlog. ++ */ ++void ispccdc_set_wenlog(u32 wenlog) ++{ ++ ispccdc_obj.wenlog = wenlog; ++} ++EXPORT_SYMBOL(ispccdc_set_wenlog); ++ ++/** ++ * ispccdc_request - Reserves the CCDC module. ++ * ++ * Reserves the CCDC module and assures that is used only once at a time. ++ * ++ * Returns 0 if successful, or -EBUSY if CCDC module is busy. ++ **/ ++int ispccdc_request(void) ++{ ++ mutex_lock(&ispccdc_obj.mutexlock); ++ if (ispccdc_obj.ccdc_inuse) { ++ mutex_unlock(&ispccdc_obj.mutexlock); ++ DPRINTK_ISPCCDC("ISP_ERR : CCDC Module Busy\n"); ++ return -EBUSY; ++ } ++ ++ ispccdc_obj.ccdc_inuse = 1; ++ mutex_unlock(&ispccdc_obj.mutexlock); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ISPCTRL_CCDC_RAM_EN | ++ ISPCTRL_CCDC_CLK_EN | ++ ISPCTRL_SBL_WR1_RAM_EN); ++ isp_reg_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC); ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_request); ++ ++/** ++ * ispccdc_free - Frees the CCDC module. ++ * ++ * Frees the CCDC module so it can be used by another process. ++ * ++ * Returns 0 if successful, or -EINVAL if module has been already freed. ++ **/ ++int ispccdc_free(void) ++{ ++ mutex_lock(&ispccdc_obj.mutexlock); ++ if (!ispccdc_obj.ccdc_inuse) { ++ mutex_unlock(&ispccdc_obj.mutexlock); ++ DPRINTK_ISPCCDC("ISP_ERR: CCDC Module already freed\n"); ++ return -EINVAL; ++ } ++ ++ ispccdc_obj.ccdc_inuse = 0; ++ mutex_unlock(&ispccdc_obj.mutexlock); ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ~(ISPCTRL_CCDC_CLK_EN | ++ ISPCTRL_CCDC_RAM_EN | ++ ISPCTRL_SBL_WR1_RAM_EN)); ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_free); ++ ++/** ++ * ispccdc_free_lsc - Frees Lens Shading Compensation table ++ * ++ * Always returns 0. ++ **/ ++static int ispccdc_free_lsc(void) ++{ ++ if (!lsc_ispmmu_addr) ++ return 0; ++ ++ ispccdc_enable_lsc(0); ++ lsc_initialized = 0; ++ isp_reg_writel(0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); ++ ispmmu_kunmap(lsc_ispmmu_addr); ++ kfree(lsc_gain_table); ++ return 0; ++} ++ ++/** ++ * ispccdc_allocate_lsc - Allocate space for Lens Shading Compensation table ++ * @table_size: LSC gain table size. ++ * ++ * Returns 0 if successful, -ENOMEM of its no memory available, or -EINVAL if ++ * table_size is zero. ++ **/ ++static int ispccdc_allocate_lsc(u32 table_size) ++{ ++ if (table_size == 0) ++ return -EINVAL; ++ ++ if ((lsc_config.size >= table_size) && lsc_gain_table) ++ return 0; ++ ++ ispccdc_free_lsc(); ++ ++ lsc_gain_table = kmalloc(table_size, GFP_KERNEL | GFP_DMA); ++ ++ if (!lsc_gain_table) { ++ printk(KERN_ERR "Cannot allocate memory for gain tables \n"); ++ return -ENOMEM; ++ } ++ ++ lsc_ispmmu_addr = ispmmu_kmap(virt_to_phys(lsc_gain_table), table_size); ++ if (lsc_ispmmu_addr <= 0) { ++ printk(KERN_ERR "Cannot map memory for gain tables \n"); ++ kfree(lsc_gain_table); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++/** ++ * ispccdc_program_lsc - Program Lens Shading Compensation table. ++ * @table_size: LSC gain table size. ++ * ++ * Returns 0 if successful, or -EINVAL if there's no mapped address for the ++ * table yet. ++ **/ ++static int ispccdc_program_lsc(void) ++{ ++ if (!lsc_ispmmu_addr) ++ return -EINVAL; ++ ++ if (lsc_initialized) ++ return 0; ++ ++ isp_reg_writel(lsc_ispmmu_addr, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_TABLE_BASE); ++ lsc_initialized = 1; ++ return 0; ++} ++ ++/** ++ * ispccdc_load_lsc - Load Lens Shading Compensation table. ++ * @table_addr: LSC gain table MMU Mapped address. ++ * @table_size: LSC gain table size. ++ * ++ * Returns 0 if successful, -ENOMEM of its no memory available, or -EINVAL if ++ * table_size is zero. ++ **/ ++int ispccdc_load_lsc(u8 *table_addr, u32 table_size) ++{ ++ int ret; ++ ++ if (!is_isplsc_activated()) ++ return 0; ++ ++ if (!table_addr) ++ return -EINVAL; ++ ++ ret = ispccdc_allocate_lsc(table_size); ++ if (ret) ++ return ret; ++ ++ if (table_addr != lsc_gain_table) ++ memcpy(lsc_gain_table, table_addr, table_size); ++ ret = ispccdc_program_lsc(); ++ if (ret) ++ return ret; ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_load_lsc); ++ ++/** ++ * ispccdc_config_lsc - Configures the lens shading compensation module ++ * @lsc_cfg: LSC configuration structure ++ **/ ++void ispccdc_config_lsc(struct ispccdc_lsc_config *lsc_cfg) ++{ ++ int reg; ++ ++ if (!is_isplsc_activated()) ++ return; ++ ++ ispccdc_enable_lsc(0); ++ isp_reg_writel(lsc_cfg->offset, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_TABLE_OFFSET); ++ ++ reg = 0; ++ reg |= lsc_cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; ++ reg |= lsc_cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; ++ reg |= lsc_cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG); ++ ++ reg = 0; ++ reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; ++ reg |= lsc_cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; ++ reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; ++ reg |= lsc_cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_INITIAL); ++} ++EXPORT_SYMBOL(ispccdc_config_lsc); ++ ++int __ispccdc_enable_lsc(u8 enable) ++{ ++ if (!is_isplsc_activated()) ++ return -ENODEV; ++ ++ if (enable) { ++ if (!ispccdc_busy()) { ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_SBL_SHARED_RPORTB ++ | ISPCTRL_SBL_RD_RAM_EN); ++ ++ isp_reg_or(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_CONFIG, 0x1); ++ ++ ispccdc_obj.lsc_state = 1; ++ } else { ++ /* Postpone enabling LSC */ ++ ispccdc_obj.lsc_enable = 1; ++ return -EBUSY; ++ } ++ } else { ++ isp_reg_and(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, 0xFFFE); ++ ispccdc_obj.lsc_state = ispccdc_obj.lsc_enable = 0; ++ } ++ ++ return 0; ++} ++ ++/** ++ * ispccdc_enable_lsc - Enables/Disables the Lens Shading Compensation module. ++ * @enable: 0 Disables LSC, 1 Enables LSC. ++ **/ ++void ispccdc_enable_lsc(u8 enable) ++{ ++ if (__ispccdc_enable_lsc(enable)) { ++ if (enable) ++ ispccdc_obj.lsc_state = 1; ++ else ++ ispccdc_obj.lsc_state = ispccdc_obj.lsc_enable = 0; ++ } ++} ++EXPORT_SYMBOL(ispccdc_enable_lsc); ++ ++void ispccdc_lsc_error_handler(void) ++{ ++ int lsc_enable = ispccdc_obj.lsc_state; ++ ++ ispccdc_enable_lsc(0); ++ ++ ispccdc_obj.lsc_enable = lsc_enable; ++} ++ ++/** ++ * ispccdc_config_crop - Configures crop parameters for the ISP CCDC. ++ * @left: Left offset of the crop area. ++ * @top: Top offset of the crop area. ++ * @height: Height of the crop area. ++ * @width: Width of the crop area. ++ * ++ * The following restrictions are applied for the crop settings. If incoming ++ * values do not follow these restrictions then we map the settings to the ++ * closest acceptable crop value. ++ * 1) Left offset is always odd. This can be avoided if we enable byte swap ++ * option for incoming data into CCDC. ++ * 2) Top offset is always even. ++ * 3) Crop height is always even. ++ * 4) Crop width is always a multiple of 16 pixels ++ **/ ++void ispccdc_config_crop(u32 left, u32 top, u32 height, u32 width) ++{ ++ ispccdc_obj.ccdcin_woffset = left + (left % 2); ++ ispccdc_obj.ccdcin_hoffset = top + (top % 2); ++ ++ ispccdc_obj.crop_w = width - (width % 16); ++ ispccdc_obj.crop_h = height + (height % 2); ++ ++ DPRINTK_ISPCCDC("\n\tOffsets L %d T %d W %d H %d\n", ++ ispccdc_obj.ccdcin_woffset, ++ ispccdc_obj.ccdcin_hoffset, ++ ispccdc_obj.crop_w, ++ ispccdc_obj.crop_h); ++} ++ ++/** ++ * ispccdc_config_datapath - Specifies the input and output modules for CCDC. ++ * @input: Indicates the module that inputs the image to the CCDC. ++ * @output: Indicates the module to which the CCDC outputs the image. ++ * ++ * Configures the default configuration for the CCDC to work with. ++ * ++ * The valid values for the input are CCDC_RAW (0), CCDC_YUV_SYNC (1), ++ * CCDC_YUV_BT (2), and CCDC_OTHERS (3). ++ * ++ * The valid values for the output are CCDC_YUV_RSZ (0), CCDC_YUV_MEM_RSZ (1), ++ * CCDC_OTHERS_VP (2), CCDC_OTHERS_MEM (3), CCDC_OTHERS_VP_MEM (4). ++ * ++ * Returns 0 if successful, or -EINVAL if wrong I/O combination or wrong input ++ * or output values. ++ **/ ++int ispccdc_config_datapath(enum ccdc_input input, enum ccdc_output output) ++{ ++ u32 syn_mode = 0; ++ struct ispccdc_vp vpcfg; ++ struct ispccdc_syncif syncif; ++ struct ispccdc_bclamp blkcfg; ++ ++ u32 colptn = ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; ++ ++ /* CCDC does not convert the image format */ ++ if ((input == CCDC_RAW || input == CCDC_OTHERS) && ++ output == CCDC_YUV_RSZ) { ++ DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC I/O Combination\n"); ++ return -EINVAL; ++ } ++ ++ syn_mode = isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ switch (output) { ++ case CCDC_YUV_RSZ: ++ syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; ++ syn_mode &= ~ISPCCDC_SYN_MODE_WEN; ++ break; ++ ++ case CCDC_YUV_MEM_RSZ: ++ syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; ++ ispccdc_obj.wen = 1; ++ syn_mode |= ISPCCDC_SYN_MODE_WEN; ++ break; ++ ++ case CCDC_OTHERS_VP: ++ syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; ++ syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; ++ syn_mode &= ~ISPCCDC_SYN_MODE_WEN; ++ vpcfg.bitshift_sel = BIT9_0; ++ vpcfg.freq_sel = PIXCLKBY2; ++ ispccdc_config_vp(vpcfg); ++ ispccdc_enable_vp(1); ++ break; ++ ++ case CCDC_OTHERS_MEM: ++ syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; ++ syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; ++ syn_mode |= ISPCCDC_SYN_MODE_WEN; ++ syn_mode &= ~ISPCCDC_SYN_MODE_EXWEN; ++ isp_reg_and(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ++ ~ISPCCDC_CFG_WENLOG); ++ vpcfg.bitshift_sel = BIT11_2; ++ vpcfg.freq_sel = PIXCLKBY2; ++ ispccdc_config_vp(vpcfg); ++ ispccdc_enable_vp(0); ++ break; ++ ++ case CCDC_OTHERS_VP_MEM: ++ syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; ++ syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; ++ syn_mode |= ISPCCDC_SYN_MODE_WEN; ++ syn_mode &= ~ISPCCDC_SYN_MODE_EXWEN; ++ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ++ ~ISPCCDC_CFG_WENLOG, ++ ispccdc_obj.wenlog); ++ vpcfg.bitshift_sel = BIT9_0; ++ vpcfg.freq_sel = PIXCLKBY2; ++ ispccdc_config_vp(vpcfg); ++ ispccdc_enable_vp(1); ++ break; ++ default: ++ DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC Output\n"); ++ return -EINVAL; ++ }; ++ ++ isp_reg_writel(syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ switch (input) { ++ case CCDC_RAW: ++ syncif.ccdc_mastermode = 0; ++ syncif.datapol = 0; ++ syncif.datsz = DAT10; ++ syncif.fldmode = 0; ++ syncif.fldout = 0; ++ syncif.fldpol = 0; ++ syncif.fldstat = 0; ++ syncif.hdpol = 0; ++ syncif.ipmod = RAW; ++ syncif.vdpol = 0; ++ ispccdc_config_sync_if(syncif); ++ ispccdc_config_imgattr(colptn); ++ blkcfg.dcsubval = 64; ++ ispccdc_config_black_clamp(blkcfg); ++ if (is_isplsc_activated()) { ++ ispccdc_config_lsc(&lsc_config); ++ ispccdc_load_lsc(lsc_gain_table_tmp, ++ LSC_TABLE_INIT_SIZE); ++ } ++ ++ break; ++ case CCDC_YUV_SYNC: ++ syncif.ccdc_mastermode = 0; ++ syncif.datapol = 0; ++ syncif.datsz = DAT8; ++ syncif.fldmode = 0; ++ syncif.fldout = 0; ++ syncif.fldpol = 0; ++ syncif.fldstat = 0; ++ syncif.hdpol = 0; ++ syncif.ipmod = YUV16; ++ syncif.vdpol = 1; ++ ispccdc_config_imgattr(0); ++ ispccdc_config_sync_if(syncif); ++ blkcfg.dcsubval = 0; ++ ispccdc_config_black_clamp(blkcfg); ++ break; ++ case CCDC_YUV_BT: ++ break; ++ case CCDC_OTHERS: ++ break; ++ default: ++ DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC Input\n"); ++ return -EINVAL; ++ } ++ ++ ispccdc_obj.ccdc_inpfmt = input; ++ ispccdc_obj.ccdc_outfmt = output; ++ ispccdc_print_status(); ++ isp_print_status(); ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_config_datapath); ++ ++/** ++ * ispccdc_config_sync_if - Sets the sync i/f params between sensor and CCDC. ++ * @syncif: Structure containing the sync parameters like field state, CCDC in ++ * master/slave mode, raw/yuv data, polarity of data, field, hs, vs ++ * signals. ++ **/ ++void ispccdc_config_sync_if(struct ispccdc_syncif syncif) ++{ ++ u32 syn_mode = isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; ++ ++ if (syncif.fldstat) ++ syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; ++ ++ syn_mode &= ISPCCDC_SYN_MODE_INPMOD_MASK; ++ ispccdc_obj.syncif_ipmod = syncif.ipmod; ++ ++ switch (syncif.ipmod) { ++ case RAW: ++ break; ++ case YUV16: ++ syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; ++ break; ++ case YUV8: ++ syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8; ++ break; ++ }; ++ ++ syn_mode &= ISPCCDC_SYN_MODE_DATSIZ_MASK; ++ switch (syncif.datsz) { ++ case DAT8: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; ++ break; ++ case DAT10: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; ++ break; ++ case DAT11: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; ++ break; ++ case DAT12: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; ++ break; ++ }; ++ ++ if (syncif.fldmode) ++ syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; ++ ++ if (syncif.datapol) ++ syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; ++ ++ if (syncif.fldpol) ++ syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; ++ ++ if (syncif.hdpol) ++ syn_mode |= ISPCCDC_SYN_MODE_HDPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; ++ ++ if (syncif.vdpol) ++ syn_mode |= ISPCCDC_SYN_MODE_VDPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; ++ ++ if (syncif.ccdc_mastermode) { ++ syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; ++ isp_reg_writel(syncif.hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT ++ | syncif.vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_HD_VD_WID); ++ ++ isp_reg_writel(syncif.ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT ++ | syncif.hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_PIX_LINES); ++ } else ++ syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | ++ ISPCCDC_SYN_MODE_VDHDOUT); ++ ++ isp_reg_writel(syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ if (!(syncif.bt_r656_en)) { ++ isp_reg_and(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, ++ ~ISPCCDC_REC656IF_R656ON); ++ } ++} ++EXPORT_SYMBOL(ispccdc_config_sync_if); ++ ++/** ++ * ispccdc_config_black_clamp - Configures the clamp parameters in CCDC. ++ * @bclamp: Structure containing the optical black average gain, optical black ++ * sample length, sample lines, and the start pixel position of the ++ * samples w.r.t the HS pulse. ++ * Configures the clamp parameters in CCDC. Either if its being used the ++ * optical black clamp, or the digital clamp. If its a digital clamp, then ++ * assures to put a valid DC substraction level. ++ * ++ * Returns always 0 when completed. ++ **/ ++int ispccdc_config_black_clamp(struct ispccdc_bclamp bclamp) ++{ ++ u32 bclamp_val = 0; ++ ++ if (ispccdc_obj.obclamp_en) { ++ bclamp_val |= bclamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; ++ bclamp_val |= bclamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; ++ bclamp_val |= bclamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; ++ bclamp_val |= bclamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; ++ isp_reg_writel(bclamp_val, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_CLAMP); ++ } else { ++ if (omap_rev() < OMAP3430_REV_ES2_0) ++ if (ispccdc_obj.syncif_ipmod == YUV16 || ++ ispccdc_obj.syncif_ipmod == YUV8 || ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_REC656IF) & ++ ISPCCDC_REC656IF_R656ON) ++ bclamp.dcsubval = 0; ++ isp_reg_writel(bclamp.dcsubval, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_DCSUB); ++ } ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_config_black_clamp); ++ ++/** ++ * ispccdc_enable_black_clamp - Enables/Disables the optical black clamp. ++ * @enable: 0 Disables optical black clamp, 1 Enables optical black clamp. ++ * ++ * Enables or disables the optical black clamp. When disabled, the digital ++ * clamp operates. ++ **/ ++void ispccdc_enable_black_clamp(u8 enable) ++{ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, ++ ~ISPCCDC_CLAMP_CLAMPEN, ++ enable ? ISPCCDC_CLAMP_CLAMPEN : 0); ++ ispccdc_obj.obclamp_en = enable; ++} ++EXPORT_SYMBOL(ispccdc_enable_black_clamp); ++ ++/** ++ * ispccdc_config_fpc - Configures the Faulty Pixel Correction parameters. ++ * @fpc: Structure containing the number of faulty pixels corrected in the ++ * frame, address of the FPC table. ++ * ++ * Returns 0 if successful, or -EINVAL if FPC Address is not on the 64 byte ++ * boundary. ++ **/ ++int ispccdc_config_fpc(struct ispccdc_fpc fpc) ++{ ++ u32 fpc_val = 0; ++ ++ fpc_val = isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); ++ ++ if ((fpc.fpcaddr & 0xFFFFFFC0) == fpc.fpcaddr) { ++ isp_reg_writel(fpc_val & (~ISPCCDC_FPC_FPCEN), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); ++ isp_reg_writel(fpc.fpcaddr, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR); ++ } else { ++ DPRINTK_ISPCCDC("FPC Address should be on 64byte boundary\n"); ++ return -EINVAL; ++ } ++ isp_reg_writel(fpc_val | (fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_config_fpc); ++ ++/** ++ * ispccdc_enable_fpc - Enables the Faulty Pixel Correction. ++ * @enable: 0 Disables FPC, 1 Enables FPC. ++ **/ ++void ispccdc_enable_fpc(u8 enable) ++{ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ++ ~ISPCCDC_FPC_FPCEN, ++ enable ? ISPCCDC_FPC_FPCEN : 0); ++} ++EXPORT_SYMBOL(ispccdc_enable_fpc); ++ ++/** ++ * ispccdc_config_black_comp - Configures Black Level Compensation parameters. ++ * @blcomp: Structure containing the black level compensation value for RGrGbB ++ * pixels. in 2's complement. ++ **/ ++void ispccdc_config_black_comp(struct ispccdc_blcomp blcomp) ++{ ++ u32 blcomp_val = 0; ++ ++ blcomp_val |= blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; ++ blcomp_val |= blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; ++ blcomp_val |= blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; ++ blcomp_val |= blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; ++ ++ isp_reg_writel(blcomp_val, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP); ++} ++EXPORT_SYMBOL(ispccdc_config_black_comp); ++ ++/** ++ * ispccdc_config_vp - Configures the Video Port Configuration parameters. ++ * @vpcfg: Structure containing the Video Port input frequency, and the 10 bit ++ * format. ++ **/ ++void ispccdc_config_vp(struct ispccdc_vp vpcfg) ++{ ++ u32 fmtcfg_vp = isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); ++ ++ fmtcfg_vp &= ISPCCDC_FMTCFG_VPIN_MASK & ISPCCDC_FMTCF_VPIF_FRQ_MASK; ++ ++ switch (vpcfg.bitshift_sel) { ++ case BIT9_0: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; ++ break; ++ case BIT10_1: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; ++ break; ++ case BIT11_2: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; ++ break; ++ case BIT12_3: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; ++ break; ++ }; ++ switch (vpcfg.freq_sel) { ++ case PIXCLKBY2: ++ fmtcfg_vp |= ISPCCDC_FMTCF_VPIF_FRQ_BY2; ++ break; ++ case PIXCLKBY3_5: ++ fmtcfg_vp |= ISPCCDC_FMTCF_VPIF_FRQ_BY3; ++ break; ++ case PIXCLKBY4_5: ++ fmtcfg_vp |= ISPCCDC_FMTCF_VPIF_FRQ_BY4; ++ break; ++ case PIXCLKBY5_5: ++ fmtcfg_vp |= ISPCCDC_FMTCF_VPIF_FRQ_BY5; ++ break; ++ case PIXCLKBY6_5: ++ fmtcfg_vp |= ISPCCDC_FMTCF_VPIF_FRQ_BY6; ++ break; ++ }; ++ isp_reg_writel(fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); ++} ++EXPORT_SYMBOL(ispccdc_config_vp); ++ ++/** ++ * ispccdc_enable_vp - Enables the Video Port. ++ * @enable: 0 Disables VP, 1 Enables VP ++ **/ ++void ispccdc_enable_vp(u8 enable) ++{ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, ++ ~ISPCCDC_FMTCFG_VPEN, ++ enable ? ISPCCDC_FMTCFG_VPEN : 0); ++} ++EXPORT_SYMBOL(ispccdc_enable_vp); ++ ++/** ++ * ispccdc_config_reformatter - Configures the Reformatter. ++ * @refmt: Structure containing the memory address to format and the bit fields ++ * for the reformatter registers. ++ * ++ * Configures the Reformatter register values if line alternating is disabled. ++ * Else, just enabling line alternating is enough. ++ **/ ++void ispccdc_config_reformatter(struct ispccdc_refmt refmt) ++{ ++ u32 fmtcfg_val = 0; ++ ++ fmtcfg_val = isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); ++ ++ if (refmt.lnalt) ++ fmtcfg_val |= ISPCCDC_FMTCFG_LNALT; ++ else { ++ fmtcfg_val &= ~ISPCCDC_FMTCFG_LNALT; ++ fmtcfg_val &= 0xFFFFF003; ++ fmtcfg_val |= refmt.lnum << ISPCCDC_FMTCFG_LNUM_SHIFT; ++ fmtcfg_val |= refmt.plen_even << ++ ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT; ++ fmtcfg_val |= refmt.plen_odd << ISPCCDC_FMTCFG_PLEN_ODD_SHIFT; ++ ++ isp_reg_writel(refmt.prgeven0, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_PRGEVEN0); ++ isp_reg_writel(refmt.prgeven1, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_PRGEVEN1); ++ isp_reg_writel(refmt.prgodd0, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_PRGODD0); ++ isp_reg_writel(refmt.prgodd1, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_PRGODD1); ++ isp_reg_writel(refmt.fmtaddr0, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR0); ++ isp_reg_writel(refmt.fmtaddr1, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR1); ++ isp_reg_writel(refmt.fmtaddr2, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR2); ++ isp_reg_writel(refmt.fmtaddr3, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR3); ++ isp_reg_writel(refmt.fmtaddr4, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR4); ++ isp_reg_writel(refmt.fmtaddr5, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR5); ++ isp_reg_writel(refmt.fmtaddr6, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR6); ++ isp_reg_writel(refmt.fmtaddr7, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_ADDR7); ++ } ++ isp_reg_writel(fmtcfg_val, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); ++} ++EXPORT_SYMBOL(ispccdc_config_reformatter); ++ ++/** ++ * ispccdc_enable_reformatter - Enables the Reformatter. ++ * @enable: 0 Disables Reformatter, 1- Enables Data Reformatter ++ **/ ++void ispccdc_enable_reformatter(u8 enable) ++{ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, ++ ~ISPCCDC_FMTCFG_FMTEN, ++ enable ? ISPCCDC_FMTCFG_FMTEN : 0); ++ ispccdc_obj.refmt_en = enable; ++} ++EXPORT_SYMBOL(ispccdc_enable_reformatter); ++ ++/** ++ * ispccdc_config_culling - Configures the culling parameters. ++ * @cull: Structure containing the vertical culling pattern, and horizontal ++ * culling pattern for odd and even lines. ++ **/ ++void ispccdc_config_culling(struct ispccdc_culling cull) ++{ ++ u32 culling_val = 0; ++ ++ culling_val |= cull.v_pattern << ISPCCDC_CULLING_CULV_SHIFT; ++ culling_val |= cull.h_even << ISPCCDC_CULLING_CULHEVN_SHIFT; ++ culling_val |= cull.h_odd << ISPCCDC_CULLING_CULHODD_SHIFT; ++ ++ isp_reg_writel(culling_val, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CULLING); ++} ++EXPORT_SYMBOL(ispccdc_config_culling); ++ ++/** ++ * ispccdc_enable_lpf - Enables the Low-Pass Filter (LPF). ++ * @enable: 0 Disables LPF, 1 Enables LPF ++ **/ ++void ispccdc_enable_lpf(u8 enable) ++{ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, ++ ~ISPCCDC_SYN_MODE_LPF, ++ enable ? ISPCCDC_SYN_MODE_LPF : 0); ++} ++EXPORT_SYMBOL(ispccdc_enable_lpf); ++ ++/** ++ * ispccdc_config_alaw - Configures the input width for A-law. ++ * @ipwidth: Input width for A-law ++ **/ ++void ispccdc_config_alaw(enum alaw_ipwidth ipwidth) ++{ ++ isp_reg_writel(ipwidth << ISPCCDC_ALAW_GWDI_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); ++} ++EXPORT_SYMBOL(ispccdc_config_alaw); ++ ++/** ++ * ispccdc_enable_alaw - Enables the A-law compression. ++ * @enable: 0 - Disables A-law, 1 - Enables A-law ++ **/ ++void ispccdc_enable_alaw(u8 enable) ++{ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW, ++ ~ISPCCDC_ALAW_CCDTBL, ++ enable ? ISPCCDC_ALAW_CCDTBL : 0); ++} ++EXPORT_SYMBOL(ispccdc_enable_alaw); ++ ++/** ++ * ispccdc_config_imgattr - Configures the sensor image specific attributes. ++ * @colptn: Color pattern of the sensor. ++ **/ ++void ispccdc_config_imgattr(u32 colptn) ++{ ++ isp_reg_writel(colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN); ++} ++EXPORT_SYMBOL(ispccdc_config_imgattr); ++ ++void ispccdc_config_shadow_registers(void) ++{ ++ if (ispccdc_obj.lsc_enable) { ++ ispccdc_enable_lsc(1); ++ ispccdc_obj.lsc_enable = 0; ++ } ++} ++ ++/** ++ * ispccdc_try_size - Checks if requested Input/output dimensions are valid ++ * @input_w: input width for the CCDC in number of pixels per line ++ * @input_h: input height for the CCDC in number of lines ++ * @output_w: output width from the CCDC in number of pixels per line ++ * @output_h: output height for the CCDC in number of lines ++ * ++ * Calculates the number of pixels cropped if the reformater is disabled, ++ * Fills up the output width and height variables in the isp_ccdc structure. ++ * ++ * Returns 0 if successful, or -EINVAL if the input width is less than 2 pixels ++ **/ ++int ispccdc_try_size(u32 input_w, u32 input_h, u32 *output_w, u32 *output_h) ++{ ++ if (input_w < 32 || input_h < 32) { ++ DPRINTK_ISPCCDC("ISP_ERR: CCDC cannot handle input width less" ++ " than 32 pixels or height less than 32\n"); ++ return -EINVAL; ++ } ++ ++ if (ispccdc_obj.crop_w) ++ *output_w = ispccdc_obj.crop_w; ++ else ++ *output_w = input_w; ++ ++ if (ispccdc_obj.crop_h) ++ *output_h = ispccdc_obj.crop_h; ++ else ++ *output_h = input_h; ++ ++ if (!ispccdc_obj.refmt_en ++ && ispccdc_obj.ccdc_outfmt != CCDC_OTHERS_MEM ++ && ispccdc_obj.ccdc_outfmt != CCDC_OTHERS_VP_MEM) ++ *output_h -= 1; ++ ++ if (ispccdc_obj.ccdc_outfmt == CCDC_OTHERS_MEM ++ || ispccdc_obj.ccdc_outfmt == CCDC_OTHERS_VP_MEM) { ++ if (*output_w % 16) { ++ *output_w -= (*output_w % 16); ++ *output_w += 16; ++ } ++ } ++ ++ ispccdc_obj.ccdcout_w = *output_w; ++ ispccdc_obj.ccdcout_h = *output_h; ++ ispccdc_obj.ccdcin_w = input_w; ++ ispccdc_obj.ccdcin_h = input_h; ++ ++ DPRINTK_ISPCCDC("try size: ccdcin_w=%u,ccdcin_h=%u,ccdcout_w=%u," ++ " ccdcout_h=%u\n", ++ ispccdc_obj.ccdcin_w, ++ ispccdc_obj.ccdcin_h, ++ ispccdc_obj.ccdcout_w, ++ ispccdc_obj.ccdcout_h); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_try_size); ++ ++/** ++ * ispccdc_config_size - Configure the dimensions of the CCDC input/output ++ * @input_w: input width for the CCDC in number of pixels per line ++ * @input_h: input height for the CCDC in number of lines ++ * @output_w: output width from the CCDC in number of pixels per line ++ * @output_h: output height for the CCDC in number of lines ++ * ++ * Configures the appropriate values stored in the isp_ccdc structure to ++ * HORZ/VERT_INFO registers and the VP_OUT depending on whether the image ++ * is stored in memory or given to the another module in the ISP pipeline. ++ * ++ * Returns 0 if successful, or -EINVAL if try_size was not called before to ++ * validate the requested dimensions. ++ **/ ++int ispccdc_config_size(u32 input_w, u32 input_h, u32 output_w, u32 output_h) ++{ ++ DPRINTK_ISPCCDC("config size: input_w=%u, input_h=%u, output_w=%u," ++ " output_h=%u\n", ++ input_w, input_h, ++ output_w, output_h); ++ if (output_w != ispccdc_obj.ccdcout_w ++ || output_h != ispccdc_obj.ccdcout_h) { ++ DPRINTK_ISPCCDC("ISP_ERR : ispccdc_try_size should" ++ " be called before config size\n"); ++ return -EINVAL; ++ } ++ ++ if (ispccdc_obj.ccdc_outfmt == CCDC_OTHERS_VP) { ++ isp_reg_writel((ispccdc_obj.ccdcin_woffset << ++ ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | ++ (ispccdc_obj.ccdcin_w << ++ ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_HORZ); ++ isp_reg_writel((ispccdc_obj.ccdcin_hoffset << ++ ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | ++ (ispccdc_obj.ccdcin_h << ++ ISPCCDC_FMT_VERT_FMTLNV_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_VERT); ++ isp_reg_writel((ispccdc_obj.ccdcout_w << ++ ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | ++ (ispccdc_obj.ccdcout_h - 1) << ++ ISPCCDC_VP_OUT_VERT_NUM_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VP_OUT); ++ isp_reg_writel((((ispccdc_obj.ccdcout_h - 25) & ++ ISPCCDC_VDINT_0_MASK) << ++ ISPCCDC_VDINT_0_SHIFT) | ++ ((50 & ISPCCDC_VDINT_1_MASK) << ++ ISPCCDC_VDINT_1_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VDINT); ++ ++ } else if (ispccdc_obj.ccdc_outfmt == CCDC_OTHERS_MEM) { ++ isp_reg_writel(0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); ++ if (ispccdc_obj.ccdc_inpfmt == CCDC_RAW) { ++ isp_reg_writel(0 << ISPCCDC_HORZ_INFO_SPH_SHIFT ++ | ((ispccdc_obj.ccdcout_w - 1) ++ << ISPCCDC_HORZ_INFO_NPH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_HORZ_INFO); ++ } else { ++ isp_reg_writel(0 << ISPCCDC_HORZ_INFO_SPH_SHIFT ++ | ((ispccdc_obj.ccdcout_w - 1) ++ << ISPCCDC_HORZ_INFO_NPH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_HORZ_INFO); ++ } ++ isp_reg_writel(0 << ISPCCDC_VERT_START_SLV0_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_START); ++ isp_reg_writel((ispccdc_obj.ccdcout_h - 1) << ++ ISPCCDC_VERT_LINES_NLV_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_LINES); ++ ++ ispccdc_config_outlineoffset(ispccdc_obj.ccdcout_w * 2, 0, 0); ++ isp_reg_writel((((ispccdc_obj.ccdcout_h - 2) & ++ ISPCCDC_VDINT_0_MASK) << ++ ISPCCDC_VDINT_0_SHIFT) | ++ ((100 & ISPCCDC_VDINT_1_MASK) << ++ ISPCCDC_VDINT_1_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VDINT); ++ } else if (ispccdc_obj.ccdc_outfmt == CCDC_OTHERS_VP_MEM) { ++ isp_reg_writel((0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | ++ (ispccdc_obj.ccdcin_w << ++ ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_HORZ); ++ isp_reg_writel((0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | ++ ((ispccdc_obj.ccdcin_h) << ++ ISPCCDC_FMT_VERT_FMTLNV_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FMT_VERT); ++ isp_reg_writel((ispccdc_obj.ccdcout_w ++ << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | ++ ((ispccdc_obj.ccdcout_h - 1) << ++ ISPCCDC_VP_OUT_VERT_NUM_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VP_OUT); ++ isp_reg_writel(0 << ISPCCDC_HORZ_INFO_SPH_SHIFT | ++ ((ispccdc_obj.ccdcout_w - 1) << ++ ISPCCDC_HORZ_INFO_NPH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_HORZ_INFO); ++ isp_reg_writel(0 << ISPCCDC_VERT_START_SLV0_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_START); ++ isp_reg_writel((ispccdc_obj.ccdcout_h - 1) << ++ ISPCCDC_VERT_LINES_NLV_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_LINES); ++ ispccdc_config_outlineoffset(ispccdc_obj.ccdcout_w * 2, 0, 0); ++ isp_reg_writel((((ispccdc_obj.ccdcout_h - 2) & ++ ISPCCDC_VDINT_0_MASK) << ++ ISPCCDC_VDINT_0_SHIFT) | ++ ((100 & ISPCCDC_VDINT_1_MASK) << ++ ISPCCDC_VDINT_1_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VDINT); ++ } ++ ++ if (is_isplsc_activated()) { ++ if (ispccdc_obj.ccdc_inpfmt == CCDC_RAW) { ++ ispccdc_config_lsc(&lsc_config); ++ ispccdc_load_lsc(lsc_gain_table, lsc_config.size); ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_config_size); ++ ++/** ++ * ispccdc_config_outlineoffset - Configures the output line offset ++ * @offset: Must be twice the Output width and aligned on 32 byte boundary ++ * @oddeven: Specifies the odd/even line pattern to be chosen to store the ++ * output. ++ * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. ++ * ++ * - Configures the output line offset when stored in memory ++ * - Sets the odd/even line pattern to store the output ++ * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) ++ * - Configures the number of even and odd line fields in case of rearranging ++ * the lines. ++ * ++ * Returns 0 if successful, or -EINVAL if the offset is not in 32 byte ++ * boundary. ++ **/ ++int ispccdc_config_outlineoffset(u32 offset, u8 oddeven, u8 numlines) ++{ ++ if ((offset & ISP_32B_BOUNDARY_OFFSET) == offset) { ++ isp_reg_writel((offset & 0xFFFF), OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_HSIZE_OFF); ++ } else { ++ DPRINTK_ISPCCDC("ISP_ERR : Offset should be in 32 byte" ++ " boundary\n"); ++ return -EINVAL; ++ } ++ ++ isp_reg_and(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ ~ISPCCDC_SDOFST_FINV); ++ ++ isp_reg_and(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ ~ISPCCDC_SDOFST_FOFST_4L); ++ ++ switch (oddeven) { ++ case EVENEVEN: ++ isp_reg_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); ++ break; ++ case ODDEVEN: ++ isp_reg_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); ++ break; ++ case EVENODD: ++ isp_reg_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); ++ break; ++ case ODDODD: ++ isp_reg_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++EXPORT_SYMBOL(ispccdc_config_outlineoffset); ++ ++/** ++ * ispccdc_set_outaddr - Sets the memory address where the output will be saved ++ * @addr: 32-bit memory address aligned on 32 byte boundary. ++ * ++ * Sets the memory address where the output will be saved. ++ * ++ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte ++ * boundary. ++ **/ ++int ispccdc_set_outaddr(u32 addr) ++{ ++ if ((addr & ISP_32B_BOUNDARY_BUF) == addr) { ++ isp_reg_writel(addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR); ++ return 0; ++ } else { ++ DPRINTK_ISPCCDC("ISP_ERR : Address should be in 32 byte" ++ " boundary\n"); ++ return -EINVAL; ++ } ++ ++} ++EXPORT_SYMBOL(ispccdc_set_outaddr); ++ ++void __ispccdc_enable(u8 enable) ++{ ++ if (enable) { ++ if (ispccdc_obj.lsc_enable ++ && ispccdc_obj.ccdc_inpfmt == CCDC_RAW) ++ ispccdc_enable_lsc(1); ++ ++ } else { ++ ispccdc_obj.lsc_enable = ispccdc_obj.lsc_state; ++ } ++ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, ~ISPCCDC_PCR_EN, ++ enable ? ISPCCDC_PCR_EN : 0); ++} ++ ++/** ++ * ispccdc_enable - Enables the CCDC module. ++ * @enable: 0 Disables CCDC, 1 Enables CCDC ++ * ++ * Client should configure all the sub modules in CCDC before this. ++ **/ ++void ispccdc_enable(u8 enable) ++{ ++ __ispccdc_enable(enable); ++ ispccdc_obj.pm_state = enable; ++} ++EXPORT_SYMBOL(ispccdc_enable); ++ ++/** ++ * ispccdc_suspend - Suspend the CCDC module. ++ **/ ++void ispccdc_suspend(void) ++{ ++ if (ispccdc_obj.pm_state) { ++ if (ispccdc_obj.lsc_state) ++ __ispccdc_enable_lsc(0); ++ else if (ispccdc_obj.lsc_enable) { ++ ispccdc_obj.lsc_state = 1; ++ ispccdc_obj.lsc_enable = 0; ++ } ++ __ispccdc_enable(0); ++ } ++} ++EXPORT_SYMBOL(ispccdc_suspend); ++ ++/** ++ * ispccdc_resume - Resume the CCDC module. ++ **/ ++void ispccdc_resume(void) ++{ ++ if (ispccdc_obj.pm_state) { ++ if (ispccdc_obj.lsc_state) ++ __ispccdc_enable_lsc(1); ++ __ispccdc_enable(1); ++ } ++} ++EXPORT_SYMBOL(ispccdc_resume); ++ ++/* ++ * Returns zero if the CCDC is idle and the image has been written to ++ * memory, too. ++ */ ++int ispccdc_sbl_busy(void) ++{ ++ return ispccdc_busy() ++ | (isp_reg_readl(OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) & ++ ISPSBL_CCDC_WR_0_DATA_READY) ++ | (isp_reg_readl(OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) & ++ ISPSBL_CCDC_WR_0_DATA_READY) ++ | (isp_reg_readl(OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) & ++ ISPSBL_CCDC_WR_0_DATA_READY) ++ | (isp_reg_readl(OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) & ++ ISPSBL_CCDC_WR_0_DATA_READY); ++} ++EXPORT_SYMBOL(ispccdc_sbl_busy); ++ ++/** ++ * ispccdc_busy - Gets busy state of the CCDC. ++ **/ ++int ispccdc_busy(void) ++{ ++ return isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) & ++ ISPCCDC_PCR_BUSY; ++} ++EXPORT_SYMBOL(ispccdc_busy); ++ ++/** ++ * ispccdc_save_context - Saves the values of the CCDC module registers ++ **/ ++void ispccdc_save_context(void) ++{ ++ DPRINTK_ISPCCDC("Saving context\n"); ++ isp_save_context(ispccdc_reg_list); ++} ++EXPORT_SYMBOL(ispccdc_save_context); ++ ++/** ++ * ispccdc_restore_context - Restores the values of the CCDC module registers ++ **/ ++void ispccdc_restore_context(void) ++{ ++ DPRINTK_ISPCCDC("Restoring context\n"); ++ isp_restore_context(ispccdc_reg_list); ++} ++EXPORT_SYMBOL(ispccdc_restore_context); ++ ++/** ++ * ispccdc_print_status - Prints the values of the CCDC Module registers ++ * ++ * Also prints other debug information stored in the CCDC module. ++ **/ ++void ispccdc_print_status(void) ++{ ++ if (!is_ispccdc_debug_enabled()) ++ return; ++ ++ DPRINTK_ISPCCDC("Module in use =%d\n", ispccdc_obj.ccdc_inuse); ++ DPRINTK_ISPCCDC("Accepted CCDC Input (width = %d,Height = %d)\n", ++ ispccdc_obj.ccdcin_w, ++ ispccdc_obj.ccdcin_h); ++ DPRINTK_ISPCCDC("Accepted CCDC Output (width = %d,Height = %d)\n", ++ ispccdc_obj.ccdcout_w, ++ ispccdc_obj.ccdcout_h); ++ DPRINTK_ISPCCDC("###CCDC PCR=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR)); ++ DPRINTK_ISPCCDC("ISP_CTRL =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); ++ switch (ispccdc_obj.ccdc_inpfmt) { ++ case CCDC_RAW: ++ DPRINTK_ISPCCDC("ccdc input format is CCDC_RAW\n"); ++ break; ++ case CCDC_YUV_SYNC: ++ DPRINTK_ISPCCDC("ccdc input format is CCDC_YUV_SYNC\n"); ++ break; ++ case CCDC_YUV_BT: ++ DPRINTK_ISPCCDC("ccdc input format is CCDC_YUV_BT\n"); ++ break; ++ } ++ ++ switch (ispccdc_obj.ccdc_outfmt) { ++ case CCDC_OTHERS_VP: ++ DPRINTK_ISPCCDC("ccdc output format is CCDC_OTHERS_VP\n"); ++ break; ++ case CCDC_OTHERS_MEM: ++ DPRINTK_ISPCCDC("ccdc output format is CCDC_OTHERS_MEM\n"); ++ break; ++ case CCDC_YUV_RSZ: ++ DPRINTK_ISPCCDC("ccdc output format is CCDC_YUV_RSZ\n"); ++ break; ++ } ++ ++ DPRINTK_ISPCCDC("###ISP_CTRL in ccdc =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); ++ DPRINTK_ISPCCDC("###ISP_IRQ0ENABLE in ccdc =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE)); ++ DPRINTK_ISPCCDC("###ISP_IRQ0STATUS in ccdc =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); ++ DPRINTK_ISPCCDC("###CCDC SYN_MODE=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE)); ++ DPRINTK_ISPCCDC("###CCDC HORZ_INFO=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO)); ++ DPRINTK_ISPCCDC("###CCDC VERT_START=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_START)); ++ DPRINTK_ISPCCDC("###CCDC VERT_LINES=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_LINES)); ++ DPRINTK_ISPCCDC("###CCDC CULLING=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CULLING)); ++ DPRINTK_ISPCCDC("###CCDC HSIZE_OFF=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF)); ++ DPRINTK_ISPCCDC("###CCDC SDOFST=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST)); ++ DPRINTK_ISPCCDC("###CCDC SDR_ADDR=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR)); ++ DPRINTK_ISPCCDC("###CCDC CLAMP=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP)); ++ DPRINTK_ISPCCDC("###CCDC COLPTN=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN)); ++ DPRINTK_ISPCCDC("###CCDC CFG=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG)); ++ DPRINTK_ISPCCDC("###CCDC VP_OUT=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT)); ++ DPRINTK_ISPCCDC("###CCDC_SDR_ADDR= 0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR)); ++ DPRINTK_ISPCCDC("###CCDC FMTCFG=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)); ++ DPRINTK_ISPCCDC("###CCDC FMT_HORZ=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ)); ++ DPRINTK_ISPCCDC("###CCDC FMT_VERT=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT)); ++ DPRINTK_ISPCCDC("###CCDC LSC_CONFIG=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_CONFIG)); ++ DPRINTK_ISPCCDC("###CCDC LSC_INIT=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_INITIAL)); ++ DPRINTK_ISPCCDC("###CCDC LSC_TABLE BASE=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_TABLE_BASE)); ++ DPRINTK_ISPCCDC("###CCDC LSC TABLE OFFSET=0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_TABLE_OFFSET)); ++} ++EXPORT_SYMBOL(ispccdc_print_status); ++ ++/** ++ * isp_ccdc_init - CCDC module initialization. ++ * ++ * Always returns 0 ++ **/ ++int __init isp_ccdc_init(void) ++{ ++ ispccdc_obj.ccdc_inuse = 0; ++ ispccdc_config_crop(0, 0, 0, 0); ++ mutex_init(&ispccdc_obj.mutexlock); ++ ++ if (is_isplsc_activated()) { ++ lsc_gain_table_tmp = kmalloc(LSC_TABLE_INIT_SIZE, GFP_KERNEL | ++ GFP_DMA); ++ memset(lsc_gain_table_tmp, 0x40, LSC_TABLE_INIT_SIZE); ++ lsc_config.initial_x = 0; ++ lsc_config.initial_y = 0; ++ lsc_config.gain_mode_n = 0x6; ++ lsc_config.gain_mode_m = 0x6; ++ lsc_config.gain_format = 0x4; ++ lsc_config.offset = 0x60; ++ lsc_config.size = LSC_TABLE_INIT_SIZE; ++ ispccdc_obj.lsc_enable = 1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * isp_ccdc_cleanup - CCDC module cleanup. ++ **/ ++void isp_ccdc_cleanup(void) ++{ ++ if (is_isplsc_activated()) { ++ ispccdc_free_lsc(); ++ kfree(lsc_gain_table_tmp); ++ } ++ ++ if (fpc_table_add_m != 0) { ++ ispmmu_kunmap(fpc_table_add_m); ++ kfree(fpc_table_add); ++ } ++} +diff --git a/drivers/media/video/isp/ispccdc.h b/drivers/media/video/isp/ispccdc.h +new file mode 100644 +index 0000000..4ef40a6 +--- /dev/null ++++ b/drivers/media/video/isp/ispccdc.h +@@ -0,0 +1,209 @@ ++/* ++ * ispccdc.h ++ * ++ * Driver header file for CCDC module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Senthilvadivu Guruswamy <svadivu@ti.com> ++ * Pallavi Kulkarni <p-kulkarni@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_CCDC_H ++#define OMAP_ISP_CCDC_H ++ ++#include <mach/isp_user.h> ++ ++#define is_isplsc_activated() 1 ++ ++/* Enumeration constants for CCDC input output format */ ++enum ccdc_input { ++ CCDC_RAW, ++ CCDC_YUV_SYNC, ++ CCDC_YUV_BT, ++ CCDC_OTHERS ++}; ++ ++enum ccdc_output { ++ CCDC_YUV_RSZ, ++ CCDC_YUV_MEM_RSZ, ++ CCDC_OTHERS_VP, ++ CCDC_OTHERS_MEM, ++ CCDC_OTHERS_VP_MEM ++}; ++ ++/* Enumeration constants for the sync interface parameters */ ++enum inpmode { ++ RAW, ++ YUV16, ++ YUV8 ++}; ++enum datasize { ++ DAT8, ++ DAT10, ++ DAT11, ++ DAT12 ++}; ++ ++ ++/** ++ * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC ++ * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave. ++ * @fldstat: Field state. 0 - Odd Field, 1 - Even Field. ++ * @ipmod: Input mode. ++ * @datsz: Data size. ++ * @fldmode: 0 - Progressive, 1 - Interlaced. ++ * @datapol: 0 - Positive, 1 - Negative. ++ * @fldpol: 0 - Positive, 1 - Negative. ++ * @hdpol: 0 - Positive, 1 - Negative. ++ * @vdpol: 0 - Positive, 1 - Negative. ++ * @fldout: 0 - Input, 1 - Output. ++ * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output. ++ * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output. ++ * @ppln: Number of pixels per line, used for HS/VS Output. ++ * @hlprf: Number of half lines per frame, used for HS/VS Output. ++ * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode. ++ */ ++struct ispccdc_syncif { ++ u8 ccdc_mastermode; ++ u8 fldstat; ++ enum inpmode ipmod; ++ enum datasize datsz; ++ u8 fldmode; ++ u8 datapol; ++ u8 fldpol; ++ u8 hdpol; ++ u8 vdpol; ++ u8 fldout; ++ u8 hs_width; ++ u8 vs_width; ++ u8 ppln; ++ u8 hlprf; ++ u8 bt_r656_en; ++}; ++ ++/** ++ * ispccdc_refmt - Structure for Reformatter parameters ++ * @lnalt: Line alternating mode enable. 0 - Enable, 1 - Disable. ++ * @lnum: Number of output lines from 1 input line. 1 to 4 lines. ++ * @plen_even: Number of program entries in even line minus 1. ++ * @plen_odd: Number of program entries in odd line minus 1. ++ * @prgeven0: Program entries 0-7 for even lines register ++ * @prgeven1: Program entries 8-15 for even lines register ++ * @prgodd0: Program entries 0-7 for odd lines register ++ * @prgodd1: Program entries 8-15 for odd lines register ++ * @fmtaddr0: Output line in which the original pixel is to be placed ++ * @fmtaddr1: Output line in which the original pixel is to be placed ++ * @fmtaddr2: Output line in which the original pixel is to be placed ++ * @fmtaddr3: Output line in which the original pixel is to be placed ++ * @fmtaddr4: Output line in which the original pixel is to be placed ++ * @fmtaddr5: Output line in which the original pixel is to be placed ++ * @fmtaddr6: Output line in which the original pixel is to be placed ++ * @fmtaddr7: Output line in which the original pixel is to be placed ++ */ ++struct ispccdc_refmt { ++ u8 lnalt; ++ u8 lnum; ++ u8 plen_even; ++ u8 plen_odd; ++ u32 prgeven0; ++ u32 prgeven1; ++ u32 prgodd0; ++ u32 prgodd1; ++ u32 fmtaddr0; ++ u32 fmtaddr1; ++ u32 fmtaddr2; ++ u32 fmtaddr3; ++ u32 fmtaddr4; ++ u32 fmtaddr5; ++ u32 fmtaddr6; ++ u32 fmtaddr7; ++}; ++ ++int ispccdc_request(void); ++ ++int ispccdc_free(void); ++ ++int ispccdc_config_datapath(enum ccdc_input input, enum ccdc_output output); ++ ++void ispccdc_config_crop(u32 left, u32 top, u32 height, u32 width); ++ ++void ispccdc_config_sync_if(struct ispccdc_syncif syncif); ++ ++int ispccdc_config_black_clamp(struct ispccdc_bclamp bclamp); ++ ++void ispccdc_enable_black_clamp(u8 enable); ++ ++int ispccdc_config_fpc(struct ispccdc_fpc fpc); ++ ++void ispccdc_enable_fpc(u8 enable); ++ ++void ispccdc_config_black_comp(struct ispccdc_blcomp blcomp); ++ ++void ispccdc_config_vp(struct ispccdc_vp vp); ++ ++void ispccdc_enable_vp(u8 enable); ++ ++void ispccdc_config_reformatter(struct ispccdc_refmt refmt); ++ ++void ispccdc_enable_reformatter(u8 enable); ++ ++void ispccdc_config_culling(struct ispccdc_culling culling); ++ ++void ispccdc_enable_lpf(u8 enable); ++ ++void ispccdc_config_alaw(enum alaw_ipwidth ipwidth); ++ ++void ispccdc_enable_alaw(u8 enable); ++ ++int ispccdc_load_lsc(u8 *table_addr, u32 table_size); ++ ++void ispccdc_config_lsc(struct ispccdc_lsc_config *lsc_cfg); ++ ++void ispccdc_enable_lsc(u8 enable); ++ ++void ispccdc_lsc_error_handler(void); ++ ++void ispccdc_config_imgattr(u32 colptn); ++ ++void ispccdc_config_shadow_registers(void); ++ ++int ispccdc_try_size(u32 input_w, u32 input_h, u32 *output_w, u32 *output_h); ++ ++int ispccdc_config_size(u32 input_w, u32 input_h, u32 output_w, u32 output_h); ++ ++int ispccdc_config_outlineoffset(u32 offset, u8 oddeven, u8 numlines); ++ ++int ispccdc_set_outaddr(u32 addr); ++ ++void ispccdc_enable(u8 enable); ++ ++void ispccdc_suspend(void); ++ ++void ispccdc_resume(void); ++ ++int ispccdc_sbl_busy(void); ++ ++int ispccdc_busy(void); ++ ++void ispccdc_save_context(void); ++ ++void ispccdc_restore_context(void); ++ ++void ispccdc_print_status(void); ++ ++int omap34xx_isp_ccdc_config(void *userspace_add); ++ ++void ispccdc_set_wenlog(u32 wenlog); ++ ++#endif /* OMAP_ISP_CCDC_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0005-omap3isp-Add-ISP-backend-PRV-and-RSZ.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0005-omap3isp-Add-ISP-backend-PRV-and-RSZ.patch new file mode 100644 index 0000000000..c549eadc88 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0005-omap3isp-Add-ISP-backend-PRV-and-RSZ.patch @@ -0,0 +1,3413 @@ +From 926b51afea146826c8076e5fb305eaa0332399b4 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add ISP backend (PRV and RSZ) + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/isp/isppreview.c | 1929 ++++++++++++++++++++++++++++++++++ + drivers/media/video/isp/isppreview.h | 354 +++++++ + drivers/media/video/isp/ispresizer.c | 928 ++++++++++++++++ + drivers/media/video/isp/ispresizer.h | 158 +++ + 4 files changed, 3369 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/isppreview.c + create mode 100644 drivers/media/video/isp/isppreview.h + create mode 100644 drivers/media/video/isp/ispresizer.c + create mode 100644 drivers/media/video/isp/ispresizer.h + +diff --git a/drivers/media/video/isp/isppreview.c b/drivers/media/video/isp/isppreview.c +new file mode 100644 +index 0000000..17f1abc +--- /dev/null ++++ b/drivers/media/video/isp/isppreview.c +@@ -0,0 +1,1929 @@ ++/* ++ * isppreview.c ++ * ++ * Driver Library for Preview module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Senthilvadivu Guruswamy <svadivu@ti.com> ++ * Pallavi Kulkarni <p-kulkarni@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <linux/mutex.h> ++#include <linux/module.h> ++#include <linux/uaccess.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "isppreview.h" ++ ++static struct ispprev_nf prev_nf_t; ++static struct prev_params *params; ++static int rg_update, gg_update, bg_update, nf_enable, nf_update; ++ ++/* Structure for saving/restoring preview module registers */ ++static struct isp_reg ispprev_reg_list[] = { ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RADR_OFFSET, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_DSDR_ADDR, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_DRKF_OFFSET, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_WADD_OFFSET, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_NF, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3, 0x0000}, ++ {OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, 0x0000}, ++ {0, ISP_TOK_TERM, 0x0000} ++}; ++ ++ ++/* Default values in Office Flourescent Light for RGBtoRGB Blending */ ++static struct ispprev_rgbtorgb flr_rgb2rgb = { ++ { /* RGB-RGB Matrix */ ++ {0x01E2, 0x0F30, 0x0FEE}, ++ {0x0F9B, 0x01AC, 0x0FB9}, ++ {0x0FE0, 0x0EC0, 0x0260} ++ }, /* RGB Offset */ ++ {0x0000, 0x0000, 0x0000} ++}; ++ ++/* Default values in Office Flourescent Light for RGB to YUV Conversion*/ ++static struct ispprev_csc flr_prev_csc[] = { ++ { ++ { /* CSC Coef Matrix */ ++ {66, 129, 25}, ++ {-38, -75, 112}, ++ {112, -94 , -18} ++ }, /* CSC Offset */ ++ {0x0, 0x0, 0x0} ++ }, ++ { ++ { /* CSC Coef Matrix BW */ ++ {66, 129, 25}, ++ {0, 0, 0}, ++ {0, 0, 0} ++ }, /* CSC Offset */ ++ {0x0, 0x0, 0x0} ++ }, ++ { ++ { /* CSC Coef Matrix Sepia */ ++ {19, 38, 7}, ++ {0, 0, 0}, ++ {0, 0, 0} ++ }, /* CSC Offset */ ++ {0x0, 0xE7, 0x14} ++ } ++}; ++ ++ ++/* Default values in Office Flourescent Light for CFA Gradient*/ ++#define FLR_CFA_GRADTHRS_HORZ 0x28 ++#define FLR_CFA_GRADTHRS_VERT 0x28 ++ ++/* Default values in Office Flourescent Light for Chroma Suppression*/ ++#define FLR_CSUP_GAIN 0x0D ++#define FLR_CSUP_THRES 0xEB ++ ++/* Default values in Office Flourescent Light for Noise Filter*/ ++#define FLR_NF_STRGTH 0x03 ++ ++/* Default values in Office Flourescent Light for White Balance*/ ++#define FLR_WBAL_DGAIN 0x100 ++#define FLR_WBAL_COEF0 0x20 ++#define FLR_WBAL_COEF1 0x29 ++#define FLR_WBAL_COEF2 0x2d ++#define FLR_WBAL_COEF3 0x20 ++ ++#define FLR_WBAL_COEF0_ES1 0x20 ++#define FLR_WBAL_COEF1_ES1 0x23 ++#define FLR_WBAL_COEF2_ES1 0x39 ++#define FLR_WBAL_COEF3_ES1 0x20 ++ ++/* Default values in Office Flourescent Light for Black Adjustment*/ ++#define FLR_BLKADJ_BLUE 0x0 ++#define FLR_BLKADJ_GREEN 0x0 ++#define FLR_BLKADJ_RED 0x0 ++ ++static int update_color_matrix; ++ ++/** ++ * struct isp_prev - Structure for storing ISP Preview module information ++ * @prev_inuse: Flag to determine if CCDC has been reserved or not (0 or 1). ++ * @prevout_w: Preview output width. ++ * @prevout_h: Preview output height. ++ * @previn_w: Preview input width. ++ * @previn_h: Preview input height. ++ * @prev_inpfmt: Preview input format. ++ * @prev_outfmt: Preview output format. ++ * @hmed_en: Horizontal median filter enable. ++ * @nf_en: Noise filter enable. ++ * @dcor_en: Defect correction enable. ++ * @cfa_en: Color Filter Array (CFA) interpolation enable. ++ * @csup_en: Chrominance suppression enable. ++ * @yenh_en: Luma enhancement enable. ++ * @fmtavg: Number of horizontal pixels to average in input formatter. The ++ * input width should be a multiple of this number. ++ * @brightness: Brightness in preview module. ++ * @contrast: Contrast in preview module. ++ * @color: Color effect in preview module. ++ * @cfafmt: Color Filter Array (CFA) Format. ++ * @ispprev_mutex: Mutex for isp preview. ++ * ++ * This structure is used to store the OMAP ISP Preview module Information. ++ */ ++static struct isp_prev { ++ int pm_state; ++ u8 prev_inuse; ++ u32 prevout_w; ++ u32 prevout_h; ++ u32 previn_w; ++ u32 previn_h; ++ enum preview_input prev_inpfmt; ++ enum preview_output prev_outfmt; ++ u8 hmed_en; ++ u8 nf_en; ++ u8 dcor_en; ++ u8 cfa_en; ++ u8 csup_en; ++ u8 yenh_en; ++ u8 fmtavg; ++ u8 brightness; ++ u8 contrast; ++ enum v4l2_colorfx color; ++ enum cfa_fmt cfafmt; ++ struct mutex ispprev_mutex; /* For checking/modifying prev_inuse */ ++ u32 sph; ++ u32 slv; ++} ispprev_obj; ++ ++/* Saved parameters */ ++static struct prev_params *prev_config_params; ++ ++/* ++ * Coeficient Tables for the submodules in Preview. ++ * Array is initialised with the values from.the tables text file. ++ */ ++ ++/* ++ * CFA Filter Coefficient Table ++ * ++ */ ++static u32 cfa_coef_table[] = { ++#include "cfa_coef_table.h" ++}; ++ ++/* ++ * Gamma Correction Table - Red ++ */ ++static u32 redgamma_table[] = { ++#include "redgamma_table.h" ++}; ++ ++/* ++ * Gamma Correction Table - Green ++ */ ++static u32 greengamma_table[] = { ++#include "greengamma_table.h" ++}; ++ ++/* ++ * Gamma Correction Table - Blue ++ */ ++static u32 bluegamma_table[] = { ++#include "bluegamma_table.h" ++}; ++ ++/* ++ * Noise Filter Threshold table ++ */ ++static u32 noise_filter_table[] = { ++#include "noise_filter_table.h" ++}; ++ ++/* ++ * Luminance Enhancement Table ++ */ ++static u32 luma_enhance_table[] = { ++#include "luma_enhance_table.h" ++}; ++ ++/** ++ * omap34xx_isp_preview_config - Abstraction layer Preview configuration. ++ * @userspace_add: Pointer from Userspace to structure with flags and data to ++ * update. ++ **/ ++int omap34xx_isp_preview_config(void *userspace_add) ++{ ++ struct ispprev_hmed prev_hmed_t; ++ struct ispprev_cfa prev_cfa_t; ++ struct ispprev_csup csup_t; ++ struct ispprev_wbal prev_wbal_t; ++ struct ispprev_blkadj prev_blkadj_t; ++ struct ispprev_rgbtorgb rgb2rgb_t; ++ struct ispprev_csc prev_csc_t; ++ struct ispprev_yclimit yclimit_t; ++ struct ispprev_dcor prev_dcor_t; ++ struct ispprv_update_config *preview_struct; ++ struct isptables_update isp_table_update; ++ int yen_t[ISPPRV_YENH_TBL_SIZE]; ++ ++ if (userspace_add == NULL) ++ return -EINVAL; ++ ++ preview_struct = userspace_add; ++ ++ if (ISP_ABS_PREV_LUMAENH & preview_struct->flag) { ++ if (ISP_ABS_PREV_LUMAENH & preview_struct->update) { ++ if (copy_from_user(yen_t, preview_struct->yen, ++ sizeof(yen_t))) ++ goto err_copy_from_user; ++ isppreview_config_luma_enhancement(yen_t); ++ } ++ params->features |= PREV_LUMA_ENHANCE; ++ } else if (ISP_ABS_PREV_LUMAENH & preview_struct->update) ++ params->features &= ~PREV_LUMA_ENHANCE; ++ ++ if (ISP_ABS_PREV_INVALAW & preview_struct->flag) { ++ isppreview_enable_invalaw(1); ++ params->features |= PREV_INVERSE_ALAW; ++ } else { ++ isppreview_enable_invalaw(0); ++ params->features &= ~PREV_INVERSE_ALAW; ++ } ++ ++ if (ISP_ABS_PREV_HRZ_MED & preview_struct->flag) { ++ if (ISP_ABS_PREV_HRZ_MED & preview_struct->update) { ++ if (copy_from_user(&prev_hmed_t, ++ (struct ispprev_hmed *) ++ preview_struct->prev_hmed, ++ sizeof(struct ispprev_hmed))) ++ goto err_copy_from_user; ++ isppreview_config_hmed(prev_hmed_t); ++ } ++ isppreview_enable_hmed(1); ++ params->features |= PREV_HORZ_MEDIAN_FILTER; ++ } else if (ISP_ABS_PREV_HRZ_MED & preview_struct->update) { ++ isppreview_enable_hmed(0); ++ params->features &= ~PREV_HORZ_MEDIAN_FILTER; ++ } ++ ++ if (ISP_ABS_PREV_CFA & preview_struct->flag) { ++ if (ISP_ABS_PREV_CFA & preview_struct->update) { ++ if (copy_from_user(&prev_cfa_t, ++ (struct ispprev_cfa *) ++ preview_struct->prev_cfa, ++ sizeof(struct ispprev_cfa))) ++ goto err_copy_from_user; ++ ++ isppreview_config_cfa(prev_cfa_t); ++ } ++ isppreview_enable_cfa(1); ++ params->features |= PREV_CFA; ++ } else if (ISP_ABS_PREV_CFA & preview_struct->update) { ++ isppreview_enable_cfa(0); ++ params->features &= ~PREV_CFA; ++ } ++ ++ if (ISP_ABS_PREV_CHROMA_SUPP & preview_struct->flag) { ++ if (ISP_ABS_PREV_CHROMA_SUPP & preview_struct->update) { ++ if (copy_from_user(&csup_t, ++ (struct ispprev_csup *) ++ preview_struct->csup, ++ sizeof(struct ispprev_csup))) ++ goto err_copy_from_user; ++ isppreview_config_chroma_suppression(csup_t); ++ } ++ isppreview_enable_chroma_suppression(1); ++ params->features |= PREV_CHROMA_SUPPRESS; ++ } else if (ISP_ABS_PREV_CHROMA_SUPP & preview_struct->update) { ++ isppreview_enable_chroma_suppression(0); ++ params->features &= ~PREV_CHROMA_SUPPRESS; ++ } ++ ++ if (ISP_ABS_PREV_WB & preview_struct->update) { ++ if (copy_from_user(&prev_wbal_t, (struct ispprev_wbal *) ++ preview_struct->prev_wbal, ++ sizeof(struct ispprev_wbal))) ++ goto err_copy_from_user; ++ isppreview_config_whitebalance(prev_wbal_t); ++ } ++ ++ if (ISP_ABS_PREV_BLKADJ & preview_struct->update) { ++ if (copy_from_user(&prev_blkadj_t, (struct ispprev_blkadjl *) ++ preview_struct->prev_blkadj, ++ sizeof(struct ispprev_blkadj))) ++ goto err_copy_from_user; ++ isppreview_config_blkadj(prev_blkadj_t); ++ } ++ ++ if (ISP_ABS_PREV_RGB2RGB & preview_struct->update) { ++ if (copy_from_user(&rgb2rgb_t, (struct ispprev_rgbtorgb *) ++ preview_struct->rgb2rgb, ++ sizeof(struct ispprev_rgbtorgb))) ++ goto err_copy_from_user; ++ isppreview_config_rgb_blending(rgb2rgb_t); ++ } ++ ++ if (ISP_ABS_PREV_COLOR_CONV & preview_struct->update) { ++ if (copy_from_user(&prev_csc_t, (struct ispprev_csc *) ++ preview_struct->prev_csc, ++ sizeof(struct ispprev_csc))) ++ goto err_copy_from_user; ++ isppreview_config_rgb_to_ycbcr(prev_csc_t); ++ } ++ ++ if (ISP_ABS_PREV_YC_LIMIT & preview_struct->update) { ++ if (copy_from_user(&yclimit_t, (struct ispprev_yclimit *) ++ preview_struct->yclimit, ++ sizeof(struct ispprev_yclimit))) ++ goto err_copy_from_user; ++ isppreview_config_yc_range(yclimit_t); ++ } ++ ++ if (ISP_ABS_PREV_DEFECT_COR & preview_struct->flag) { ++ if (ISP_ABS_PREV_DEFECT_COR & preview_struct->update) { ++ if (copy_from_user(&prev_dcor_t, ++ (struct ispprev_dcor *) ++ preview_struct->prev_dcor, ++ sizeof(struct ispprev_dcor))) ++ goto err_copy_from_user; ++ isppreview_config_dcor(prev_dcor_t); ++ } ++ isppreview_enable_dcor(1); ++ params->features |= PREV_DEFECT_COR; ++ } else if (ISP_ABS_PREV_DEFECT_COR & preview_struct->update) { ++ isppreview_enable_dcor(0); ++ params->features &= ~PREV_DEFECT_COR; ++ } ++ ++ if (ISP_ABS_PREV_GAMMABYPASS & preview_struct->flag) { ++ isppreview_enable_gammabypass(1); ++ params->features |= PREV_GAMMA_BYPASS; ++ } else { ++ isppreview_enable_gammabypass(0); ++ params->features &= ~PREV_GAMMA_BYPASS; ++ } ++ ++ isp_table_update.update = preview_struct->update; ++ isp_table_update.flag = preview_struct->flag; ++ isp_table_update.prev_nf = preview_struct->prev_nf; ++ isp_table_update.red_gamma = preview_struct->red_gamma; ++ isp_table_update.green_gamma = preview_struct->green_gamma; ++ isp_table_update.blue_gamma = preview_struct->blue_gamma; ++ ++ if (omap34xx_isp_tables_update(&isp_table_update)) ++ goto err_copy_from_user; ++ ++ return 0; ++ ++err_copy_from_user: ++ printk(KERN_ERR "Preview Config: Copy From User Error\n"); ++ return -EFAULT; ++} ++EXPORT_SYMBOL_GPL(omap34xx_isp_preview_config); ++ ++/** ++ * omap34xx_isp_tables_update - Abstraction layer Tables update. ++ * @isptables_struct: Pointer from Userspace to structure with flags and table ++ * data to update. ++ **/ ++int omap34xx_isp_tables_update(struct isptables_update *isptables_struct) ++{ ++ ++ if (ISP_ABS_TBL_NF & isptables_struct->flag) { ++ nf_enable = 1; ++ params->features |= PREV_NOISE_FILTER; ++ if (ISP_ABS_TBL_NF & isptables_struct->update) { ++ if (copy_from_user(&prev_nf_t, (struct ispprev_nf *) ++ isptables_struct->prev_nf, ++ sizeof(struct ispprev_nf))) ++ goto err_copy_from_user; ++ ++ nf_update = 1; ++ } else ++ nf_update = 0; ++ } else { ++ nf_enable = 0; ++ params->features &= ~PREV_NOISE_FILTER; ++ if (ISP_ABS_TBL_NF & isptables_struct->update) ++ nf_update = 1; ++ else ++ nf_update = 0; ++ } ++ ++ if (ISP_ABS_TBL_REDGAMMA & isptables_struct->update) { ++ if (copy_from_user(redgamma_table, isptables_struct->red_gamma, ++ sizeof(redgamma_table))) { ++ goto err_copy_from_user; ++ } ++ rg_update = 1; ++ } else ++ rg_update = 0; ++ ++ if (ISP_ABS_TBL_GREENGAMMA & isptables_struct->update) { ++ if (copy_from_user(greengamma_table, ++ isptables_struct->green_gamma, ++ sizeof(greengamma_table))) ++ goto err_copy_from_user; ++ gg_update = 1; ++ } else ++ gg_update = 0; ++ ++ if (ISP_ABS_TBL_BLUEGAMMA & isptables_struct->update) { ++ if (copy_from_user(bluegamma_table, ++ isptables_struct->blue_gamma, ++ sizeof(bluegamma_table))) { ++ goto err_copy_from_user; ++ } ++ bg_update = 1; ++ } else ++ bg_update = 0; ++ ++ return 0; ++ ++err_copy_from_user: ++ printk(KERN_ERR "Preview Tables:Copy From User Error\n"); ++ return -EFAULT; ++} ++ ++/** ++ * isppreview_config_shadow_registers - Program shadow registers for preview. ++ * ++ * Allows user to program shadow registers associated with preview module. ++ **/ ++void isppreview_config_shadow_registers() ++{ ++ u8 current_brightness_contrast; ++ int ctr, prv_disabled; ++ ++ isppreview_query_brightness(¤t_brightness_contrast); ++ if (current_brightness_contrast != ++ (ispprev_obj.brightness * ISPPRV_BRIGHT_UNITS)) { ++ DPRINTK_ISPPREV(" Changing Brightness level to %d\n", ++ ispprev_obj.brightness); ++ isppreview_config_brightness(ispprev_obj.brightness * ++ ISPPRV_BRIGHT_UNITS); ++ } ++ ++ isppreview_query_contrast(¤t_brightness_contrast); ++ if (current_brightness_contrast != ++ (ispprev_obj.contrast * ISPPRV_CONTRAST_UNITS)) { ++ DPRINTK_ISPPREV(" Changing Contrast level to %d\n", ++ ispprev_obj.contrast); ++ isppreview_config_contrast(ispprev_obj.contrast * ++ ISPPRV_CONTRAST_UNITS); ++ } ++ if (update_color_matrix) { ++ isppreview_config_rgb_to_ycbcr(flr_prev_csc[ispprev_obj.color]); ++ update_color_matrix = 0; ++ } ++ if (gg_update || rg_update || bg_update || nf_update) { ++ isppreview_enable(0); ++ prv_disabled = 1; ++ } ++ ++ if (gg_update) { ++ isp_reg_writel(ISPPRV_TBL_ADDR_GREEN_G_START, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ ++ for (ctr = 0; ctr < ISP_GAMMA_TABLE_SIZE; ctr++) { ++ isp_reg_writel(greengamma_table[ctr], ++ OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ } ++ gg_update = 0; ++ } ++ ++ if (rg_update) { ++ isp_reg_writel(ISPPRV_TBL_ADDR_RED_G_START, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ ++ for (ctr = 0; ctr < ISP_GAMMA_TABLE_SIZE; ctr++) { ++ isp_reg_writel(redgamma_table[ctr], ++ OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ } ++ rg_update = 0; ++ } ++ ++ if (bg_update) { ++ isp_reg_writel(ISPPRV_TBL_ADDR_BLUE_G_START, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ ++ for (ctr = 0; ctr < ISP_GAMMA_TABLE_SIZE; ctr++) { ++ isp_reg_writel(bluegamma_table[ctr], ++ OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ } ++ bg_update = 0; ++ } ++ ++ if (nf_update && nf_enable) { ++ isp_reg_writel(0xC00, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ isp_reg_writel(prev_nf_t.spread, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); ++ for (ctr = 0; ctr < ISPPRV_NF_TBL_SIZE; ctr++) { ++ isp_reg_writel(prev_nf_t.table[ctr], ++ OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ } ++ isppreview_enable_noisefilter(1); ++ nf_update = 0; ++ } ++ ++ if (~nf_update && nf_enable) ++ isppreview_enable_noisefilter(1); ++ ++ if (nf_update && ~nf_enable) ++ isppreview_enable_noisefilter(0); ++ ++ if (prv_disabled) { ++ isppreview_enable(1); ++ prv_disabled = 0; ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_config_shadow_registers); ++ ++/** ++ * isppreview_request - Reserves the preview module. ++ * ++ * Returns 0 if successful, or -EBUSY if the module was already reserved. ++ **/ ++int isppreview_request() ++{ ++ mutex_lock(&ispprev_obj.ispprev_mutex); ++ if (ispprev_obj.prev_inuse) { ++ mutex_unlock(&ispprev_obj.ispprev_mutex); ++ printk(KERN_ERR "ISP_ERR : Preview Module Busy\n"); ++ return -EBUSY; ++ } ++ ispprev_obj.prev_inuse = 1; ++ mutex_unlock(&ispprev_obj.ispprev_mutex); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ISPCTRL_PREV_RAM_EN | ++ ISPCTRL_PREV_CLK_EN | ++ ISPCTRL_SBL_WR1_RAM_EN); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_request); ++ ++/** ++ * isppreview_free - Frees the preview module. ++ * ++ * Returns 0 if successful, or -EINVAL if the module was already freed. ++ **/ ++int isppreview_free() ++{ ++ mutex_lock(&ispprev_obj.ispprev_mutex); ++ if (ispprev_obj.prev_inuse) { ++ ispprev_obj.prev_inuse = 0; ++ mutex_unlock(&ispprev_obj.ispprev_mutex); ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ~(ISPCTRL_PREV_CLK_EN | ++ ISPCTRL_PREV_RAM_EN | ++ ISPCTRL_SBL_WR1_RAM_EN)); ++ return 0; ++ } else { ++ mutex_unlock(&ispprev_obj.ispprev_mutex); ++ DPRINTK_ISPPREV("ISP_ERR : Preview Module already freed\n"); ++ return -EINVAL; ++ } ++ ++} ++EXPORT_SYMBOL_GPL(isppreview_free); ++ ++/** isppreview_config_datapath - Specifies input and output modules for Preview ++ * @input: Indicates the module that gives the image to preview. ++ * @output: Indicates the module to which the preview outputs to. ++ * ++ * Configures the default configuration for the CCDC to work with. ++ * ++ * The valid values for the input are PRV_RAW_CCDC (0), PRV_RAW_MEM (1), ++ * PRV_RGBBAYERCFA (2), PRV_COMPCFA (3), PRV_CCDC_DRKF (4), PRV_OTHERS (5). ++ * ++ * The valid values for the output are PREVIEW_RSZ (0), PREVIEW_MEM (1). ++ * ++ * Returns 0 if successful, or -EINVAL if wrong input or output values are ++ * specified. ++ **/ ++int isppreview_config_datapath(enum preview_input input, ++ enum preview_output output) ++{ ++ u32 pcr = 0; ++ u8 enable = 0; ++ struct prev_params *params = prev_config_params; ++ struct ispprev_yclimit yclimit; ++ ++ pcr = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ ++ switch (input) { ++ case PRV_RAW_CCDC: ++ pcr &= ~ISPPRV_PCR_SOURCE; ++ pcr &= ~ISPPRV_PCR_ONESHOT; ++ ispprev_obj.prev_inpfmt = PRV_RAW_CCDC; ++ break; ++ case PRV_RAW_MEM: ++ pcr |= ISPPRV_PCR_SOURCE; ++ pcr |= ISPPRV_PCR_ONESHOT; ++ ispprev_obj.prev_inpfmt = PRV_RAW_MEM; ++ break; ++ case PRV_CCDC_DRKF: ++ pcr |= ISPPRV_PCR_DRKFCAP; ++ pcr |= ISPPRV_PCR_ONESHOT; ++ ispprev_obj.prev_inpfmt = PRV_CCDC_DRKF; ++ break; ++ case PRV_COMPCFA: ++ ispprev_obj.prev_inpfmt = PRV_COMPCFA; ++ break; ++ case PRV_OTHERS: ++ ispprev_obj.prev_inpfmt = PRV_OTHERS; ++ break; ++ case PRV_RGBBAYERCFA: ++ ispprev_obj.prev_inpfmt = PRV_RGBBAYERCFA; ++ break; ++ default: ++ printk(KERN_ERR "ISP_ERR : Wrong Input\n"); ++ return -EINVAL; ++ }; ++ ++ switch (output) { ++ case PREVIEW_RSZ: ++ pcr |= ISPPRV_PCR_RSZPORT; ++ pcr &= ~ISPPRV_PCR_SDRPORT; ++ break; ++ case PREVIEW_MEM: ++ pcr &= ~ISPPRV_PCR_RSZPORT; ++ pcr |= ISPPRV_PCR_SDRPORT; ++ break; ++ default: ++ printk(KERN_ERR "ISP_ERR : Wrong Output\n"); ++ return -EINVAL; ++ } ++ ispprev_obj.prev_outfmt = output; ++ ++ isp_reg_writel(pcr, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ ++ isppreview_config_ycpos(params->pix_fmt); ++ ++ if (params->cfa.cfa_table != NULL) ++ isppreview_config_cfa(params->cfa); ++ if (params->csup.hypf_en == 1) ++ isppreview_config_chroma_suppression(params->csup); ++ if (params->ytable != NULL) ++ isppreview_config_luma_enhancement(params->ytable); ++ ++ if (params->gtable.redtable != NULL) ++ isppreview_config_gammacorrn(params->gtable); ++ ++ enable = (params->features & PREV_CFA) ? 1 : 0; ++ isppreview_enable_cfa(enable); ++ ++ enable = (params->features & PREV_CHROMA_SUPPRESS) ? 1 : 0; ++ isppreview_enable_chroma_suppression(enable); ++ ++ enable = (params->features & PREV_LUMA_ENHANCE) ? 1 : 0; ++ isppreview_enable_luma_enhancement(enable); ++ ++ enable = (params->features & PREV_NOISE_FILTER) ? 1 : 0; ++ if (enable) ++ isppreview_config_noisefilter(params->nf); ++ isppreview_enable_noisefilter(enable); ++ ++ enable = (params->features & PREV_DEFECT_COR) ? 1 : 0; ++ if (enable) ++ isppreview_config_dcor(params->dcor); ++ isppreview_enable_dcor(enable); ++ ++ enable = (params->features & PREV_GAMMA_BYPASS) ? 1 : 0; ++ isppreview_enable_gammabypass(enable); ++ ++ isppreview_config_whitebalance(params->wbal); ++ isppreview_config_blkadj(params->blk_adj); ++ isppreview_config_rgb_blending(params->rgb2rgb); ++ isppreview_config_rgb_to_ycbcr(params->rgb2ycbcr); ++ ++ isppreview_config_contrast(params->contrast * ISPPRV_CONTRAST_UNITS); ++ isppreview_config_brightness(params->brightness * ISPPRV_BRIGHT_UNITS); ++ ++ yclimit.minC = ISPPRV_YC_MIN; ++ yclimit.maxC = ISPPRV_YC_MAX; ++ yclimit.minY = ISPPRV_YC_MIN; ++ yclimit.maxY = ISPPRV_YC_MAX; ++ isppreview_config_yc_range(yclimit); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_config_datapath); ++ ++/** ++ * isppreview_set_skip - Set the number of rows/columns that should be skipped. ++ * h - Start Pixel Horizontal. ++ * v - Start Line Vertical. ++ **/ ++void isppreview_set_skip(u32 h, u32 v) ++{ ++ ispprev_obj.sph = h; ++ ispprev_obj.slv = v; ++} ++EXPORT_SYMBOL_GPL(isppreview_set_skip); ++ ++/** ++ * isppreview_config_ycpos - Configure byte layout of YUV image. ++ * @mode: Indicates the required byte layout. ++ **/ ++void isppreview_config_ycpos(enum preview_ycpos_mode mode) ++{ ++ u32 pcr = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ pcr &= ~ISPPRV_PCR_YCPOS_CrYCbY; ++ pcr |= (mode << ISPPRV_PCR_YCPOS_SHIFT); ++ isp_reg_writel(pcr, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_ycpos); ++ ++/** ++ * isppreview_config_averager - Enable / disable / configure averager ++ * @average: Average value to be configured. ++ **/ ++void isppreview_config_averager(u8 average) ++{ ++ int reg = 0; ++ ++ reg = AVE_ODD_PIXEL_DIST | AVE_EVEN_PIXEL_DIST | average; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_averager); ++ ++/** ++ * isppreview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview. ++ * @enable: 1 - Reverse the A-Law done in CCDC. ++ **/ ++void isppreview_enable_invalaw(u8 enable) ++{ ++ u32 pcr_val = 0; ++ pcr_val = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ ++ if (enable) { ++ isp_reg_writel(pcr_val | ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ } else { ++ isp_reg_writel(pcr_val & ++ ~(ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_invalaw); ++ ++/** ++ * isppreview_enable_drkframe - Enable/Disable of the darkframe subtract. ++ * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is ++ * subtracted with the pixels in the current frame. ++ * ++ * The proccess is applied for each captured frame. ++ **/ ++void isppreview_enable_drkframe(u8 enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_DRKFEN); ++ else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_DRKFEN); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_drkframe); ++ ++/** ++ * isppreview_enable_shadcomp - Enables/Disables the shading compensation. ++ * @enable: 1 - Enables the shading compensation. ++ * ++ * If dark frame subtract won't be used, then enable this shading ++ * compensation. ++ **/ ++void isppreview_enable_shadcomp(u8 enable) ++{ ++ ++ if (enable) { ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SCOMP_EN); ++ isppreview_enable_drkframe(1); ++ } else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_SCOMP_EN); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_shadcomp); ++ ++/** ++ * isppreview_config_drkf_shadcomp - Configures shift value in shading comp. ++ * @scomp_shtval: 3bit value of shift used in shading compensation. ++ **/ ++void isppreview_config_drkf_shadcomp(u8 scomp_shtval) ++{ ++ u32 pcr_val = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++ ++ pcr_val &= ISPPRV_PCR_SCOMP_SFT_MASK; ++ isp_reg_writel(pcr_val | (scomp_shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_drkf_shadcomp); ++ ++/** ++ * isppreview_enable_hmed - Enables/Disables of the Horizontal Median Filter. ++ * @enable: 1 - Enables Horizontal Median Filter. ++ **/ ++void isppreview_enable_hmed(u8 enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_HMEDEN); ++ else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_HMEDEN); ++ } ++ ispprev_obj.hmed_en = enable ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_hmed); ++ ++/** ++ * isppreview_config_hmed - Configures the Horizontal Median Filter. ++ * @prev_hmed: Structure containing the odd and even distance between the ++ * pixels in the image along with the filter threshold. ++ **/ ++void isppreview_config_hmed(struct ispprev_hmed prev_hmed) ++{ ++ ++ u32 odddist = 0; ++ u32 evendist = 0; ++ ++ if (prev_hmed.odddist == 1) ++ odddist = ~ISPPRV_HMED_ODDDIST; ++ else ++ odddist = ISPPRV_HMED_ODDDIST; ++ ++ if (prev_hmed.evendist == 1) ++ evendist = ~ISPPRV_HMED_EVENDIST; ++ else ++ evendist = ISPPRV_HMED_EVENDIST; ++ ++ isp_reg_writel(odddist | evendist | (prev_hmed.thres << ++ ISPPRV_HMED_THRESHOLD_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED); ++ ++} ++EXPORT_SYMBOL_GPL(isppreview_config_hmed); ++ ++/** ++ * isppreview_config_noisefilter - Configures the Noise Filter. ++ * @prev_nf: Structure containing the noisefilter table, strength to be used ++ * for the noise filter and the defect correction enable flag. ++ **/ ++void isppreview_config_noisefilter(struct ispprev_nf prev_nf) ++{ ++ int i = 0; ++ ++ isp_reg_writel(prev_nf.spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); ++ isp_reg_writel(ISPPRV_NF_TABLE_ADDR, OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < ISPPRV_NF_TBL_SIZE; i++) { ++ isp_reg_writel(prev_nf.table[i], OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_config_noisefilter); ++ ++/** ++ * isppreview_config_dcor - Configures the defect correction ++ * @prev_nf: Structure containing the defect correction structure ++ **/ ++void isppreview_config_dcor(struct ispprev_dcor prev_dcor) ++{ ++ if (prev_dcor.couplet_mode_en) { ++ isp_reg_writel(prev_dcor.detect_correct[0], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0); ++ isp_reg_writel(prev_dcor.detect_correct[1], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1); ++ isp_reg_writel(prev_dcor.detect_correct[2], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2); ++ isp_reg_writel(prev_dcor.detect_correct[3], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3); ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_DCCOUP); ++ } else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_DCCOUP); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_config_dcor); ++ ++/** ++ * isppreview_config_cfa - Configures the CFA Interpolation parameters. ++ * @prev_cfa: Structure containing the CFA interpolation table, CFA format ++ * in the image, vertical and horizontal gradient threshold. ++ **/ ++void isppreview_config_cfa(struct ispprev_cfa prev_cfa) ++{ ++ int i = 0; ++ ++ ispprev_obj.cfafmt = prev_cfa.cfafmt; ++ ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ (prev_cfa.cfafmt << ISPPRV_PCR_CFAFMT_SHIFT)); ++ ++ isp_reg_writel( ++ (prev_cfa.cfa_gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) | ++ (prev_cfa.cfa_gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA); ++ ++ isp_reg_writel(ISPPRV_CFA_TABLE_ADDR, OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_ADDR); ++ ++ for (i = 0; i < ISPPRV_CFA_TBL_SIZE; i++) { ++ isp_reg_writel(prev_cfa.cfa_table[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_config_cfa); ++ ++/** ++ * isppreview_config_gammacorrn - Configures the Gamma Correction table values ++ * @gtable: Structure containing the table for red, blue, green gamma table. ++ **/ ++void isppreview_config_gammacorrn(struct ispprev_gtable gtable) ++{ ++ int i = 0; ++ ++ isp_reg_writel(ISPPRV_REDGAMMA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < ISPPRV_GAMMA_TBL_SIZE; i++) { ++ isp_reg_writel(gtable.redtable[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++ ++ isp_reg_writel(ISPPRV_GREENGAMMA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < ISPPRV_GAMMA_TBL_SIZE; i++) { ++ isp_reg_writel(gtable.greentable[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++ ++ isp_reg_writel(ISPPRV_BLUEGAMMA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < ISPPRV_GAMMA_TBL_SIZE; i++) { ++ isp_reg_writel(gtable.bluetable[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_config_gammacorrn); ++ ++/** ++ * isppreview_config_luma_enhancement - Sets the Luminance Enhancement table. ++ * @ytable: Structure containing the table for Luminance Enhancement table. ++ **/ ++void isppreview_config_luma_enhancement(u32 *ytable) ++{ ++ int i = 0; ++ ++ isp_reg_writel(ISPPRV_YENH_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < ISPPRV_YENH_TBL_SIZE; i++) { ++ isp_reg_writel(ytable[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_config_luma_enhancement); ++ ++/** ++ * isppreview_config_chroma_suppression - Configures the Chroma Suppression. ++ * @csup: Structure containing the threshold value for suppression ++ * and the hypass filter enable flag. ++ **/ ++void isppreview_config_chroma_suppression(struct ispprev_csup csup) ++{ ++ isp_reg_writel(csup.gain | (csup.thres << ISPPRV_CSUP_THRES_SHIFT) | ++ (csup.hypf_en << ISPPRV_CSUP_HPYF_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_chroma_suppression); ++ ++/** ++ * isppreview_enable_noisefilter - Enables/Disables the Noise Filter. ++ * @enable: 1 - Enables the Noise Filter. ++ **/ ++void isppreview_enable_noisefilter(u8 enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_NFEN); ++ else ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ~ISPPRV_PCR_NFEN); ++ ispprev_obj.nf_en = enable ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_noisefilter); ++ ++/** ++ * isppreview_enable_dcor - Enables/Disables the defect correction. ++ * @enable: 1 - Enables the defect correction. ++ **/ ++void isppreview_enable_dcor(u8 enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_DCOREN); ++ else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_DCOREN); ++ } ++ ispprev_obj.dcor_en = enable ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_dcor); ++ ++/** ++ * isppreview_enable_cfa - Enable/Disable the CFA Interpolation. ++ * @enable: 1 - Enables the CFA. ++ **/ ++void isppreview_enable_cfa(u8 enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_CFAEN); ++ else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_CFAEN); ++ } ++ ispprev_obj.cfa_en = enable ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_cfa); ++ ++/** ++ * isppreview_enable_gammabypass - Enables/Disables the GammaByPass ++ * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. ++ * 0 - Goes through Gamma Correction. input and output is 10bit. ++ **/ ++void isppreview_enable_gammabypass(u8 enable) ++{ ++ if (enable) { ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_GAMMA_BYPASS); ++ } else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_GAMMA_BYPASS); ++ } ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_gammabypass); ++ ++/** ++ * isppreview_enable_luma_enhancement - Enables/Disables Luminance Enhancement ++ * @enable: 1 - Enable the Luminance Enhancement. ++ **/ ++void isppreview_enable_luma_enhancement(u8 enable) ++{ ++ if (enable) { ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_YNENHEN); ++ } else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_YNENHEN); ++ } ++ ispprev_obj.yenh_en = enable ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_luma_enhancement); ++ ++/** ++ * isppreview_enable_chroma_suppression - Enables/Disables Chrominance Suppr. ++ * @enable: 1 - Enable the Chrominance Suppression. ++ **/ ++void isppreview_enable_chroma_suppression(u8 enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_SUPEN); ++ else { ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ~ISPPRV_PCR_SUPEN); ++ } ++ ispprev_obj.csup_en = enable ? 1 : 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable_chroma_suppression); ++ ++/** ++ * isppreview_config_whitebalance - Configures the White Balance parameters. ++ * @prev_wbal: Structure containing the digital gain and white balance ++ * coefficient. ++ * ++ * Coefficient matrix always with default values. ++ **/ ++void isppreview_config_whitebalance(struct ispprev_wbal prev_wbal) ++{ ++ u32 val; ++ ++ isp_reg_writel(prev_wbal.dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); ++ ++ val = prev_wbal.coef0 << ISPPRV_WBGAIN_COEF0_SHIFT; ++ val |= prev_wbal.coef1 << ISPPRV_WBGAIN_COEF1_SHIFT; ++ val |= prev_wbal.coef2 << ISPPRV_WBGAIN_COEF2_SHIFT; ++ val |= prev_wbal.coef3 << ISPPRV_WBGAIN_COEF3_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); ++ ++ isp_reg_writel(ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT | ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT | ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT | ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_whitebalance); ++ ++/** ++ * isppreview_config_whitebalance2 - Configures the White Balance parameters. ++ * @prev_wbal: Structure containing the digital gain and white balance ++ * coefficient. ++ * ++ * Coefficient matrix can be changed. ++ **/ ++void isppreview_config_whitebalance2(struct prev_white_balance prev_wbal) ++{ ++ isp_reg_writel(prev_wbal.wb_dgain, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); ++ isp_reg_writel(prev_wbal.wb_gain[0] | ++ prev_wbal.wb_gain[1] << ISPPRV_WBGAIN_COEF1_SHIFT | ++ prev_wbal.wb_gain[2] << ISPPRV_WBGAIN_COEF2_SHIFT | ++ prev_wbal.wb_gain[3] << ISPPRV_WBGAIN_COEF3_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); ++ ++ isp_reg_writel( ++ prev_wbal.wb_coefmatrix[0][0] << ISPPRV_WBSEL_N0_0_SHIFT | ++ prev_wbal.wb_coefmatrix[0][1] << ISPPRV_WBSEL_N0_1_SHIFT | ++ prev_wbal.wb_coefmatrix[0][2] << ISPPRV_WBSEL_N0_2_SHIFT | ++ prev_wbal.wb_coefmatrix[0][3] << ISPPRV_WBSEL_N0_3_SHIFT | ++ prev_wbal.wb_coefmatrix[1][0] << ISPPRV_WBSEL_N1_0_SHIFT | ++ prev_wbal.wb_coefmatrix[1][1] << ISPPRV_WBSEL_N1_1_SHIFT | ++ prev_wbal.wb_coefmatrix[1][2] << ISPPRV_WBSEL_N1_2_SHIFT | ++ prev_wbal.wb_coefmatrix[1][3] << ISPPRV_WBSEL_N1_3_SHIFT | ++ prev_wbal.wb_coefmatrix[2][0] << ISPPRV_WBSEL_N2_0_SHIFT | ++ prev_wbal.wb_coefmatrix[2][1] << ISPPRV_WBSEL_N2_1_SHIFT | ++ prev_wbal.wb_coefmatrix[2][2] << ISPPRV_WBSEL_N2_2_SHIFT | ++ prev_wbal.wb_coefmatrix[2][3] << ISPPRV_WBSEL_N2_3_SHIFT | ++ prev_wbal.wb_coefmatrix[3][0] << ISPPRV_WBSEL_N3_0_SHIFT | ++ prev_wbal.wb_coefmatrix[3][1] << ISPPRV_WBSEL_N3_1_SHIFT | ++ prev_wbal.wb_coefmatrix[3][2] << ISPPRV_WBSEL_N3_2_SHIFT | ++ prev_wbal.wb_coefmatrix[3][3] << ISPPRV_WBSEL_N3_3_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_whitebalance2); ++ ++/** ++ * isppreview_config_blkadj - Configures the Black Adjustment parameters. ++ * @prev_blkadj: Structure containing the black adjustment towards red, green, ++ * blue. ++ **/ ++void isppreview_config_blkadj(struct ispprev_blkadj prev_blkadj) ++{ ++ isp_reg_writel(prev_blkadj.blue | ++ (prev_blkadj.green << ISPPRV_BLKADJOFF_G_SHIFT) | ++ (prev_blkadj.red << ISPPRV_BLKADJOFF_R_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_blkadj); ++ ++/** ++ * isppreview_config_rgb_blending - Configures the RGB-RGB Blending matrix. ++ * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb ++ * offset. ++ **/ ++void isppreview_config_rgb_blending(struct ispprev_rgbtorgb rgb2rgb) ++{ ++ u32 val = 0; ++ ++ val = (rgb2rgb.matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT; ++ val |= (rgb2rgb.matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1); ++ ++ val = (rgb2rgb.matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT; ++ val |= (rgb2rgb.matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2); ++ ++ val = (rgb2rgb.matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT; ++ val |= (rgb2rgb.matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3); ++ ++ val = (rgb2rgb.matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT; ++ val |= (rgb2rgb.matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4); ++ ++ val = (rgb2rgb.matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5); ++ ++ val = (rgb2rgb.offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT; ++ val |= (rgb2rgb.offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1); ++ ++ val = (rgb2rgb.offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_rgb_blending); ++ ++/** ++ * Configures the RGB-YCbYCr conversion matrix ++ * @prev_csc: Structure containing the RGB to YCbYCr matrix and the ++ * YCbCr offset. ++ **/ ++void isppreview_config_rgb_to_ycbcr(struct ispprev_csc prev_csc) ++{ ++ u32 val = 0; ++ ++ val = (prev_csc.matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT; ++ val |= (prev_csc.matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT; ++ val |= (prev_csc.matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0); ++ ++ val = (prev_csc.matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT; ++ val |= (prev_csc.matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT; ++ val |= (prev_csc.matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1); ++ ++ val = (prev_csc.matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT; ++ val |= (prev_csc.matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT; ++ val |= (prev_csc.matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2); ++ ++ val = (prev_csc.offset[0] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT; ++ val |= (prev_csc.offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT; ++ val |= (prev_csc.offset[2] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT; ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_rgb_to_ycbcr); ++ ++/** ++ * isppreview_query_contrast - Query the contrast. ++ * @contrast: Pointer to hold the current programmed contrast value. ++ **/ ++void isppreview_query_contrast(u8 *contrast) ++{ ++ u32 brt_cnt_val = 0; ++ ++ brt_cnt_val = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); ++ *contrast = (brt_cnt_val >> ISPPRV_CNT_BRT_CNT_SHIFT) & 0xff; ++ DPRINTK_ISPPREV(" Current brt cnt value in hw is %x\n", brt_cnt_val); ++} ++EXPORT_SYMBOL_GPL(isppreview_query_contrast); ++ ++/** ++ * isppreview_update_contrast - Updates the contrast. ++ * @contrast: Pointer to hold the current programmed contrast value. ++ * ++ * Value should be programmed before enabling the module. ++ **/ ++void isppreview_update_contrast(u8 *contrast) ++{ ++ ispprev_obj.contrast = *contrast; ++} ++EXPORT_SYMBOL_GPL(isppreview_update_contrast); ++ ++/** ++ * isppreview_config_contrast - Configures the Contrast. ++ * @contrast: 8 bit value in U8Q4 format. ++ * ++ * Value should be programmed before enabling the module. ++ **/ ++void isppreview_config_contrast(u8 contrast) ++{ ++ u32 brt_cnt_val = 0; ++ ++ brt_cnt_val = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); ++ brt_cnt_val &= ~(0xff << ISPPRV_CNT_BRT_CNT_SHIFT); ++ contrast &= 0xff; ++ isp_reg_writel(brt_cnt_val | contrast << ISPPRV_CNT_BRT_CNT_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_contrast); ++ ++/** ++ * isppreview_get_contrast_range - Gets the range contrast value. ++ * @min_contrast: Pointer to hold the minimum Contrast value. ++ * @max_contrast: Pointer to hold the maximum Contrast value. ++ **/ ++void isppreview_get_contrast_range(u8 *min_contrast, u8 *max_contrast) ++{ ++ *min_contrast = ISPPRV_CONTRAST_MIN; ++ *max_contrast = ISPPRV_CONTRAST_MAX; ++} ++EXPORT_SYMBOL_GPL(isppreview_get_contrast_range); ++ ++/** ++ * isppreview_update_brightness - Updates the brightness in preview module. ++ * @brightness: Pointer to hold the current programmed brightness value. ++ * ++ **/ ++void isppreview_update_brightness(u8 *brightness) ++{ ++ ispprev_obj.brightness = *brightness; ++} ++EXPORT_SYMBOL_GPL(isppreview_update_brightness); ++ ++/** ++ * isppreview_config_brightness - Configures the brightness. ++ * @contrast: 8bitvalue in U8Q0 format. ++ **/ ++void isppreview_config_brightness(u8 brightness) ++{ ++ u32 brt_cnt_val = 0; ++ ++ DPRINTK_ISPPREV("\tConfiguring brightness in ISP: %d\n", brightness); ++ brt_cnt_val = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); ++ brt_cnt_val &= ~(0xff << ISPPRV_CNT_BRT_BRT_SHIFT); ++ brightness &= 0xff; ++ isp_reg_writel(brt_cnt_val | brightness << ISPPRV_CNT_BRT_BRT_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_brightness); ++ ++/** ++ * isppreview_query_brightness - Query the brightness. ++ * @brightness: Pointer to hold the current programmed brightness value. ++ **/ ++void isppreview_query_brightness(u8 *brightness) ++{ ++ *brightness = isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT); ++} ++EXPORT_SYMBOL_GPL(isppreview_query_brightness); ++ ++/** ++ * isppreview_get_brightness_range - Gets the range brightness value ++ * @min_brightness: Pointer to hold the minimum brightness value ++ * @max_brightness: Pointer to hold the maximum brightness value ++ **/ ++void isppreview_get_brightness_range(u8 *min_brightness, u8 *max_brightness) ++{ ++ *min_brightness = ISPPRV_BRIGHT_MIN; ++ *max_brightness = ISPPRV_BRIGHT_MAX; ++} ++EXPORT_SYMBOL_GPL(isppreview_get_brightness_range); ++ ++/** ++ * isppreview_set_color - Sets the color effect. ++ * @mode: Indicates the required color effect. ++ **/ ++void isppreview_set_color(u8 *mode) ++{ ++ ispprev_obj.color = *mode; ++ update_color_matrix = 1; ++} ++EXPORT_SYMBOL_GPL(isppreview_set_color); ++ ++/** ++ * isppreview_get_color - Gets the current color effect. ++ * @mode: Indicates the current color effect. ++ **/ ++void isppreview_get_color(u8 *mode) ++{ ++ *mode = ispprev_obj.color; ++} ++EXPORT_SYMBOL_GPL(isppreview_get_color); ++ ++/** ++ * isppreview_config_yc_range - Configures the max and min Y and C values. ++ * @yclimit: Structure containing the range of Y and C values. ++ **/ ++void isppreview_config_yc_range(struct ispprev_yclimit yclimit) ++{ ++ isp_reg_writel(yclimit.maxC << ISPPRV_SETUP_YC_MAXC_SHIFT | ++ yclimit.maxY << ISPPRV_SETUP_YC_MAXY_SHIFT | ++ yclimit.minC << ISPPRV_SETUP_YC_MINC_SHIFT | ++ yclimit.minY << ISPPRV_SETUP_YC_MINY_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); ++} ++EXPORT_SYMBOL_GPL(isppreview_config_yc_range); ++ ++/** ++ * isppreview_try_size - Calculates output dimensions with the modules enabled. ++ * @input_w: input width for the preview in number of pixels per line ++ * @input_h: input height for the preview in number of lines ++ * @output_w: output width from the preview in number of pixels per line ++ * @output_h: output height for the preview in number of lines ++ * ++ * Calculates the number of pixels cropped in the submodules that are enabled, ++ * Fills up the output width height variables in the isp_prev structure. ++ **/ ++int isppreview_try_size(u32 input_w, u32 input_h, u32 *output_w, u32 *output_h) ++{ ++ u32 prevout_w = input_w; ++ u32 prevout_h = input_h; ++ u32 div = 0; ++ int max_out; ++ ++ ispprev_obj.previn_w = input_w; ++ ispprev_obj.previn_h = input_h; ++ ++ if (input_w < 32 || input_h < 32) { ++ printk(KERN_ERR "ISP_ERR : preview does not support " ++ "width < 16 or height < 32 \n"); ++ return -EINVAL; ++ } ++ if (omap_rev() == OMAP3430_REV_ES1_0) ++ max_out = ISPPRV_MAXOUTPUT_WIDTH; ++ else ++ max_out = ISPPRV_MAXOUTPUT_WIDTH_ES2; ++ ++ ispprev_obj.fmtavg = 0; ++ ++ if (input_w > max_out) { ++ div = (input_w/max_out); ++ if (div >= 2 && div < 4) { ++ ispprev_obj.fmtavg = 1; ++ prevout_w /= 2; ++ } else if (div >= 4 && div < 8) { ++ ispprev_obj.fmtavg = 2; ++ prevout_w /= 4; ++ } else if (div >= 8) { ++ ispprev_obj.fmtavg = 3; ++ prevout_w /= 8; ++ } ++ } ++ ++ if (ispprev_obj.hmed_en) ++ prevout_w -= 4; ++ if (ispprev_obj.nf_en) { ++ prevout_w -= 4; ++ prevout_h -= 4; ++ } ++ if (ispprev_obj.cfa_en) { ++ switch (ispprev_obj.cfafmt) { ++ case CFAFMT_BAYER: ++ case CFAFMT_SONYVGA: ++ prevout_w -= 4; ++ prevout_h -= 4; ++ break; ++ case CFAFMT_RGBFOVEON: ++ case CFAFMT_RRGGBBFOVEON: ++ case CFAFMT_DNSPL: ++ case CFAFMT_HONEYCOMB: ++ prevout_h -= 2; ++ break; ++ }; ++ } ++ if (ispprev_obj.yenh_en || ispprev_obj.csup_en) ++ prevout_w -= 2; ++ ++ /* Start at the correct row/column by skipping ++ * a Sensor specific amount. ++ */ ++ prevout_w -= ispprev_obj.sph; ++ prevout_h -= ispprev_obj.slv; ++ ++ ++ if (prevout_w % 2) ++ prevout_w -= 1; ++ ++ if (ispprev_obj.prev_outfmt == PREVIEW_MEM) { ++ if (((prevout_w * 2) & ISP_32B_BOUNDARY_OFFSET) != ++ (prevout_w * 2)) { ++ prevout_w = ((prevout_w * 2) & ++ ISP_32B_BOUNDARY_OFFSET) / 2; ++ } ++ } ++ *output_w = prevout_w; ++ ispprev_obj.prevout_w = prevout_w; ++ *output_h = prevout_h; ++ ispprev_obj.prevout_h = prevout_h; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_try_size); ++ ++/** ++ * isppreview_config_size - Sets the size of ISP preview output. ++ * @input_w: input width for the preview in number of pixels per line ++ * @input_h: input height for the preview in number of lines ++ * @output_w: output width from the preview in number of pixels per line ++ * @output_h: output height for the preview in number of lines ++ * ++ * Configures the appropriate values stored in the isp_prev structure to ++ * HORZ/VERT_INFO. Configures PRV_AVE if needed for downsampling as calculated ++ * in trysize. ++ **/ ++int isppreview_config_size(u32 input_w, u32 input_h, u32 output_w, u32 output_h) ++{ ++ u32 prevsdroff; ++ ++ if ((output_w != ispprev_obj.prevout_w) || ++ (output_h != ispprev_obj.prevout_h)) { ++ printk(KERN_ERR "ISP_ERR : isppreview_try_size should " ++ "be called before config size\n"); ++ return -EINVAL; ++ } ++ ++ isp_reg_writel((ispprev_obj.sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | ++ (ispprev_obj.previn_w - 1), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); ++ isp_reg_writel((ispprev_obj.slv << ISPPRV_VERT_INFO_SLV_SHIFT) | ++ (ispprev_obj.previn_h - 2), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO); ++ ++ if (ispprev_obj.cfafmt == CFAFMT_BAYER) ++ isp_reg_writel(ISPPRV_AVE_EVENDIST_2 << ++ ISPPRV_AVE_EVENDIST_SHIFT | ++ ISPPRV_AVE_ODDDIST_2 << ++ ISPPRV_AVE_ODDDIST_SHIFT | ++ ispprev_obj.fmtavg, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); ++ ++ if (ispprev_obj.prev_outfmt == PREVIEW_MEM) { ++ prevsdroff = ispprev_obj.prevout_w * 2; ++ if ((prevsdroff & ISP_32B_BOUNDARY_OFFSET) != prevsdroff) { ++ DPRINTK_ISPPREV("ISP_WARN: Preview output buffer line" ++ " size is truncated" ++ " to 32byte boundary\n"); ++ prevsdroff &= ISP_32B_BOUNDARY_BUF ; ++ } ++ isppreview_config_outlineoffset(prevsdroff); ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_config_size); ++ ++/** ++ * isppreview_config_inlineoffset - Configures the Read address line offset. ++ * @offset: Line Offset for the input image. ++ **/ ++int isppreview_config_inlineoffset(u32 offset) ++{ ++ if ((offset & ISP_32B_BOUNDARY_OFFSET) == offset) { ++ isp_reg_writel(offset & 0xffff, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_RADR_OFFSET); ++ } else { ++ printk(KERN_ERR "ISP_ERR : Offset should be in 32 byte " ++ "boundary\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_config_inlineoffset); ++ ++/** ++ * isppreview_set_inaddr - Sets memory address of input frame. ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * ++ * Configures the memory address from which the input frame is to be read. ++ **/ ++int isppreview_set_inaddr(u32 addr) ++{ ++ if ((addr & ISP_32B_BOUNDARY_BUF) == addr) ++ isp_reg_writel(addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR); ++ else { ++ printk(KERN_ERR "ISP_ERR: Address should be in 32 byte " ++ "boundary\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_set_inaddr); ++ ++/** ++ * isppreview_config_outlineoffset - Configures the Write address line offset. ++ * @offset: Line Offset for the preview output. ++ **/ ++int isppreview_config_outlineoffset(u32 offset) ++{ ++ if ((offset & ISP_32B_BOUNDARY_OFFSET) != offset) { ++ printk(KERN_ERR "ISP_ERR : Offset should be in 32 byte " ++ "boundary\n"); ++ return -EINVAL; ++ } ++ isp_reg_writel(offset & 0xffff, OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_WADD_OFFSET); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_config_outlineoffset); ++ ++/** ++ * isppreview_set_outaddr - Sets the memory address to store output frame ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * ++ * Configures the memory address to which the output frame is written. ++ **/ ++int isppreview_set_outaddr(u32 addr) ++{ ++ if ((addr & ISP_32B_BOUNDARY_BUF) != addr) { ++ printk(KERN_ERR "ISP_ERR: Address should be in 32 byte " ++ "boundary\n"); ++ return -EINVAL; ++ } ++ isp_reg_writel(addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_set_outaddr); ++ ++/** ++ * isppreview_config_darklineoffset - Sets the Dark frame address line offset. ++ * @offset: Line Offset for the Darkframe. ++ **/ ++int isppreview_config_darklineoffset(u32 offset) ++{ ++ if ((offset & ISP_32B_BOUNDARY_OFFSET) != offset) { ++ printk(KERN_ERR "ISP_ERR : Offset should be in 32 byte " ++ "boundary\n"); ++ return -EINVAL; ++ } ++ isp_reg_writel(offset & 0xffff, OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_DRKF_OFFSET); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_config_darklineoffset); ++ ++/** ++ * isppreview_set_darkaddr - Sets the memory address to store Dark frame. ++ * @addr: 32bit memory address aligned on 32 bit boundary. ++ **/ ++int isppreview_set_darkaddr(u32 addr) ++{ ++ if ((addr & ISP_32B_BOUNDARY_BUF) != addr) { ++ printk(KERN_ERR "ISP_ERR : Address should be in 32 byte " ++ "boundary\n"); ++ return -EINVAL; ++ } ++ isp_reg_writel(addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_DSDR_ADDR); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(isppreview_set_darkaddr); ++ ++void __isppreview_enable(int enable) ++{ ++ if (enable) ++ isp_reg_or(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_EN); ++ else ++ isp_reg_and(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ~ISPPRV_PCR_EN); ++} ++ ++/** ++ * isppreview_enable - Enables the Preview module. ++ * @enable: 1 - Enables the preview module. ++ * ++ * Client should configure all the sub modules in Preview before this. ++ **/ ++void isppreview_enable(int enable) ++{ ++ __isppreview_enable(enable); ++ ispprev_obj.pm_state = enable; ++} ++EXPORT_SYMBOL_GPL(isppreview_enable); ++ ++/** ++ * isppreview_suspend - Suspend Preview module. ++ **/ ++void isppreview_suspend(void) ++{ ++ if (ispprev_obj.pm_state) ++ __isppreview_enable(0); ++} ++EXPORT_SYMBOL_GPL(isppreview_suspend); ++ ++/** ++ * isppreview_resume - Resume Preview module. ++ **/ ++void isppreview_resume(void) ++{ ++ if (ispprev_obj.pm_state) ++ __isppreview_enable(1); ++} ++EXPORT_SYMBOL_GPL(isppreview_resume); ++ ++ ++/** ++ * isppreview_busy - Gets busy state of preview module. ++ **/ ++int isppreview_busy(void) ++{ ++ return isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR) & ++ ISPPRV_PCR_BUSY; ++} ++EXPORT_SYMBOL_GPL(isppreview_busy); ++ ++/** ++ * isppreview_get_config - Gets parameters of preview module. ++ **/ ++struct prev_params *isppreview_get_config(void) ++{ ++ return prev_config_params; ++} ++EXPORT_SYMBOL_GPL(isppreview_get_config); ++ ++/** ++ * isppreview_save_context - Saves the values of the preview module registers. ++ **/ ++void isppreview_save_context(void) ++{ ++ DPRINTK_ISPPREV("Saving context\n"); ++ isp_save_context(ispprev_reg_list); ++} ++EXPORT_SYMBOL_GPL(isppreview_save_context); ++ ++/** ++ * isppreview_restore_context - Restores the values of preview module registers ++ **/ ++void isppreview_restore_context(void) ++{ ++ DPRINTK_ISPPREV("Restoring context\n"); ++ isp_restore_context(ispprev_reg_list); ++} ++EXPORT_SYMBOL_GPL(isppreview_restore_context); ++ ++/** ++ * isppreview_print_status - Prints the values of the Preview Module registers. ++ * ++ * Also prints other debug information stored in the preview moduel. ++ **/ ++void isppreview_print_status(void) ++{ ++ DPRINTK_ISPPREV("Module in use =%d\n", ispprev_obj.prev_inuse); ++ DPRINTK_ISPPREV("Preview Input format =%d, Output Format =%d\n", ++ ispprev_obj.prev_inpfmt, ++ ispprev_obj.prev_outfmt); ++ DPRINTK_ISPPREV("Accepted Preview Input (width = %d,Height = %d)\n", ++ ispprev_obj.previn_w, ++ ispprev_obj.previn_h); ++ DPRINTK_ISPPREV("Accepted Preview Output (width = %d,Height = %d)\n", ++ ispprev_obj.prevout_w, ++ ispprev_obj.prevout_h); ++ DPRINTK_ISPPREV("###ISP_CTRL in preview =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); ++ DPRINTK_ISPPREV("###ISP_IRQ0ENABLE in preview =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE)); ++ DPRINTK_ISPPREV("###ISP_IRQ0STATUS in preview =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); ++ DPRINTK_ISPPREV("###PRV PCR =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR)); ++ DPRINTK_ISPPREV("###PRV HORZ_INFO =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO)); ++ DPRINTK_ISPPREV("###PRV VERT_INFO =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO)); ++ DPRINTK_ISPPREV("###PRV WSDR_ADDR =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR)); ++ DPRINTK_ISPPREV("###PRV WADD_OFFSET =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_WADD_OFFSET)); ++ DPRINTK_ISPPREV("###PRV AVE =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE)); ++ DPRINTK_ISPPREV("###PRV HMED =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED)); ++ DPRINTK_ISPPREV("###PRV NF =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_NF)); ++ DPRINTK_ISPPREV("###PRV WB_DGAIN =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN)); ++ DPRINTK_ISPPREV("###PRV WBGAIN =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN)); ++ DPRINTK_ISPPREV("###PRV WBSEL =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL)); ++ DPRINTK_ISPPREV("###PRV CFA =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA)); ++ DPRINTK_ISPPREV("###PRV BLKADJOFF =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF)); ++ DPRINTK_ISPPREV("###PRV RGB_MAT1 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1)); ++ DPRINTK_ISPPREV("###PRV RGB_MAT2 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2)); ++ DPRINTK_ISPPREV("###PRV RGB_MAT3 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3)); ++ DPRINTK_ISPPREV("###PRV RGB_MAT4 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4)); ++ DPRINTK_ISPPREV("###PRV RGB_MAT5 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5)); ++ DPRINTK_ISPPREV("###PRV RGB_OFF1 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1)); ++ DPRINTK_ISPPREV("###PRV RGB_OFF2 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2)); ++ DPRINTK_ISPPREV("###PRV CSC0 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0)); ++ DPRINTK_ISPPREV("###PRV CSC1 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1)); ++ DPRINTK_ISPPREV("###PRV CSC2 =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2)); ++ DPRINTK_ISPPREV("###PRV CSC_OFFSET =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET)); ++ DPRINTK_ISPPREV("###PRV CNT_BRT =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT)); ++ DPRINTK_ISPPREV("###PRV CSUP =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP)); ++ DPRINTK_ISPPREV("###PRV SETUP_YC =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC)); ++} ++EXPORT_SYMBOL_GPL(isppreview_print_status); ++ ++/** ++ * isp_preview_init - Module Initialization. ++ **/ ++int __init isp_preview_init(void) ++{ ++ int i = 0; ++ ++ prev_config_params = kmalloc(sizeof(*prev_config_params), GFP_KERNEL); ++ if (!prev_config_params) { ++ printk(KERN_ERR "Can't get memory for isp_preview params!\n"); ++ return -ENOMEM; ++ } ++ params = prev_config_params; ++ ++ ispprev_obj.prev_inuse = 0; ++ mutex_init(&ispprev_obj.ispprev_mutex); ++ ++ /* Init values */ ++ ispprev_obj.sph = 2; ++ ispprev_obj.slv = 0; ++ ispprev_obj.color = V4L2_COLORFX_NONE; ++ ispprev_obj.contrast = ISPPRV_CONTRAST_DEF; ++ params->contrast = ISPPRV_CONTRAST_DEF; ++ ispprev_obj.brightness = ISPPRV_BRIGHT_DEF; ++ params->brightness = ISPPRV_BRIGHT_DEF; ++ params->average = NO_AVE; ++ params->lens_shading_shift = 0; ++ params->pix_fmt = YCPOS_YCrYCb; ++ params->cfa.cfafmt = CFAFMT_BAYER; ++ params->cfa.cfa_table = cfa_coef_table; ++ params->cfa.cfa_gradthrs_horz = FLR_CFA_GRADTHRS_HORZ; ++ params->cfa.cfa_gradthrs_vert = FLR_CFA_GRADTHRS_VERT; ++ params->csup.gain = FLR_CSUP_GAIN; ++ params->csup.thres = FLR_CSUP_THRES; ++ params->csup.hypf_en = 0; ++ params->ytable = luma_enhance_table; ++ params->nf.spread = FLR_NF_STRGTH; ++ memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table)); ++ params->dcor.couplet_mode_en = 1; ++ for (i = 0; i < 4; i++) ++ params->dcor.detect_correct[i] = 0xE; ++ params->gtable.bluetable = bluegamma_table; ++ params->gtable.greentable = greengamma_table; ++ params->gtable.redtable = redgamma_table; ++ params->wbal.dgain = FLR_WBAL_DGAIN; ++ if (omap_rev() > OMAP3430_REV_ES1_0) { ++ params->wbal.coef0 = FLR_WBAL_COEF0_ES1; ++ params->wbal.coef1 = FLR_WBAL_COEF1_ES1; ++ params->wbal.coef2 = FLR_WBAL_COEF2_ES1; ++ params->wbal.coef3 = FLR_WBAL_COEF3_ES1; ++ } else { ++ params->wbal.coef0 = FLR_WBAL_COEF0; ++ params->wbal.coef1 = FLR_WBAL_COEF1; ++ params->wbal.coef2 = FLR_WBAL_COEF2; ++ params->wbal.coef3 = FLR_WBAL_COEF3; ++ } ++ params->blk_adj.red = FLR_BLKADJ_RED; ++ params->blk_adj.green = FLR_BLKADJ_GREEN; ++ params->blk_adj.blue = FLR_BLKADJ_BLUE; ++ params->rgb2rgb = flr_rgb2rgb; ++ params->rgb2ycbcr = flr_prev_csc[ispprev_obj.color]; ++ ++ params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER; ++ params->features &= ~(PREV_AVERAGER | PREV_INVERSE_ALAW | ++ PREV_HORZ_MEDIAN_FILTER | ++ PREV_GAMMA_BYPASS | ++ PREV_DARK_FRAME_SUBTRACT | ++ PREV_LENS_SHADING | ++ PREV_DARK_FRAME_CAPTURE | ++ PREV_CHROMA_SUPPRESS | ++ PREV_LUMA_ENHANCE); ++ return 0; ++} ++ ++/** ++ * isp_preview_cleanup - Module Cleanup. ++ **/ ++void isp_preview_cleanup(void) ++{ ++ kfree(prev_config_params); ++} +diff --git a/drivers/media/video/isp/isppreview.h b/drivers/media/video/isp/isppreview.h +new file mode 100644 +index 0000000..e88c329 +--- /dev/null ++++ b/drivers/media/video/isp/isppreview.h +@@ -0,0 +1,354 @@ ++/* ++ * isppreview.h ++ * ++ * Driver header file for Preview module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Senthilvadivu Guruswamy <svadivu@ti.com> ++ * Pallavi Kulkarni <p-kulkarni@ti.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_PREVIEW_H ++#define OMAP_ISP_PREVIEW_H ++ ++#include <mach/isp_user.h> ++/* Isp query control structure */ ++ ++#define ISPPRV_BRIGHT_STEP 0x1 ++#define ISPPRV_BRIGHT_DEF 0x0 ++#define ISPPRV_BRIGHT_LOW 0x0 ++#define ISPPRV_BRIGHT_HIGH 0xF ++#define ISPPRV_BRIGHT_UNITS 0x7 ++ ++#define ISPPRV_CONTRAST_STEP 0x1 ++#define ISPPRV_CONTRAST_DEF 0x4 ++#define ISPPRV_CONTRAST_LOW 0x0 ++#define ISPPRV_CONTRAST_HIGH 0xF ++#define ISPPRV_CONTRAST_UNITS 0x4 ++ ++#define NO_AVE 0x0 ++#define AVE_2_PIX 0x1 ++#define AVE_4_PIX 0x2 ++#define AVE_8_PIX 0x3 ++#define AVE_ODD_PIXEL_DIST (1 << 4) /* For Bayer Sensors */ ++#define AVE_EVEN_PIXEL_DIST (1 << 2) ++ ++#define WB_GAIN_MAX 4 ++ ++/* Features list */ ++#define PREV_AVERAGER (1 << 0) ++#define PREV_INVERSE_ALAW (1 << 1) ++#define PREV_HORZ_MEDIAN_FILTER (1 << 2) ++#define PREV_NOISE_FILTER (1 << 3) ++#define PREV_CFA (1 << 4) ++#define PREV_GAMMA_BYPASS (1 << 5) ++#define PREV_LUMA_ENHANCE (1 << 6) ++#define PREV_CHROMA_SUPPRESS (1 << 7) ++#define PREV_DARK_FRAME_SUBTRACT (1 << 8) ++#define PREV_LENS_SHADING (1 << 9) ++#define PREV_DARK_FRAME_CAPTURE (1 << 10) ++#define PREV_DEFECT_COR (1 << 11) ++ ++ ++#define ISP_NF_TABLE_SIZE (1 << 10) ++ ++#define ISP_GAMMA_TABLE_SIZE (1 << 10) ++ ++/* Table addresses */ ++#define ISPPRV_TBL_ADDR_RED_G_START 0x00 ++#define ISPPRV_TBL_ADDR_BLUE_G_START 0x800 ++#define ISPPRV_TBL_ADDR_GREEN_G_START 0x400 ++ ++/* ++ *Enumeration Constants for input and output format ++ */ ++enum preview_input { ++ PRV_RAW_CCDC, ++ PRV_RAW_MEM, ++ PRV_RGBBAYERCFA, ++ PRV_COMPCFA, ++ PRV_CCDC_DRKF, ++ PRV_OTHERS ++}; ++enum preview_output { ++ PREVIEW_RSZ, ++ PREVIEW_MEM ++}; ++/* ++ * Configure byte layout of YUV image ++ */ ++enum preview_ycpos_mode { ++ YCPOS_YCrYCb = 0, ++ YCPOS_YCbYCr = 1, ++ YCPOS_CbYCrY = 2, ++ YCPOS_CrYCbY = 3 ++}; ++ ++/** ++ * struct ispprev_gtable - Structure for Gamma Correction. ++ * @redtable: Pointer to the red gamma table. ++ * @greentable: Pointer to the green gamma table. ++ * @bluetable: Pointer to the blue gamma table. ++ */ ++struct ispprev_gtable { ++ u32 *redtable; ++ u32 *greentable; ++ u32 *bluetable; ++}; ++ ++/** ++ * struct prev_white_balance - Structure for White Balance 2. ++ * @wb_dgain: White balance common gain. ++ * @wb_gain: Individual color gains. ++ * @wb_coefmatrix: Coefficient matrix ++ */ ++struct prev_white_balance { ++ u16 wb_dgain; /* white balance common gain */ ++ u8 wb_gain[WB_GAIN_MAX]; /* individual color gains */ ++ u8 wb_coefmatrix[WB_GAIN_MAX][WB_GAIN_MAX]; ++}; ++ ++/** ++ * struct prev_size_params - Structure for size parameters. ++ * @hstart: Starting pixel. ++ * @vstart: Starting line. ++ * @hsize: Width of input image. ++ * @vsize: Height of input image. ++ * @pixsize: Pixel size of the image in terms of bits. ++ * @in_pitch: Line offset of input image. ++ * @out_pitch: Line offset of output image. ++ */ ++struct prev_size_params { ++ unsigned int hstart; ++ unsigned int vstart; ++ unsigned int hsize; ++ unsigned int vsize; ++ unsigned char pixsize; ++ unsigned short in_pitch; ++ unsigned short out_pitch; ++}; ++ ++/** ++ * struct prev_rgb2ycbcr_coeffs - Structure RGB2YCbCr parameters. ++ * @coeff: Color conversion gains in 3x3 matrix. ++ * @offset: Color conversion offsets. ++ */ ++struct prev_rgb2ycbcr_coeffs { ++ short coeff[RGB_MAX][RGB_MAX]; ++ short offset[RGB_MAX]; ++}; ++ ++/** ++ * struct prev_darkfrm_params - Structure for Dark frame suppression. ++ * @addr: Memory start address. ++ * @offset: Line offset. ++ */ ++struct prev_darkfrm_params { ++ u32 addr; ++ u32 offset; ++ }; ++ ++/** ++ * struct prev_params - Structure for all configuration ++ * @features: Set of features enabled. ++ * @pix_fmt: Output pixel format. ++ * @cfa: CFA coefficients. ++ * @csup: Chroma suppression coefficients. ++ * @ytable: Pointer to Luma enhancement coefficients. ++ * @nf: Noise filter coefficients. ++ * @dcor: Noise filter coefficients. ++ * @gtable: Gamma coefficients. ++ * @wbal: White Balance parameters. ++ * @blk_adj: Black adjustment parameters. ++ * @rgb2rgb: RGB blending parameters. ++ * @rgb2ycbcr: RGB to ycbcr parameters. ++ * @hmf_params: Horizontal median filter. ++ * @size_params: Size parameters. ++ * @drkf_params: Darkframe parameters. ++ * @lens_shading_shift: ++ * @average: Downsampling rate for averager. ++ * @contrast: Contrast. ++ * @brightness: Brightness. ++ */ ++struct prev_params { ++ u16 features; ++ enum preview_ycpos_mode pix_fmt; ++ struct ispprev_cfa cfa; ++ struct ispprev_csup csup; ++ u32 *ytable; ++ struct ispprev_nf nf; ++ struct ispprev_dcor dcor; ++ struct ispprev_gtable gtable; ++ struct ispprev_wbal wbal; ++ struct ispprev_blkadj blk_adj; ++ struct ispprev_rgbtorgb rgb2rgb; ++ struct ispprev_csc rgb2ycbcr; ++ struct ispprev_hmed hmf_params; ++ struct prev_size_params size_params; ++ struct prev_darkfrm_params drkf_params; ++ u8 lens_shading_shift; ++ u8 average; ++ u8 contrast; ++ u8 brightness; ++}; ++ ++/** ++ * struct isptables_update - Structure for Table Configuration. ++ * @update: Specifies which tables should be updated. ++ * @flag: Specifies which tables should be enabled. ++ * @prev_nf: Pointer to structure for Noise Filter ++ * @lsc: Pointer to LSC gain table. (currently not used) ++ * @red_gamma: Pointer to red gamma correction table. ++ * @green_gamma: Pointer to green gamma correction table. ++ * @blue_gamma: Pointer to blue gamma correction table. ++ */ ++struct isptables_update { ++ u16 update; ++ u16 flag; ++ struct ispprev_nf *prev_nf; ++ u32 *lsc; ++ u32 *red_gamma; ++ u32 *green_gamma; ++ u32 *blue_gamma; ++}; ++ ++void isppreview_config_shadow_registers(void); ++ ++int isppreview_request(void); ++ ++int isppreview_free(void); ++ ++int isppreview_config_datapath(enum preview_input input, ++ enum preview_output output); ++ ++void isppreview_config_ycpos(enum preview_ycpos_mode mode); ++ ++void isppreview_config_averager(u8 average); ++ ++void isppreview_enable_invalaw(u8 enable); ++ ++void isppreview_enable_drkframe(u8 enable); ++ ++void isppreview_enable_shadcomp(u8 enable); ++ ++void isppreview_config_drkf_shadcomp(u8 scomp_shtval); ++ ++void isppreview_enable_gammabypass(u8 enable); ++ ++void isppreview_enable_hmed(u8 enable); ++ ++void isppreview_config_hmed(struct ispprev_hmed); ++ ++void isppreview_enable_noisefilter(u8 enable); ++ ++void isppreview_config_noisefilter(struct ispprev_nf prev_nf); ++ ++void isppreview_enable_dcor(u8 enable); ++ ++void isppreview_config_dcor(struct ispprev_dcor prev_dcor); ++ ++ ++void isppreview_config_cfa(struct ispprev_cfa); ++ ++void isppreview_config_gammacorrn(struct ispprev_gtable); ++ ++void isppreview_config_chroma_suppression(struct ispprev_csup csup); ++ ++void isppreview_enable_cfa(u8 enable); ++ ++void isppreview_config_luma_enhancement(u32 *ytable); ++ ++void isppreview_enable_luma_enhancement(u8 enable); ++ ++void isppreview_enable_chroma_suppression(u8 enable); ++ ++void isppreview_config_whitebalance(struct ispprev_wbal); ++ ++void isppreview_config_blkadj(struct ispprev_blkadj); ++ ++void isppreview_config_rgb_blending(struct ispprev_rgbtorgb); ++ ++void isppreview_config_rgb_to_ycbcr(struct ispprev_csc); ++ ++void isppreview_update_contrast(u8 *contrast); ++ ++void isppreview_query_contrast(u8 *contrast); ++ ++void isppreview_config_contrast(u8 contrast); ++ ++void isppreview_get_contrast_range(u8 *min_contrast, u8 *max_contrast); ++ ++void isppreview_update_brightness(u8 *brightness); ++ ++void isppreview_config_brightness(u8 brightness); ++ ++void isppreview_get_brightness_range(u8 *min_brightness, u8 *max_brightness); ++ ++void isppreview_set_color(u8 *mode); ++ ++void isppreview_get_color(u8 *mode); ++ ++void isppreview_query_brightness(u8 *brightness); ++ ++void isppreview_config_yc_range(struct ispprev_yclimit yclimit); ++ ++int isppreview_try_size(u32 input_w, u32 input_h, u32 *output_w, ++ u32 *output_h); ++ ++int isppreview_config_size(u32 input_w, u32 input_h, u32 output_w, ++ u32 output_h); ++ ++int isppreview_config_inlineoffset(u32 offset); ++ ++int isppreview_set_inaddr(u32 addr); ++ ++int isppreview_config_outlineoffset(u32 offset); ++ ++int isppreview_set_outaddr(u32 addr); ++ ++int isppreview_config_darklineoffset(u32 offset); ++ ++int isppreview_set_darkaddr(u32 addr); ++ ++void isppreview_enable(int enable); ++ ++void isppreview_suspend(void); ++ ++void isppreview_resume(void); ++ ++int isppreview_busy(void); ++ ++struct prev_params *isppreview_get_config(void); ++ ++void isppreview_print_status(void); ++ ++#ifndef CONFIG_ARCH_OMAP3410 ++void isppreview_save_context(void); ++#else ++static inline void isppreview_save_context(void) {} ++#endif ++ ++#ifndef CONFIG_ARCH_OMAP3410 ++void isppreview_restore_context(void); ++#else ++static inline void isppreview_restore_context(void) {} ++#endif ++ ++int omap34xx_isp_preview_config(void *userspace_add); ++ ++int omap34xx_isp_tables_update(struct isptables_update *isptables_struct); ++ ++void isppreview_set_skip(u32 h, u32 v); ++ ++#endif/* OMAP_ISP_PREVIEW_H */ +diff --git a/drivers/media/video/isp/ispresizer.c b/drivers/media/video/isp/ispresizer.c +new file mode 100644 +index 0000000..f78ddb3 +--- /dev/null ++++ b/drivers/media/video/isp/ispresizer.c +@@ -0,0 +1,928 @@ ++/* ++ * ispresizer.c ++ * ++ * Driver Library for Resizer module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C)2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sameer Venkatraman <sameerv@ti.com> ++ * Mohit Jalori ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <linux/module.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispresizer.h" ++ ++/* Default configuration of resizer,filter coefficients,yenh for camera isp */ ++static struct isprsz_yenh ispreszdefaultyenh = {0, 0, 0, 0}; ++static struct isprsz_coef ispreszdefcoef = { ++ { ++ 0x0027, 0x00B2, 0x00B2, 0x0027, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ 0x0027, 0x00B2, 0x0027, 0x00B2, ++ }, ++ { ++ 0x0000, 0x0100, 0x0000, 0x0000, ++ 0x03FA, 0x00F6, 0x0010, 0x0000, ++ 0x03F9, 0x00DB, 0x002C, 0x0000, ++ 0x03FB, 0x00B3, 0x0053, 0x03FF, ++ 0x03FD, 0x0082, 0x0084, 0x03FD, ++ 0x03FF, 0x0053, 0x00B3, 0x03FB, ++ 0x0000, 0x002C, 0x00DB, 0x03F9, ++ 0x0000, 0x0010, 0x00F6, 0x03FA ++ }, ++ { ++ 0x0004, 0x0023, 0x0023, 0x005A, ++ 0x005A, 0x0058, 0x0058, 0x0004, ++ 0x0023, 0x0023, 0x005A, 0x005A, ++ 0x0058, 0x0058, 0x0004, 0x0023, ++ 0x0023, 0x005A, 0x005A, 0x0058, ++ 0x0058, 0x0004, 0x0023, 0x0023, ++ 0x005A, 0x005A, 0x0058, 0x0058 ++ }, ++ { ++ 0x0004, 0x0023, 0x005A, 0x0058, ++ 0x0023, 0x0004, 0x0000, 0x0002, ++ 0x0018, 0x004d, 0x0060, 0x0031, ++ 0x0008, 0x0000, 0x0001, 0x000f, ++ 0x003f, 0x0062, 0x003f, 0x000f, ++ 0x0001, 0x0000, 0x0008, 0x0031, ++ 0x0060, 0x004d, 0x0018, 0x0002 ++ } ++}; ++ ++/** ++ * struct isp_res - Structure for the resizer module to store its information. ++ * @res_inuse: Indicates if resizer module has been reserved. 1 - Reserved, ++ * 0 - Freed. ++ * @h_startphase: Horizontal starting phase. ++ * @v_startphase: Vertical starting phase. ++ * @h_resz: Horizontal resizing value. ++ * @v_resz: Vertical resizing value. ++ * @outputwidth: Output Image Width in pixels. ++ * @outputheight: Output Image Height in pixels. ++ * @inputwidth: Input Image Width in pixels. ++ * @inputheight: Input Image Height in pixels. ++ * @algo: Algorithm select. 0 - Disable, 1 - [-1 2 -1]/2 high-pass filter, ++ * 2 - [-1 -2 6 -2 -1]/4 high-pass filter. ++ * @ipht_crop: Vertical start line for cropping. ++ * @ipwd_crop: Horizontal start pixel for cropping. ++ * @cropwidth: Crop Width. ++ * @cropheight: Crop Height. ++ * @resinput: Resizer input. ++ * @coeflist: Register configuration for Resizer. ++ * @ispres_mutex: Mutex for isp resizer. ++ */ ++static struct isp_res { ++ int pm_state; ++ u8 res_inuse; ++ u8 h_startphase; ++ u8 v_startphase; ++ u16 h_resz; ++ u16 v_resz; ++ u32 outputwidth; ++ u32 outputheight; ++ u32 inputwidth; ++ u32 inputheight; ++ u8 algo; ++ u32 ipht_crop; ++ u32 ipwd_crop; ++ u32 cropwidth; ++ u32 cropheight; ++ dma_addr_t tmp_buf; ++ enum ispresizer_input resinput; ++ struct isprsz_coef coeflist; ++ struct mutex ispres_mutex; /* For checking/modifying res_inuse */ ++} ispres_obj; ++ ++/* Structure for saving/restoring resizer module registers */ ++static struct isp_reg isprsz_reg_list[] = { ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT10, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT32, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT54, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT76, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT98, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1110, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1312, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1514, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1716, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1918, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2120, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2322, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2524, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2726, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2928, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT3130, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT10, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT32, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT54, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT76, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT98, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1110, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1312, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1514, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1716, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1918, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2120, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2322, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2524, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2726, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2928, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT3130, 0x0000}, ++ {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH, 0x0000}, ++ {0, ISP_TOK_TERM, 0x0000} ++}; ++ ++/** ++ * ispresizer_config_shadow_registers - Configure shadow registers. ++ **/ ++void ispresizer_config_shadow_registers() ++{ ++ return; ++} ++EXPORT_SYMBOL(ispresizer_config_shadow_registers); ++ ++/** ++ * ispresizer_trycrop - Validate crop dimensions. ++ * @left: Left distance to start position of crop. ++ * @top: Top distance to start position of crop. ++ * @width: Width of input image. ++ * @height: Height of input image. ++ * @ow: Width of output image. ++ * @oh: Height of output image. ++ **/ ++void ispresizer_trycrop(u32 left, u32 top, u32 width, u32 height, u32 ow, ++ u32 oh) ++{ ++ ispres_obj.cropwidth = width + 6; ++ ispres_obj.cropheight = height + 6; ++ ispresizer_try_size(&ispres_obj.cropwidth, &ispres_obj.cropheight, &ow, ++ &oh); ++ ispres_obj.ipht_crop = top; ++ ispres_obj.ipwd_crop = left; ++} ++EXPORT_SYMBOL(ispresizer_trycrop); ++ ++/** ++ * ispresizer_applycrop - Apply crop to input image. ++ **/ ++void ispresizer_applycrop(void) ++{ ++ ispresizer_config_size(ispres_obj.cropwidth, ispres_obj.cropheight, ++ ispres_obj.outputwidth, ++ ispres_obj.outputheight); ++ return; ++} ++EXPORT_SYMBOL(ispresizer_applycrop); ++ ++/** ++ * ispresizer_request - Reserves the Resizer module. ++ * ++ * Allows only one user at a time. ++ * ++ * Returns 0 if successful, or -EBUSY if resizer module was already requested. ++ **/ ++int ispresizer_request() ++{ ++ mutex_lock(&ispres_obj.ispres_mutex); ++ if (!ispres_obj.res_inuse) { ++ ispres_obj.res_inuse = 1; ++ mutex_unlock(&ispres_obj.ispres_mutex); ++ isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL) | ++ ISPCTRL_SBL_WR0_RAM_EN | ++ ISPCTRL_RSZ_CLK_EN, ++ OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); ++ return 0; ++ } else { ++ mutex_unlock(&ispres_obj.ispres_mutex); ++ printk(KERN_ERR "ISP_ERR : Resizer Module Busy\n"); ++ return -EBUSY; ++ } ++} ++EXPORT_SYMBOL(ispresizer_request); ++ ++/** ++ * ispresizer_free - Makes Resizer module free. ++ * ++ * Returns 0 if successful, or -EINVAL if resizer module was already freed. ++ **/ ++int ispresizer_free() ++{ ++ mutex_lock(&ispres_obj.ispres_mutex); ++ if (ispres_obj.res_inuse) { ++ ispres_obj.res_inuse = 0; ++ mutex_unlock(&ispres_obj.ispres_mutex); ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ~(ISPCTRL_RSZ_CLK_EN | ISPCTRL_SBL_WR0_RAM_EN)); ++ return 0; ++ } else { ++ mutex_unlock(&ispres_obj.ispres_mutex); ++ DPRINTK_ISPRESZ("ISP_ERR : Resizer Module already freed\n"); ++ return -EINVAL; ++ } ++} ++EXPORT_SYMBOL(ispresizer_free); ++ ++/** ++ * ispresizer_config_datapath - Specifies which input to use in resizer module ++ * @input: Indicates the module that gives the image to resizer. ++ * ++ * Sets up the default resizer configuration according to the arguments. ++ * ++ * Returns 0 if successful, or -EINVAL if an unsupported input was requested. ++ **/ ++int ispresizer_config_datapath(enum ispresizer_input input) ++{ ++ u32 cnt = 0; ++ DPRINTK_ISPRESZ("ispresizer_config_datapath()+\n"); ++ ispres_obj.resinput = input; ++ switch (input) { ++ case RSZ_OTFLY_YUV: ++ cnt &= ~ISPRSZ_CNT_INPTYP; ++ cnt &= ~ISPRSZ_CNT_INPSRC; ++ ispresizer_set_inaddr(0); ++ ispresizer_config_inlineoffset(0); ++ break; ++ case RSZ_MEM_YUV: ++ cnt |= ISPRSZ_CNT_INPSRC; ++ cnt &= ~ISPRSZ_CNT_INPTYP; ++ break; ++ case RSZ_MEM_COL8: ++ cnt |= ISPRSZ_CNT_INPSRC; ++ cnt |= ISPRSZ_CNT_INPTYP; ++ break; ++ default: ++ printk(KERN_ERR "ISP_ERR : Wrong Input\n"); ++ return -EINVAL; ++ } ++ isp_reg_or(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, cnt); ++ ispresizer_config_ycpos(0); ++ ispresizer_config_filter_coef(&ispreszdefcoef); ++ ispresizer_enable_cbilin(0); ++ ispresizer_config_luma_enhance(&ispreszdefaultyenh); ++ DPRINTK_ISPRESZ("ispresizer_config_datapath()-\n"); ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_config_datapath); ++ ++/** ++ * ispresizer_try_size - Validates input and output images size. ++ * @input_w: input width for the resizer in number of pixels per line ++ * @input_h: input height for the resizer in number of lines ++ * @output_w: output width from the resizer in number of pixels per line ++ * resizer when writing to memory needs this to be multiple of 16. ++ * @output_h: output height for the resizer in number of lines, must be even. ++ * ++ * Calculates the horizontal and vertical resize ratio, number of pixels to ++ * be cropped in the resizer module and checks the validity of various ++ * parameters. Formula used for calculation is:- ++ * ++ * 8-phase 4-tap mode :- ++ * inputwidth = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 ++ * inputheight = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 ++ * endpahse for width = ((32 * sph + (ow - 1) * hrsz + 16) >> 5) % 8 ++ * endphase for height = ((32 * sph + (oh - 1) * hrsz + 16) >> 5) % 8 ++ * ++ * 4-phase 7-tap mode :- ++ * inputwidth = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 ++ * inputheight = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 ++ * endpahse for width = ((64 * sph + (ow - 1) * hrsz + 32) >> 6) % 4 ++ * endphase for height = ((64 * sph + (oh - 1) * hrsz + 32) >> 6) % 4 ++ * ++ * Where: ++ * sph = Start phase horizontal ++ * spv = Start phase vertical ++ * ow = Output width ++ * oh = Output height ++ * hrsz = Horizontal resize value ++ * vrsz = Vertical resize value ++ * ++ * Fills up the output/input widht/height, horizontal/vertical resize ratio, ++ * horizontal/vertical crop variables in the isp_res structure. ++ **/ ++int ispresizer_try_size(u32 *input_width, u32 *input_height, u32 *output_w, ++ u32 *output_h) ++{ ++ u32 rsz, rsz_7, rsz_4; ++ u32 sph; ++ u32 input_w, input_h; ++ int max_in_otf, max_out_7tap; ++ ++ input_w = *input_width; ++ input_h = *input_height; ++ ++ if (input_w < 32 || input_h < 32) { ++ DPRINTK_ISPCCDC("ISP_ERR: RESIZER cannot handle input width" ++ " less than 32 pixels or height less than" ++ " 32\n"); ++ return -EINVAL; ++ } ++ input_w -= 6; ++ input_h -= 6; ++ ++ if (input_h > MAX_IN_HEIGHT) ++ return -EINVAL; ++ ++ if (*output_w < 16) ++ *output_w = 16; ++ ++ if (*output_h < 2) ++ *output_h = 2; ++ ++ if (omap_rev() == OMAP3430_REV_ES1_0) { ++ max_in_otf = MAX_IN_WIDTH_ONTHEFLY_MODE; ++ max_out_7tap = MAX_7TAP_VRSZ_OUTWIDTH; ++ } else { ++ max_in_otf = MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; ++ max_out_7tap = MAX_7TAP_VRSZ_OUTWIDTH_ES2; ++ } ++ ++ if (ispres_obj.resinput == RSZ_OTFLY_YUV) { ++ if (input_w > max_in_otf) ++ return -EINVAL; ++ } else { ++ if (input_w > MAX_IN_WIDTH_MEMORY_MODE) ++ return -EINVAL; ++ } ++ ++ *output_h &= 0xfffffffe; ++ sph = DEFAULTSTPHASE; ++ ++ rsz_7 = ((input_h - 7) * 256) / (*output_h - 1); ++ rsz_4 = ((input_h - 4) * 256) / (*output_h - 1); ++ ++ rsz = (input_h * 256) / *output_h; ++ ++ if (rsz <= MID_RESIZE_VALUE) { ++ rsz = rsz_4; ++ if (rsz < MINIMUM_RESIZE_VALUE) { ++ rsz = MINIMUM_RESIZE_VALUE; ++ *output_h = (((input_h - 4) * 256) / rsz) + 1; ++ printk(KERN_INFO "%s: using output_h %d instead\n", ++ __func__, *output_h); ++ } ++ } else { ++ rsz = rsz_7; ++ if (*output_w > max_out_7tap) ++ *output_w = max_out_7tap; ++ if (rsz > MAXIMUM_RESIZE_VALUE) { ++ rsz = MAXIMUM_RESIZE_VALUE; ++ *output_h = (((input_h - 7) * 256) / rsz) + 1; ++ printk(KERN_INFO "%s: using output_h %d instead\n", ++ __func__, *output_h); ++ } ++ } ++ ++ if (rsz > MID_RESIZE_VALUE) { ++ input_h = ++ (((64 * sph) + ((*output_h - 1) * rsz) + 32) / 256) + 7; ++ } else { ++ input_h = ++ (((32 * sph) + ((*output_h - 1) * rsz) + 16) / 256) + 4; ++ } ++ ++ ispres_obj.outputheight = *output_h; ++ ispres_obj.v_resz = rsz; ++ ispres_obj.inputheight = input_h; ++ ispres_obj.ipht_crop = DEFAULTSTPIXEL; ++ ispres_obj.v_startphase = sph; ++ ++ *output_w &= 0xfffffff0; ++ sph = DEFAULTSTPHASE; ++ ++ rsz_7 = ((input_w - 7) * 256) / (*output_w - 1); ++ rsz_4 = ((input_w - 4) * 256) / (*output_w - 1); ++ ++ rsz = (input_w * 256) / *output_w; ++ if (rsz > MID_RESIZE_VALUE) { ++ rsz = rsz_7; ++ if (rsz > MAXIMUM_RESIZE_VALUE) { ++ rsz = MAXIMUM_RESIZE_VALUE; ++ *output_w = (((input_w - 7) * 256) / rsz) + 1; ++ *output_w = (*output_w + 0xf) & 0xfffffff0; ++ printk(KERN_INFO "%s: using output_w %d instead\n", ++ __func__, *output_w); ++ } ++ } else { ++ rsz = rsz_4; ++ if (rsz < MINIMUM_RESIZE_VALUE) { ++ rsz = MINIMUM_RESIZE_VALUE; ++ *output_w = (((input_w - 4) * 256) / rsz) + 1; ++ *output_w = (*output_w + 0xf) & 0xfffffff0; ++ printk(KERN_INFO "%s: using output_w %d instead\n", ++ __func__, *output_w); ++ } ++ } ++ ++ /* Recalculate input based on TRM equations */ ++ if (rsz > MID_RESIZE_VALUE) { ++ input_w = ++ (((64 * sph) + ((*output_w - 1) * rsz) + 32) / 256) + 7; ++ } else { ++ input_w = ++ (((32 * sph) + ((*output_w - 1) * rsz) + 16) / 256) + 7; ++ } ++ ++ ispres_obj.outputwidth = *output_w; ++ ispres_obj.h_resz = rsz; ++ ispres_obj.inputwidth = input_w; ++ ispres_obj.ipwd_crop = DEFAULTSTPIXEL; ++ ispres_obj.h_startphase = sph; ++ ++ *input_height = input_h; ++ *input_width = input_w; ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_try_size); ++ ++/** ++ * ispresizer_config_size - Configures input and output image size. ++ * @input_w: input width for the resizer in number of pixels per line. ++ * @input_h: input height for the resizer in number of lines. ++ * @output_w: output width from the resizer in number of pixels per line. ++ * @output_h: output height for the resizer in number of lines. ++ * ++ * Configures the appropriate values stored in the isp_res structure in the ++ * resizer registers. ++ * ++ * Returns 0 if successful, or -EINVAL if passed values haven't been verified ++ * with ispresizer_try_size() previously. ++ **/ ++int ispresizer_config_size(u32 input_w, u32 input_h, u32 output_w, ++ u32 output_h) ++{ ++ int i, j; ++ u32 res; ++ DPRINTK_ISPRESZ("ispresizer_config_size()+, input_w = %d,input_h =" ++ " %d, output_w = %d, output_h" ++ " = %d,hresz = %d,vresz = %d," ++ " hcrop = %d, vcrop = %d," ++ " hstph = %d, vstph = %d\n", ++ ispres_obj.inputwidth, ++ ispres_obj.inputheight, ++ ispres_obj.outputwidth, ++ ispres_obj.outputheight, ++ ispres_obj.h_resz, ++ ispres_obj.v_resz, ++ ispres_obj.ipwd_crop, ++ ispres_obj.ipht_crop, ++ ispres_obj.h_startphase, ++ ispres_obj.v_startphase); ++ if ((output_w != ispres_obj.outputwidth) ++ || (output_h != ispres_obj.outputheight)) { ++ printk(KERN_ERR "Output parameters passed do not match the" ++ " values calculated by the" ++ " trysize passed w %d, h %d" ++ " \n", output_w , output_h); ++ return -EINVAL; ++ } ++ ++ /* Set Resizer input address and offset adderss */ ++ ispresizer_config_inlineoffset(isp_reg_readl(OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_WADD_OFFSET)); ++ ++ res = isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & ++ ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); ++ isp_reg_writel(res | ++ (ispres_obj.h_startphase << ISPRSZ_CNT_HSTPH_SHIFT) | ++ (ispres_obj.v_startphase << ISPRSZ_CNT_VSTPH_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_CNT); ++ /* Set start address for cropping */ ++ isp_reg_writel(ispres_obj.tmp_buf + 2 * ++ (ispres_obj.ipht_crop * ispres_obj.inputwidth + ++ (ispres_obj.ipwd_crop & ~15)), ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); ++ ++ isp_reg_writel( ++ ((ispres_obj.ipwd_crop & 15) << ISPRSZ_IN_START_HORZ_ST_SHIFT) | ++ (0x00 << ISPRSZ_IN_START_VERT_ST_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); ++ ++ isp_reg_writel((0x00 << ISPRSZ_IN_START_HORZ_ST_SHIFT) | ++ (0x00 << ISPRSZ_IN_START_VERT_ST_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_IN_START); ++ ++ isp_reg_writel((ispres_obj.inputwidth << ISPRSZ_IN_SIZE_HORZ_SHIFT) | ++ (ispres_obj.inputheight << ++ ISPRSZ_IN_SIZE_VERT_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_IN_SIZE); ++ if (!ispres_obj.algo) { ++ isp_reg_writel((output_w << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | ++ (output_h << ISPRSZ_OUT_SIZE_VERT_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_OUT_SIZE); ++ } else { ++ isp_reg_writel(((output_w - 4) << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | ++ (output_h << ISPRSZ_OUT_SIZE_VERT_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_OUT_SIZE); ++ } ++ ++ res = isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & ++ ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); ++ isp_reg_writel(res | ++ ((ispres_obj.h_resz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) | ++ ((ispres_obj.v_resz - 1) << ISPRSZ_CNT_VRSZ_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_CNT); ++ if (ispres_obj.h_resz <= MID_RESIZE_VALUE) { ++ j = 0; ++ for (i = 0; i < 16; i++) { ++ isp_reg_writel( ++ (ispres_obj.coeflist.h_filter_coef_4tap[j] ++ << ISPRSZ_HFILT10_COEF0_SHIFT) | ++ (ispres_obj.coeflist.h_filter_coef_4tap[j + 1] ++ << ISPRSZ_HFILT10_COEF1_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_HFILT10 + (i * 0x04)); ++ j += 2; ++ } ++ } else { ++ j = 0; ++ for (i = 0; i < 16; i++) { ++ if ((i + 1) % 4 == 0) { ++ isp_reg_writel((ispres_obj.coeflist. ++ h_filter_coef_7tap[j] << ++ ISPRSZ_HFILT10_COEF0_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_HFILT10 + (i * 0x04)); ++ j += 1; ++ } else { ++ isp_reg_writel((ispres_obj.coeflist. ++ h_filter_coef_7tap[j] << ++ ISPRSZ_HFILT10_COEF0_SHIFT) | ++ (ispres_obj.coeflist. ++ h_filter_coef_7tap[j+1] << ++ ISPRSZ_HFILT10_COEF1_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_HFILT10 + (i * 0x04)); ++ j += 2; ++ } ++ } ++ } ++ if (ispres_obj.v_resz <= MID_RESIZE_VALUE) { ++ j = 0; ++ for (i = 0; i < 16; i++) { ++ isp_reg_writel((ispres_obj.coeflist. ++ v_filter_coef_4tap[j] << ++ ISPRSZ_VFILT10_COEF0_SHIFT) | ++ (ispres_obj.coeflist. ++ v_filter_coef_4tap[j + 1] << ++ ISPRSZ_VFILT10_COEF1_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_VFILT10 + (i * 0x04)); ++ j += 2; ++ } ++ } else { ++ j = 0; ++ for (i = 0; i < 16; i++) { ++ if ((i + 1) % 4 == 0) { ++ isp_reg_writel((ispres_obj.coeflist. ++ v_filter_coef_7tap[j] << ++ ISPRSZ_VFILT10_COEF0_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_VFILT10 + (i * 0x04)); ++ j += 1; ++ } else { ++ isp_reg_writel((ispres_obj.coeflist. ++ v_filter_coef_7tap[j] << ++ ISPRSZ_VFILT10_COEF0_SHIFT) | ++ (ispres_obj.coeflist. ++ v_filter_coef_7tap[j+1] << ++ ISPRSZ_VFILT10_COEF1_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_VFILT10 + (i * 0x04)); ++ j += 2; ++ } ++ } ++ } ++ ++ ispresizer_config_outlineoffset(output_w*2); ++ DPRINTK_ISPRESZ("ispresizer_config_size()-\n"); ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_config_size); ++ ++void __ispresizer_enable(int enable) ++{ ++ int val; ++ DPRINTK_ISPRESZ("+ispresizer_enable()+\n"); ++ if (enable) { ++ val = (isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & 0x2) | ++ ISPRSZ_PCR_ENABLE; ++ } else { ++ val = isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & ++ ~ISPRSZ_PCR_ENABLE; ++ } ++ isp_reg_writel(val, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR); ++ DPRINTK_ISPRESZ("+ispresizer_enable()-\n"); ++} ++ ++/** ++ * ispresizer_enable - Enables the resizer module. ++ * @enable: 1 - Enable, 0 - Disable ++ * ++ * Client should configure all the sub modules in resizer before this. ++ **/ ++void ispresizer_enable(int enable) ++{ ++ __ispresizer_enable(enable); ++ ispres_obj.pm_state = enable; ++} ++EXPORT_SYMBOL(ispresizer_enable); ++ ++/** ++ * ispresizer_suspend - Suspend resizer module. ++ **/ ++void ispresizer_suspend(void) ++{ ++ if (ispres_obj.pm_state) ++ __ispresizer_enable(0); ++} ++EXPORT_SYMBOL(ispresizer_suspend); ++ ++/** ++ * ispresizer_resume - Resume resizer module. ++ **/ ++void ispresizer_resume(void) ++{ ++ if (ispres_obj.pm_state) ++ __ispresizer_enable(1); ++} ++EXPORT_SYMBOL(ispresizer_resume); ++ ++/** ++ * ispresizer_busy - Checks if ISP resizer is busy. ++ * ++ * Returns busy field from ISPRSZ_PCR register. ++ **/ ++int ispresizer_busy(void) ++{ ++ return isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & ++ ISPPRV_PCR_BUSY; ++} ++EXPORT_SYMBOL(ispresizer_busy); ++ ++/** ++ * ispresizer_config_startphase - Sets the horizontal and vertical start phase. ++ * @hstartphase: horizontal start phase (0 - 7). ++ * @vstartphase: vertical startphase (0 - 7). ++ * ++ * This API just updates the isp_res struct. Actual register write happens in ++ * ispresizer_config_size. ++ **/ ++void ispresizer_config_startphase(u8 hstartphase, u8 vstartphase) ++{ ++ DPRINTK_ISPRESZ("ispresizer_config_startphase()+\n"); ++ ispres_obj.h_startphase = hstartphase; ++ ispres_obj.v_startphase = vstartphase; ++ DPRINTK_ISPRESZ("ispresizer_config_startphase()-\n"); ++} ++EXPORT_SYMBOL(ispresizer_config_startphase); ++ ++/** ++ * ispresizer_config_ycpos - Specifies if output should be in YC or CY format. ++ * @yc: 0 - YC format, 1 - CY format ++ **/ ++void ispresizer_config_ycpos(u8 yc) ++{ ++ DPRINTK_ISPRESZ("ispresizer_config_ycpos()+\n"); ++ isp_reg_and_or(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ~ISPRSZ_CNT_YCPOS, ++ (yc ? ISPRSZ_CNT_YCPOS : 0)); ++ DPRINTK_ISPRESZ("ispresizer_config_ycpos()-\n"); ++} ++EXPORT_SYMBOL(ispresizer_config_ycpos); ++ ++/** ++ * Sets the chrominance algorithm ++ * @cbilin: 0 - chrominance uses same processing as luminance, ++ * 1 - bilinear interpolation processing ++ **/ ++void ispresizer_enable_cbilin(u8 enable) ++{ ++ DPRINTK_ISPRESZ("ispresizer_enable_cbilin()+\n"); ++ isp_reg_and_or(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ~ISPRSZ_CNT_CBILIN, ++ (enable ? ISPRSZ_CNT_CBILIN : 0)); ++ DPRINTK_ISPRESZ("ispresizer_enable_cbilin()-\n"); ++} ++EXPORT_SYMBOL(ispresizer_enable_cbilin); ++ ++/** ++ * ispresizer_config_luma_enhance - Configures luminance enhancer parameters. ++ * @yenh: Pointer to structure containing desired values for core, slope, gain ++ * and algo parameters. ++ **/ ++void ispresizer_config_luma_enhance(struct isprsz_yenh *yenh) ++{ ++ DPRINTK_ISPRESZ("ispresizer_config_luma_enhance()+\n"); ++ ispres_obj.algo = yenh->algo; ++ isp_reg_writel((yenh->algo << ISPRSZ_YENH_ALGO_SHIFT) | ++ (yenh->gain << ISPRSZ_YENH_GAIN_SHIFT) | ++ (yenh->slope << ISPRSZ_YENH_SLOP_SHIFT) | ++ (yenh->coreoffset << ISPRSZ_YENH_CORE_SHIFT), ++ OMAP3_ISP_IOMEM_RESZ, ++ ISPRSZ_YENH); ++ DPRINTK_ISPRESZ("ispresizer_config_luma_enhance()-\n"); ++} ++EXPORT_SYMBOL(ispresizer_config_luma_enhance); ++ ++/** ++ * ispresizer_config_filter_coef - Sets filter coefficients for 4 & 7-tap mode. ++ * This API just updates the isp_res struct.Actual register write happens in ++ * ispresizer_config_size. ++ * @coef: Structure containing horizontal and vertical filter coefficients for ++ * both 4-tap and 7-tap mode. ++ **/ ++void ispresizer_config_filter_coef(struct isprsz_coef *coef) ++{ ++ int i; ++ DPRINTK_ISPRESZ("ispresizer_config_filter_coef()+\n"); ++ for (i = 0; i < 32; i++) { ++ ispres_obj.coeflist.h_filter_coef_4tap[i] = ++ coef->h_filter_coef_4tap[i]; ++ ispres_obj.coeflist.v_filter_coef_4tap[i] = ++ coef->v_filter_coef_4tap[i]; ++ } ++ for (i = 0; i < 28; i++) { ++ ispres_obj.coeflist.h_filter_coef_7tap[i] = ++ coef->h_filter_coef_7tap[i]; ++ ispres_obj.coeflist.v_filter_coef_7tap[i] = ++ coef->v_filter_coef_7tap[i]; ++ } ++ DPRINTK_ISPRESZ("ispresizer_config_filter_coef()-\n"); ++} ++EXPORT_SYMBOL(ispresizer_config_filter_coef); ++ ++/** ++ * ispresizer_config_inlineoffset - Configures the read address line offset. ++ * @offset: Line Offset for the input image. ++ * ++ * Returns 0 if successful, or -EINVAL if offset is not 32 bits aligned. ++ **/ ++int ispresizer_config_inlineoffset(u32 offset) ++{ ++ DPRINTK_ISPRESZ("ispresizer_config_inlineoffset()+\n"); ++ if (offset % 32) ++ return -EINVAL; ++ isp_reg_writel(offset << ISPRSZ_SDR_INOFF_OFFSET_SHIFT, ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); ++ DPRINTK_ISPRESZ("ispresizer_config_inlineoffset()-\n"); ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_config_inlineoffset); ++ ++/** ++ * ispresizer_set_inaddr - Sets the memory address of the input frame. ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * ++ * Returns 0 if successful, or -EINVAL if address is not 32 bits aligned. ++ **/ ++int ispresizer_set_inaddr(u32 addr) ++{ ++ DPRINTK_ISPRESZ("ispresizer_set_inaddr()+\n"); ++ if (addr % 32) ++ return -EINVAL; ++ isp_reg_writel(addr << ISPRSZ_SDR_INADD_ADDR_SHIFT, ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); ++ ispres_obj.tmp_buf = addr; ++ DPRINTK_ISPRESZ("ispresizer_set_inaddr()-\n"); ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_set_inaddr); ++ ++/** ++ * ispresizer_config_outlineoffset - Configures the write address line offset. ++ * @offset: Line offset for the preview output. ++ * ++ * Returns 0 if successful, or -EINVAL if address is not 32 bits aligned. ++ **/ ++int ispresizer_config_outlineoffset(u32 offset) ++{ ++ DPRINTK_ISPRESZ("ispresizer_config_outlineoffset()+\n"); ++ if (offset % 32) ++ return -EINVAL; ++ isp_reg_writel(offset << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT, ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); ++ DPRINTK_ISPRESZ("ispresizer_config_outlineoffset()-\n"); ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_config_outlineoffset); ++ ++/** ++ * Configures the memory address to which the output frame is written. ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ **/ ++int ispresizer_set_outaddr(u32 addr) ++{ ++ DPRINTK_ISPRESZ("ispresizer_set_outaddr()+\n"); ++ if (addr % 32) ++ return -EINVAL; ++ isp_reg_writel(addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); ++ DPRINTK_ISPRESZ("ispresizer_set_outaddr()-\n"); ++ return 0; ++} ++EXPORT_SYMBOL(ispresizer_set_outaddr); ++ ++/** ++ * ispresizer_save_context - Saves the values of the resizer module registers. ++ **/ ++void ispresizer_save_context(void) ++{ ++ DPRINTK_ISPRESZ("Saving context\n"); ++ isp_save_context(isprsz_reg_list); ++} ++EXPORT_SYMBOL(ispresizer_save_context); ++ ++/** ++ * ispresizer_restore_context - Restores resizer module register values. ++ **/ ++void ispresizer_restore_context(void) ++{ ++ DPRINTK_ISPRESZ("Restoring context\n"); ++ isp_restore_context(isprsz_reg_list); ++} ++EXPORT_SYMBOL(ispresizer_restore_context); ++ ++/** ++ * ispresizer_print_status - Prints the values of the resizer module registers. ++ **/ ++void ispresizer_print_status() ++{ ++ if (!is_ispresz_debug_enabled()) ++ return; ++ DPRINTK_ISPRESZ("###ISP_CTRL inresizer =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); ++ DPRINTK_ISPRESZ("###ISP_IRQ0ENABLE in resizer =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE)); ++ DPRINTK_ISPRESZ("###ISP_IRQ0STATUS in resizer =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); ++ DPRINTK_ISPRESZ("###RSZ PCR =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR)); ++ DPRINTK_ISPRESZ("###RSZ CNT =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT)); ++ DPRINTK_ISPRESZ("###RSZ OUT SIZE =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE)); ++ DPRINTK_ISPRESZ("###RSZ IN START =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START)); ++ DPRINTK_ISPRESZ("###RSZ IN SIZE =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE)); ++ DPRINTK_ISPRESZ("###RSZ SDR INADD =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD)); ++ DPRINTK_ISPRESZ("###RSZ SDR INOFF =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF)); ++ DPRINTK_ISPRESZ("###RSZ SDR OUTADD =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD)); ++ DPRINTK_ISPRESZ("###RSZ SDR OTOFF =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF)); ++ DPRINTK_ISPRESZ("###RSZ YENH =0x%x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH)); ++} ++EXPORT_SYMBOL(ispresizer_print_status); ++ ++/** ++ * isp_resizer_init - Module Initialisation. ++ * ++ * Always returns 0. ++ **/ ++int __init isp_resizer_init(void) ++{ ++ mutex_init(&ispres_obj.ispres_mutex); ++ ispres_obj.pm_state = 0; ++ return 0; ++} ++ ++/** ++ * isp_resizer_cleanup - Module Cleanup. ++ **/ ++void isp_resizer_cleanup(void) ++{ ++} +diff --git a/drivers/media/video/isp/ispresizer.h b/drivers/media/video/isp/ispresizer.h +new file mode 100644 +index 0000000..4e92225 +--- /dev/null ++++ b/drivers/media/video/isp/ispresizer.h +@@ -0,0 +1,158 @@ ++/* ++ * ispresizer.h ++ * ++ * Driver header file for Resizer module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sameer Venkatraman <sameerv@ti.com> ++ * Mohit Jalori ++ * Sergio Aguirre <saaguirre@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_RESIZER_H ++#define OMAP_ISP_RESIZER_H ++ ++/* ++ * Resizer Constants ++ */ ++#define MAX_IN_WIDTH_MEMORY_MODE 4095 ++ ++#define MAX_IN_WIDTH_ONTHEFLY_MODE 1280 ++#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095 ++#define MAX_IN_HEIGHT 4095 ++#define MINIMUM_RESIZE_VALUE 64 ++#define MAXIMUM_RESIZE_VALUE 1024 ++#define MID_RESIZE_VALUE 512 ++ ++#define MAX_7TAP_HRSZ_OUTWIDTH 1280 ++#define MAX_7TAP_VRSZ_OUTWIDTH 640 ++ ++#define MAX_7TAP_HRSZ_OUTWIDTH_ES2 3300 ++#define MAX_7TAP_VRSZ_OUTWIDTH_ES2 1650 ++ ++#define DEFAULTSTPIXEL 0 ++#define DEFAULTSTPHASE 1 ++#define DEFAULTHSTPIXEL4TAPMODE 3 ++#define FOURPHASE 4 ++#define EIGHTPHASE 8 ++#define RESIZECONSTANT 256 ++#define SHIFTER4TAPMODE 0 ++#define SHIFTER7TAPMODE 1 ++#define DEFAULTOFFSET 7 ++#define OFFSETVERT4TAPMODE 4 ++#define OPWDALIGNCONSTANT 0xfffffff0 ++ ++/* ++ * The client is supposed to call resizer API in the following sequence: ++ * - request() ++ * - config_datatpath() ++ * - optionally config/enable sub modules ++ * - try/config size ++ * - setup callback ++ * - setup in/out memory offsets and ptrs ++ * - enable() ++ * ... ++ * - disable() ++ * - free() ++ */ ++ ++enum ispresizer_input { ++ RSZ_OTFLY_YUV, ++ RSZ_MEM_YUV, ++ RSZ_MEM_COL8 ++}; ++ ++/** ++ * struct isprsz_coef - Structure for resizer filter coeffcients. ++ * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap ++ * mode (.5x-4x) ++ * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap ++ * mode (.5x-4x) ++ * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap ++ * mode (.25x-.5x) ++ * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap ++ * mode (.25x-.5x) ++ */ ++struct isprsz_coef { ++ u16 h_filter_coef_4tap[32]; ++ u16 v_filter_coef_4tap[32]; ++ u16 h_filter_coef_7tap[28]; ++ u16 v_filter_coef_7tap[28]; ++}; ++ ++/** ++ * struct isprsz_yenh - Structure for resizer luminance enhancer parameters. ++ * @algo: Algorithm select. ++ * @gain: Maximum gain. ++ * @slope: Slope. ++ * @coreoffset: Coring offset. ++ */ ++struct isprsz_yenh { ++ u8 algo; ++ u8 gain; ++ u8 slope; ++ u8 coreoffset; ++}; ++ ++void ispresizer_config_shadow_registers(void); ++ ++int ispresizer_request(void); ++ ++int ispresizer_free(void); ++ ++int ispresizer_config_datapath(enum ispresizer_input input); ++ ++void ispresizer_enable_cbilin(u8 enable); ++ ++void ispresizer_config_ycpos(u8 yc); ++ ++void ispresizer_config_startphase(u8 hstartphase, u8 vstartphase); ++ ++void ispresizer_config_filter_coef(struct isprsz_coef *coef); ++ ++void ispresizer_config_luma_enhance(struct isprsz_yenh *yenh); ++ ++int ispresizer_try_size(u32 *input_w, u32 *input_h, u32 *output_w, ++ u32 *output_h); ++ ++void ispresizer_applycrop(void); ++ ++void ispresizer_trycrop(u32 left, u32 top, u32 width, u32 height, u32 ow, ++ u32 oh); ++ ++int ispresizer_config_size(u32 input_w, u32 input_h, u32 output_w, ++ u32 output_h); ++ ++int ispresizer_config_inlineoffset(u32 offset); ++ ++int ispresizer_set_inaddr(u32 addr); ++ ++int ispresizer_config_outlineoffset(u32 offset); ++ ++int ispresizer_set_outaddr(u32 addr); ++ ++void ispresizer_enable(int enable); ++ ++void ispresizer_suspend(void); ++ ++void ispresizer_resume(void); ++ ++int ispresizer_busy(void); ++ ++void ispresizer_save_context(void); ++ ++void ispresizer_restore_context(void); ++ ++void ispresizer_print_status(void); ++ ++#endif /* OMAP_ISP_RESIZER_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0006-omap3isp-Add-statistics-collection-modules-H3A-and.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0006-omap3isp-Add-statistics-collection-modules-H3A-and.patch new file mode 100644 index 0000000000..876ce780f0 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0006-omap3isp-Add-statistics-collection-modules-H3A-and.patch @@ -0,0 +1,2741 @@ +From 9a39eab5ed1b70711c3b10de95cd90749293ef7a Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add statistics collection modules (H3A and HIST) + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/isp/isp_af.c | 784 +++++++++++++++++++++++++++++++ + drivers/media/video/isp/isp_af.h | 125 +++++ + drivers/media/video/isp/isph3a.c | 932 +++++++++++++++++++++++++++++++++++++ + drivers/media/video/isp/isph3a.h | 127 +++++ + drivers/media/video/isp/isphist.c | 608 ++++++++++++++++++++++++ + drivers/media/video/isp/isphist.h | 105 +++++ + 6 files changed, 2681 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/isp_af.c + create mode 100644 drivers/media/video/isp/isp_af.h + create mode 100644 drivers/media/video/isp/isph3a.c + create mode 100644 drivers/media/video/isp/isph3a.h + create mode 100644 drivers/media/video/isp/isphist.c + create mode 100644 drivers/media/video/isp/isphist.h + +diff --git a/drivers/media/video/isp/isp_af.c b/drivers/media/video/isp/isp_af.c +new file mode 100644 +index 0000000..a607b97 +--- /dev/null ++++ b/drivers/media/video/isp/isp_af.c +@@ -0,0 +1,784 @@ ++/* ++ * isp_af.c ++ * ++ * AF module for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* Linux specific include files */ ++#include <asm/cacheflush.h> ++ ++#include <linux/uaccess.h> ++#include <linux/dma-mapping.h> ++#include <asm/atomic.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "isph3a.h" ++#include "isp_af.h" ++#include "ispmmu.h" ++ ++/** ++ * struct isp_af_buffer - AF frame stats buffer. ++ * @virt_addr: Virtual address to mmap the buffer. ++ * @phy_addr: Physical address of the buffer. ++ * @addr_align: Virtual Address 32 bytes aligned. ++ * @ispmmu_addr: Address of the buffer mapped by the ISPMMU. ++ * @mmap_addr: Mapped memory area of buffer. For userspace access. ++ * @locked: 1 - Buffer locked from write. 0 - Buffer can be overwritten. ++ * @frame_num: Frame number from which the statistics are taken. ++ * @lens_position: Lens position currently set in the DW9710 Coil motor driver. ++ * @next: Pointer to link next buffer. ++ */ ++struct isp_af_buffer { ++ unsigned long virt_addr; ++ unsigned long phy_addr; ++ unsigned long addr_align; ++ unsigned long ispmmu_addr; ++ unsigned long mmap_addr; ++ ++ u8 locked; ++ u16 frame_num; ++ u32 config_counter; ++ struct isp_af_xtrastats xtrastats; ++ struct isp_af_buffer *next; ++}; ++ ++/** ++ * struct isp_af_status - AF status. ++ * @initialized: 1 - Buffers initialized. ++ * @update: 1 - Update registers. ++ * @stats_req: 1 - Future stats requested. ++ * @stats_done: 1 - Stats ready for user. ++ * @frame_req: Number of frame requested for statistics. ++ * @af_buff: Array of statistics buffers to access. ++ * @stats_buf_size: Statistics buffer size. ++ * @curr_cfg_buf_size: Current user configured stats buff size. ++ * @min_buf_size: Minimum statisitics buffer size. ++ * @frame_count: Frame Count. ++ * @stats_wait: Wait primitive for locking/unlocking the stats request. ++ * @buffer_lock: Spinlock for statistics buffers access. ++ */ ++static struct isp_af_status { ++ u8 initialized; ++ u8 update; ++ u8 stats_req; ++ u8 stats_done; ++ u16 frame_req; ++ ++ struct isp_af_buffer af_buff[H3A_MAX_BUFF]; ++ unsigned int stats_buf_size; ++ unsigned int min_buf_size; ++ unsigned int curr_cfg_buf_size; ++ ++ int pm_state; ++ u32 frame_count; ++ wait_queue_head_t stats_wait; ++ atomic_t config_counter; ++ spinlock_t buffer_lock; /* For stats buffers read/write sync */ ++} afstat; ++ ++struct af_device *af_dev_configptr; ++static struct isp_af_buffer *active_buff; ++static int af_major = -1; ++static int camnotify; ++ ++/** ++ * isp_af_setxtrastats - Receives extra statistics from prior frames. ++ * @xtrastats: Pointer to structure containing extra statistics fields like ++ * field count and timestamp of frame. ++ * ++ * Called from update_vbq in camera driver ++ **/ ++void isp_af_setxtrastats(struct isp_af_xtrastats *xtrastats, u8 updateflag) ++{ ++ int i, past_i; ++ ++ if (active_buff == NULL) ++ return; ++ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ if (afstat.af_buff[i].frame_num == active_buff->frame_num) ++ break; ++ } ++ ++ if (i == H3A_MAX_BUFF) ++ return; ++ ++ if (i == 0) { ++ if (afstat.af_buff[H3A_MAX_BUFF - 1].locked == 0) ++ past_i = H3A_MAX_BUFF - 1; ++ else ++ past_i = H3A_MAX_BUFF - 2; ++ } else if (i == 1) { ++ if (afstat.af_buff[0].locked == 0) ++ past_i = 0; ++ else ++ past_i = H3A_MAX_BUFF - 1; ++ } else { ++ if (afstat.af_buff[i - 1].locked == 0) ++ past_i = i - 1; ++ else ++ past_i = i - 2; ++ } ++ ++ if (updateflag & AF_UPDATEXS_TS) ++ afstat.af_buff[past_i].xtrastats.ts = xtrastats->ts; ++ ++ if (updateflag & AF_UPDATEXS_FIELDCOUNT) ++ afstat.af_buff[past_i].xtrastats.field_count = ++ xtrastats->field_count; ++} ++EXPORT_SYMBOL(isp_af_setxtrastats); ++ ++/* ++ * Helper function to update buffer cache pages ++ */ ++static void isp_af_update_req_buffer(struct isp_af_buffer *buffer) ++{ ++ int size = afstat.stats_buf_size; ++ ++ size = PAGE_ALIGN(size); ++ /* Update the kernel pages of the requested buffer */ ++ dmac_inv_range((void *)buffer->addr_align, (void *)buffer->addr_align + ++ size); ++} ++ ++#define IS_OUT_OF_BOUNDS(value, min, max) \ ++ (((value) < (min)) || ((value) > (max))) ++ ++/* Function to check paxel parameters */ ++int isp_af_check_paxel(void) ++{ ++ struct af_paxel *paxel_cfg = &af_dev_configptr->config->paxel_config; ++ struct af_iir *iir_cfg = &af_dev_configptr->config->iir_config; ++ ++ /* Check horizontal Count */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->hz_cnt, AF_PAXEL_HORIZONTAL_COUNT_MIN, ++ AF_PAXEL_HORIZONTAL_COUNT_MAX)) { ++ DPRINTK_ISP_AF("Error : Horizontal Count is incorrect"); ++ return -AF_ERR_HZ_COUNT; ++ } ++ ++ /*Check Vertical Count */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->vt_cnt, AF_PAXEL_VERTICAL_COUNT_MIN, ++ AF_PAXEL_VERTICAL_COUNT_MAX)) { ++ DPRINTK_ISP_AF("Error : Vertical Count is incorrect"); ++ return -AF_ERR_VT_COUNT; ++ } ++ ++ /*Check Height */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->height, AF_PAXEL_HEIGHT_MIN, ++ AF_PAXEL_HEIGHT_MAX)) { ++ DPRINTK_ISP_AF("Error : Height is incorrect"); ++ return -AF_ERR_HEIGHT; ++ } ++ ++ /*Check width */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->width, AF_PAXEL_WIDTH_MIN, ++ AF_PAXEL_WIDTH_MAX)) { ++ DPRINTK_ISP_AF("Error : Width is incorrect"); ++ return -AF_ERR_WIDTH; ++ } ++ ++ /*Check Line Increment */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->line_incr, AF_PAXEL_INCREMENT_MIN, ++ AF_PAXEL_INCREMENT_MAX)) { ++ DPRINTK_ISP_AF("Error : Line Increment is incorrect"); ++ return -AF_ERR_INCR; ++ } ++ ++ /*Check Horizontal Start */ ++ if ((paxel_cfg->hz_start % 2 != 0) || ++ (paxel_cfg->hz_start < (iir_cfg->hz_start_pos + 2)) || ++ IS_OUT_OF_BOUNDS(paxel_cfg->hz_start, ++ AF_PAXEL_HZSTART_MIN, AF_PAXEL_HZSTART_MAX)) { ++ DPRINTK_ISP_AF("Error : Horizontal Start is incorrect"); ++ return -AF_ERR_HZ_START; ++ } ++ ++ /*Check Vertical Start */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->vt_start, AF_PAXEL_VTSTART_MIN, ++ AF_PAXEL_VTSTART_MAX)) { ++ DPRINTK_ISP_AF("Error : Vertical Start is incorrect"); ++ return -AF_ERR_VT_START; ++ } ++ return 0; ++} ++ ++/** ++ * isp_af_check_iir - Function to check IIR Coefficient. ++ **/ ++int isp_af_check_iir(void) ++{ ++ struct af_iir *iir_cfg = &af_dev_configptr->config->iir_config; ++ int index; ++ ++ for (index = 0; index < AF_NUMBER_OF_COEF; index++) { ++ if ((iir_cfg->coeff_set0[index]) > AF_COEF_MAX) { ++ DPRINTK_ISP_AF("Error : Coefficient for set 0 is " ++ "incorrect"); ++ return -AF_ERR_IIR_COEF; ++ } ++ ++ if ((iir_cfg->coeff_set1[index]) > AF_COEF_MAX) { ++ DPRINTK_ISP_AF("Error : Coefficient for set 1 is " ++ "incorrect"); ++ return -AF_ERR_IIR_COEF; ++ } ++ } ++ ++ if (IS_OUT_OF_BOUNDS(iir_cfg->hz_start_pos, AF_IIRSH_MIN, ++ AF_IIRSH_MAX)) { ++ DPRINTK_ISP_AF("Error : IIRSH is incorrect"); ++ return -AF_ERR_IIRSH; ++ } ++ ++ return 0; ++} ++/** ++ * isp_af_unlock_buffers - Helper function to unlock all buffers. ++ **/ ++static void isp_af_unlock_buffers(void) ++{ ++ int i; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&afstat.buffer_lock, irqflags); ++ for (i = 0; i < H3A_MAX_BUFF; i++) ++ afstat.af_buff[i].locked = 0; ++ ++ spin_unlock_irqrestore(&afstat.buffer_lock, irqflags); ++} ++ ++/* ++ * Helper function to link allocated buffers ++ */ ++static void isp_af_link_buffers(void) ++{ ++ int i; ++ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ if ((i + 1) < H3A_MAX_BUFF) ++ afstat.af_buff[i].next = &afstat.af_buff[i + 1]; ++ else ++ afstat.af_buff[i].next = &afstat.af_buff[0]; ++ } ++} ++ ++/* Function to perform hardware set up */ ++int isp_af_configure(struct af_configuration *afconfig) ++{ ++ int result; ++ int buff_size, i; ++ unsigned int busyaf; ++ struct af_configuration *af_curr_cfg = af_dev_configptr->config; ++ ++ if (NULL == afconfig) { ++ printk(KERN_ERR "Null argument in configuration. \n"); ++ return -EINVAL; ++ } ++ ++ memcpy(af_curr_cfg, afconfig, sizeof(struct af_configuration)); ++ /* Get the value of PCR register */ ++ busyaf = isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); ++ ++ if ((busyaf & AF_BUSYAF) == AF_BUSYAF) { ++ DPRINTK_ISP_AF("AF_register_setup_ERROR : Engine Busy"); ++ DPRINTK_ISP_AF("\n Configuration cannot be done "); ++ return -AF_ERR_ENGINE_BUSY; ++ } ++ ++ /* Check IIR Coefficient and start Values */ ++ result = isp_af_check_iir(); ++ if (result < 0) ++ return result; ++ ++ /* Check Paxel Values */ ++ result = isp_af_check_paxel(); ++ if (result < 0) ++ return result; ++ ++ /* Check HMF Threshold Values */ ++ if (af_curr_cfg->hmf_config.threshold > AF_THRESHOLD_MAX) { ++ DPRINTK_ISP_AF("Error : HMF Threshold is incorrect"); ++ return -AF_ERR_THRESHOLD; ++ } ++ ++ /* Compute buffer size */ ++ buff_size = (af_curr_cfg->paxel_config.hz_cnt + 1) * ++ (af_curr_cfg->paxel_config.vt_cnt + 1) * AF_PAXEL_SIZE; ++ ++ afstat.curr_cfg_buf_size = buff_size; ++ /* Deallocate the previous buffers */ ++ if (afstat.stats_buf_size && buff_size > afstat.stats_buf_size) { ++ isp_af_enable(0); ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ ispmmu_kunmap(afstat.af_buff[i].ispmmu_addr); ++ dma_free_coherent( ++ NULL, afstat.min_buf_size, ++ (void *)afstat.af_buff[i].virt_addr, ++ (dma_addr_t)afstat.af_buff[i].phy_addr); ++ afstat.af_buff[i].virt_addr = 0; ++ } ++ afstat.stats_buf_size = 0; ++ } ++ ++ if (!afstat.af_buff[0].virt_addr) { ++ afstat.stats_buf_size = buff_size; ++ afstat.min_buf_size = PAGE_ALIGN(afstat.stats_buf_size); ++ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ afstat.af_buff[i].virt_addr = ++ (unsigned long)dma_alloc_coherent( ++ NULL, ++ afstat.min_buf_size, ++ (dma_addr_t *) ++ &afstat.af_buff[i].phy_addr, ++ GFP_KERNEL | GFP_DMA); ++ if (afstat.af_buff[i].virt_addr == 0) { ++ printk(KERN_ERR "Can't acquire memory for " ++ "buffer[%d]\n", i); ++ return -ENOMEM; ++ } ++ afstat.af_buff[i].addr_align = ++ afstat.af_buff[i].virt_addr; ++ while ((afstat.af_buff[i].addr_align & 0xFFFFFFC0) != ++ afstat.af_buff[i].addr_align) ++ afstat.af_buff[i].addr_align++; ++ afstat.af_buff[i].ispmmu_addr = ++ ispmmu_kmap(afstat.af_buff[i].phy_addr, ++ afstat.min_buf_size); ++ } ++ isp_af_unlock_buffers(); ++ isp_af_link_buffers(); ++ ++ /* First active buffer */ ++ if (active_buff == NULL) ++ active_buff = &afstat.af_buff[0]; ++ isp_af_set_address(active_buff->ispmmu_addr); ++ } ++ ++ result = isp_af_register_setup(af_dev_configptr); ++ if (result < 0) ++ return result; ++ af_dev_configptr->size_paxel = buff_size; ++ atomic_inc(&afstat.config_counter); ++ afstat.initialized = 1; ++ afstat.frame_count = 1; ++ active_buff->frame_num = 1; ++ /* Set configuration flag to indicate HW setup done */ ++ if (af_curr_cfg->af_config) ++ isp_af_enable(1); ++ else ++ isp_af_enable(0); ++ ++ /* Success */ ++ return 0; ++} ++EXPORT_SYMBOL(isp_af_configure); ++ ++int isp_af_register_setup(struct af_device *af_dev) ++{ ++ unsigned int pcr = 0, pax1 = 0, pax2 = 0, paxstart = 0; ++ unsigned int coef = 0; ++ unsigned int base_coef_set0 = 0; ++ unsigned int base_coef_set1 = 0; ++ int index; ++ ++ /* Configure Hardware Registers */ ++ /* Read PCR Register */ ++ pcr = isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); ++ ++ /* Set Accumulator Mode */ ++ if (af_dev->config->mode == ACCUMULATOR_PEAK) ++ pcr |= FVMODE; ++ else ++ pcr &= ~FVMODE; ++ ++ /* Set A-law */ ++ if (af_dev->config->alaw_enable == H3A_AF_ALAW_ENABLE) ++ pcr |= AF_ALAW_EN; ++ else ++ pcr &= ~AF_ALAW_EN; ++ ++ /* Set RGB Position */ ++ pcr &= ~RGBPOS; ++ pcr |= af_dev->config->rgb_pos << AF_RGBPOS_SHIFT; ++ ++ /* HMF Configurations */ ++ if (af_dev->config->hmf_config.enable == H3A_AF_HMF_ENABLE) { ++ pcr &= ~AF_MED_EN; ++ /* Enable HMF */ ++ pcr |= AF_MED_EN; ++ ++ /* Set Median Threshold */ ++ pcr &= ~MED_TH; ++ pcr |= af_dev->config->hmf_config.threshold << AF_MED_TH_SHIFT; ++ } else ++ pcr &= ~AF_MED_EN; ++ ++ /* Set PCR Register */ ++ isp_reg_writel(pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); ++ ++ pax1 &= ~PAXW; ++ pax1 |= af_dev->config->paxel_config.width << AF_PAXW_SHIFT; ++ ++ /* Set height in AFPAX1 */ ++ pax1 &= ~PAXH; ++ pax1 |= af_dev->config->paxel_config.height; ++ ++ isp_reg_writel(pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); ++ ++ /* Configure AFPAX2 Register */ ++ /* Set Line Increment in AFPAX2 Register */ ++ pax2 &= ~AFINCV; ++ pax2 |= af_dev->config->paxel_config.line_incr << AF_LINE_INCR_SHIFT; ++ /* Set Vertical Count */ ++ pax2 &= ~PAXVC; ++ pax2 |= af_dev->config->paxel_config.vt_cnt << AF_VT_COUNT_SHIFT; ++ /* Set Horizontal Count */ ++ pax2 &= ~PAXHC; ++ pax2 |= af_dev->config->paxel_config.hz_cnt; ++ isp_reg_writel(pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); ++ ++ /* Configure PAXSTART Register */ ++ /*Configure Horizontal Start */ ++ paxstart &= ~PAXSH; ++ paxstart |= af_dev->config->paxel_config.hz_start << AF_HZ_START_SHIFT; ++ /* Configure Vertical Start */ ++ paxstart &= ~PAXSV; ++ paxstart |= af_dev->config->paxel_config.vt_start; ++ isp_reg_writel(paxstart, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAXSTART); ++ ++ /*SetIIRSH Register */ ++ isp_reg_writel(af_dev->config->iir_config.hz_start_pos, ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); ++ ++ /*Set IIR Filter0 Coefficients */ ++ base_coef_set0 = ISPH3A_AFCOEF010; ++ for (index = 0; index <= 8; index += 2) { ++ coef &= ~COEF_MASK0; ++ coef |= af_dev->config->iir_config.coeff_set0[index]; ++ coef &= ~COEF_MASK1; ++ coef |= af_dev->config->iir_config.coeff_set0[index + 1] << ++ AF_COEF_SHIFT; ++ isp_reg_writel(coef, OMAP3_ISP_IOMEM_H3A, base_coef_set0); ++ base_coef_set0 = base_coef_set0 + AFCOEF_OFFSET; ++ } ++ ++ /* set AFCOEF0010 Register */ ++ isp_reg_writel(af_dev->config->iir_config.coeff_set0[10], ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF010); ++ ++ /*Set IIR Filter1 Coefficients */ ++ ++ base_coef_set1 = ISPH3A_AFCOEF110; ++ for (index = 0; index <= 8; index += 2) { ++ coef &= ~COEF_MASK0; ++ coef |= af_dev->config->iir_config.coeff_set1[index]; ++ coef &= ~COEF_MASK1; ++ coef |= af_dev->config->iir_config.coeff_set1[index + 1] << ++ AF_COEF_SHIFT; ++ isp_reg_writel(coef, OMAP3_ISP_IOMEM_H3A, base_coef_set1); ++ ++ base_coef_set1 = base_coef_set1 + AFCOEF_OFFSET; ++ } ++ isp_reg_writel(af_dev->config->iir_config.coeff_set1[10], ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); ++ ++ return 0; ++} ++ ++/* Function to set address */ ++void isp_af_set_address(unsigned long address) ++{ ++ isp_reg_writel(address, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFBUFST); ++} ++ ++static int isp_af_stats_available(struct isp_af_data *afdata) ++{ ++ int i, ret; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&afstat.buffer_lock, irqflags); ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ DPRINTK_ISP_AF("Checking Stats buff[%d] (%d) for %d\n", ++ i, afstat.af_buff[i].frame_num, ++ afdata->frame_number); ++ if (afdata->frame_number == afstat.af_buff[i].frame_num ++ && afstat.af_buff[i].frame_num != active_buff->frame_num) { ++ afstat.af_buff[i].locked = 1; ++ spin_unlock_irqrestore(&afstat.buffer_lock, irqflags); ++ isp_af_update_req_buffer(&afstat.af_buff[i]); ++ afstat.af_buff[i].frame_num = 0; ++ ret = copy_to_user((void *)afdata->af_statistics_buf, ++ (void *)afstat.af_buff[i].virt_addr, ++ afstat.curr_cfg_buf_size); ++ if (ret) { ++ printk(KERN_ERR "Failed copy_to_user for " ++ "H3A stats buff, %d\n", ret); ++ } ++ afdata->xtrastats.ts = afstat.af_buff[i].xtrastats.ts; ++ afdata->xtrastats.field_count = ++ afstat.af_buff[i].xtrastats.field_count; ++ return 0; ++ } ++ } ++ spin_unlock_irqrestore(&afstat.buffer_lock, irqflags); ++ /* Stats unavailable */ ++ ++ return -1; ++} ++ ++void isp_af_notify(int notify) ++{ ++ camnotify = notify; ++ if (camnotify && afstat.initialized) { ++ printk(KERN_DEBUG "Warning Camera Off \n"); ++ afstat.stats_req = 0; ++ afstat.stats_done = 1; ++ wake_up_interruptible(&afstat.stats_wait); ++ } ++} ++EXPORT_SYMBOL(isp_af_notify); ++/* ++ * This API allows the user to update White Balance gains, as well as ++ * exposure time and analog gain. It is also used to request frame ++ * statistics. ++ */ ++int isp_af_request_statistics(struct isp_af_data *afdata) ++{ ++ int ret = 0; ++ u16 frame_diff = 0; ++ u16 frame_cnt = afstat.frame_count; ++ wait_queue_t wqt; ++ ++ if (!af_dev_configptr->config->af_config) { ++ printk(KERN_ERR "AF engine not enabled\n"); ++ return -EINVAL; ++ } ++ ++ if (!(afdata->update & REQUEST_STATISTICS)) { ++ afdata->af_statistics_buf = NULL; ++ goto out; ++ } ++ ++ isp_af_unlock_buffers(); ++ /* Stats available? */ ++ DPRINTK_ISP_AF("Stats available?\n"); ++ ret = isp_af_stats_available(afdata); ++ if (!ret) ++ goto out; ++ ++ /* Stats in near future? */ ++ DPRINTK_ISP_AF("Stats in near future?\n"); ++ if (afdata->frame_number > frame_cnt) ++ frame_diff = afdata->frame_number - frame_cnt; ++ else if (afdata->frame_number < frame_cnt) { ++ if (frame_cnt > MAX_FRAME_COUNT - MAX_FUTURE_FRAMES ++ && afdata->frame_number < MAX_FRAME_COUNT) { ++ frame_diff = afdata->frame_number + MAX_FRAME_COUNT - ++ frame_cnt; ++ } else { ++ /* Frame unavailable */ ++ frame_diff = MAX_FUTURE_FRAMES + 1; ++ } ++ } ++ ++ if (frame_diff > MAX_FUTURE_FRAMES) { ++ printk(KERN_ERR "Invalid frame requested, returning current" ++ " frame stats\n"); ++ afdata->frame_number = frame_cnt; ++ } ++ if (!camnotify) { ++ /* Block until frame in near future completes */ ++ afstat.frame_req = afdata->frame_number; ++ afstat.stats_req = 1; ++ afstat.stats_done = 0; ++ init_waitqueue_entry(&wqt, current); ++ ret = wait_event_interruptible(afstat.stats_wait, ++ afstat.stats_done == 1); ++ if (ret < 0) { ++ afdata->af_statistics_buf = NULL; ++ return ret; ++ } ++ DPRINTK_ISP_AF("ISP AF request status interrupt raised\n"); ++ ++ /* Stats now available */ ++ ret = isp_af_stats_available(afdata); ++ if (ret) { ++ printk(KERN_ERR "After waiting for stats, stats not" ++ " available!!\n"); ++ afdata->af_statistics_buf = NULL; ++ } ++ } ++ ++out: ++ afdata->curr_frame = afstat.frame_count; ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_af_request_statistics); ++ ++/* This function will handle the H3A interrupt. */ ++static void isp_af_isr(unsigned long status, isp_vbq_callback_ptr arg1, ++ void *arg2) ++{ ++ u16 frame_align; ++ ++ if ((H3A_AF_DONE & status) != H3A_AF_DONE) ++ return; ++ ++ /* timestamp stats buffer */ ++ do_gettimeofday(&active_buff->xtrastats.ts); ++ active_buff->config_counter = atomic_read(&afstat.config_counter); ++ ++ /* Exchange buffers */ ++ active_buff = active_buff->next; ++ if (active_buff->locked == 1) ++ active_buff = active_buff->next; ++ isp_af_set_address(active_buff->ispmmu_addr); ++ ++ /* Update frame counter */ ++ afstat.frame_count++; ++ frame_align = afstat.frame_count; ++ if (afstat.frame_count > MAX_FRAME_COUNT) { ++ afstat.frame_count = 1; ++ frame_align++; ++ } ++ active_buff->frame_num = afstat.frame_count; ++ ++ /* Future Stats requested? */ ++ if (afstat.stats_req) { ++ /* Is the frame we want already done? */ ++ if (frame_align >= afstat.frame_req + 1) { ++ afstat.stats_req = 0; ++ afstat.stats_done = 1; ++ wake_up_interruptible(&afstat.stats_wait); ++ } ++ } ++} ++ ++int __isp_af_enable(int enable) ++{ ++ unsigned int pcr; ++ ++ pcr = isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); ++ ++ /* Set AF_EN bit in PCR Register */ ++ if (enable) { ++ if (isp_set_callback(CBK_H3A_AF_DONE, isp_af_isr, ++ (void *)NULL, (void *)NULL)) { ++ printk(KERN_ERR "No callback for AF\n"); ++ return -EINVAL; ++ } ++ ++ pcr |= AF_EN; ++ } else { ++ isp_unset_callback(CBK_H3A_AF_DONE); ++ pcr &= ~AF_EN; ++ } ++ isp_reg_writel(pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); ++ return 0; ++} ++ ++/* Function to Enable/Disable AF Engine */ ++int isp_af_enable(int enable) ++{ ++ int rval; ++ ++ rval = __isp_af_enable(enable); ++ ++ if (!rval) ++ afstat.pm_state = enable; ++ ++ return rval; ++} ++ ++/* Function to Suspend AF Engine */ ++void isp_af_suspend(void) ++{ ++ if (afstat.pm_state) ++ __isp_af_enable(0); ++} ++ ++/* Function to Resume AF Engine */ ++void isp_af_resume(void) ++{ ++ if (afstat.pm_state) ++ __isp_af_enable(1); ++} ++ ++int isp_af_busy(void) ++{ ++ return isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) ++ & ISPH3A_PCR_BUSYAF; ++} ++ ++/* Function to register the AF character device driver. */ ++int __init isp_af_init(void) ++{ ++ /*allocate memory for device structure and initialize it with 0 */ ++ af_dev_configptr = kzalloc(sizeof(struct af_device), GFP_KERNEL); ++ if (!af_dev_configptr) ++ goto err_nomem1; ++ ++ active_buff = NULL; ++ ++ af_dev_configptr->config = (struct af_configuration *) ++ kzalloc(sizeof(struct af_configuration), GFP_KERNEL); ++ ++ if (af_dev_configptr->config == NULL) ++ goto err_nomem2; ++ ++ memset(&afstat, 0, sizeof(afstat)); ++ ++ init_waitqueue_head(&afstat.stats_wait); ++ spin_lock_init(&afstat.buffer_lock); ++ ++ return 0; ++ ++err_nomem2: ++ kfree(af_dev_configptr); ++err_nomem1: ++ printk(KERN_ERR "Error: kmalloc fail"); ++ return -ENOMEM; ++} ++ ++void isp_af_exit(void) ++{ ++ int i; ++ ++ /* Free buffers */ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ if (!afstat.af_buff[i].phy_addr) ++ continue; ++ ++ ispmmu_kunmap(afstat.af_buff[i].ispmmu_addr); ++ ++ dma_free_coherent(NULL, ++ afstat.min_buf_size, ++ (void *)afstat.af_buff[i].virt_addr, ++ (dma_addr_t)afstat.af_buff[i].phy_addr); ++ } ++ kfree(af_dev_configptr->config); ++ kfree(af_dev_configptr); ++ ++ memset(&afstat, 0, sizeof(afstat)); ++ ++ af_major = -1; ++} +diff --git a/drivers/media/video/isp/isp_af.h b/drivers/media/video/isp/isp_af.h +new file mode 100644 +index 0000000..ee2b89f +--- /dev/null ++++ b/drivers/media/video/isp/isp_af.h +@@ -0,0 +1,125 @@ ++/* ++ * isp_af.h ++ * ++ * Include file for AF module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++/* Device Constants */ ++#ifndef OMAP_ISP_AF_H ++#define OMAP_ISP_AF_H ++ ++#include <mach/isp_user.h> ++ ++#define AF_MAJOR_NUMBER 0 ++#define ISPAF_NAME "OMAPISP_AF" ++#define AF_NR_DEVS 1 ++#define AF_TIMEOUT ((300 * HZ) / 1000) ++ ++ ++ ++/* Print Macros */ ++/*list of error code */ ++#define AF_ERR_HZ_COUNT 800 /* Invalid Horizontal Count */ ++#define AF_ERR_VT_COUNT 801 /* Invalid Vertical Count */ ++#define AF_ERR_HEIGHT 802 /* Invalid Height */ ++#define AF_ERR_WIDTH 803 /* Invalid width */ ++#define AF_ERR_INCR 804 /* Invalid Increment */ ++#define AF_ERR_HZ_START 805 /* Invalid horizontal Start */ ++#define AF_ERR_VT_START 806 /* Invalud vertical Start */ ++#define AF_ERR_IIRSH 807 /* Invalid IIRSH value */ ++#define AF_ERR_IIR_COEF 808 /* Invalid Coefficient */ ++#define AF_ERR_SETUP 809 /* Setup not done */ ++#define AF_ERR_THRESHOLD 810 /* Invalid Threshold */ ++#define AF_ERR_ENGINE_BUSY 811 /* Engine is busy */ ++ ++#define AFPID 0x0 /* Peripheral Revision ++ * and Class Information ++ */ ++ ++#define AFCOEF_OFFSET 0x00000004 /* COEFFICIENT BASE ++ * ADDRESS ++ */ ++ ++/* ++ * PCR fields ++ */ ++#define AF_BUSYAF (1 << 15) ++#define FVMODE (1 << 14) ++#define RGBPOS (0x7 << 11) ++#define MED_TH (0xFF << 3) ++#define AF_MED_EN (1 << 2) ++#define AF_ALAW_EN (1 << 1) ++#define AF_EN (1 << 0) ++ ++/* ++ * AFPAX1 fields ++ */ ++#define PAXW (0x7F << 16) ++#define PAXH 0x7F ++ ++/* ++ * AFPAX2 fields ++ */ ++#define AFINCV (0xF << 13) ++#define PAXVC (0x7F << 6) ++#define PAXHC 0x3F ++ ++/* ++ * AFPAXSTART fields ++ */ ++#define PAXSH (0xFFF<<16) ++#define PAXSV 0xFFF ++ ++/* ++ * COEFFICIENT MASK ++ */ ++ ++#define COEF_MASK0 0xFFF ++#define COEF_MASK1 (0xFFF<<16) ++ ++/* BIT SHIFTS */ ++#define AF_RGBPOS_SHIFT 11 ++#define AF_MED_TH_SHIFT 3 ++#define AF_PAXW_SHIFT 16 ++#define AF_LINE_INCR_SHIFT 13 ++#define AF_VT_COUNT_SHIFT 6 ++#define AF_HZ_START_SHIFT 16 ++#define AF_COEF_SHIFT 16 ++ ++#define AF_UPDATEXS_TS (1 << 0) ++#define AF_UPDATEXS_FIELDCOUNT (1 << 1) ++#define AF_UPDATEXS_LENSPOS (1 << 2) ++ ++/* Structure for device of AF Engine */ ++struct af_device { ++ struct af_configuration *config; /*Device configuration structure */ ++ int size_paxel; /*Paxel size in bytes */ ++}; ++ ++int isp_af_check_paxel(void); ++int isp_af_check_iir(void); ++int isp_af_register_setup(struct af_device *af_dev); ++int isp_af_enable(int); ++void isp_af_suspend(void); ++void isp_af_resume(void); ++int isp_af_busy(void); ++void isp_af_notify(int notify); ++int isp_af_request_statistics(struct isp_af_data *afdata); ++int isp_af_configure(struct af_configuration *afconfig); ++void isp_af_set_address(unsigned long); ++void isp_af_setxtrastats(struct isp_af_xtrastats *xtrastats, u8 updateflag); ++#endif /* OMAP_ISP_AF_H */ +diff --git a/drivers/media/video/isp/isph3a.c b/drivers/media/video/isp/isph3a.c +new file mode 100644 +index 0000000..7ff1c5b +--- /dev/null ++++ b/drivers/media/video/isp/isph3a.c +@@ -0,0 +1,932 @@ ++/* ++ * isph3a.c ++ * ++ * H3A module for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <asm/cacheflush.h> ++ ++#include <linux/dma-mapping.h> ++#include <linux/uaccess.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "isph3a.h" ++#include "ispmmu.h" ++#include "isppreview.h" ++ ++/** ++ * struct isph3a_aewb_buffer - AE, AWB frame stats buffer. ++ * @virt_addr: Virtual address to mmap the buffer. ++ * @phy_addr: Physical address of the buffer. ++ * @addr_align: Virtual Address 32 bytes aligned. ++ * @ispmmu_addr: Address of the buffer mapped by the ISPMMU. ++ * @mmap_addr: Mapped memory area of buffer. For userspace access. ++ * @locked: 1 - Buffer locked from write. 0 - Buffer can be overwritten. ++ * @frame_num: Frame number from which the statistics are taken. ++ * @next: Pointer to link next buffer. ++ */ ++struct isph3a_aewb_buffer { ++ unsigned long virt_addr; ++ unsigned long phy_addr; ++ unsigned long addr_align; ++ unsigned long ispmmu_addr; ++ unsigned long mmap_addr; /* For userspace */ ++ struct timeval ts; ++ u32 config_counter; ++ ++ u8 locked; ++ u16 frame_num; ++ struct isph3a_aewb_buffer *next; ++}; ++ ++/** ++ * struct isph3a_aewb_status - AE, AWB status. ++ * @initialized: 1 - Buffers initialized. ++ * @update: 1 - Update registers. ++ * @stats_req: 1 - Future stats requested. ++ * @stats_done: 1 - Stats ready for user. ++ * @frame_req: Number of frame requested for statistics. ++ * @h3a_buff: Array of statistics buffers to access. ++ * @stats_buf_size: Statistics buffer size. ++ * @min_buf_size: Minimum statisitics buffer size. ++ * @win_count: Window Count. ++ * @frame_count: Frame Count. ++ * @stats_wait: Wait primitive for locking/unlocking the stats request. ++ * @buffer_lock: Spinlock for statistics buffers access. ++ */ ++static struct isph3a_aewb_status { ++ u8 initialized; ++ u8 update; ++ u8 stats_req; ++ u8 stats_done; ++ u16 frame_req; ++ int pm_state; ++ ++ struct isph3a_aewb_buffer h3a_buff[H3A_MAX_BUFF]; ++ unsigned int stats_buf_size; ++ unsigned int min_buf_size; ++ unsigned int curr_cfg_buf_size; ++ ++ atomic_t config_counter; ++ ++ u16 win_count; ++ u32 frame_count; ++ wait_queue_head_t stats_wait; ++ spinlock_t buffer_lock; /* For stats buffers read/write sync */ ++} aewbstat; ++ ++/** ++ * struct isph3a_aewb_regs - Current value of AE, AWB configuration registers. ++ * reg_pcr: Peripheral control register. ++ * reg_win1: Control register. ++ * reg_start: Start position register. ++ * reg_blk: Black line register. ++ * reg_subwin: Configuration register. ++ */ ++static struct isph3a_aewb_regs { ++ u32 reg_pcr; ++ u32 reg_win1; ++ u32 reg_start; ++ u32 reg_blk; ++ u32 reg_subwin; ++} aewb_regs; ++ ++static struct isph3a_aewb_config aewb_config_local = { ++ .saturation_limit = 0x3FF, ++ .win_height = 0, ++ .win_width = 0, ++ .ver_win_count = 0, ++ .hor_win_count = 0, ++ .ver_win_start = 0, ++ .hor_win_start = 0, ++ .blk_ver_win_start = 0, ++ .blk_win_height = 0, ++ .subsample_ver_inc = 0, ++ .subsample_hor_inc = 0, ++ .alaw_enable = 0, ++ .aewb_enable = 0, ++}; ++ ++/* Structure for saving/restoring h3a module registers */ ++static struct isp_reg isph3a_reg_list[] = { ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, 0}, /* Should be the first one */ ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINSTART, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWSUBWIN, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAXSTART, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFBUFST, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF010, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF032, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF054, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF076, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF098, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF110, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF132, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF154, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF176, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF198, 0}, ++ {OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010, 0}, ++ {0, ISP_TOK_TERM, 0} ++}; ++ ++static struct ispprev_wbal h3awb_update; ++static struct isph3a_aewb_buffer *active_buff; ++static struct isph3a_aewb_xtrastats h3a_xtrastats[H3A_MAX_BUFF]; ++static int camnotify; ++static int wb_update; ++static void isph3a_print_status(void); ++ ++/** ++ * isph3a_aewb_setxtrastats - Receives extra statistics from prior frames. ++ * @xtrastats: Pointer to structure containing extra statistics fields like ++ * field count and timestamp of frame. ++ * ++ * Called from update_vbq in camera driver ++ **/ ++void isph3a_aewb_setxtrastats(struct isph3a_aewb_xtrastats *xtrastats) ++{ ++ int i; ++ ++ if (active_buff == NULL) ++ return; ++ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ if (aewbstat.h3a_buff[i].frame_num != active_buff->frame_num) ++ continue; ++ ++ if (i == 0) { ++ if (aewbstat.h3a_buff[H3A_MAX_BUFF - 1].locked == 0) { ++ h3a_xtrastats[H3A_MAX_BUFF - 1] = ++ *xtrastats; ++ } else { ++ h3a_xtrastats[H3A_MAX_BUFF - 2] = ++ *xtrastats; ++ } ++ } else if (i == 1) { ++ if (aewbstat.h3a_buff[0].locked == 0) ++ h3a_xtrastats[0] = *xtrastats; ++ else { ++ h3a_xtrastats[H3A_MAX_BUFF - 1] = ++ *xtrastats; ++ } ++ } else { ++ if (aewbstat.h3a_buff[i - 1].locked == 0) ++ h3a_xtrastats[i - 1] = *xtrastats; ++ else ++ h3a_xtrastats[i - 2] = *xtrastats; ++ } ++ return; ++ } ++} ++EXPORT_SYMBOL(isph3a_aewb_setxtrastats); ++ ++void __isph3a_aewb_enable(u8 enable) ++{ ++ isp_reg_writel(IRQ0STATUS_H3A_AWB_DONE_IRQ, OMAP3_ISP_IOMEM_MAIN, ++ ISP_IRQ0STATUS); ++ ++ if (enable) { ++ aewb_regs.reg_pcr |= ISPH3A_PCR_AEW_EN; ++ DPRINTK_ISPH3A(" H3A enabled \n"); ++ } else { ++ aewb_regs.reg_pcr &= ~ISPH3A_PCR_AEW_EN; ++ DPRINTK_ISPH3A(" H3A disabled \n"); ++ } ++ isp_reg_and_or(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ~ISPH3A_PCR_AEW_EN, ++ (enable ? ISPH3A_PCR_AEW_EN : 0)); ++ aewb_config_local.aewb_enable = enable; ++} ++ ++/** ++ * isph3a_aewb_enable - Enables AE, AWB engine in the H3A module. ++ * @enable: 1 - Enables the AE & AWB engine. ++ * ++ * Client should configure all the AE & AWB registers in H3A before this. ++ **/ ++void isph3a_aewb_enable(u8 enable) ++{ ++ __isph3a_aewb_enable(enable); ++ aewbstat.pm_state = enable; ++} ++ ++/** ++ * isph3a_aewb_suspend - Suspend AE, AWB engine in the H3A module. ++ **/ ++void isph3a_aewb_suspend(void) ++{ ++ if (aewbstat.pm_state) ++ __isph3a_aewb_enable(0); ++} ++ ++/** ++ * isph3a_aewb_resume - Resume AE, AWB engine in the H3A module. ++ **/ ++void isph3a_aewb_resume(void) ++{ ++ if (aewbstat.pm_state) ++ __isph3a_aewb_enable(1); ++} ++ ++int isph3a_aewb_busy(void) ++{ ++ return isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) ++ & ISPH3A_PCR_BUSYAEAWB; ++} ++ ++/** ++ * isph3a_update_wb - Updates WB parameters. ++ * ++ * Needs to be called when no ISP Preview processing is taking place. ++ **/ ++void isph3a_update_wb(void) ++{ ++ if (wb_update) { ++ isppreview_config_whitebalance(h3awb_update); ++ wb_update = 0; ++ } ++ return; ++} ++EXPORT_SYMBOL(isph3a_update_wb); ++ ++/** ++ * isph3a_aewb_update_regs - Helper function to update h3a registers. ++ **/ ++static void isph3a_aewb_update_regs(void) ++{ ++ isp_reg_writel(aewb_regs.reg_pcr, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR); ++ isp_reg_writel(aewb_regs.reg_win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); ++ isp_reg_writel(aewb_regs.reg_start, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AEWINSTART); ++ isp_reg_writel(aewb_regs.reg_blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); ++ isp_reg_writel(aewb_regs.reg_subwin, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AEWSUBWIN); ++ ++ aewbstat.update = 0; ++ aewbstat.frame_count = 1; ++} ++ ++/** ++ * isph3a_aewb_update_req_buffer - Helper function to update buffer cache pages ++ * @buffer: Pointer to structure ++ **/ ++static void isph3a_aewb_update_req_buffer(struct isph3a_aewb_buffer *buffer) ++{ ++ int size = aewbstat.stats_buf_size; ++ ++ size = PAGE_ALIGN(size); ++ dmac_inv_range((void *)buffer->addr_align, ++ (void *)buffer->addr_align + size); ++} ++ ++/** ++ * isph3a_aewb_stats_available - Check for stats available of specified frame. ++ * @aewbdata: Pointer to return AE AWB statistics data ++ * ++ * Returns 0 if successful, or -1 if statistics are unavailable. ++ **/ ++static int isph3a_aewb_stats_available(struct isph3a_aewb_data *aewbdata) ++{ ++ int i, ret; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&aewbstat.buffer_lock, irqflags); ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ DPRINTK_ISPH3A("Checking Stats buff[%d] (%d) for %d\n", ++ i, aewbstat.h3a_buff[i].frame_num, ++ aewbdata->frame_number); ++ if ((aewbdata->frame_number != ++ aewbstat.h3a_buff[i].frame_num) || ++ (aewbstat.h3a_buff[i].frame_num == ++ active_buff->frame_num)) ++ continue; ++ aewbstat.h3a_buff[i].locked = 1; ++ spin_unlock_irqrestore(&aewbstat.buffer_lock, irqflags); ++ isph3a_aewb_update_req_buffer(&aewbstat.h3a_buff[i]); ++ aewbstat.h3a_buff[i].frame_num = 0; ++ ret = copy_to_user((void *)aewbdata->h3a_aewb_statistics_buf, ++ (void *)aewbstat.h3a_buff[i].virt_addr, ++ aewbstat.curr_cfg_buf_size); ++ if (ret) { ++ printk(KERN_ERR "Failed copy_to_user for " ++ "H3A stats buff, %d\n", ret); ++ } ++ aewbdata->ts = aewbstat.h3a_buff[i].ts; ++ aewbdata->config_counter = aewbstat.h3a_buff[i].config_counter; ++ aewbdata->field_count = h3a_xtrastats[i].field_count; ++ return 0; ++ } ++ spin_unlock_irqrestore(&aewbstat.buffer_lock, irqflags); ++ ++ return -1; ++} ++ ++/** ++ * isph3a_aewb_link_buffers - Helper function to link allocated buffers. ++ **/ ++static void isph3a_aewb_link_buffers(void) ++{ ++ int i; ++ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ if ((i + 1) < H3A_MAX_BUFF) { ++ aewbstat.h3a_buff[i].next = &aewbstat.h3a_buff[i + 1]; ++ h3a_xtrastats[i].next = &h3a_xtrastats[i + 1]; ++ } else { ++ aewbstat.h3a_buff[i].next = &aewbstat.h3a_buff[0]; ++ h3a_xtrastats[i].next = &h3a_xtrastats[0]; ++ } ++ } ++} ++ ++/** ++ * isph3a_aewb_unlock_buffers - Helper function to unlock all buffers. ++ **/ ++static void isph3a_aewb_unlock_buffers(void) ++{ ++ int i; ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&aewbstat.buffer_lock, irqflags); ++ for (i = 0; i < H3A_MAX_BUFF; i++) ++ aewbstat.h3a_buff[i].locked = 0; ++ ++ spin_unlock_irqrestore(&aewbstat.buffer_lock, irqflags); ++} ++ ++/** ++ * isph3a_aewb_isr - Callback from ISP driver for H3A AEWB interrupt. ++ * @status: IRQ0STATUS in case of MMU error, 0 for H3A interrupt. ++ * @arg1: Not used as of now. ++ * @arg2: Not used as of now. ++ */ ++static void isph3a_aewb_isr(unsigned long status, isp_vbq_callback_ptr arg1, ++ void *arg2) ++{ ++ u16 frame_align; ++ ++ if ((H3A_AWB_DONE & status) != H3A_AWB_DONE) ++ return; ++ ++ do_gettimeofday(&active_buff->ts); ++ active_buff->config_counter = atomic_read(&aewbstat.config_counter); ++ active_buff = active_buff->next; ++ if (active_buff->locked == 1) ++ active_buff = active_buff->next; ++ isp_reg_writel(active_buff->ispmmu_addr, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AEWBUFST); ++ ++ aewbstat.frame_count++; ++ frame_align = aewbstat.frame_count; ++ if (aewbstat.frame_count > MAX_FRAME_COUNT) { ++ aewbstat.frame_count = 1; ++ frame_align++; ++ } ++ active_buff->frame_num = aewbstat.frame_count; ++ ++ if (aewbstat.stats_req) { ++ DPRINTK_ISPH3A("waiting for frame %d\n", aewbstat.frame_req); ++ if (frame_align >= aewbstat.frame_req + 1) { ++ aewbstat.stats_req = 0; ++ aewbstat.stats_done = 1; ++ wake_up_interruptible(&aewbstat.stats_wait); ++ } ++ } ++ ++ if (aewbstat.update) ++ isph3a_aewb_update_regs(); ++} ++ ++/** ++ * isph3a_aewb_set_params - Helper function to check & store user given params. ++ * @user_cfg: Pointer to AE and AWB parameters struct. ++ * ++ * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to ++ * program them during ISR. ++ * ++ * Returns 0 if successful, or -EINVAL if any of the parameters are invalid. ++ **/ ++static int isph3a_aewb_set_params(struct isph3a_aewb_config *user_cfg) ++{ ++ if (unlikely(user_cfg->saturation_limit > MAX_SATURATION_LIM)) { ++ printk(KERN_ERR "Invalid Saturation_limit: %d\n", ++ user_cfg->saturation_limit); ++ return -EINVAL; ++ } ++ if (aewb_config_local.saturation_limit != user_cfg->saturation_limit) { ++ WRITE_SAT_LIM(aewb_regs.reg_pcr, user_cfg->saturation_limit); ++ aewb_config_local.saturation_limit = ++ user_cfg->saturation_limit; ++ aewbstat.update = 1; ++ } ++ ++ if (aewb_config_local.alaw_enable != user_cfg->alaw_enable) { ++ WRITE_ALAW(aewb_regs.reg_pcr, user_cfg->alaw_enable); ++ aewb_config_local.alaw_enable = user_cfg->alaw_enable; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->win_height < MIN_WIN_H || ++ user_cfg->win_height > MAX_WIN_H || ++ user_cfg->win_height & 0x01)) { ++ printk(KERN_ERR "Invalid window height: %d\n", ++ user_cfg->win_height); ++ return -EINVAL; ++ } ++ if (aewb_config_local.win_height != user_cfg->win_height) { ++ WRITE_WIN_H(aewb_regs.reg_win1, user_cfg->win_height); ++ aewb_config_local.win_height = user_cfg->win_height; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->win_width < MIN_WIN_W || ++ user_cfg->win_width > MAX_WIN_W || ++ user_cfg->win_width & 0x01)) { ++ printk(KERN_ERR "Invalid window width: %d\n", ++ user_cfg->win_width); ++ return -EINVAL; ++ } ++ if (aewb_config_local.win_width != user_cfg->win_width) { ++ WRITE_WIN_W(aewb_regs.reg_win1, user_cfg->win_width); ++ aewb_config_local.win_width = user_cfg->win_width; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->ver_win_count < 1 || ++ user_cfg->ver_win_count > MAX_WINVC)) { ++ printk(KERN_ERR "Invalid vertical window count: %d\n", ++ user_cfg->ver_win_count); ++ return -EINVAL; ++ } ++ if (aewb_config_local.ver_win_count != user_cfg->ver_win_count) { ++ WRITE_VER_C(aewb_regs.reg_win1, user_cfg->ver_win_count); ++ aewb_config_local.ver_win_count = user_cfg->ver_win_count; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->hor_win_count < 1 || ++ user_cfg->hor_win_count > MAX_WINHC)) { ++ printk(KERN_ERR "Invalid horizontal window count: %d\n", ++ user_cfg->hor_win_count); ++ return -EINVAL; ++ } ++ if (aewb_config_local.hor_win_count != user_cfg->hor_win_count) { ++ WRITE_HOR_C(aewb_regs.reg_win1, user_cfg->hor_win_count); ++ aewb_config_local.hor_win_count = user_cfg->hor_win_count; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->ver_win_start > MAX_WINSTART)) { ++ printk(KERN_ERR "Invalid vertical window start: %d\n", ++ user_cfg->ver_win_start); ++ return -EINVAL; ++ } ++ if (aewb_config_local.ver_win_start != user_cfg->ver_win_start) { ++ WRITE_VER_WIN_ST(aewb_regs.reg_start, user_cfg->ver_win_start); ++ aewb_config_local.ver_win_start = user_cfg->ver_win_start; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->hor_win_start > MAX_WINSTART)) { ++ printk(KERN_ERR "Invalid horizontal window start: %d\n", ++ user_cfg->hor_win_start); ++ return -EINVAL; ++ } ++ if (aewb_config_local.hor_win_start != user_cfg->hor_win_start) { ++ WRITE_HOR_WIN_ST(aewb_regs.reg_start, user_cfg->hor_win_start); ++ aewb_config_local.hor_win_start = user_cfg->hor_win_start; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->blk_ver_win_start > MAX_WINSTART)) { ++ printk(KERN_ERR "Invalid black vertical window start: %d\n", ++ user_cfg->blk_ver_win_start); ++ return -EINVAL; ++ } ++ if (aewb_config_local.blk_ver_win_start != ++ user_cfg->blk_ver_win_start) { ++ WRITE_BLK_VER_WIN_ST(aewb_regs.reg_blk, ++ user_cfg->blk_ver_win_start); ++ aewb_config_local.blk_ver_win_start = ++ user_cfg->blk_ver_win_start; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->blk_win_height < MIN_WIN_H || ++ user_cfg->blk_win_height > MAX_WIN_H || ++ user_cfg->blk_win_height & 0x01)) { ++ printk(KERN_ERR "Invalid black window height: %d\n", ++ user_cfg->blk_win_height); ++ return -EINVAL; ++ } ++ if (aewb_config_local.blk_win_height != user_cfg->blk_win_height) { ++ WRITE_BLK_WIN_H(aewb_regs.reg_blk, user_cfg->blk_win_height); ++ aewb_config_local.blk_win_height = user_cfg->blk_win_height; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->subsample_ver_inc < MIN_SUB_INC || ++ user_cfg->subsample_ver_inc > MAX_SUB_INC || ++ user_cfg->subsample_ver_inc & 0x01)) { ++ printk(KERN_ERR "Invalid vertical subsample increment: %d\n", ++ user_cfg->subsample_ver_inc); ++ return -EINVAL; ++ } ++ if (aewb_config_local.subsample_ver_inc != ++ user_cfg->subsample_ver_inc) { ++ WRITE_SUB_VER_INC(aewb_regs.reg_subwin, ++ user_cfg->subsample_ver_inc); ++ aewb_config_local.subsample_ver_inc = ++ user_cfg->subsample_ver_inc; ++ aewbstat.update = 1; ++ } ++ ++ if (unlikely(user_cfg->subsample_hor_inc < MIN_SUB_INC || ++ user_cfg->subsample_hor_inc > MAX_SUB_INC || ++ user_cfg->subsample_hor_inc & 0x01)) { ++ printk(KERN_ERR "Invalid horizontal subsample increment: %d\n", ++ user_cfg->subsample_hor_inc); ++ return -EINVAL; ++ } ++ if (aewb_config_local.subsample_hor_inc != ++ user_cfg->subsample_hor_inc) { ++ WRITE_SUB_HOR_INC(aewb_regs.reg_subwin, ++ user_cfg->subsample_hor_inc); ++ aewb_config_local.subsample_hor_inc = ++ user_cfg->subsample_hor_inc; ++ aewbstat.update = 1; ++ } ++ ++ if (!aewbstat.initialized || !aewb_config_local.aewb_enable) { ++ isph3a_aewb_update_regs(); ++ aewbstat.initialized = 1; ++ } ++ return 0; ++} ++ ++/** ++ * isph3a_aewb_configure - Configure AEWB regs, enable/disable H3A engine. ++ * @aewbcfg: Pointer to AEWB config structure. ++ * ++ * Returns 0 if successful, -EINVAL if aewbcfg pointer is NULL, -ENOMEM if ++ * was unable to allocate memory for the buffer, of other errors if H3A ++ * callback is not set or the parameters for AEWB are invalid. ++ **/ ++int isph3a_aewb_configure(struct isph3a_aewb_config *aewbcfg) ++{ ++ int ret = 0; ++ int i; ++ int win_count = 0; ++ ++ if (NULL == aewbcfg) { ++ printk(KERN_ERR "Null argument in configuration. \n"); ++ return -EINVAL; ++ } ++ ++ if (!aewbstat.initialized) { ++ DPRINTK_ISPH3A("Setting callback for H3A\n"); ++ ret = isp_set_callback(CBK_H3A_AWB_DONE, isph3a_aewb_isr, ++ (void *)NULL, (void *)NULL); ++ if (ret) { ++ printk(KERN_ERR "No callback for H3A\n"); ++ return ret; ++ } ++ } ++ ++ ret = isph3a_aewb_set_params(aewbcfg); ++ if (ret) { ++ printk(KERN_ERR "Invalid parameters! \n"); ++ return ret; ++ } ++ ++ win_count = aewbcfg->ver_win_count * aewbcfg->hor_win_count; ++ win_count += aewbcfg->hor_win_count; ++ ret = win_count / 8; ++ win_count += win_count % 8 ? 1 : 0; ++ win_count += ret; ++ ++ aewbstat.win_count = win_count; ++ aewbstat.curr_cfg_buf_size = win_count * AEWB_PACKET_SIZE; ++ ++ if (aewbstat.stats_buf_size ++ && win_count * AEWB_PACKET_SIZE > aewbstat.stats_buf_size) { ++ DPRINTK_ISPH3A("There was a previous buffer... " ++ "Freeing/unmapping current stat busffs\n"); ++ isph3a_aewb_enable(0); ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ ispmmu_kunmap(aewbstat.h3a_buff[i].ispmmu_addr); ++ dma_free_coherent( ++ NULL, ++ aewbstat.min_buf_size, ++ (void *)aewbstat.h3a_buff[i].virt_addr, ++ (dma_addr_t)aewbstat.h3a_buff[i].phy_addr); ++ aewbstat.h3a_buff[i].virt_addr = 0; ++ } ++ aewbstat.stats_buf_size = 0; ++ } ++ ++ if (!aewbstat.h3a_buff[0].virt_addr) { ++ aewbstat.stats_buf_size = win_count * AEWB_PACKET_SIZE; ++ aewbstat.min_buf_size = PAGE_ALIGN(aewbstat.stats_buf_size); ++ ++ DPRINTK_ISPH3A("Allocating/mapping new stat buffs\n"); ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ aewbstat.h3a_buff[i].virt_addr = ++ (unsigned long)dma_alloc_coherent( ++ NULL, ++ aewbstat.min_buf_size, ++ (dma_addr_t *) ++ &aewbstat.h3a_buff[i].phy_addr, ++ GFP_KERNEL | GFP_DMA); ++ if (aewbstat.h3a_buff[i].virt_addr == 0) { ++ printk(KERN_ERR "Can't acquire memory for " ++ "buffer[%d]\n", i); ++ return -ENOMEM; ++ } ++ aewbstat.h3a_buff[i].addr_align = ++ aewbstat.h3a_buff[i].virt_addr; ++ while ((aewbstat.h3a_buff[i].addr_align & 0xFFFFFFC0) != ++ aewbstat.h3a_buff[i].addr_align) ++ aewbstat.h3a_buff[i].addr_align++; ++ aewbstat.h3a_buff[i].ispmmu_addr = ++ ispmmu_kmap(aewbstat.h3a_buff[i].phy_addr, ++ aewbstat.min_buf_size); ++ } ++ isph3a_aewb_unlock_buffers(); ++ isph3a_aewb_link_buffers(); ++ ++ if (active_buff == NULL) ++ active_buff = &aewbstat.h3a_buff[0]; ++ ++ isp_reg_writel(active_buff->ispmmu_addr, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AEWBUFST); ++ } ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ DPRINTK_ISPH3A("buff[%d] addr is:\n virt 0x%lX\n" ++ " aligned 0x%lX\n" ++ " phys 0x%lX\n" ++ " ispmmu 0x%08lX\n" ++ " mmapped 0x%lX\n" ++ " frame_num %d\n", i, ++ aewbstat.h3a_buff[i].virt_addr, ++ aewbstat.h3a_buff[i].addr_align, ++ aewbstat.h3a_buff[i].phy_addr, ++ aewbstat.h3a_buff[i].ispmmu_addr, ++ aewbstat.h3a_buff[i].mmap_addr, ++ aewbstat.h3a_buff[i].frame_num); ++ } ++ ++ active_buff->frame_num = 1; ++ ++ atomic_inc(&aewbstat.config_counter); ++ isph3a_aewb_enable(aewbcfg->aewb_enable); ++ isph3a_print_status(); ++ ++ return 0; ++} ++EXPORT_SYMBOL(isph3a_aewb_configure); ++ ++/** ++ * isph3a_aewb_request_statistics - REquest statistics and update gains in AEWB ++ * @aewbdata: Pointer to return AE AWB statistics data. ++ * ++ * This API allows the user to update White Balance gains, as well as ++ * exposure time and analog gain. It is also used to request frame ++ * statistics. ++ * ++ * Returns 0 if successful, -EINVAL when H3A engine is not enabled, or other ++ * errors when setting gains. ++ **/ ++int isph3a_aewb_request_statistics(struct isph3a_aewb_data *aewbdata) ++{ ++ int ret = 0; ++ u16 frame_diff = 0; ++ u16 frame_cnt = aewbstat.frame_count; ++ wait_queue_t wqt; ++ ++ if (!aewb_config_local.aewb_enable) { ++ printk(KERN_ERR "H3A engine not enabled\n"); ++ return -EINVAL; ++ } ++ ++ DPRINTK_ISPH3A("isph3a_aewb_request_statistics: Enter " ++ "(frame req. => %d, current frame => %d," ++ "update => %d)\n", ++ aewbdata->frame_number, frame_cnt, aewbdata->update); ++ DPRINTK_ISPH3A("User data received: \n"); ++ DPRINTK_ISPH3A("Digital gain = 0x%04x\n", aewbdata->dgain); ++ DPRINTK_ISPH3A("WB gain b *= 0x%04x\n", aewbdata->wb_gain_b); ++ DPRINTK_ISPH3A("WB gain r *= 0x%04x\n", aewbdata->wb_gain_r); ++ DPRINTK_ISPH3A("WB gain gb = 0x%04x\n", aewbdata->wb_gain_gb); ++ DPRINTK_ISPH3A("WB gain gr = 0x%04x\n", aewbdata->wb_gain_gr); ++ ++ if (!aewbdata->update) { ++ aewbdata->h3a_aewb_statistics_buf = NULL; ++ goto out; ++ } ++ if (aewbdata->update & SET_DIGITAL_GAIN) ++ h3awb_update.dgain = (u16)aewbdata->dgain; ++ if (aewbdata->update & SET_COLOR_GAINS) { ++ h3awb_update.coef0 = (u8)aewbdata->wb_gain_gr; ++ h3awb_update.coef1 = (u8)aewbdata->wb_gain_r; ++ h3awb_update.coef2 = (u8)aewbdata->wb_gain_b; ++ h3awb_update.coef3 = (u8)aewbdata->wb_gain_gb; ++ } ++ if (aewbdata->update & (SET_COLOR_GAINS | SET_DIGITAL_GAIN)) ++ wb_update = 1; ++ ++ if (!(aewbdata->update & REQUEST_STATISTICS)) { ++ aewbdata->h3a_aewb_statistics_buf = NULL; ++ goto out; ++ } ++ ++ if (aewbdata->frame_number < 1) { ++ printk(KERN_ERR "Illeagal frame number " ++ "requested (%d)\n", ++ aewbdata->frame_number); ++ return -EINVAL; ++ } ++ ++ isph3a_aewb_unlock_buffers(); ++ ++ DPRINTK_ISPH3A("Stats available?\n"); ++ ret = isph3a_aewb_stats_available(aewbdata); ++ if (!ret) ++ goto out; ++ ++ DPRINTK_ISPH3A("Stats in near future?\n"); ++ if (aewbdata->frame_number > frame_cnt) ++ frame_diff = aewbdata->frame_number - frame_cnt; ++ else if (aewbdata->frame_number < frame_cnt) { ++ if ((frame_cnt > (MAX_FRAME_COUNT - MAX_FUTURE_FRAMES)) && ++ (aewbdata->frame_number < MAX_FRAME_COUNT)) { ++ frame_diff = aewbdata->frame_number + MAX_FRAME_COUNT - ++ frame_cnt; ++ } else ++ frame_diff = MAX_FUTURE_FRAMES + 1; ++ } ++ ++ if (frame_diff > MAX_FUTURE_FRAMES) { ++ printk(KERN_ERR "Invalid frame requested, returning current" ++ " frame stats\n"); ++ aewbdata->frame_number = frame_cnt; ++ } ++ if (camnotify) { ++ DPRINTK_ISPH3A("NOT Waiting on stats IRQ for frame %d " ++ "because camnotify set\n", ++ aewbdata->frame_number); ++ aewbdata->h3a_aewb_statistics_buf = NULL; ++ goto out; ++ } ++ DPRINTK_ISPH3A("Waiting on stats IRQ for frame %d\n", ++ aewbdata->frame_number); ++ aewbstat.frame_req = aewbdata->frame_number; ++ aewbstat.stats_req = 1; ++ aewbstat.stats_done = 0; ++ init_waitqueue_entry(&wqt, current); ++ ret = wait_event_interruptible(aewbstat.stats_wait, ++ aewbstat.stats_done == 1); ++ if (ret < 0) { ++ printk(KERN_ERR "isph3a_aewb_request_statistics" ++ " Error on wait event %d\n", ret); ++ aewbdata->h3a_aewb_statistics_buf = NULL; ++ return ret; ++ } ++ ++ DPRINTK_ISPH3A("ISP AEWB request status interrupt raised\n"); ++ ret = isph3a_aewb_stats_available(aewbdata); ++ if (ret) { ++ DPRINTK_ISPH3A("After waiting for stats," ++ " stats not available!!\n"); ++ aewbdata->h3a_aewb_statistics_buf = NULL; ++ } ++out: ++ DPRINTK_ISPH3A("isph3a_aewb_request_statistics: " ++ "aewbdata->h3a_aewb_statistics_buf => %p\n", ++ aewbdata->h3a_aewb_statistics_buf); ++ aewbdata->curr_frame = aewbstat.frame_count; ++ ++ return 0; ++} ++EXPORT_SYMBOL(isph3a_aewb_request_statistics); ++ ++/** ++ * isph3a_aewb_init - Module Initialisation. ++ * ++ * Always returns 0. ++ **/ ++int __init isph3a_aewb_init(void) ++{ ++ memset(&aewbstat, 0, sizeof(aewbstat)); ++ memset(&aewb_regs, 0, sizeof(aewb_regs)); ++ ++ init_waitqueue_head(&aewbstat.stats_wait); ++ spin_lock_init(&aewbstat.buffer_lock); ++ return 0; ++} ++ ++/** ++ * isph3a_aewb_cleanup - Module exit. ++ **/ ++void isph3a_aewb_cleanup(void) ++{ ++ int i; ++ ++ for (i = 0; i < H3A_MAX_BUFF; i++) { ++ if (!aewbstat.h3a_buff[i].phy_addr) ++ continue; ++ ++ ispmmu_kunmap(aewbstat.h3a_buff[i].ispmmu_addr); ++ dma_free_coherent(NULL, ++ aewbstat.min_buf_size, ++ (void *)aewbstat.h3a_buff[i].virt_addr, ++ (dma_addr_t)aewbstat.h3a_buff[i].phy_addr); ++ } ++ memset(&aewbstat, 0, sizeof(aewbstat)); ++ memset(&aewb_regs, 0, sizeof(aewb_regs)); ++} ++ ++/** ++ * isph3a_print_status - Debug print. Values of H3A related registers. ++ **/ ++static void isph3a_print_status(void) ++{ ++ DPRINTK_ISPH3A("ISPH3A_PCR = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)); ++ DPRINTK_ISPH3A("ISPH3A_AEWWIN1 = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1)); ++ DPRINTK_ISPH3A("ISPH3A_AEWINSTART = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINSTART)); ++ DPRINTK_ISPH3A("ISPH3A_AEWINBLK = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK)); ++ DPRINTK_ISPH3A("ISPH3A_AEWSUBWIN = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWSUBWIN)); ++ DPRINTK_ISPH3A("ISPH3A_AEWBUFST = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST)); ++ DPRINTK_ISPH3A("stats windows = %d\n", aewbstat.win_count); ++ DPRINTK_ISPH3A("stats buff size = %d\n", aewbstat.stats_buf_size); ++ DPRINTK_ISPH3A("currently configured stats buff size = %d\n", ++ aewbstat.curr_cfg_buf_size); ++} ++ ++/** ++ * isph3a_notify - Unblocks user request for statistics when camera is off ++ * @notify: 1 - Camera is turned off ++ * ++ * Used when the user has requested statistics about a future frame, but the ++ * camera is turned off before it happens, and this function unblocks the ++ * request so the user can continue in its program. ++ **/ ++void isph3a_notify(int notify) ++{ ++ camnotify = notify; ++ if (camnotify && aewbstat.initialized) { ++ printk(KERN_DEBUG "Warning Camera Off \n"); ++ aewbstat.stats_req = 0; ++ aewbstat.stats_done = 1; ++ wake_up_interruptible(&aewbstat.stats_wait); ++ } ++} ++EXPORT_SYMBOL(isph3a_notify); ++ ++/** ++ * isph3a_save_context - Saves the values of the h3a module registers. ++ **/ ++void isph3a_save_context(void) ++{ ++ DPRINTK_ISPH3A(" Saving context\n"); ++ isp_save_context(isph3a_reg_list); ++ /* Avoid enable during restore ctx */ ++ isph3a_reg_list[0].val &= ~ISPH3A_PCR_AEW_EN; ++} ++EXPORT_SYMBOL(isph3a_save_context); ++ ++/** ++ * isph3a_restore_context - Restores the values of the h3a module registers. ++ **/ ++void isph3a_restore_context(void) ++{ ++ DPRINTK_ISPH3A(" Restoring context\n"); ++ isp_restore_context(isph3a_reg_list); ++} ++EXPORT_SYMBOL(isph3a_restore_context); +diff --git a/drivers/media/video/isp/isph3a.h b/drivers/media/video/isp/isph3a.h +new file mode 100644 +index 0000000..7d4c765 +--- /dev/null ++++ b/drivers/media/video/isp/isph3a.h +@@ -0,0 +1,127 @@ ++/* ++ * isph3a.h ++ * ++ * Include file for H3A module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_H3A_H ++#define OMAP_ISP_H3A_H ++ ++#include <mach/isp_user.h> ++ ++#define AEWB_PACKET_SIZE 16 ++#define H3A_MAX_BUFF 5 ++ ++/* Flags for changed registers */ ++#define PCR_CHNG (1 << 0) ++#define AEWWIN1_CHNG (1 << 1) ++#define AEWINSTART_CHNG (1 << 2) ++#define AEWINBLK_CHNG (1 << 3) ++#define AEWSUBWIN_CHNG (1 << 4) ++#define PRV_WBDGAIN_CHNG (1 << 5) ++#define PRV_WBGAIN_CHNG (1 << 6) ++ ++/* ISPH3A REGISTERS bits */ ++#define ISPH3A_PCR_AF_EN (1 << 0) ++#define ISPH3A_PCR_AF_ALAW_EN (1 << 1) ++#define ISPH3A_PCR_AF_MED_EN (1 << 2) ++#define ISPH3A_PCR_AF_BUSY (1 << 15) ++#define ISPH3A_PCR_AEW_EN (1 << 16) ++#define ISPH3A_PCR_AEW_ALAW_EN (1 << 17) ++#define ISPH3A_PCR_AEW_BUSY (1 << 18) ++ ++#define WRITE_SAT_LIM(reg, sat_limit) \ ++ (reg = (reg & (~(ISPH3A_PCR_AEW_AVE2LMT_MASK))) \ ++ | (sat_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT)) ++ ++#define WRITE_ALAW(reg, alaw_en) \ ++ (reg = (reg & (~(ISPH3A_PCR_AEW_ALAW_EN))) \ ++ | ((alaw_en & ISPH3A_PCR_AF_ALAW_EN) \ ++ << ISPH3A_PCR_AEW_ALAW_EN_SHIFT)) ++ ++#define WRITE_WIN_H(reg, height) \ ++ (reg = (reg & (~(ISPH3A_AEWWIN1_WINH_MASK))) \ ++ | (((height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT)) ++ ++#define WRITE_WIN_W(reg, width) \ ++ (reg = (reg & (~(ISPH3A_AEWWIN1_WINW_MASK))) \ ++ | (((width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT)) ++ ++#define WRITE_VER_C(reg, ver_count) \ ++ (reg = (reg & ~(ISPH3A_AEWWIN1_WINVC_MASK)) \ ++ | ((ver_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT)) ++ ++#define WRITE_HOR_C(reg, hor_count) \ ++ (reg = (reg & ~(ISPH3A_AEWWIN1_WINHC_MASK)) \ ++ | ((hor_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT)) ++ ++#define WRITE_VER_WIN_ST(reg, ver_win_st) \ ++ (reg = (reg & ~(ISPH3A_AEWINSTART_WINSV_MASK)) \ ++ | (ver_win_st << ISPH3A_AEWINSTART_WINSV_SHIFT)) ++ ++#define WRITE_HOR_WIN_ST(reg, hor_win_st) \ ++ (reg = (reg & ~(ISPH3A_AEWINSTART_WINSH_MASK)) \ ++ | (hor_win_st << ISPH3A_AEWINSTART_WINSH_SHIFT)) ++ ++#define WRITE_BLK_VER_WIN_ST(reg, blk_win_st) \ ++ (reg = (reg & ~(ISPH3A_AEWINBLK_WINSV_MASK)) \ ++ | (blk_win_st << ISPH3A_AEWINBLK_WINSV_SHIFT)) ++ ++#define WRITE_BLK_WIN_H(reg, height) \ ++ (reg = (reg & ~(ISPH3A_AEWINBLK_WINH_MASK)) \ ++ | (((height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT)) ++ ++#define WRITE_SUB_VER_INC(reg, sub_ver_inc) \ ++ (reg = (reg & ~(ISPH3A_AEWSUBWIN_AEWINCV_MASK)) \ ++ | (((sub_ver_inc >> 1) - 1) << ISPH3A_AEWSUBWIN_AEWINCV_SHIFT)) ++ ++#define WRITE_SUB_HOR_INC(reg, sub_hor_inc) \ ++ (reg = (reg & ~(ISPH3A_AEWSUBWIN_AEWINCH_MASK)) \ ++ | (((sub_hor_inc >> 1) - 1) << ISPH3A_AEWSUBWIN_AEWINCH_SHIFT)) ++ ++/** ++ * struct isph3a_aewb_xtrastats - Structure with extra statistics sent by cam. ++ * @field_count: Sequence number of returned framestats. ++ * @isph3a_aewb_xtrastats: Pointer to next buffer with extra stats. ++ */ ++struct isph3a_aewb_xtrastats { ++ unsigned long field_count; ++ struct isph3a_aewb_xtrastats *next; ++}; ++ ++void isph3a_aewb_setxtrastats(struct isph3a_aewb_xtrastats *xtrastats); ++ ++int isph3a_aewb_configure(struct isph3a_aewb_config *aewbcfg); ++ ++int isph3a_aewb_request_statistics(struct isph3a_aewb_data *aewbdata); ++ ++void isph3a_save_context(void); ++ ++void isph3a_restore_context(void); ++ ++void isph3a_aewb_enable(u8 enable); ++ ++int isph3a_aewb_busy(void); ++ ++void isph3a_aewb_suspend(void); ++ ++void isph3a_aewb_resume(void); ++ ++void isph3a_update_wb(void); ++ ++void isph3a_notify(int notify); ++#endif /* OMAP_ISP_H3A_H */ +diff --git a/drivers/media/video/isp/isphist.c b/drivers/media/video/isp/isphist.c +new file mode 100644 +index 0000000..c6f6a77 +--- /dev/null ++++ b/drivers/media/video/isp/isphist.c +@@ -0,0 +1,608 @@ ++/* ++ * isphist.c ++ * ++ * HISTOGRAM module for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <asm/cacheflush.h> ++ ++#include <linux/delay.h> ++#include <linux/dma-mapping.h> ++#include <linux/uaccess.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "isphist.h" ++#include "ispmmu.h" ++ ++/** ++ * struct isp_hist_status - Histogram status. ++ * @hist_enable: Enables the histogram module. ++ * @initialized: Flag to indicate that the module is correctly initializated. ++ * @frame_cnt: Actual frame count. ++ * @frame_req: Frame requested by user. ++ * @completed: Flag to indicate if a frame request is completed. ++ */ ++struct isp_hist_status { ++ u8 hist_enable; ++ u8 pm_state; ++ u8 initialized; ++ u8 frame_cnt; ++ u8 frame_req; ++ u8 completed; ++} histstat; ++ ++/** ++ * struct isp_hist_buffer - Frame histogram buffer. ++ * @virt_addr: Virtual address to mmap the buffer. ++ * @phy_addr: Physical address of the buffer. ++ * @addr_align: Virtual Address 32 bytes aligned. ++ * @ispmmu_addr: Address of the buffer mapped by the ISPMMU. ++ * @mmap_addr: Mapped memory area of buffer. For userspace access. ++ */ ++struct isp_hist_buffer { ++ unsigned long virt_addr; ++ unsigned long phy_addr; ++ unsigned long addr_align; ++ unsigned long ispmmu_addr; ++ unsigned long mmap_addr; ++} hist_buff; ++ ++/** ++ * struct isp_hist_regs - Current value of Histogram configuration registers. ++ * @reg_pcr: Peripheral control register. ++ * @reg_cnt: Histogram control register. ++ * @reg_wb_gain: Histogram white balance gain register. ++ * @reg_r0_h: Region 0 horizontal register. ++ * @reg_r0_v: Region 0 vertical register. ++ * @reg_r1_h: Region 1 horizontal register. ++ * @reg_r1_v: Region 1 vertical register. ++ * @reg_r2_h: Region 2 horizontal register. ++ * @reg_r2_v: Region 2 vertical register. ++ * @reg_r3_h: Region 3 horizontal register. ++ * @reg_r3_v: Region 3 vertical register. ++ * @reg_hist_addr: Histogram address register. ++ * @reg_hist_data: Histogram data. ++ * @reg_hist_radd: Address register. When input data comes from mem. ++ * @reg_hist_radd_off: Address offset register. When input data comes from mem. ++ * @reg_h_v_info: Image size register. When input data comes from mem. ++ */ ++static struct isp_hist_regs { ++ u32 reg_pcr; ++ u32 reg_cnt; ++ u32 reg_wb_gain; ++ u32 reg_r0_h; ++ u32 reg_r0_v; ++ u32 reg_r1_h; ++ u32 reg_r1_v; ++ u32 reg_r2_h; ++ u32 reg_r2_v; ++ u32 reg_r3_h; ++ u32 reg_r3_v; ++ u32 reg_hist_addr; ++ u32 reg_hist_data; ++ u32 reg_hist_radd; ++ u32 reg_hist_radd_off; ++ u32 reg_h_v_info; ++} hist_regs; ++ ++/* Structure for saving/restoring histogram module registers */ ++struct isp_reg isphist_reg_list[] = { ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD_OFF, 0}, ++ {OMAP3_ISP_IOMEM_HIST, ISPHIST_H_V_INFO, 0}, ++ {0, ISP_TOK_TERM, 0} ++}; ++ ++static void isp_hist_print_status(void); ++ ++void __isp_hist_enable(u8 enable) ++{ ++ if (enable) ++ DPRINTK_ISPHIST(" histogram enabled \n"); ++ else ++ DPRINTK_ISPHIST(" histogram disabled \n"); ++ ++ isp_reg_and_or(OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, ~ISPHIST_PCR_EN, ++ (enable ? ISPHIST_PCR_EN : 0)); ++ histstat.hist_enable = enable; ++} ++ ++/** ++ * isp_hist_enable - Enables ISP Histogram submodule operation. ++ * @enable: 1 - Enables the histogram submodule. ++ * ++ * Client should configure all the Histogram registers before calling this ++ * function. ++ **/ ++void isp_hist_enable(u8 enable) ++{ ++ __isp_hist_enable(enable); ++ histstat.pm_state = enable; ++} ++ ++/** ++ * isp_hist_suspend - Suspend ISP Histogram submodule. ++ **/ ++void isp_hist_suspend(void) ++{ ++ if (histstat.pm_state) ++ __isp_hist_enable(0); ++} ++ ++/** ++ * isp_hist_resume - Resume ISP Histogram submodule. ++ **/ ++void isp_hist_resume(void) ++{ ++ if (histstat.pm_state) ++ __isp_hist_enable(1); ++} ++ ++int isp_hist_busy(void) ++{ ++ return isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) & ++ ISPHIST_PCR_BUSY; ++} ++ ++ ++/** ++ * isp_hist_update_regs - Helper function to update Histogram registers. ++ **/ ++static void isp_hist_update_regs(void) ++{ ++ isp_reg_writel(hist_regs.reg_pcr, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR); ++ isp_reg_writel(hist_regs.reg_cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); ++ isp_reg_writel(hist_regs.reg_wb_gain, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_WB_GAIN); ++ isp_reg_writel(hist_regs.reg_r0_h, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R0_HORZ); ++ isp_reg_writel(hist_regs.reg_r0_v, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R0_VERT); ++ isp_reg_writel(hist_regs.reg_r1_h, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R1_HORZ); ++ isp_reg_writel(hist_regs.reg_r1_v, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R1_VERT); ++ isp_reg_writel(hist_regs.reg_r2_h, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R2_HORZ); ++ isp_reg_writel(hist_regs.reg_r2_v, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R2_VERT); ++ isp_reg_writel(hist_regs.reg_r3_h, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R3_HORZ); ++ isp_reg_writel(hist_regs.reg_r3_v, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_R3_VERT); ++ isp_reg_writel(hist_regs.reg_hist_addr, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_ADDR); ++ isp_reg_writel(hist_regs.reg_hist_data, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_DATA); ++ isp_reg_writel(hist_regs.reg_hist_radd, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_RADD); ++ isp_reg_writel(hist_regs.reg_hist_radd_off, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_RADD_OFF); ++ isp_reg_writel(hist_regs.reg_h_v_info, OMAP3_ISP_IOMEM_HIST, ++ ISPHIST_H_V_INFO); ++} ++ ++/** ++ * isp_hist_isr - Callback from ISP driver for HIST interrupt. ++ * @status: IRQ0STATUS in case of MMU error, 0 for hist interrupt. ++ * arg1 and arg2 Not used as of now. ++ **/ ++static void isp_hist_isr(unsigned long status, isp_vbq_callback_ptr arg1, ++ void *arg2) ++{ ++ isp_hist_enable(0); ++ ++ if (!(status & HIST_DONE)) ++ return; ++ ++ if (!histstat.completed) { ++ if (histstat.frame_req == histstat.frame_cnt) { ++ histstat.frame_cnt = 0; ++ histstat.frame_req = 0; ++ histstat.completed = 1; ++ } else { ++ isp_hist_enable(1); ++ histstat.frame_cnt++; ++ } ++ } ++} ++ ++/** ++ * isp_hist_reset_mem - clear Histogram memory before start stats engine. ++ * ++ * Returns 0 after histogram memory was cleared. ++ **/ ++static int isp_hist_reset_mem(void) ++{ ++ int i; ++ ++ isp_reg_or(OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLR_EN); ++ ++ for (i = 0; i < HIST_MEM_SIZE; i++) ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ ++ isp_reg_and(OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ~ISPHIST_CNT_CLR_EN); ++ ++ return 0; ++} ++ ++/** ++ * isp_hist_set_params - Helper function to check and store user given params. ++ * @user_cfg: Pointer to user configuration structure. ++ * ++ * Returns 0 on success configuration. ++ **/ ++static int isp_hist_set_params(struct isp_hist_config *user_cfg) ++{ ++ ++ int reg_num = 0; ++ int bit_shift = 0; ++ ++ ++ if (isp_hist_busy()) ++ return -EINVAL; ++ ++ if (user_cfg->input_bit_width > MIN_BIT_WIDTH) ++ WRITE_DATA_SIZE(hist_regs.reg_cnt, 0); ++ else ++ WRITE_DATA_SIZE(hist_regs.reg_cnt, 1); ++ ++ WRITE_SOURCE(hist_regs.reg_cnt, user_cfg->hist_source); ++ ++ if (user_cfg->hist_source) { ++ WRITE_HV_INFO(hist_regs.reg_h_v_info, user_cfg->hist_h_v_info); ++ ++ if ((user_cfg->hist_radd & ISP_32B_BOUNDARY_BUF) == ++ user_cfg->hist_radd) { ++ WRITE_RADD(hist_regs.reg_hist_radd, ++ user_cfg->hist_radd); ++ } else { ++ printk(KERN_ERR "Address should be in 32 byte boundary" ++ "\n"); ++ return -EINVAL; ++ } ++ ++ if ((user_cfg->hist_radd_off & ISP_32B_BOUNDARY_OFFSET) == ++ user_cfg->hist_radd_off) { ++ WRITE_RADD_OFF(hist_regs.reg_hist_radd_off, ++ user_cfg->hist_radd_off); ++ } else { ++ printk(KERN_ERR "Offset should be in 32 byte boundary" ++ "\n"); ++ return -EINVAL; ++ } ++ ++ } ++ ++ isp_hist_reset_mem(); ++ DPRINTK_ISPHIST("ISPHIST: Memory Cleared\n"); ++ histstat.frame_req = user_cfg->hist_frames; ++ ++ if (unlikely(user_cfg->wb_gain_R > MAX_WB_GAIN || ++ user_cfg->wb_gain_RG > MAX_WB_GAIN || ++ user_cfg->wb_gain_B > MAX_WB_GAIN || ++ user_cfg->wb_gain_BG > MAX_WB_GAIN)) { ++ printk(KERN_ERR "Invalid WB gain\n"); ++ return -EINVAL; ++ } else { ++ WRITE_WB_R(hist_regs.reg_wb_gain, user_cfg->wb_gain_R); ++ WRITE_WB_RG(hist_regs.reg_wb_gain, user_cfg->wb_gain_RG); ++ WRITE_WB_B(hist_regs.reg_wb_gain, user_cfg->wb_gain_B); ++ WRITE_WB_BG(hist_regs.reg_wb_gain, user_cfg->wb_gain_BG); ++ } ++ ++ /* Regions size and position */ ++ ++ if (user_cfg->num_regions > MAX_REGIONS) ++ return -EINVAL; ++ ++ if (likely((user_cfg->reg0_hor & ISPHIST_REGHORIZ_HEND_MASK) - ++ ((user_cfg->reg0_hor & ISPHIST_REGHORIZ_HSTART_MASK) >> ++ ISPHIST_REGHORIZ_HSTART_SHIFT))) { ++ WRITE_REG_HORIZ(hist_regs.reg_r0_h, user_cfg->reg0_hor); ++ reg_num++; ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ ++ if (likely((user_cfg->reg0_ver & ISPHIST_REGVERT_VEND_MASK) - ++ ((user_cfg->reg0_ver & ISPHIST_REGVERT_VSTART_MASK) >> ++ ISPHIST_REGVERT_VSTART_SHIFT))) { ++ WRITE_REG_VERT(hist_regs.reg_r0_v, user_cfg->reg0_ver); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ ++ if (user_cfg->num_regions >= 1) { ++ if (likely((user_cfg->reg1_hor & ISPHIST_REGHORIZ_HEND_MASK) - ++ ((user_cfg->reg1_hor & ++ ISPHIST_REGHORIZ_HSTART_MASK) >> ++ ISPHIST_REGHORIZ_HSTART_SHIFT))) { ++ WRITE_REG_HORIZ(hist_regs.reg_r1_h, user_cfg->reg1_hor); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ ++ if (likely((user_cfg->reg1_ver & ISPHIST_REGVERT_VEND_MASK) - ++ ((user_cfg->reg1_ver & ++ ISPHIST_REGVERT_VSTART_MASK) >> ++ ISPHIST_REGVERT_VSTART_SHIFT))) { ++ WRITE_REG_VERT(hist_regs.reg_r1_v, user_cfg->reg1_ver); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (user_cfg->num_regions >= 2) { ++ if (likely((user_cfg->reg2_hor & ISPHIST_REGHORIZ_HEND_MASK) - ++ ((user_cfg->reg2_hor & ++ ISPHIST_REGHORIZ_HSTART_MASK) >> ++ ISPHIST_REGHORIZ_HSTART_SHIFT))) { ++ WRITE_REG_HORIZ(hist_regs.reg_r2_h, user_cfg->reg2_hor); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ ++ if (likely((user_cfg->reg2_ver & ISPHIST_REGVERT_VEND_MASK) - ++ ((user_cfg->reg2_ver & ++ ISPHIST_REGVERT_VSTART_MASK) >> ++ ISPHIST_REGVERT_VSTART_SHIFT))) { ++ WRITE_REG_VERT(hist_regs.reg_r2_v, user_cfg->reg2_ver); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (user_cfg->num_regions >= 3) { ++ if (likely((user_cfg->reg3_hor & ISPHIST_REGHORIZ_HEND_MASK) - ++ ((user_cfg->reg3_hor & ++ ISPHIST_REGHORIZ_HSTART_MASK) >> ++ ISPHIST_REGHORIZ_HSTART_SHIFT))) { ++ WRITE_REG_HORIZ(hist_regs.reg_r3_h, user_cfg->reg3_hor); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ ++ if (likely((user_cfg->reg3_ver & ISPHIST_REGVERT_VEND_MASK) - ++ ((user_cfg->reg3_ver & ++ ISPHIST_REGVERT_VSTART_MASK) >> ++ ISPHIST_REGVERT_VSTART_SHIFT))) { ++ WRITE_REG_VERT(hist_regs.reg_r3_v, user_cfg->reg3_ver); ++ } else { ++ printk(KERN_ERR "Invalid Region parameters\n"); ++ return -EINVAL; ++ } ++ } ++ reg_num = user_cfg->num_regions; ++ if (unlikely(((user_cfg->hist_bins > BINS_256) && ++ (user_cfg->hist_bins != BINS_32)) || ++ ((user_cfg->hist_bins == BINS_256) && ++ reg_num != 0) || ((user_cfg->hist_bins == ++ BINS_128) && reg_num >= 2))) { ++ printk(KERN_ERR "Invalid Bins Number: %d\n", ++ user_cfg->hist_bins); ++ return -EINVAL; ++ } else { ++ WRITE_NUM_BINS(hist_regs.reg_cnt, user_cfg->hist_bins); ++ } ++ ++ if (user_cfg->input_bit_width > MAX_BIT_WIDTH || ++ user_cfg->input_bit_width < MIN_BIT_WIDTH) { ++ printk(KERN_ERR "Invalid Bit Width: %d\n", ++ user_cfg->input_bit_width); ++ return -EINVAL; ++ } else { ++ switch (user_cfg->hist_bins) { ++ case BINS_256: ++ bit_shift = user_cfg->input_bit_width - 8; ++ break; ++ case BINS_128: ++ bit_shift = user_cfg->input_bit_width - 7; ++ break; ++ case BINS_64: ++ bit_shift = user_cfg->input_bit_width - 6; ++ break; ++ case BINS_32: ++ bit_shift = user_cfg->input_bit_width - 5; ++ break; ++ default: ++ return -EINVAL; ++ } ++ WRITE_BIT_SHIFT(hist_regs.reg_cnt, bit_shift); ++ } ++ ++ isp_hist_update_regs(); ++ histstat.initialized = 1; ++ ++ return 0; ++} ++ ++/** ++ * isp_hist_configure - API to configure HIST registers. ++ * @histcfg: Pointer to user configuration structure. ++ * ++ * Returns 0 on success configuration. ++ **/ ++int isp_hist_configure(struct isp_hist_config *histcfg) ++{ ++ ++ int ret = 0; ++ ++ if (NULL == histcfg) { ++ printk(KERN_ERR "Null argument in configuration. \n"); ++ return -EINVAL; ++ } ++ ++ if (!histstat.initialized) { ++ DPRINTK_ISPHIST("Setting callback for HISTOGRAM\n"); ++ ret = isp_set_callback(CBK_HIST_DONE, isp_hist_isr, ++ (void *)NULL, (void *)NULL); ++ if (ret) { ++ printk(KERN_ERR "No callback for HIST\n"); ++ return ret; ++ } ++ } ++ ++ ret = isp_hist_set_params(histcfg); ++ if (ret) { ++ printk(KERN_ERR "Invalid parameters! \n"); ++ return ret; ++ } ++ ++ histstat.frame_cnt = 0; ++ histstat.completed = 0; ++ isp_hist_enable(1); ++ isp_hist_print_status(); ++ ++ return 0; ++} ++EXPORT_SYMBOL(isp_hist_configure); ++ ++/** ++ * isp_hist_request_statistics - Request statistics in Histogram. ++ * @histdata: Pointer to data structure. ++ * ++ * This API allows the user to request for histogram statistics. ++ * ++ * Returns 0 on successful request. ++ **/ ++int isp_hist_request_statistics(struct isp_hist_data *histdata) ++{ ++ int i, ret; ++ u32 curr; ++ ++ if (isp_hist_busy()) ++ return -EBUSY; ++ ++ if (!histstat.completed && histstat.initialized) ++ return -EINVAL; ++ ++ isp_reg_or(OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLR_EN); ++ ++ for (i = 0; i < HIST_MEM_SIZE; i++) { ++ curr = isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ ret = put_user(curr, histdata->hist_statistics_buf + i); ++ if (ret) { ++ printk(KERN_ERR "Failed copy_to_user for " ++ "HIST stats buff, %d\n", ret); ++ } ++ } ++ ++ isp_reg_and(OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ++ ~ISPHIST_CNT_CLR_EN); ++ histstat.completed = 0; ++ return 0; ++} ++EXPORT_SYMBOL(isp_hist_request_statistics); ++ ++/** ++ * isp_hist_init - Module Initialization. ++ * ++ * Returns 0 if successful. ++ **/ ++int __init isp_hist_init(void) ++{ ++ memset(&histstat, 0, sizeof(histstat)); ++ memset(&hist_regs, 0, sizeof(hist_regs)); ++ ++ return 0; ++} ++ ++/** ++ * isp_hist_cleanup - Module cleanup. ++ **/ ++void isp_hist_cleanup(void) ++{ ++ memset(&histstat, 0, sizeof(histstat)); ++ memset(&hist_regs, 0, sizeof(hist_regs)); ++} ++ ++/** ++ * isphist_save_context - Saves the values of the histogram module registers. ++ **/ ++void isphist_save_context(void) ++{ ++ DPRINTK_ISPHIST(" Saving context\n"); ++ isp_save_context(isphist_reg_list); ++} ++EXPORT_SYMBOL(isphist_save_context); ++ ++/** ++ * isphist_restore_context - Restores the values of the histogram module regs. ++ **/ ++void isphist_restore_context(void) ++{ ++ DPRINTK_ISPHIST(" Restoring context\n"); ++ isp_restore_context(isphist_reg_list); ++} ++EXPORT_SYMBOL(isphist_restore_context); ++ ++/** ++ * isp_hist_print_status - Debug print ++ **/ ++static void isp_hist_print_status(void) ++{ ++ DPRINTK_ISPHIST("ISPHIST_PCR = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR)); ++ DPRINTK_ISPHIST("ISPHIST_CNT = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT)); ++ DPRINTK_ISPHIST("ISPHIST_WB_GAIN = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN)); ++ DPRINTK_ISPHIST("ISPHIST_R0_HORZ = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ)); ++ DPRINTK_ISPHIST("ISPHIST_R0_VERT = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT)); ++ DPRINTK_ISPHIST("ISPHIST_R1_HORZ = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ)); ++ DPRINTK_ISPHIST("ISPHIST_R1_VERT = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT)); ++ DPRINTK_ISPHIST("ISPHIST_R2_HORZ = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ)); ++ DPRINTK_ISPHIST("ISPHIST_R2_VERT = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT)); ++ DPRINTK_ISPHIST("ISPHIST_R3_HORZ = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ)); ++ DPRINTK_ISPHIST("ISPHIST_R3_VERT = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT)); ++ DPRINTK_ISPHIST("ISPHIST_ADDR = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR)); ++ DPRINTK_ISPHIST("ISPHIST_RADD = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD)); ++ DPRINTK_ISPHIST("ISPHIST_RADD_OFF = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_RADD_OFF)); ++ DPRINTK_ISPHIST("ISPHIST_H_V_INFO = 0x%08x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_HIST, ISPHIST_H_V_INFO)); ++} +diff --git a/drivers/media/video/isp/isphist.h b/drivers/media/video/isp/isphist.h +new file mode 100644 +index 0000000..6b17c4e +--- /dev/null ++++ b/drivers/media/video/isp/isphist.h +@@ -0,0 +1,105 @@ ++/* ++ * isphist.h ++ * ++ * Header file for HISTOGRAM module in TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_HIST_H ++#define OMAP_ISP_HIST_H ++ ++#include <mach/isp_user.h> ++ ++#define MAX_REGIONS 0x4 ++#define MAX_WB_GAIN 255 ++#define MIN_WB_GAIN 0x0 ++#define MAX_BIT_WIDTH 14 ++#define MIN_BIT_WIDTH 8 ++ ++#define ISPHIST_PCR_EN (1 << 0) ++#define HIST_MEM_SIZE 1024 ++#define ISPHIST_CNT_CLR_EN (1 << 7) ++ ++#define WRITE_SOURCE(reg, source) \ ++ (reg = (reg & ~(ISPHIST_CNT_SOURCE_MASK)) \ ++ | (source << ISPHIST_CNT_SOURCE_SHIFT)) ++ ++#define WRITE_HV_INFO(reg, hv_info) \ ++ (reg = ((reg & ~(ISPHIST_HV_INFO_MASK)) \ ++ | (hv_info & ISPHIST_HV_INFO_MASK))) ++ ++#define WRITE_RADD(reg, radd) \ ++ (reg = (reg & ~(ISPHIST_RADD_MASK)) \ ++ | (radd << ISPHIST_RADD_SHIFT)) ++ ++#define WRITE_RADD_OFF(reg, radd_off) \ ++ (reg = (reg & ~(ISPHIST_RADD_OFF_MASK)) \ ++ | (radd_off << ISPHIST_RADD_OFF_SHIFT)) ++ ++#define WRITE_BIT_SHIFT(reg, bit_shift) \ ++ (reg = (reg & ~(ISPHIST_CNT_SHIFT_MASK)) \ ++ | (bit_shift << ISPHIST_CNT_SHIFT_SHIFT)) ++ ++#define WRITE_DATA_SIZE(reg, data_size) \ ++ (reg = (reg & ~(ISPHIST_CNT_DATASIZE_MASK)) \ ++ | (data_size << ISPHIST_CNT_DATASIZE_SHIFT)) ++ ++#define WRITE_NUM_BINS(reg, num_bins) \ ++ (reg = (reg & ~(ISPHIST_CNT_BINS_MASK)) \ ++ | (num_bins << ISPHIST_CNT_BINS_SHIFT)) ++ ++#define WRITE_WB_R(reg, reg_wb_gain) \ ++ reg = ((reg & ~(ISPHIST_WB_GAIN_WG00_MASK)) \ ++ | (reg_wb_gain << ISPHIST_WB_GAIN_WG00_SHIFT)) ++ ++#define WRITE_WB_RG(reg, reg_wb_gain) \ ++ (reg = (reg & ~(ISPHIST_WB_GAIN_WG01_MASK)) \ ++ | (reg_wb_gain << ISPHIST_WB_GAIN_WG01_SHIFT)) ++ ++#define WRITE_WB_B(reg, reg_wb_gain) \ ++ (reg = (reg & ~(ISPHIST_WB_GAIN_WG02_MASK)) \ ++ | (reg_wb_gain << ISPHIST_WB_GAIN_WG02_SHIFT)) ++ ++#define WRITE_WB_BG(reg, reg_wb_gain) \ ++ (reg = (reg & ~(ISPHIST_WB_GAIN_WG03_MASK)) \ ++ | (reg_wb_gain << ISPHIST_WB_GAIN_WG03_SHIFT)) ++ ++#define WRITE_REG_HORIZ(reg, reg_n_hor) \ ++ (reg = ((reg & ~ISPHIST_REGHORIZ_MASK) \ ++ | (reg_n_hor & ISPHIST_REGHORIZ_MASK))) ++ ++#define WRITE_REG_VERT(reg, reg_n_vert) \ ++ (reg = ((reg & ~ISPHIST_REGVERT_MASK) \ ++ | (reg_n_vert & ISPHIST_REGVERT_MASK))) ++ ++ ++void isp_hist_enable(u8 enable); ++ ++int isp_hist_busy(void); ++ ++int isp_hist_configure(struct isp_hist_config *histcfg); ++ ++int isp_hist_request_statistics(struct isp_hist_data *histdata); ++ ++void isphist_save_context(void); ++ ++void isp_hist_suspend(void); ++ ++void isp_hist_resume(void); ++ ++void isphist_restore_context(void); ++ ++#endif /* OMAP_ISP_HIST */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0007-omap3isp-Add-CSI2-interface-support.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0007-omap3isp-Add-CSI2-interface-support.patch new file mode 100644 index 0000000000..842f395388 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0007-omap3isp-Add-CSI2-interface-support.patch @@ -0,0 +1,2384 @@ +From 9fbe7b786427d981cac890a7407da09232f5d1e2 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add CSI2 interface support + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/isp/ispcsi2.c | 2124 +++++++++++++++++++++++++++++++++++++ + drivers/media/video/isp/ispcsi2.h | 232 ++++ + 2 files changed, 2356 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/ispcsi2.c + create mode 100644 drivers/media/video/isp/ispcsi2.h + +diff --git a/drivers/media/video/isp/ispcsi2.c b/drivers/media/video/isp/ispcsi2.c +new file mode 100644 +index 0000000..5141b5a +--- /dev/null ++++ b/drivers/media/video/isp/ispcsi2.c +@@ -0,0 +1,2124 @@ ++/* ++ * ispcsi2.c ++ * ++ * Driver Library for ISP CSI Control module in TI's OMAP3 Camera ISP ++ * ISP CSI interface and IRQ related APIs are defined here. ++ * ++ * Copyright (C) 2009 Texas Instruments. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Dominic Curran <dcurran@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <linux/delay.h> ++#include <media/v4l2-common.h> ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispcsi2.h" ++ ++static struct isp_csi2_cfg current_csi2_cfg; ++static struct isp_csi2_cfg_update current_csi2_cfg_update; ++ ++static bool update_complexio_cfg1; ++static bool update_phy_cfg0; ++static bool update_phy_cfg1; ++static bool update_ctx_ctrl1[8]; ++static bool update_ctx_ctrl2[8]; ++static bool update_ctx_ctrl3[8]; ++static bool update_timing; ++static bool update_ctrl; ++static bool uses_videoport; ++ ++/** ++ * isp_csi2_complexio_lanes_config - Configuration of CSI2 ComplexIO lanes. ++ * @reqcfg: Pointer to structure containing desired lane configuration ++ * ++ * Validates and saves to internal driver memory the passed configuration. ++ * Returns 0 if successful, or -EINVAL if null pointer is passed, invalid ++ * lane position or polarity is set, and if 2 lanes try to occupy the same ++ * position. To apply this settings, use the isp_csi2_complexio_lanes_update() ++ * function just after calling this function. ++ **/ ++int isp_csi2_complexio_lanes_config(struct isp_csi2_lanes_cfg *reqcfg) ++{ ++ int i; ++ bool pos_occupied[5] = {false, false, false, false, false}; ++ struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; ++ struct isp_csi2_lanes_cfg_update *currlanes_u = ++ ¤t_csi2_cfg_update.lanes; ++ ++ /* Validating parameters sent by driver */ ++ if (reqcfg == NULL) { ++ printk(KERN_ERR "Invalid Complex IO Configuration sent by" ++ " sensor\n"); ++ goto err_einval; ++ } ++ ++ /* Data lanes verification */ ++ for (i = 0; i < 4; i++) { ++ if ((reqcfg->data[i].pol > 1) || (reqcfg->data[i].pos > 5)) { ++ printk(KERN_ERR "Invalid CSI-2 Complex IO configuration" ++ " parameters for data lane #%d\n", i); ++ goto err_einval; ++ } ++ if (pos_occupied[reqcfg->data[i].pos - 1] && ++ reqcfg->data[i].pos > 0) { ++ printk(KERN_ERR "Lane #%d already occupied\n", ++ reqcfg->data[i].pos); ++ goto err_einval; ++ } else ++ pos_occupied[reqcfg->data[i].pos - 1] = true; ++ } ++ ++ /* Clock lane verification */ ++ if ((reqcfg->clk.pol > 1) || (reqcfg->clk.pos > 5) || ++ (reqcfg->clk.pos == 0)) { ++ printk(KERN_ERR "Invalid CSI-2 Complex IO configuration" ++ " parameters for clock lane\n"); ++ goto err_einval; ++ } ++ if (pos_occupied[reqcfg->clk.pos - 1]) { ++ printk(KERN_ERR "Lane #%d already occupied", ++ reqcfg->clk.pos); ++ goto err_einval; ++ } else ++ pos_occupied[reqcfg->clk.pos - 1] = true; ++ ++ for (i = 0; i < 4; i++) { ++ if (currlanes->data[i].pos != reqcfg->data[i].pos) { ++ currlanes->data[i].pos = reqcfg->data[i].pos; ++ currlanes_u->data[i] = true; ++ update_complexio_cfg1 = true; ++ } ++ if (currlanes->data[i].pol != reqcfg->data[i].pol) { ++ currlanes->data[i].pol = reqcfg->data[i].pol; ++ currlanes_u->data[i] = true; ++ update_complexio_cfg1 = true; ++ } ++ } ++ ++ if (currlanes->clk.pos != reqcfg->clk.pos) { ++ currlanes->clk.pos = reqcfg->clk.pos; ++ currlanes_u->clk = true; ++ update_complexio_cfg1 = true; ++ } ++ if (currlanes->clk.pol != reqcfg->clk.pol) { ++ currlanes->clk.pol = reqcfg->clk.pol; ++ currlanes_u->clk = true; ++ update_complexio_cfg1 = true; ++ } ++ return 0; ++err_einval: ++ return -EINVAL; ++} ++ ++/** ++ * isp_csi2_complexio_lanes_update - Applies CSI2 ComplexIO lanes configuration. ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_complexio_lanes_config() function. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_complexio_lanes_config() function, unless the force_update flag is ++ * set to true. ++ * Always returns 0. ++ **/ ++int isp_csi2_complexio_lanes_update(bool force_update) ++{ ++ struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; ++ struct isp_csi2_lanes_cfg_update *currlanes_u = ++ ¤t_csi2_cfg_update.lanes; ++ u32 reg; ++ int i; ++ ++ if (!update_complexio_cfg1 && !force_update) ++ return 0; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1); ++ for (i = 0; i < 4; i++) { ++ if (currlanes_u->data[i] || force_update) { ++ reg &= ~(ISPCSI2_COMPLEXIO_CFG1_DATA_POL_MASK(i + 1) | ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_MASK(i + ++ 1)); ++ reg |= (currlanes->data[i].pol << ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(i + 1)); ++ reg |= (currlanes->data[i].pos << ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(i + ++ 1)); ++ currlanes_u->data[i] = false; ++ } ++ } ++ ++ if (currlanes_u->clk || force_update) { ++ reg &= ~(ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_MASK | ++ ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_MASK); ++ reg |= (currlanes->clk.pol << ++ ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT); ++ reg |= (currlanes->clk.pos << ++ ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT); ++ currlanes_u->clk = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1); ++ ++ update_complexio_cfg1 = false; ++ return 0; ++} ++ ++/** ++ * isp_csi2_complexio_lanes_get - Gets CSI2 ComplexIO lanes configuration. ++ * ++ * Gets settings from HW registers and fills in the internal driver memory ++ * Always returns 0. ++ **/ ++int isp_csi2_complexio_lanes_get(void) ++{ ++ struct isp_csi2_lanes_cfg *currlanes = ¤t_csi2_cfg.lanes; ++ struct isp_csi2_lanes_cfg_update *currlanes_u = ++ ¤t_csi2_cfg_update.lanes; ++ u32 reg; ++ int i; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1); ++ for (i = 0; i < 4; i++) { ++ currlanes->data[i].pol = (reg & ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POL_MASK(i + 1)) >> ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POL_SHIFT(i + 1); ++ currlanes->data[i].pos = (reg & ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_MASK(i + 1)) >> ++ ISPCSI2_COMPLEXIO_CFG1_DATA_POSITION_SHIFT(i + 1); ++ currlanes_u->data[i] = false; ++ } ++ currlanes->clk.pol = (reg & ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_MASK) >> ++ ISPCSI2_COMPLEXIO_CFG1_CLOCK_POL_SHIFT; ++ currlanes->clk.pos = (reg & ++ ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_MASK) >> ++ ISPCSI2_COMPLEXIO_CFG1_CLOCK_POSITION_SHIFT; ++ currlanes_u->clk = false; ++ ++ update_complexio_cfg1 = false; ++ return 0; ++} ++ ++/** ++ * isp_csi2_complexio_power_status - Gets CSI2 ComplexIO power status. ++ * ++ * Returns 3 possible valid states: ISP_CSI2_POWER_OFF, ISP_CSI2_POWER_ON, ++ * and ISP_CSI2_POWER_ULPW. ++ **/ ++static enum isp_csi2_power_cmds isp_csi2_complexio_power_status(void) ++{ ++ enum isp_csi2_power_cmds ret; ++ u32 reg; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1) & ++ ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_MASK; ++ switch (reg) { ++ case ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_OFF: ++ ret = ISP_CSI2_POWER_OFF; ++ break; ++ case ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ON: ++ ret = ISP_CSI2_POWER_ON; ++ break; ++ case ISPCSI2_COMPLEXIO_CFG1_PWR_STATUS_ULPW: ++ ret = ISP_CSI2_POWER_ULPW; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return ret; ++} ++ ++/** ++ * isp_csi2_complexio_power_autoswitch - Sets CSI2 ComplexIO power autoswitch. ++ * @enable: Sets or clears the autoswitch function enable flag. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_complexio_power_autoswitch(bool enable) ++{ ++ u32 reg; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1); ++ reg &= ~ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_MASK; ++ ++ if (enable) ++ reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_ENABLE; ++ else ++ reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_AUTO_DISABLE; ++ ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1); ++ return 0; ++} ++ ++/** ++ * isp_csi2_complexio_power - Sets the desired power command for CSI2 ComplexIO. ++ * @power_cmd: Power command to be set. ++ * ++ * Returns 0 if successful, or -EBUSY if the retry count is exceeded. ++ **/ ++int isp_csi2_complexio_power(enum isp_csi2_power_cmds power_cmd) ++{ ++ enum isp_csi2_power_cmds current_state; ++ u32 reg; ++ u8 retry_count; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1) & ++ ~ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_MASK; ++ switch (power_cmd) { ++ case ISP_CSI2_POWER_OFF: ++ reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_OFF; ++ break; ++ case ISP_CSI2_POWER_ON: ++ reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ON; ++ break; ++ case ISP_CSI2_POWER_ULPW: ++ reg |= ISPCSI2_COMPLEXIO_CFG1_PWR_CMD_ULPW; ++ break; ++ default: ++ printk(KERN_ERR "CSI2: ERROR - Wrong Power command!\n"); ++ return -EINVAL; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_COMPLEXIO_CFG1); ++ ++ retry_count = 0; ++ do { ++ udelay(50); ++ current_state = isp_csi2_complexio_power_status(); ++ ++ if (current_state != power_cmd) { ++ printk(KERN_DEBUG "CSI2: Complex IO power command not" ++ " yet taken."); ++ if (++retry_count < 100) { ++ printk(KERN_DEBUG " Retrying...\n"); ++ udelay(50); ++ } else { ++ printk(KERN_DEBUG " Retry count exceeded!\n"); ++ } ++ } ++ } while ((current_state != power_cmd) && (retry_count < 100)); ++ ++ if (retry_count == 100) ++ return -EBUSY; ++ ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_frame_mode - Configure if_en behaviour for CSI2 ++ * @frame_mode: Desired action for IF_EN switch off. 0 - disable IF immediately ++ * 1 - disable after all Frame end Code is received in all ++ * contexts. ++ * ++ * Validates and saves to internal driver memory the passed configuration. ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_frame_mode(enum isp_csi2_frame_mode frame_mode) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->frame_mode != frame_mode) { ++ currctrl->frame_mode = frame_mode; ++ currctrl_u->frame_mode = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_vp_clk_enable - Enables/disables CSI2 Videoport clock. ++ * @vp_clk_enable: Boolean value to specify the Videoport clock state. ++ * ++ * Validates and saves to internal driver memory the passed configuration. ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_vp_clk_enable(bool vp_clk_enable) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->vp_clk_enable != vp_clk_enable) { ++ currctrl->vp_clk_enable = vp_clk_enable; ++ currctrl_u->vp_clk_enable = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_vp_only_enable - Sets CSI2 Videoport clock as exclusive ++ * @vp_only_enable: Boolean value to specify if the Videoport clock is ++ * exclusive, setting the OCP port as disabled. ++ * ++ * Validates and saves to internal driver memory the passed configuration. ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_vp_only_enable(bool vp_only_enable) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->vp_only_enable != vp_only_enable) { ++ currctrl->vp_only_enable = vp_only_enable; ++ currctrl_u->vp_only_enable = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_vp_out_ctrl - Sets CSI2 Videoport clock divider ++ * @vp_out_ctrl: Divider value for setting videoport clock frequency based on ++ * OCP port frequency, valid dividers are between 1 and 4. ++ * ++ * Validates and saves to internal driver memory the passed configuration. ++ * Returns 0 if successful, or -EINVAL if wrong divider value is passed. ++ **/ ++int isp_csi2_ctrl_config_vp_out_ctrl(u8 vp_out_ctrl) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if ((vp_out_ctrl == 0) || (vp_out_ctrl > 4)) { ++ printk(KERN_ERR "CSI2: Wrong divisor value. Must be between" ++ " 1 and 4"); ++ return -EINVAL; ++ } ++ ++ if (currctrl->vp_out_ctrl != vp_out_ctrl) { ++ currctrl->vp_out_ctrl = vp_out_ctrl; ++ currctrl_u->vp_out_ctrl = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_debug_enable - Sets CSI2 debug ++ * @debug_enable: Boolean for setting debug configuration on CSI2. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_debug_enable(bool debug_enable) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->debug_enable != debug_enable) { ++ currctrl->debug_enable = debug_enable; ++ currctrl_u->debug_enable = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_burst_size - Sets CSI2 burst size. ++ * @burst_size: Burst size of the memory saving capability of receiver. ++ * ++ * Returns 0 if successful, or -EINVAL if burst size is wrong. ++ **/ ++int isp_csi2_ctrl_config_burst_size(u8 burst_size) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ if (burst_size > 3) { ++ printk(KERN_ERR "CSI2: Wrong burst size. Must be between" ++ " 0 and 3"); ++ return -EINVAL; ++ } ++ ++ if (currctrl->burst_size != burst_size) { ++ currctrl->burst_size = burst_size; ++ currctrl_u->burst_size = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_ecc_enable - Enables ECC on CSI2 Receiver ++ * @ecc_enable: Boolean to enable/disable the CSI2 receiver ECC handling. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_ecc_enable(bool ecc_enable) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->ecc_enable != ecc_enable) { ++ currctrl->ecc_enable = ecc_enable; ++ currctrl_u->ecc_enable = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_ecc_enable - Enables ECC on CSI2 Receiver ++ * @ecc_enable: Boolean to enable/disable the CSI2 receiver ECC handling. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_secure_mode(bool secure_mode) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->secure_mode != secure_mode) { ++ currctrl->secure_mode = secure_mode; ++ currctrl_u->secure_mode = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_config_if_enable - Enables CSI2 Receiver interface. ++ * @if_enable: Boolean to enable/disable the CSI2 receiver interface. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_config_if_enable(bool if_enable) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ ++ if (currctrl->if_enable != if_enable) { ++ currctrl->if_enable = if_enable; ++ currctrl_u->if_enable = true; ++ update_ctrl = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_update - Applies CSI2 control configuration. ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_ctrl_config_*() functions. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_ctrl_config_*() functions, unless the force_update flag is ++ * set to true. ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_update(bool force_update) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ u32 reg; ++ ++ if (update_ctrl || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTRL); ++ if (currctrl_u->frame_mode || force_update) { ++ reg &= ~ISPCSI2_CTRL_FRAME_MASK; ++ if (currctrl->frame_mode) ++ reg |= ISPCSI2_CTRL_FRAME_DISABLE_FEC; ++ else ++ reg |= ISPCSI2_CTRL_FRAME_DISABLE_IMM; ++ currctrl_u->frame_mode = false; ++ } ++ if (currctrl_u->vp_clk_enable || force_update) { ++ reg &= ~ISPCSI2_CTRL_VP_CLK_EN_MASK; ++ if (currctrl->vp_clk_enable) ++ reg |= ISPCSI2_CTRL_VP_CLK_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTRL_VP_CLK_EN_DISABLE; ++ currctrl_u->vp_clk_enable = false; ++ } ++ if (currctrl_u->vp_only_enable || force_update) { ++ reg &= ~ISPCSI2_CTRL_VP_ONLY_EN_MASK; ++ uses_videoport = currctrl->vp_only_enable; ++ if (currctrl->vp_only_enable) ++ reg |= ISPCSI2_CTRL_VP_ONLY_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTRL_VP_ONLY_EN_DISABLE; ++ currctrl_u->vp_only_enable = false; ++ } ++ if (currctrl_u->vp_out_ctrl || force_update) { ++ reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; ++ reg |= (currctrl->vp_out_ctrl - 1) << ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; ++ currctrl_u->vp_out_ctrl = false; ++ } ++ if (currctrl_u->debug_enable || force_update) { ++ reg &= ~ISPCSI2_CTRL_DBG_EN_MASK; ++ if (currctrl->debug_enable) ++ reg |= ISPCSI2_CTRL_DBG_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTRL_DBG_EN_DISABLE; ++ currctrl_u->debug_enable = false; ++ } ++ if (currctrl_u->burst_size || force_update) { ++ reg &= ~ISPCSI2_CTRL_BURST_SIZE_MASK; ++ reg |= currctrl->burst_size << ++ ISPCSI2_CTRL_BURST_SIZE_SHIFT; ++ currctrl_u->burst_size = false; ++ } ++ if (currctrl_u->ecc_enable || force_update) { ++ reg &= ~ISPCSI2_CTRL_ECC_EN_MASK; ++ if (currctrl->ecc_enable) ++ reg |= ISPCSI2_CTRL_ECC_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTRL_ECC_EN_DISABLE; ++ currctrl_u->ecc_enable = false; ++ } ++ if (currctrl_u->secure_mode || force_update) { ++ reg &= ~ISPCSI2_CTRL_SECURE_MASK; ++ if (currctrl->secure_mode) ++ reg |= ISPCSI2_CTRL_SECURE_ENABLE; ++ else ++ reg |= ISPCSI2_CTRL_SECURE_DISABLE; ++ currctrl_u->secure_mode = false; ++ } ++ if (currctrl_u->if_enable || force_update) { ++ reg &= ~ISPCSI2_CTRL_IF_EN_MASK; ++ if (currctrl->if_enable) ++ reg |= ISPCSI2_CTRL_IF_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTRL_IF_EN_DISABLE; ++ currctrl_u->if_enable = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTRL); ++ update_ctrl = false; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctrl_get - Gets CSI2 control configuration ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctrl_get(void) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = ¤t_csi2_cfg.ctrl; ++ struct isp_csi2_ctrl_cfg_update *currctrl_u = ++ ¤t_csi2_cfg_update.ctrl; ++ u32 reg; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTRL); ++ currctrl->frame_mode = (reg & ISPCSI2_CTRL_FRAME_MASK) >> ++ ISPCSI2_CTRL_FRAME_SHIFT; ++ currctrl_u->frame_mode = false; ++ ++ if ((reg & ISPCSI2_CTRL_VP_CLK_EN_MASK) == ++ ISPCSI2_CTRL_VP_CLK_EN_ENABLE) ++ currctrl->vp_clk_enable = true; ++ else ++ currctrl->vp_clk_enable = false; ++ currctrl_u->vp_clk_enable = false; ++ ++ if ((reg & ISPCSI2_CTRL_VP_ONLY_EN_MASK) == ++ ISPCSI2_CTRL_VP_ONLY_EN_ENABLE) ++ currctrl->vp_only_enable = true; ++ else ++ currctrl->vp_only_enable = false; ++ uses_videoport = currctrl->vp_only_enable; ++ currctrl_u->vp_only_enable = false; ++ ++ currctrl->vp_out_ctrl = ((reg & ISPCSI2_CTRL_VP_OUT_CTRL_MASK) >> ++ ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) + 1; ++ currctrl_u->vp_out_ctrl = false; ++ ++ if ((reg & ISPCSI2_CTRL_DBG_EN_MASK) == ISPCSI2_CTRL_DBG_EN_ENABLE) ++ currctrl->debug_enable = true; ++ else ++ currctrl->debug_enable = false; ++ currctrl_u->debug_enable = false; ++ ++ currctrl->burst_size = (reg & ISPCSI2_CTRL_BURST_SIZE_MASK) >> ++ ISPCSI2_CTRL_BURST_SIZE_SHIFT; ++ currctrl_u->burst_size = false; ++ ++ if ((reg & ISPCSI2_CTRL_ECC_EN_MASK) == ISPCSI2_CTRL_ECC_EN_ENABLE) ++ currctrl->ecc_enable = true; ++ else ++ currctrl->ecc_enable = false; ++ currctrl_u->ecc_enable = false; ++ ++ if ((reg & ISPCSI2_CTRL_SECURE_MASK) == ISPCSI2_CTRL_SECURE_ENABLE) ++ currctrl->secure_mode = true; ++ else ++ currctrl->secure_mode = false; ++ currctrl_u->secure_mode = false; ++ ++ if ((reg & ISPCSI2_CTRL_IF_EN_MASK) == ISPCSI2_CTRL_IF_EN_ENABLE) ++ currctrl->if_enable = true; ++ else ++ currctrl->if_enable = false; ++ currctrl_u->if_enable = false; ++ ++ update_ctrl = false; ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_validate - Validates the context number value ++ * @ctxnum: Pointer to variable containing context number. ++ * ++ * If the value is not in range (3 bits), it is being ANDed with 0x7 to force ++ * it to be on range. ++ **/ ++static void isp_csi2_ctx_validate(u8 *ctxnum) ++{ ++ if (*ctxnum > 7) { ++ printk(KERN_ERR "Invalid context number. Forcing valid" ++ " value...\n"); ++ *ctxnum &= ~(0x7); ++ } ++} ++ ++/** ++ * isp_csi2_ctx_config_virtual_id - Maps a virtual ID with a CSI2 Rx context ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @virtual_id: CSI2 Virtual ID to associate with specified context number. ++ * ++ * Returns 0 if successful, or -EINVAL if Virtual ID is not in range (0-3). ++ **/ ++int isp_csi2_ctx_config_virtual_id(u8 ctxnum, u8 virtual_id) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ if (virtual_id > 3) { ++ printk(KERN_ERR "Wrong requested virtual_id\n"); ++ return -EINVAL; ++ } ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->virtual_id != virtual_id) { ++ selected_ctx->virtual_id = virtual_id; ++ selected_ctx_u->virtual_id = true; ++ update_ctx_ctrl2[ctxnum] = true; ++ } ++ ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_frame_count - Sets frame count to be received in CSI2 Rx. ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @frame_count: Number of frames to acquire. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_frame_count(u8 ctxnum, u8 frame_count) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->frame_count != frame_count) { ++ selected_ctx->frame_count = frame_count; ++ selected_ctx_u->frame_count = true; ++ update_ctx_ctrl1[ctxnum] = true; ++ } ++ ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_format - Maps a pixel format to a specified context. ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @pixformat: V4L2 structure for pixel format. ++ * ++ * Returns 0 if successful, or -EINVAL if the format is not supported by the ++ * receiver. ++ **/ ++int isp_csi2_ctx_config_format(u8 ctxnum, u32 pixformat) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ struct v4l2_pix_format pix; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ pix.pixelformat = pixformat; ++ switch (pix.pixelformat) { ++ case V4L2_PIX_FMT_RGB565: ++ case V4L2_PIX_FMT_RGB565X: ++ case V4L2_PIX_FMT_YUYV: ++ case V4L2_PIX_FMT_UYVY: ++ case V4L2_PIX_FMT_RGB555: ++ case V4L2_PIX_FMT_RGB555X: ++ case V4L2_PIX_FMT_SGRBG10: ++ break; ++ default: ++ printk(KERN_ERR "Context config pixel format unsupported\n"); ++ return -EINVAL; ++ } ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ selected_ctx->format = pix; ++ selected_ctx_u->format = true; ++ update_ctx_ctrl2[ctxnum] = true; ++ ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_alpha - Sets the alpha value for pixel format ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @alpha: Alpha value. ++ * ++ * Returns 0 if successful, or -EINVAL if the alpha value is bigger than 16383. ++ **/ ++int isp_csi2_ctx_config_alpha(u8 ctxnum, u16 alpha) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ if (alpha > 0x3FFF) { ++ printk(KERN_ERR "Wrong alpha value\n"); ++ return -EINVAL; ++ } ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->alpha != alpha) { ++ selected_ctx->alpha = alpha; ++ selected_ctx_u->alpha = true; ++ update_ctx_ctrl3[ctxnum] = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_data_offset - Sets the offset between received lines ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @data_offset: Offset between first pixel of each 2 contiguous lines. ++ * ++ * Returns 0 if successful, or -EINVAL if the line offset is bigger than 1023. ++ **/ ++int isp_csi2_ctx_config_data_offset(u8 ctxnum, u16 data_offset) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ if (data_offset > 0x3FF) { ++ printk(KERN_ERR "Wrong line offset\n"); ++ return -EINVAL; ++ } ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->data_offset != data_offset) { ++ selected_ctx->data_offset = data_offset; ++ selected_ctx_u->data_offset = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_ping_addr - Sets Ping address for CSI2 Rx. buffer saving ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @ping_addr: 32 bit ISP MMU mapped address. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_ping_addr(u8 ctxnum, u32 ping_addr) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ ping_addr &= ~(0x1F); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->ping_addr != ping_addr) { ++ selected_ctx->ping_addr = ping_addr; ++ selected_ctx_u->ping_addr = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_pong_addr - Sets Pong address for CSI2 Rx. buffer saving ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @pong_addr: 32 bit ISP MMU mapped address. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_pong_addr(u8 ctxnum, u32 pong_addr) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ pong_addr &= ~(0x1F); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->pong_addr != pong_addr) { ++ selected_ctx->pong_addr = pong_addr; ++ selected_ctx_u->pong_addr = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_eof_enabled - Enables EOF signal assertion ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @eof_enabled: Boolean to enable/disable EOF signal assertion on received ++ * packets. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_eof_enabled(u8 ctxnum, bool eof_enabled) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->eof_enabled != eof_enabled) { ++ selected_ctx->eof_enabled = eof_enabled; ++ selected_ctx_u->eof_enabled = true; ++ update_ctx_ctrl1[ctxnum] = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_eol_enabled - Enables EOL signal assertion ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @eol_enabled: Boolean to enable/disable EOL signal assertion on received ++ * packets. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_eol_enabled(u8 ctxnum, bool eol_enabled) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->eol_enabled != eol_enabled) { ++ selected_ctx->eol_enabled = eol_enabled; ++ selected_ctx_u->eol_enabled = true; ++ update_ctx_ctrl1[ctxnum] = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_checksum_enabled - Enables Checksum check in rcvd packets ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @checksum_enabled: Boolean to enable/disable Checksum check on received ++ * packets ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_checksum_enabled(u8 ctxnum, bool checksum_enabled) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->checksum_enabled != checksum_enabled) { ++ selected_ctx->checksum_enabled = checksum_enabled; ++ selected_ctx_u->checksum_enabled = true; ++ update_ctx_ctrl1[ctxnum] = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_config_enabled - Enables specified CSI2 context ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @enabled: Boolean to enable/disable specified context. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_config_enabled(u8 ctxnum, bool enabled) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (selected_ctx->enabled != enabled) { ++ selected_ctx->enabled = enabled; ++ selected_ctx_u->enabled = true; ++ update_ctx_ctrl1[ctxnum] = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_update - Applies CSI2 context configuration. ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_ctx_config_*() functions. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_ctx_config_*() functions, unless the force_update flag is ++ * set to true. ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_update(u8 ctxnum, bool force_update) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ u32 reg; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ if (update_ctx_ctrl1[ctxnum] || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL1(ctxnum)); ++ if (selected_ctx_u->frame_count || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL1_COUNT_MASK); ++ reg |= selected_ctx->frame_count << ++ ISPCSI2_CTX_CTRL1_COUNT_SHIFT; ++ selected_ctx_u->frame_count = false; ++ } ++ if (selected_ctx_u->eof_enabled || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL1_EOF_EN_MASK); ++ if (selected_ctx->eof_enabled) ++ reg |= ISPCSI2_CTX_CTRL1_EOF_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTX_CTRL1_EOF_EN_DISABLE; ++ selected_ctx_u->eof_enabled = false; ++ } ++ if (selected_ctx_u->eol_enabled || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL1_EOL_EN_MASK); ++ if (selected_ctx->eol_enabled) ++ reg |= ISPCSI2_CTX_CTRL1_EOL_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTX_CTRL1_EOL_EN_DISABLE; ++ selected_ctx_u->eol_enabled = false; ++ } ++ if (selected_ctx_u->checksum_enabled || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL1_CS_EN_MASK); ++ if (selected_ctx->checksum_enabled) ++ reg |= ISPCSI2_CTX_CTRL1_CS_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTX_CTRL1_CS_EN_DISABLE; ++ selected_ctx_u->checksum_enabled = false; ++ } ++ if (selected_ctx_u->enabled || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL1_CTX_EN_MASK); ++ if (selected_ctx->enabled) ++ reg |= ISPCSI2_CTX_CTRL1_CTX_EN_ENABLE; ++ else ++ reg |= ISPCSI2_CTX_CTRL1_CTX_EN_DISABLE; ++ selected_ctx_u->enabled = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL1(ctxnum)); ++ update_ctx_ctrl1[ctxnum] = false; ++ } ++ ++ if (update_ctx_ctrl2[ctxnum] || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL2(ctxnum)); ++ if (selected_ctx_u->virtual_id || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); ++ reg |= selected_ctx->virtual_id << ++ ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; ++ selected_ctx_u->virtual_id = false; ++ } ++ ++ if (selected_ctx_u->format || force_update) { ++ struct v4l2_pix_format *pix; ++ u16 new_format = 0; ++ ++ reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); ++ pix = &selected_ctx->format; ++ switch (pix->pixelformat) { ++ case V4L2_PIX_FMT_RGB565: ++ case V4L2_PIX_FMT_RGB565X: ++ new_format = 0x22; ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ case V4L2_PIX_FMT_UYVY: ++ if (uses_videoport) ++ new_format = 0x9E; ++ else ++ new_format = 0x1E; ++ break; ++ case V4L2_PIX_FMT_RGB555: ++ case V4L2_PIX_FMT_RGB555X: ++ new_format = 0xA1; ++ break; ++ case V4L2_PIX_FMT_SGRBG10: ++ if (uses_videoport) ++ new_format = 0x12F; ++ else ++ new_format = 0xAB; ++ break; ++ } ++ reg |= (new_format << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT); ++ selected_ctx_u->format = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL2(ctxnum)); ++ update_ctx_ctrl2[ctxnum] = false; ++ } ++ ++ if (update_ctx_ctrl3[ctxnum] || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL3(ctxnum)); ++ if (selected_ctx_u->alpha || force_update) { ++ reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); ++ reg |= (selected_ctx->alpha << ++ ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); ++ selected_ctx_u->alpha = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL3(ctxnum)); ++ update_ctx_ctrl3[ctxnum] = false; ++ } ++ ++ if (selected_ctx_u->data_offset) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_OFST(ctxnum)); ++ reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; ++ reg |= selected_ctx->data_offset << ++ ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_OFST(ctxnum)); ++ selected_ctx_u->data_offset = false; ++ } ++ ++ if (selected_ctx_u->ping_addr) { ++ reg = selected_ctx->ping_addr; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_PING_ADDR(ctxnum)); ++ selected_ctx_u->ping_addr = false; ++ } ++ ++ if (selected_ctx_u->pong_addr) { ++ reg = selected_ctx->pong_addr; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_PONG_ADDR(ctxnum)); ++ selected_ctx_u->pong_addr = false; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_get - Gets specific CSI2 Context configuration ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_get(u8 ctxnum) ++{ ++ struct isp_csi2_ctx_cfg *selected_ctx; ++ struct isp_csi2_ctx_cfg_update *selected_ctx_u; ++ u32 reg; ++ ++ isp_csi2_ctx_validate(&ctxnum); ++ ++ selected_ctx = ¤t_csi2_cfg.contexts[ctxnum]; ++ selected_ctx_u = ¤t_csi2_cfg_update.contexts[ctxnum]; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_CTRL1(ctxnum)); ++ selected_ctx->frame_count = (reg & ISPCSI2_CTX_CTRL1_COUNT_MASK) >> ++ ISPCSI2_CTX_CTRL1_COUNT_SHIFT; ++ selected_ctx_u->frame_count = false; ++ ++ if ((reg & ISPCSI2_CTX_CTRL1_EOF_EN_MASK) == ++ ISPCSI2_CTX_CTRL1_EOF_EN_ENABLE) ++ selected_ctx->eof_enabled = true; ++ else ++ selected_ctx->eof_enabled = false; ++ selected_ctx_u->eof_enabled = false; ++ ++ if ((reg & ISPCSI2_CTX_CTRL1_EOL_EN_MASK) == ++ ISPCSI2_CTX_CTRL1_EOL_EN_ENABLE) ++ selected_ctx->eol_enabled = true; ++ else ++ selected_ctx->eol_enabled = false; ++ selected_ctx_u->eol_enabled = false; ++ ++ if ((reg & ISPCSI2_CTX_CTRL1_CS_EN_MASK) == ++ ISPCSI2_CTX_CTRL1_CS_EN_ENABLE) ++ selected_ctx->checksum_enabled = true; ++ else ++ selected_ctx->checksum_enabled = false; ++ selected_ctx_u->checksum_enabled = false; ++ ++ if ((reg & ISPCSI2_CTX_CTRL1_CTX_EN_MASK) == ++ ISPCSI2_CTX_CTRL1_CTX_EN_ENABLE) ++ selected_ctx->enabled = true; ++ else ++ selected_ctx->enabled = false; ++ selected_ctx_u->enabled = false; ++ update_ctx_ctrl1[ctxnum] = false; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTX_CTRL2(ctxnum)); ++ ++ selected_ctx->virtual_id = (reg & ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK) >> ++ ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; ++ selected_ctx_u->virtual_id = false; ++ ++ switch ((reg & ISPCSI2_CTX_CTRL2_FORMAT_MASK) >> ++ ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) { ++ case 0x22: ++ selected_ctx->format.pixelformat = V4L2_PIX_FMT_RGB565; ++ break; ++ case 0x9E: ++ case 0x1E: ++ selected_ctx->format.pixelformat = V4L2_PIX_FMT_YUYV; ++ break; ++ case 0xA1: ++ selected_ctx->format.pixelformat = V4L2_PIX_FMT_RGB555; ++ break; ++ case 0xAB: ++ case 0x12F: ++ selected_ctx->format.pixelformat = V4L2_PIX_FMT_SGRBG10; ++ break; ++ } ++ selected_ctx_u->format = false; ++ update_ctx_ctrl2[ctxnum] = false; ++ ++ selected_ctx->alpha = (isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL3(ctxnum)) & ++ ISPCSI2_CTX_CTRL3_ALPHA_MASK) >> ++ ISPCSI2_CTX_CTRL3_ALPHA_SHIFT; ++ selected_ctx_u->alpha = false; ++ update_ctx_ctrl3[ctxnum] = false; ++ ++ selected_ctx->data_offset = (isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_OFST(ctxnum)) & ++ ISPCSI2_CTX_DAT_OFST_OFST_MASK) >> ++ ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; ++ selected_ctx_u->data_offset = false; ++ ++ selected_ctx->ping_addr = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_PING_ADDR(ctxnum)); ++ selected_ctx_u->ping_addr = false; ++ ++ selected_ctx->pong_addr = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_PONG_ADDR(ctxnum)); ++ selected_ctx_u->pong_addr = false; ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_update_all - Applies all CSI2 context configuration. ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_ctx_config_*() functions. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_ctx_config_*() functions, unless the force_update flag is ++ * set to true. ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_update_all(bool force_update) ++{ ++ u8 ctxnum; ++ ++ for (ctxnum = 0; ctxnum < 8; ctxnum++) ++ isp_csi2_ctx_update(ctxnum, force_update); ++ ++ return 0; ++} ++ ++/** ++ * isp_csi2_ctx_get_all - Gets all CSI2 Context configurations ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_ctx_get_all(void) ++{ ++ u8 ctxnum; ++ ++ for (ctxnum = 0; ctxnum < 8; ctxnum++) ++ isp_csi2_ctx_get(ctxnum); ++ ++ return 0; ++} ++ ++int isp_csi2_phy_config(struct isp_csi2_phy_cfg *desiredphyconfig) ++{ ++ struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; ++ struct isp_csi2_phy_cfg_update *currphy_u = ++ ¤t_csi2_cfg_update.phy; ++ ++ if ((desiredphyconfig->tclk_term > 0x7f) || ++ (desiredphyconfig->tclk_miss > 0x3)) { ++ printk(KERN_ERR "Invalid PHY configuration sent by the" ++ " driver\n"); ++ return -EINVAL; ++ } ++ ++ if (currphy->ths_term != desiredphyconfig->ths_term) { ++ currphy->ths_term = desiredphyconfig->ths_term; ++ currphy_u->ths_term = true; ++ update_phy_cfg0 = true; ++ } ++ if (currphy->ths_settle != desiredphyconfig->ths_settle) { ++ currphy->ths_settle = desiredphyconfig->ths_settle; ++ currphy_u->ths_settle = true; ++ update_phy_cfg0 = true; ++ } ++ if (currphy->tclk_term != desiredphyconfig->tclk_term) { ++ currphy->tclk_term = desiredphyconfig->tclk_term; ++ currphy_u->tclk_term = true; ++ update_phy_cfg1 = true; ++ } ++ if (currphy->tclk_miss != desiredphyconfig->tclk_miss) { ++ currphy->tclk_miss = desiredphyconfig->tclk_miss; ++ currphy_u->tclk_miss = true; ++ update_phy_cfg1 = true; ++ } ++ if (currphy->tclk_settle != desiredphyconfig->tclk_settle) { ++ currphy->tclk_settle = desiredphyconfig->tclk_settle; ++ currphy_u->tclk_settle = true; ++ update_phy_cfg1 = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_calc_phy_cfg0 - Calculates D-PHY config based on the MIPIClk speed. ++ * @mipiclk: MIPI clock frequency being used with CSI2 sensor. ++ * @lbound_hs_settle: Lower bound for CSI2 High Speed Settle transition. ++ * @ubound_hs_settle: Upper bound for CSI2 High Speed Settle transition. ++ * ++ * From TRM, we have the same calculation for HS Termination signal. ++ * THS_TERM = ceil( 12.5ns / DDRCLK period ) - 1 ++ * But for Settle, we use the mid value between the two passed boundaries from ++ * sensor: ++ * THS_SETTLE = (Upper bound + Lower bound) / 2 ++ * ++ * Always returns 0. ++ */ ++int isp_csi2_calc_phy_cfg0(u32 mipiclk, u32 lbound_hs_settle, ++ u32 ubound_hs_settle) ++{ ++ struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; ++ struct isp_csi2_phy_cfg_update *currphy_u = ++ ¤t_csi2_cfg_update.phy; ++ u32 tmp, ddrclk = mipiclk >> 1; ++ ++ /* Calculate THS_TERM */ ++ tmp = ddrclk / 80000000; ++ if ((ddrclk % 80000000) > 0) ++ tmp++; ++ currphy->ths_term = tmp - 1; ++ currphy_u->ths_term = true; ++ ++ /* Calculate THS_SETTLE */ ++ currphy->ths_settle = (ubound_hs_settle + lbound_hs_settle) / 2; ++ ++ currphy_u->ths_settle = true; ++ isp_csi2_phy_update(true); ++ return 0; ++} ++EXPORT_SYMBOL(isp_csi2_calc_phy_cfg0); ++ ++/** ++ * isp_csi2_phy_update - Applies CSI2 D-PHY configuration. ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_phy_config_*() functions. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_phy_config_*() functions, unless the force_update flag is ++ * set to true. ++ * Always returns 0. ++ **/ ++int isp_csi2_phy_update(bool force_update) ++{ ++ struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; ++ struct isp_csi2_phy_cfg_update *currphy_u = ++ ¤t_csi2_cfg_update.phy; ++ u32 reg; ++ ++ if (update_phy_cfg0 || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG0); ++ if (currphy_u->ths_term || force_update) { ++ reg &= ~ISPCSI2PHY_CFG0_THS_TERM_MASK; ++ reg |= (currphy->ths_term << ++ ISPCSI2PHY_CFG0_THS_TERM_SHIFT); ++ currphy_u->ths_term = false; ++ } ++ if (currphy_u->ths_settle || force_update) { ++ reg &= ~ISPCSI2PHY_CFG0_THS_SETTLE_MASK; ++ reg |= (currphy->ths_settle << ++ ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT); ++ currphy_u->ths_settle = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG0); ++ update_phy_cfg0 = false; ++ } ++ ++ if (update_phy_cfg1 || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG1); ++ if (currphy_u->tclk_term || force_update) { ++ reg &= ~ISPCSI2PHY_CFG1_TCLK_TERM_MASK; ++ reg |= (currphy->tclk_term << ++ ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT); ++ currphy_u->tclk_term = false; ++ } ++ if (currphy_u->tclk_miss || force_update) { ++ reg &= ~ISPCSI2PHY_CFG1_TCLK_MISS_MASK; ++ reg |= (currphy->tclk_miss << ++ ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT); ++ currphy_u->tclk_miss = false; ++ } ++ if (currphy_u->tclk_settle || force_update) { ++ reg &= ~ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK; ++ reg |= (currphy->tclk_settle << ++ ISPCSI2PHY_CFG1_TCLK_SETTLE_SHIFT); ++ currphy_u->tclk_settle = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG1); ++ update_phy_cfg1 = false; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_phy_get - Gets CSI2 D-PHY configuration ++ * ++ * Gets settings from HW registers and fills in the internal driver memory ++ * Always returns 0. ++ **/ ++int isp_csi2_phy_get(void) ++{ ++ struct isp_csi2_phy_cfg *currphy = ¤t_csi2_cfg.phy; ++ struct isp_csi2_phy_cfg_update *currphy_u = ++ ¤t_csi2_cfg_update.phy; ++ u32 reg; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG0); ++ currphy->ths_term = (reg & ISPCSI2PHY_CFG0_THS_TERM_MASK) >> ++ ISPCSI2PHY_CFG0_THS_TERM_SHIFT; ++ currphy_u->ths_term = false; ++ ++ currphy->ths_settle = (reg & ISPCSI2PHY_CFG0_THS_SETTLE_MASK) >> ++ ISPCSI2PHY_CFG0_THS_SETTLE_SHIFT; ++ currphy_u->ths_settle = false; ++ update_phy_cfg0 = false; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2PHY, ISPCSI2PHY_CFG1); ++ ++ currphy->tclk_term = (reg & ISPCSI2PHY_CFG1_TCLK_TERM_MASK) >> ++ ISPCSI2PHY_CFG1_TCLK_TERM_SHIFT; ++ currphy_u->tclk_term = false; ++ ++ currphy->tclk_miss = (reg & ISPCSI2PHY_CFG1_TCLK_MISS_MASK) >> ++ ISPCSI2PHY_CFG1_TCLK_MISS_SHIFT; ++ currphy_u->tclk_miss = false; ++ ++ currphy->tclk_settle = (reg & ISPCSI2PHY_CFG1_TCLK_SETTLE_MASK) >> ++ ISPCSI2PHY_CFG1_TCLK_SETTLE_SHIFT; ++ currphy_u->tclk_settle = false; ++ ++ update_phy_cfg1 = false; ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_config_forcerxmode - Sets Force Rx mode on stop state count ++ * @force_rx_mode: Boolean to enable/disable forcing Rx mode in CSI2 receiver ++ * ++ * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. ++ **/ ++int isp_csi2_timings_config_forcerxmode(u8 io, bool force_rx_mode) ++{ ++ struct isp_csi2_timings_cfg *currtimings; ++ struct isp_csi2_timings_cfg_update *currtimings_u; ++ ++ if (io < 1 || io > 2) { ++ printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); ++ return -EINVAL; ++ } ++ ++ currtimings = ¤t_csi2_cfg.timings[io - 1]; ++ currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; ++ if (currtimings->force_rx_mode != force_rx_mode) { ++ currtimings->force_rx_mode = force_rx_mode; ++ currtimings_u->force_rx_mode = true; ++ update_timing = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_config_stopstate_16x - Sets 16x factor for L3 cycles ++ * @stop_state_16x: Boolean to use or not use the 16x multiplier for stop count ++ * ++ * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. ++ **/ ++int isp_csi2_timings_config_stopstate_16x(u8 io, bool stop_state_16x) ++{ ++ struct isp_csi2_timings_cfg *currtimings; ++ struct isp_csi2_timings_cfg_update *currtimings_u; ++ ++ if (io < 1 || io > 2) { ++ printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); ++ return -EINVAL; ++ } ++ ++ currtimings = ¤t_csi2_cfg.timings[io - 1]; ++ currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; ++ if (currtimings->stop_state_16x != stop_state_16x) { ++ currtimings->stop_state_16x = stop_state_16x; ++ currtimings_u->stop_state_16x = true; ++ update_timing = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_config_stopstate_4x - Sets 4x factor for L3 cycles ++ * @stop_state_4x: Boolean to use or not use the 4x multiplier for stop count ++ * ++ * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. ++ **/ ++int isp_csi2_timings_config_stopstate_4x(u8 io, bool stop_state_4x) ++{ ++ struct isp_csi2_timings_cfg *currtimings; ++ struct isp_csi2_timings_cfg_update *currtimings_u; ++ ++ if (io < 1 || io > 2) { ++ printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); ++ return -EINVAL; ++ } ++ ++ currtimings = ¤t_csi2_cfg.timings[io - 1]; ++ currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; ++ if (currtimings->stop_state_4x != stop_state_4x) { ++ currtimings->stop_state_4x = stop_state_4x; ++ currtimings_u->stop_state_4x = true; ++ update_timing = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_config_stopstate_cnt - Sets L3 cycles ++ * @stop_state_counter: Stop state counter value for L3 cycles ++ * ++ * Returns 0 if successful, or -EINVAL if wrong ComplexIO number is selected. ++ **/ ++int isp_csi2_timings_config_stopstate_cnt(u8 io, u16 stop_state_counter) ++{ ++ struct isp_csi2_timings_cfg *currtimings; ++ struct isp_csi2_timings_cfg_update *currtimings_u; ++ ++ if (io < 1 || io > 2) { ++ printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); ++ return -EINVAL; ++ } ++ ++ currtimings = ¤t_csi2_cfg.timings[io - 1]; ++ currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; ++ if (currtimings->stop_state_counter != stop_state_counter) { ++ currtimings->stop_state_counter = (stop_state_counter & 0x1FFF); ++ currtimings_u->stop_state_counter = true; ++ update_timing = true; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_update - Applies specified CSI2 timing configuration. ++ * @io: IO number (1 or 2) which specifies which ComplexIO are we updating ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_timings_config_*() functions. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_timings_config_*() functions, unless the force_update flag is ++ * set to true. ++ * Returns 0 if successful, or -EINVAL if invalid IO number is passed. ++ **/ ++int isp_csi2_timings_update(u8 io, bool force_update) ++{ ++ struct isp_csi2_timings_cfg *currtimings; ++ struct isp_csi2_timings_cfg_update *currtimings_u; ++ u32 reg; ++ ++ if (io < 1 || io > 2) { ++ printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); ++ return -EINVAL; ++ } ++ ++ currtimings = ¤t_csi2_cfg.timings[io - 1]; ++ currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; ++ ++ if (update_timing || force_update) { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_TIMING); ++ if (currtimings_u->force_rx_mode || force_update) { ++ reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO_MASK(io); ++ if (currtimings->force_rx_mode) ++ reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO_ENABLE ++ (io); ++ else ++ reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO_DISABLE ++ (io); ++ currtimings_u->force_rx_mode = false; ++ } ++ if (currtimings_u->stop_state_16x || force_update) { ++ reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO_MASK(io); ++ if (currtimings->stop_state_16x) ++ reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO_ENABLE ++ (io); ++ else ++ reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO_DISABLE ++ (io); ++ currtimings_u->stop_state_16x = false; ++ } ++ if (currtimings_u->stop_state_4x || force_update) { ++ reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO_MASK(io); ++ if (currtimings->stop_state_4x) { ++ reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO_ENABLE ++ (io); ++ } else { ++ reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO_DISABLE ++ (io); ++ } ++ currtimings_u->stop_state_4x = false; ++ } ++ if (currtimings_u->stop_state_counter || force_update) { ++ reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(io); ++ reg |= currtimings->stop_state_counter << ++ ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(io); ++ currtimings_u->stop_state_counter = false; ++ } ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_TIMING); ++ update_timing = false; ++ } ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_get - Gets specific CSI2 ComplexIO timing configuration ++ * @io: IO number (1 or 2) which specifies which ComplexIO are we getting ++ * ++ * Gets settings from HW registers and fills in the internal driver memory ++ * Returns 0 if successful, or -EINVAL if invalid IO number is passed. ++ **/ ++int isp_csi2_timings_get(u8 io) ++{ ++ struct isp_csi2_timings_cfg *currtimings; ++ struct isp_csi2_timings_cfg_update *currtimings_u; ++ u32 reg; ++ ++ if (io < 1 || io > 2) { ++ printk(KERN_ERR "CSI2 - Timings config: Invalid IO number\n"); ++ return -EINVAL; ++ } ++ ++ currtimings = ¤t_csi2_cfg.timings[io - 1]; ++ currtimings_u = ¤t_csi2_cfg_update.timings[io - 1]; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_TIMING); ++ if ((reg & ISPCSI2_TIMING_FORCE_RX_MODE_IO_MASK(io)) == ++ ISPCSI2_TIMING_FORCE_RX_MODE_IO_ENABLE(io)) ++ currtimings->force_rx_mode = true; ++ else ++ currtimings->force_rx_mode = false; ++ currtimings_u->force_rx_mode = false; ++ ++ if ((reg & ISPCSI2_TIMING_STOP_STATE_X16_IO_MASK(io)) == ++ ISPCSI2_TIMING_STOP_STATE_X16_IO_ENABLE(io)) ++ currtimings->stop_state_16x = true; ++ else ++ currtimings->stop_state_16x = false; ++ currtimings_u->stop_state_16x = false; ++ ++ if ((reg & ISPCSI2_TIMING_STOP_STATE_X4_IO_MASK(io)) == ++ ISPCSI2_TIMING_STOP_STATE_X4_IO_ENABLE(io)) ++ currtimings->stop_state_4x = true; ++ else ++ currtimings->stop_state_4x = false; ++ currtimings_u->stop_state_4x = false; ++ ++ currtimings->stop_state_counter = (reg & ++ ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(io)) >> ++ ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(io); ++ currtimings_u->stop_state_counter = false; ++ update_timing = false; ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_update_all - Applies specified CSI2 timing configuration. ++ * @force_update: Flag to force rewrite of registers, even if they haven't been ++ * updated with the isp_csi2_timings_config_*() functions. ++ * ++ * It only saves settings when they were previously updated using the ++ * isp_csi2_timings_config_*() functions, unless the force_update flag is ++ * set to true. ++ * Always returns 0. ++ **/ ++int isp_csi2_timings_update_all(bool force_update) ++{ ++ int i; ++ ++ for (i = 1; i < 3; i++) ++ isp_csi2_timings_update(i, force_update); ++ return 0; ++} ++ ++/** ++ * isp_csi2_timings_get_all - Gets all CSI2 ComplexIO timing configurations ++ * ++ * Always returns 0. ++ **/ ++int isp_csi2_timings_get_all(void) ++{ ++ int i; ++ ++ for (i = 1; i < 3; i++) ++ isp_csi2_timings_get(i); ++ return 0; ++} ++ ++/** ++ * isp_csi2_isr - CSI2 interrupt handling. ++ **/ ++void isp_csi2_isr(void) ++{ ++ u32 csi2_irqstatus, cpxio1_irqstatus, ctxirqstatus; ++ ++ csi2_irqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_IRQSTATUS); ++ isp_reg_writel(csi2_irqstatus, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_IRQSTATUS); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { ++ cpxio1_irqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_COMPLEXIO1_IRQSTATUS); ++ isp_reg_writel(cpxio1_irqstatus, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_COMPLEXIO1_IRQSTATUS); ++ printk(KERN_ERR "CSI2: ComplexIO Error IRQ %x\n", ++ cpxio1_irqstatus); ++ } ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) { ++ ctxirqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQSTATUS(0)); ++ isp_reg_writel(ctxirqstatus, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQSTATUS(0)); ++ } ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ++ printk(KERN_ERR "CSI2: OCP Transmission Error\n"); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ++ printk(KERN_ERR "CSI2: Short packet receive error\n"); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) ++ printk(KERN_DEBUG "CSI2: ECC correction done\n"); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ++ printk(KERN_ERR "CSI2: ECC correction failed\n"); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ++ printk(KERN_ERR "CSI2: ComplexIO #2 failed\n"); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ++ printk(KERN_ERR "CSI2: FIFO overflow error\n"); ++ ++ return; ++} ++EXPORT_SYMBOL(isp_csi2_isr); ++ ++/** ++ * isp_csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. ++ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts ++ **/ ++void isp_csi2_irq_complexio1_set(int enable) ++{ ++ u32 reg; ++ reg = ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMEXIT | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_STATEALLULPMENTER | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM5 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL5 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC5 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS5 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS5 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM4 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL4 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC4 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS4 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS4 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM3 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL3 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC3 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS3 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS3 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM2 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL2 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC2 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS2 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS2 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_STATEULPM1 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRCONTROL1 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRESC1 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTSYNCHS1 | ++ ISPCSI2_COMPLEXIO1_IRQENABLE_ERRSOTHS1; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_COMPLEXIO1_IRQSTATUS); ++ if (enable) { ++ reg |= isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_COMPLEXIO1_IRQENABLE); ++ } else ++ reg = 0; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_COMPLEXIO1_IRQENABLE); ++} ++EXPORT_SYMBOL(isp_csi2_irq_complexio1_set); ++ ++/** ++ * isp_csi2_irq_ctx_set - Enables CSI2 Context IRQs. ++ * @enable: Enable/disable CSI2 Context interrupts ++ **/ ++void isp_csi2_irq_ctx_set(int enable) ++{ ++ u32 reg; ++ int i; ++ ++ reg = ISPCSI2_CTX_IRQSTATUS_FS_IRQ | ISPCSI2_CTX_IRQSTATUS_FE_IRQ; ++ for (i = 0; i < 8; i++) { ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQSTATUS(i)); ++ if (enable) { ++ isp_reg_or(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQENABLE(i), reg); ++ } else { ++ isp_reg_writel(0, OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQENABLE(i)); ++ } ++ } ++ ++} ++EXPORT_SYMBOL(isp_csi2_irq_ctx_set); ++ ++/** ++ * isp_csi2_irq_status_set - Enables CSI2 Status IRQs. ++ * @enable: Enable/disable CSI2 Status interrupts ++ **/ ++void isp_csi2_irq_status_set(int enable) ++{ ++ u32 reg; ++ reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | ++ ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | ++ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | ++ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | ++ ISPCSI2_IRQSTATUS_CONTEXT(0); ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_IRQSTATUS); ++ if (enable) ++ reg |= isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_IRQENABLE); ++ else ++ reg = 0; ++ ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_IRQENABLE); ++} ++EXPORT_SYMBOL(isp_csi2_irq_status_set); ++ ++/** ++ * isp_csi2_irq_status_set - Enables main CSI2 IRQ. ++ * @enable: Enable/disable main CSI2 interrupt ++ **/ ++void isp_csi2_irq_set(int enable) ++{ ++ isp_reg_writel(IRQ0STATUS_CSIA_IRQ, OMAP3_ISP_IOMEM_MAIN, ++ ISP_IRQ0STATUS); ++ isp_reg_and_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~IRQ0ENABLE_CSIA_IRQ, ++ (enable ? IRQ0ENABLE_CSIA_IRQ : 0)); ++} ++EXPORT_SYMBOL(isp_csi2_irq_set); ++ ++/** ++ * isp_csi2_irq_all_set - Enable/disable CSI2 interrupts. ++ * @enable: 0-Disable, 1-Enable. ++ **/ ++void isp_csi2_irq_all_set(int enable) ++{ ++ if (enable) { ++ isp_csi2_irq_complexio1_set(enable); ++ isp_csi2_irq_ctx_set(enable); ++ isp_csi2_irq_status_set(enable); ++ isp_csi2_irq_set(enable); ++ } else { ++ isp_csi2_irq_set(enable); ++ isp_csi2_irq_status_set(enable); ++ isp_csi2_irq_ctx_set(enable); ++ isp_csi2_irq_complexio1_set(enable); ++ } ++ return; ++} ++EXPORT_SYMBOL(isp_csi2_irq_all_set); ++ ++/** ++ * isp_csi2_reset - Resets the CSI2 module. ++ * ++ * Returns 0 if successful, or -EBUSY if power command didn't respond. ++ **/ ++int isp_csi2_reset(void) ++{ ++ u32 reg; ++ u8 soft_reset_retries = 0; ++ int i; ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSCONFIG); ++ reg |= ISPCSI2_SYSCONFIG_SOFT_RESET_RESET; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSCONFIG); ++ ++ do { ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSSTATUS) & ++ ISPCSI2_SYSSTATUS_RESET_DONE_MASK; ++ if (reg == ISPCSI2_SYSSTATUS_RESET_DONE_DONE) ++ break; ++ soft_reset_retries++; ++ if (soft_reset_retries < 5) ++ udelay(100); ++ } while (soft_reset_retries < 5); ++ ++ if (soft_reset_retries == 5) { ++ printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); ++ return -EBUSY; ++ } ++ ++ reg = isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSCONFIG); ++ reg &= ~ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK; ++ reg |= ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO; ++ reg &= ~ISPCSI2_SYSCONFIG_AUTO_IDLE_MASK; ++ isp_reg_writel(reg, OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_SYSCONFIG); ++ ++ uses_videoport = false; ++ update_complexio_cfg1 = false; ++ update_phy_cfg0 = false; ++ update_phy_cfg1 = false; ++ for (i = 0; i < 8; i++) { ++ update_ctx_ctrl1[i] = false; ++ update_ctx_ctrl2[i] = false; ++ update_ctx_ctrl3[i] = false; ++ } ++ update_timing = false; ++ update_ctrl = false; ++ ++ isp_csi2_complexio_lanes_get(); ++ isp_csi2_ctrl_get(); ++ isp_csi2_ctx_get_all(); ++ isp_csi2_phy_get(); ++ isp_csi2_timings_get_all(); ++ ++ isp_csi2_complexio_power_autoswitch(true); ++ isp_csi2_complexio_power(ISP_CSI2_POWER_ON); ++ ++ isp_csi2_timings_config_forcerxmode(1, true); ++ isp_csi2_timings_config_stopstate_cnt(1, 0x1FF); ++ isp_csi2_timings_update_all(true); ++ ++ return 0; ++} ++ ++/** ++ * isp_csi2_enable - Enables the CSI2 module. ++ * @enable: Enables/disables the CSI2 module. ++ **/ ++void isp_csi2_enable(int enable) ++{ ++ if (enable) { ++ isp_csi2_ctx_config_enabled(0, true); ++ isp_csi2_ctx_config_eof_enabled(0, true); ++ isp_csi2_ctx_config_checksum_enabled(0, true); ++ isp_csi2_ctx_update(0, false); ++ ++ isp_csi2_ctrl_config_ecc_enable(true); ++ isp_csi2_ctrl_config_if_enable(true); ++ isp_csi2_ctrl_update(false); ++ } else { ++ isp_csi2_ctx_config_enabled(0, false); ++ isp_csi2_ctx_config_eof_enabled(0, false); ++ isp_csi2_ctx_config_checksum_enabled(0, false); ++ isp_csi2_ctx_update(0, false); ++ ++ isp_csi2_ctrl_config_ecc_enable(false); ++ isp_csi2_ctrl_config_if_enable(false); ++ isp_csi2_ctrl_update(false); ++ } ++} ++EXPORT_SYMBOL(isp_csi2_enable); ++ ++/** ++ * isp_csi2_regdump - Prints CSI2 debug information. ++ **/ ++void isp_csi2_regdump(void) ++{ ++ printk(KERN_DEBUG "-------------Register dump-------------\n"); ++ ++ printk(KERN_DEBUG "ISP_CTRL: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); ++ printk(KERN_DEBUG "ISP_TCTRL_CTRL: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL)); ++ ++ printk(KERN_DEBUG "ISPCCDC_SDR_ADDR: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR)); ++ printk(KERN_DEBUG "ISPCCDC_SYN_MODE: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE)); ++ printk(KERN_DEBUG "ISPCCDC_CFG: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG)); ++ printk(KERN_DEBUG "ISPCCDC_FMTCFG: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)); ++ printk(KERN_DEBUG "ISPCCDC_HSIZE_OFF: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF)); ++ printk(KERN_DEBUG "ISPCCDC_HORZ_INFO: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO)); ++ printk(KERN_DEBUG "ISPCCDC_VERT_START: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_START)); ++ printk(KERN_DEBUG "ISPCCDC_VERT_LINES: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_VERT_LINES)); ++ ++ printk(KERN_DEBUG "ISPCSI2_COMPLEXIO_CFG1: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_COMPLEXIO_CFG1)); ++ printk(KERN_DEBUG "ISPCSI2_SYSSTATUS: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_SYSSTATUS)); ++ printk(KERN_DEBUG "ISPCSI2_SYSCONFIG: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_SYSCONFIG)); ++ printk(KERN_DEBUG "ISPCSI2_IRQENABLE: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_IRQENABLE)); ++ printk(KERN_DEBUG "ISPCSI2_IRQSTATUS: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_IRQSTATUS)); ++ ++ printk(KERN_DEBUG "ISPCSI2_CTX_IRQENABLE(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQENABLE(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTX_IRQSTATUS(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_IRQSTATUS(0))); ++ printk(KERN_DEBUG "ISPCSI2_TIMING: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_TIMING)); ++ printk(KERN_DEBUG "ISPCSI2PHY_CFG0: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2PHY, ++ ISPCSI2PHY_CFG0)); ++ printk(KERN_DEBUG "ISPCSI2PHY_CFG1: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2PHY, ++ ISPCSI2PHY_CFG1)); ++ printk(KERN_DEBUG "ISPCSI2_CTX_CTRL1(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL1(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTX_CTRL2(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL2(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTX_CTRL3(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_CTRL3(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTX_DAT_OFST(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_OFST(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTX_DAT_PING_ADDR(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_PING_ADDR(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTX_DAT_PONG_ADDR(0): %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ++ ISPCSI2_CTX_DAT_PONG_ADDR(0))); ++ printk(KERN_DEBUG "ISPCSI2_CTRL: %x\n", ++ isp_reg_readl(OMAP3_ISP_IOMEM_CSI2A, ISPCSI2_CTRL)); ++ printk(KERN_DEBUG "---------------------------------------\n"); ++} ++ ++/** ++ * isp_csi2_cleanup - Routine for module driver cleanup ++ **/ ++void isp_csi2_cleanup(void) ++{ ++ return; ++} ++ ++/** ++ * isp_csi2_init - Routine for module driver init ++ **/ ++int __init isp_csi2_init(void) ++{ ++ int i; ++ ++ update_complexio_cfg1 = false; ++ update_phy_cfg0 = false; ++ update_phy_cfg1 = false; ++ for (i = 0; i < 8; i++) { ++ update_ctx_ctrl1[i] = false; ++ update_ctx_ctrl2[i] = false; ++ update_ctx_ctrl3[i] = false; ++ } ++ update_timing = false; ++ update_ctrl = false; ++ ++ memset(¤t_csi2_cfg, 0, sizeof(current_csi2_cfg)); ++ memset(¤t_csi2_cfg_update, 0, sizeof(current_csi2_cfg_update)); ++ return 0; ++} ++ ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_DESCRIPTION("ISP CSI2 Receiver Module"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/video/isp/ispcsi2.h b/drivers/media/video/isp/ispcsi2.h +new file mode 100644 +index 0000000..4582c96 +--- /dev/null ++++ b/drivers/media/video/isp/ispcsi2.h +@@ -0,0 +1,232 @@ ++/* ++ * ispcsi2.h ++ * ++ * Copyright (C) 2009 Texas Instruments. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Dominic Curran <dcurran@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_ISP_CSI2_API_H ++#define OMAP_ISP_CSI2_API_H ++#include <linux/videodev2.h> ++ ++enum isp_csi2_irqevents { ++ OCP_ERR_IRQ = 0x4000, ++ SHORT_PACKET_IRQ = 0x2000, ++ ECC_CORRECTION_IRQ = 0x1000, ++ ECC_NO_CORRECTION_IRQ = 0x800, ++ COMPLEXIO2_ERR_IRQ = 0x400, ++ COMPLEXIO1_ERR_IRQ = 0x200, ++ FIFO_OVF_IRQ = 0x100, ++ CONTEXT7 = 0x80, ++ CONTEXT6 = 0x40, ++ CONTEXT5 = 0x20, ++ CONTEXT4 = 0x10, ++ CONTEXT3 = 0x8, ++ CONTEXT2 = 0x4, ++ CONTEXT1 = 0x2, ++ CONTEXT0 = 0x1, ++}; ++ ++enum isp_csi2_ctx_irqevents { ++ CTX_ECC_CORRECTION = 0x100, ++ CTX_LINE_NUMBER = 0x80, ++ CTX_FRAME_NUMBER = 0x40, ++ CTX_CS = 0x20, ++ CTX_LE = 0x8, ++ CTX_LS = 0x4, ++ CTX_FE = 0x2, ++ CTX_FS = 0x1, ++}; ++ ++enum isp_csi2_power_cmds { ++ ISP_CSI2_POWER_OFF, ++ ISP_CSI2_POWER_ON, ++ ISP_CSI2_POWER_ULPW, ++}; ++ ++enum isp_csi2_frame_mode { ++ ISP_CSI2_FRAME_IMMEDIATE, ++ ISP_CSI2_FRAME_AFTERFEC, ++}; ++ ++struct csi2_lanecfg { ++ u8 pos; ++ u8 pol; ++}; ++ ++struct isp_csi2_lanes_cfg { ++ struct csi2_lanecfg data[4]; ++ struct csi2_lanecfg clk; ++}; ++ ++struct isp_csi2_lanes_cfg_update { ++ bool data[4]; ++ bool clk; ++}; ++ ++struct isp_csi2_phy_cfg { ++ u8 ths_term; ++ u8 ths_settle; ++ u8 tclk_term; ++ unsigned tclk_miss:1; ++ u8 tclk_settle; ++}; ++ ++struct isp_csi2_phy_cfg_update { ++ bool ths_term; ++ bool ths_settle; ++ bool tclk_term; ++ bool tclk_miss; ++ bool tclk_settle; ++}; ++ ++struct isp_csi2_ctx_cfg { ++ u8 virtual_id; ++ u8 frame_count; ++ struct v4l2_pix_format format; ++ u16 alpha; ++ u16 data_offset; ++ u32 ping_addr; ++ u32 pong_addr; ++ bool eof_enabled; ++ bool eol_enabled; ++ bool checksum_enabled; ++ bool enabled; ++}; ++ ++struct isp_csi2_ctx_cfg_update { ++ bool virtual_id; ++ bool frame_count; ++ bool format; ++ bool alpha; ++ bool data_offset; ++ bool ping_addr; ++ bool pong_addr; ++ bool eof_enabled; ++ bool eol_enabled; ++ bool checksum_enabled; ++ bool enabled; ++}; ++ ++struct isp_csi2_timings_cfg { ++ bool force_rx_mode; ++ bool stop_state_16x; ++ bool stop_state_4x; ++ u16 stop_state_counter; ++}; ++ ++struct isp_csi2_timings_cfg_update { ++ bool force_rx_mode; ++ bool stop_state_16x; ++ bool stop_state_4x; ++ bool stop_state_counter; ++}; ++ ++struct isp_csi2_ctrl_cfg { ++ bool vp_clk_enable; ++ bool vp_only_enable; ++ u8 vp_out_ctrl; ++ bool debug_enable; ++ u8 burst_size; ++ enum isp_csi2_frame_mode frame_mode; ++ bool ecc_enable; ++ bool secure_mode; ++ bool if_enable; ++}; ++ ++struct isp_csi2_ctrl_cfg_update { ++ bool vp_clk_enable; ++ bool vp_only_enable; ++ bool vp_out_ctrl; ++ bool debug_enable; ++ bool burst_size; ++ bool frame_mode; ++ bool ecc_enable; ++ bool secure_mode; ++ bool if_enable; ++}; ++ ++struct isp_csi2_cfg { ++ struct isp_csi2_lanes_cfg lanes; ++ struct isp_csi2_phy_cfg phy; ++ struct isp_csi2_ctx_cfg contexts[8]; ++ struct isp_csi2_timings_cfg timings[2]; ++ struct isp_csi2_ctrl_cfg ctrl; ++}; ++ ++struct isp_csi2_cfg_update { ++ struct isp_csi2_lanes_cfg_update lanes; ++ struct isp_csi2_phy_cfg_update phy; ++ struct isp_csi2_ctx_cfg_update contexts[8]; ++ struct isp_csi2_timings_cfg_update timings[2]; ++ struct isp_csi2_ctrl_cfg_update ctrl; ++}; ++ ++int isp_csi2_complexio_lanes_config(struct isp_csi2_lanes_cfg *reqcfg); ++int isp_csi2_complexio_lanes_update(bool force_update); ++int isp_csi2_complexio_lanes_get(void); ++int isp_csi2_complexio_power_autoswitch(bool enable); ++int isp_csi2_complexio_power(enum isp_csi2_power_cmds power_cmd); ++int isp_csi2_ctrl_config_frame_mode(enum isp_csi2_frame_mode frame_mode); ++int isp_csi2_ctrl_config_vp_clk_enable(bool vp_clk_enable); ++int isp_csi2_ctrl_config_vp_only_enable(bool vp_only_enable); ++int isp_csi2_ctrl_config_debug_enable(bool debug_enable); ++int isp_csi2_ctrl_config_burst_size(u8 burst_size); ++int isp_csi2_ctrl_config_ecc_enable(bool ecc_enable); ++int isp_csi2_ctrl_config_secure_mode(bool secure_mode); ++int isp_csi2_ctrl_config_if_enable(bool if_enable); ++int isp_csi2_ctrl_config_vp_out_ctrl(u8 vp_out_ctrl); ++int isp_csi2_ctrl_update(bool force_update); ++int isp_csi2_ctrl_get(void); ++int isp_csi2_ctx_config_virtual_id(u8 ctxnum, u8 virtual_id); ++int isp_csi2_ctx_config_frame_count(u8 ctxnum, u8 frame_count); ++int isp_csi2_ctx_config_format(u8 ctxnum, u32 pixformat); ++int isp_csi2_ctx_config_alpha(u8 ctxnum, u16 alpha); ++int isp_csi2_ctx_config_data_offset(u8 ctxnum, u16 data_offset); ++int isp_csi2_ctx_config_ping_addr(u8 ctxnum, u32 ping_addr); ++int isp_csi2_ctx_config_pong_addr(u8 ctxnum, u32 pong_addr); ++int isp_csi2_ctx_config_eof_enabled(u8 ctxnum, bool eof_enabled); ++int isp_csi2_ctx_config_eol_enabled(u8 ctxnum, bool eol_enabled); ++int isp_csi2_ctx_config_checksum_enabled(u8 ctxnum, bool checksum_enabled); ++int isp_csi2_ctx_config_enabled(u8 ctxnum, bool enabled); ++int isp_csi2_ctx_update(u8 ctxnum, bool force_update); ++int isp_csi2_ctx_get(u8 ctxnum); ++int isp_csi2_ctx_update_all(bool force_update); ++int isp_csi2_ctx_get_all(void); ++int isp_csi2_phy_config(struct isp_csi2_phy_cfg *desiredphyconfig); ++int isp_csi2_calc_phy_cfg0(u32 mipiclk, u32 lbound_hs_settle, ++ u32 ubound_hs_settle); ++int isp_csi2_phy_update(bool force_update); ++int isp_csi2_phy_get(void); ++int isp_csi2_timings_config_forcerxmode(u8 io, bool force_rx_mode); ++int isp_csi2_timings_config_stopstate_16x(u8 io, bool stop_state_16x); ++int isp_csi2_timings_config_stopstate_4x(u8 io, bool stop_state_4x); ++int isp_csi2_timings_config_stopstate_cnt(u8 io, u16 stop_state_counter); ++int isp_csi2_timings_update(u8 io, bool force_update); ++int isp_csi2_timings_get(u8 io); ++int isp_csi2_timings_update_all(bool force_update); ++int isp_csi2_timings_get_all(void); ++void isp_csi2_irq_complexio1_set(int enable); ++void isp_csi2_irq_ctx_set(int enable); ++void isp_csi2_irq_status_set(int enable); ++void isp_csi2_irq_set(int enable); ++void isp_csi2_irq_all_set(int enable); ++ ++void isp_csi2_isr(void); ++int isp_csi2_reset(void); ++void isp_csi2_enable(int enable); ++void isp_csi2_regdump(void); ++ ++#endif /* OMAP_ISP_CSI2_H */ ++ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0008-omap3isp-Add-ISP-tables.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0008-omap3isp-Add-ISP-tables.patch new file mode 100644 index 0000000000..db023e514d --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0008-omap3isp-Add-ISP-tables.patch @@ -0,0 +1,4018 @@ +From 5de7cb2cac5f7d76cb025ddc8fb09c99a1007e08 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:02 +0200 +Subject: [PATCH] omap3isp: Add ISP tables + +* Blue Gamma gain table +* CFA gain table +* Green Gamma gain table +* Luma Enhancement gain table +* Noise filter gain table +* Red Gamma gain table + +TODO: + +- Get rid of this kind of tables. Either generate them at runtime or + use a user space program to fill defaults. + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/isp/bluegamma_table.h | 1040 ++++++++++++++++++++++++++ + drivers/media/video/isp/cfa_coef_table.h | 603 +++++++++++++++ + drivers/media/video/isp/greengamma_table.h | 1040 ++++++++++++++++++++++++++ + drivers/media/video/isp/luma_enhance_table.h | 144 ++++ + drivers/media/video/isp/noise_filter_table.h | 79 ++ + drivers/media/video/isp/redgamma_table.h | 1040 ++++++++++++++++++++++++++ + 6 files changed, 3946 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/bluegamma_table.h + create mode 100644 drivers/media/video/isp/cfa_coef_table.h + create mode 100644 drivers/media/video/isp/greengamma_table.h + create mode 100644 drivers/media/video/isp/luma_enhance_table.h + create mode 100644 drivers/media/video/isp/noise_filter_table.h + create mode 100644 drivers/media/video/isp/redgamma_table.h + +diff --git a/drivers/media/video/isp/bluegamma_table.h b/drivers/media/video/isp/bluegamma_table.h +new file mode 100644 +index 0000000..301382a +--- /dev/null ++++ b/drivers/media/video/isp/bluegamma_table.h +@@ -0,0 +1,1040 @@ ++/* ++ * bluegamma_table.h ++ * ++ * Gamma Table values for BLUE for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++0, ++0, ++1, ++2, ++3, ++3, ++4, ++5, ++6, ++8, ++10, ++12, ++14, ++16, ++18, ++20, ++22, ++23, ++25, ++26, ++28, ++29, ++31, ++32, ++34, ++35, ++36, ++37, ++39, ++40, ++41, ++42, ++43, ++44, ++45, ++46, ++47, ++48, ++49, ++50, ++51, ++52, ++52, ++53, ++54, ++55, ++56, ++57, ++58, ++59, ++60, ++61, ++62, ++63, ++63, ++64, ++65, ++66, ++66, ++67, ++68, ++69, ++69, ++70, ++71, ++72, ++72, ++73, ++74, ++75, ++75, ++76, ++77, ++78, ++78, ++79, ++80, ++81, ++81, ++82, ++83, ++84, ++84, ++85, ++86, ++87, ++88, ++88, ++89, ++90, ++91, ++91, ++92, ++93, ++94, ++94, ++95, ++96, ++97, ++97, ++98, ++98, ++99, ++99, ++100, ++100, ++101, ++101, ++102, ++103, ++104, ++104, ++105, ++106, ++107, ++108, ++108, ++109, ++110, ++111, ++111, ++112, ++113, ++114, ++114, ++115, ++116, ++117, ++117, ++118, ++119, ++119, ++120, ++120, ++121, ++121, ++122, ++122, ++123, ++123, ++124, ++124, ++125, ++125, ++126, ++126, ++127, ++127, ++128, ++128, ++129, ++129, ++130, ++130, ++131, ++131, ++132, ++132, ++133, ++133, ++134, ++134, ++135, ++135, ++136, ++136, ++137, ++137, ++138, ++138, ++139, ++139, ++140, ++140, ++141, ++141, ++142, ++142, ++143, ++143, ++144, ++144, ++145, ++145, ++146, ++146, ++147, ++147, ++148, ++148, ++149, ++149, ++150, ++150, ++151, ++151, ++152, ++152, ++153, ++153, ++153, ++153, ++154, ++154, ++154, ++154, ++155, ++155, ++156, ++156, ++157, ++157, ++158, ++158, ++158, ++159, ++159, ++159, ++160, ++160, ++160, ++161, ++161, ++162, ++162, ++163, ++163, ++164, ++164, ++164, ++164, ++165, ++165, ++165, ++165, ++166, ++166, ++167, ++167, ++168, ++168, ++169, ++169, ++170, ++170, ++170, ++170, ++171, ++171, ++171, ++171, ++172, ++172, ++173, ++173, ++174, ++174, ++175, ++175, ++176, ++176, ++176, ++176, ++177, ++177, ++177, ++177, ++178, ++178, ++178, ++178, ++179, ++179, ++179, ++179, ++180, ++180, ++180, ++180, ++181, ++181, ++181, ++181, ++182, ++182, ++182, ++182, ++183, ++183, ++183, ++183, ++184, ++184, ++184, ++184, ++185, ++185, ++185, ++185, ++186, ++186, ++186, ++186, ++187, ++187, ++187, ++187, ++188, ++188, ++188, ++188, ++189, ++189, ++189, ++189, ++190, ++190, ++190, ++190, ++191, ++191, ++191, ++191, ++192, ++192, ++192, ++192, ++193, ++193, ++193, ++193, ++194, ++194, ++194, ++194, ++195, ++195, ++195, ++195, ++196, ++196, ++196, ++196, ++197, ++197, ++197, ++197, ++198, ++198, ++198, ++198, ++199, ++199, ++199, ++199, ++200, ++200, ++200, ++200, ++201, ++201, ++201, ++201, ++202, ++202, ++202, ++203, ++203, ++203, ++203, ++204, ++204, ++204, ++204, ++205, ++205, ++205, ++205, ++206, ++206, ++206, ++206, ++207, ++207, ++207, ++207, ++208, ++208, ++208, ++208, ++209, ++209, ++209, ++209, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++212, ++212, ++212, ++212, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++214, ++214, ++214, ++214, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++216, ++216, ++216, ++216, ++217, ++217, ++217, ++217, ++218, ++218, ++218, ++218, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++220, ++220, ++220, ++220, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++222, ++222, ++222, ++222, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++224, ++224, ++224, ++224, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++226, ++226, ++226, ++226, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++228, ++228, ++228, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++230, ++230, ++230, ++230, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++233, ++233, ++233, ++233, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++235, ++235, ++235, ++235, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++237, ++237, ++237, ++237, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++239, ++239, ++239, ++239, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++241, ++241, ++241, ++241, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++243, ++243, ++243, ++243, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++245, ++245, ++245, ++245, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++247, ++247, ++247, ++247, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++249, ++249, ++249, ++249, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++251, ++251, ++251, ++251, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++254, ++254, ++254, ++254, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255 +diff --git a/drivers/media/video/isp/cfa_coef_table.h b/drivers/media/video/isp/cfa_coef_table.h +new file mode 100644 +index 0000000..8cafa1f +--- /dev/null ++++ b/drivers/media/video/isp/cfa_coef_table.h +@@ -0,0 +1,603 @@ ++/* ++ * cfa_coef_table.h ++ * ++ * Copyright (C) 2009 Nokia Corporation ++ * ++ * Contact: Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * ++ * Written by Gjorgji Rosikopulos ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248 ++ +diff --git a/drivers/media/video/isp/greengamma_table.h b/drivers/media/video/isp/greengamma_table.h +new file mode 100644 +index 0000000..0f5c5e4 +--- /dev/null ++++ b/drivers/media/video/isp/greengamma_table.h +@@ -0,0 +1,1040 @@ ++/* ++ * greengamma_table.h ++ * ++ * Gamma Table values for GREEN for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++0, ++0, ++1, ++2, ++3, ++3, ++4, ++5, ++6, ++8, ++10, ++12, ++14, ++16, ++18, ++20, ++22, ++23, ++25, ++26, ++28, ++29, ++31, ++32, ++34, ++35, ++36, ++37, ++39, ++40, ++41, ++42, ++43, ++44, ++45, ++46, ++47, ++48, ++49, ++50, ++51, ++52, ++52, ++53, ++54, ++55, ++56, ++57, ++58, ++59, ++60, ++61, ++62, ++63, ++63, ++64, ++65, ++66, ++66, ++67, ++68, ++69, ++69, ++70, ++71, ++72, ++72, ++73, ++74, ++75, ++75, ++76, ++77, ++78, ++78, ++79, ++80, ++81, ++81, ++82, ++83, ++84, ++84, ++85, ++86, ++87, ++88, ++88, ++89, ++90, ++91, ++91, ++92, ++93, ++94, ++94, ++95, ++96, ++97, ++97, ++98, ++98, ++99, ++99, ++100, ++100, ++101, ++101, ++102, ++103, ++104, ++104, ++105, ++106, ++107, ++108, ++108, ++109, ++110, ++111, ++111, ++112, ++113, ++114, ++114, ++115, ++116, ++117, ++117, ++118, ++119, ++119, ++120, ++120, ++121, ++121, ++122, ++122, ++123, ++123, ++124, ++124, ++125, ++125, ++126, ++126, ++127, ++127, ++128, ++128, ++129, ++129, ++130, ++130, ++131, ++131, ++132, ++132, ++133, ++133, ++134, ++134, ++135, ++135, ++136, ++136, ++137, ++137, ++138, ++138, ++139, ++139, ++140, ++140, ++141, ++141, ++142, ++142, ++143, ++143, ++144, ++144, ++145, ++145, ++146, ++146, ++147, ++147, ++148, ++148, ++149, ++149, ++150, ++150, ++151, ++151, ++152, ++152, ++153, ++153, ++153, ++153, ++154, ++154, ++154, ++154, ++155, ++155, ++156, ++156, ++157, ++157, ++158, ++158, ++158, ++159, ++159, ++159, ++160, ++160, ++160, ++161, ++161, ++162, ++162, ++163, ++163, ++164, ++164, ++164, ++164, ++165, ++165, ++165, ++165, ++166, ++166, ++167, ++167, ++168, ++168, ++169, ++169, ++170, ++170, ++170, ++170, ++171, ++171, ++171, ++171, ++172, ++172, ++173, ++173, ++174, ++174, ++175, ++175, ++176, ++176, ++176, ++176, ++177, ++177, ++177, ++177, ++178, ++178, ++178, ++178, ++179, ++179, ++179, ++179, ++180, ++180, ++180, ++180, ++181, ++181, ++181, ++181, ++182, ++182, ++182, ++182, ++183, ++183, ++183, ++183, ++184, ++184, ++184, ++184, ++185, ++185, ++185, ++185, ++186, ++186, ++186, ++186, ++187, ++187, ++187, ++187, ++188, ++188, ++188, ++188, ++189, ++189, ++189, ++189, ++190, ++190, ++190, ++190, ++191, ++191, ++191, ++191, ++192, ++192, ++192, ++192, ++193, ++193, ++193, ++193, ++194, ++194, ++194, ++194, ++195, ++195, ++195, ++195, ++196, ++196, ++196, ++196, ++197, ++197, ++197, ++197, ++198, ++198, ++198, ++198, ++199, ++199, ++199, ++199, ++200, ++200, ++200, ++200, ++201, ++201, ++201, ++201, ++202, ++202, ++202, ++203, ++203, ++203, ++203, ++204, ++204, ++204, ++204, ++205, ++205, ++205, ++205, ++206, ++206, ++206, ++206, ++207, ++207, ++207, ++207, ++208, ++208, ++208, ++208, ++209, ++209, ++209, ++209, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++212, ++212, ++212, ++212, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++214, ++214, ++214, ++214, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++216, ++216, ++216, ++216, ++217, ++217, ++217, ++217, ++218, ++218, ++218, ++218, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++220, ++220, ++220, ++220, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++222, ++222, ++222, ++222, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++224, ++224, ++224, ++224, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++226, ++226, ++226, ++226, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++228, ++228, ++228, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++230, ++230, ++230, ++230, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++233, ++233, ++233, ++233, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++235, ++235, ++235, ++235, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++237, ++237, ++237, ++237, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++239, ++239, ++239, ++239, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++241, ++241, ++241, ++241, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++243, ++243, ++243, ++243, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++245, ++245, ++245, ++245, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++247, ++247, ++247, ++247, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++249, ++249, ++249, ++249, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++251, ++251, ++251, ++251, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++254, ++254, ++254, ++254, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255 +diff --git a/drivers/media/video/isp/luma_enhance_table.h b/drivers/media/video/isp/luma_enhance_table.h +new file mode 100644 +index 0000000..99c8b05 +--- /dev/null ++++ b/drivers/media/video/isp/luma_enhance_table.h +@@ -0,0 +1,144 @@ ++/* ++ * luma_enhance_table.h ++ * ++ * Luminance Enhancement table values for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1048575, ++1047551, ++1046527, ++1045503, ++1044479, ++1043455, ++1042431, ++1041407, ++1040383, ++1039359, ++1038335, ++1037311, ++1036287, ++1035263, ++1034239, ++1033215, ++1032191, ++1031167, ++1030143, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028100, ++1032196, ++1036292, ++1040388, ++1044484, ++0, ++0, ++0, ++5, ++5125, ++10245, ++15365, ++20485, ++25605, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++31743, ++30719, ++29695, ++28671, ++27647, ++26623, ++25599, ++24575, ++23551, ++22527, ++21503, ++20479, ++19455, ++18431, ++17407, ++16383, ++15359, ++14335, ++13311, ++12287, ++11263, ++10239, ++9215, ++8191, ++7167, ++6143, ++5119, ++4095, ++3071, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024 +diff --git a/drivers/media/video/isp/noise_filter_table.h b/drivers/media/video/isp/noise_filter_table.h +new file mode 100644 +index 0000000..7345f90 +--- /dev/null ++++ b/drivers/media/video/isp/noise_filter_table.h +@@ -0,0 +1,79 @@ ++/* ++ * noise_filter_table.h ++ * ++ * Noise Filter Table values for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31 +diff --git a/drivers/media/video/isp/redgamma_table.h b/drivers/media/video/isp/redgamma_table.h +new file mode 100644 +index 0000000..ad0232a +--- /dev/null ++++ b/drivers/media/video/isp/redgamma_table.h +@@ -0,0 +1,1040 @@ ++/* ++ * redgamma_table.h ++ * ++ * Gamma Table values for RED for TI's OMAP3 Camera ISP ++ * ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++0, ++0, ++1, ++2, ++3, ++3, ++4, ++5, ++6, ++8, ++10, ++12, ++14, ++16, ++18, ++20, ++22, ++23, ++25, ++26, ++28, ++29, ++31, ++32, ++34, ++35, ++36, ++37, ++39, ++40, ++41, ++42, ++43, ++44, ++45, ++46, ++47, ++48, ++49, ++50, ++51, ++52, ++52, ++53, ++54, ++55, ++56, ++57, ++58, ++59, ++60, ++61, ++62, ++63, ++63, ++64, ++65, ++66, ++66, ++67, ++68, ++69, ++69, ++70, ++71, ++72, ++72, ++73, ++74, ++75, ++75, ++76, ++77, ++78, ++78, ++79, ++80, ++81, ++81, ++82, ++83, ++84, ++84, ++85, ++86, ++87, ++88, ++88, ++89, ++90, ++91, ++91, ++92, ++93, ++94, ++94, ++95, ++96, ++97, ++97, ++98, ++98, ++99, ++99, ++100, ++100, ++101, ++101, ++102, ++103, ++104, ++104, ++105, ++106, ++107, ++108, ++108, ++109, ++110, ++111, ++111, ++112, ++113, ++114, ++114, ++115, ++116, ++117, ++117, ++118, ++119, ++119, ++120, ++120, ++121, ++121, ++122, ++122, ++123, ++123, ++124, ++124, ++125, ++125, ++126, ++126, ++127, ++127, ++128, ++128, ++129, ++129, ++130, ++130, ++131, ++131, ++132, ++132, ++133, ++133, ++134, ++134, ++135, ++135, ++136, ++136, ++137, ++137, ++138, ++138, ++139, ++139, ++140, ++140, ++141, ++141, ++142, ++142, ++143, ++143, ++144, ++144, ++145, ++145, ++146, ++146, ++147, ++147, ++148, ++148, ++149, ++149, ++150, ++150, ++151, ++151, ++152, ++152, ++153, ++153, ++153, ++153, ++154, ++154, ++154, ++154, ++155, ++155, ++156, ++156, ++157, ++157, ++158, ++158, ++158, ++159, ++159, ++159, ++160, ++160, ++160, ++161, ++161, ++162, ++162, ++163, ++163, ++164, ++164, ++164, ++164, ++165, ++165, ++165, ++165, ++166, ++166, ++167, ++167, ++168, ++168, ++169, ++169, ++170, ++170, ++170, ++170, ++171, ++171, ++171, ++171, ++172, ++172, ++173, ++173, ++174, ++174, ++175, ++175, ++176, ++176, ++176, ++176, ++177, ++177, ++177, ++177, ++178, ++178, ++178, ++178, ++179, ++179, ++179, ++179, ++180, ++180, ++180, ++180, ++181, ++181, ++181, ++181, ++182, ++182, ++182, ++182, ++183, ++183, ++183, ++183, ++184, ++184, ++184, ++184, ++185, ++185, ++185, ++185, ++186, ++186, ++186, ++186, ++187, ++187, ++187, ++187, ++188, ++188, ++188, ++188, ++189, ++189, ++189, ++189, ++190, ++190, ++190, ++190, ++191, ++191, ++191, ++191, ++192, ++192, ++192, ++192, ++193, ++193, ++193, ++193, ++194, ++194, ++194, ++194, ++195, ++195, ++195, ++195, ++196, ++196, ++196, ++196, ++197, ++197, ++197, ++197, ++198, ++198, ++198, ++198, ++199, ++199, ++199, ++199, ++200, ++200, ++200, ++200, ++201, ++201, ++201, ++201, ++202, ++202, ++202, ++203, ++203, ++203, ++203, ++204, ++204, ++204, ++204, ++205, ++205, ++205, ++205, ++206, ++206, ++206, ++206, ++207, ++207, ++207, ++207, ++208, ++208, ++208, ++208, ++209, ++209, ++209, ++209, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++210, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++211, ++212, ++212, ++212, ++212, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++213, ++214, ++214, ++214, ++214, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++215, ++216, ++216, ++216, ++216, ++217, ++217, ++217, ++217, ++218, ++218, ++218, ++218, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++219, ++220, ++220, ++220, ++220, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++221, ++222, ++222, ++222, ++222, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++223, ++224, ++224, ++224, ++224, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++225, ++226, ++226, ++226, ++226, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++227, ++228, ++228, ++228, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++229, ++230, ++230, ++230, ++230, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++231, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++232, ++233, ++233, ++233, ++233, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++234, ++235, ++235, ++235, ++235, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++236, ++237, ++237, ++237, ++237, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++238, ++239, ++239, ++239, ++239, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++240, ++241, ++241, ++241, ++241, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++242, ++243, ++243, ++243, ++243, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++244, ++245, ++245, ++245, ++245, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++246, ++247, ++247, ++247, ++247, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++248, ++249, ++249, ++249, ++249, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++250, ++251, ++251, ++251, ++251, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++252, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++253, ++254, ++254, ++254, ++254, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255, ++255 +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch new file mode 100644 index 0000000000..22074be148 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch @@ -0,0 +1,2249 @@ +From 0edf5a50dc0164db5bc71b1a5d1aa8bb1838262c Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +Date: Tue, 10 Mar 2009 10:49:03 +0200 +Subject: [PATCH] omap34xxcam: Add camera driver + +This is the camera driver for the OMAP 3 camera ISP and v4l2-int-device +sensors, lenses and (led) flashes. There are a few connections to OMAP +3 left but after those have been broken this is hardware independent. +Namely, the OMAP 3 ISP must offer a standard interface through +v4l2_subdev (or v4l2-int-device) first. + +This driver has originated from the omap24xxcam camera driver written +specifically for OMAP 2. + +TODO: + +- Convert to use v4l2_subdev instead of v4l2-int-device. + +Signed-off-by: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> +--- + drivers/media/video/Kconfig | 9 + + drivers/media/video/Makefile | 2 + + drivers/media/video/omap34xxcam.c | 1966 +++++++++++++++++++++++++++++++++++++ + drivers/media/video/omap34xxcam.h | 207 ++++ + 4 files changed, 2184 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/omap34xxcam.c + create mode 100644 drivers/media/video/omap34xxcam.h + +diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig +index 19cf3b8..3cdb5a4 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -711,6 +711,15 @@ config VIDEO_CAFE_CCIC + CMOS camera controller. This is the controller found on first- + generation OLPC systems. + ++config VIDEO_OMAP3 ++ tristate "OMAP 3 Camera support" ++ select VIDEOBUF_GEN ++ select VIDEOBUF_DMA_SG ++ select OMAP_IOMMU ++ depends on VIDEO_V4L2 && ARCH_OMAP34XX ++ ---help--- ++ Driver for an OMAP 3 camera controller. ++ + config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 && HAS_DMA +diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile +index e654270..74a684e 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -108,6 +108,8 @@ obj-$(CONFIG_VIDEO_OV7670) += ov7670.o + + obj-y += isp/ + ++obj-$(CONFIG_VIDEO_OMAP3) += omap34xxcam.o ++ + obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o + + obj-$(CONFIG_USB_DABUSB) += dabusb.o +diff --git a/drivers/media/video/omap34xxcam.c b/drivers/media/video/omap34xxcam.c +new file mode 100644 +index 0000000..00fdbf2 +--- /dev/null ++++ b/drivers/media/video/omap34xxcam.c +@@ -0,0 +1,1966 @@ ++/* ++ * omap34xxcam.c ++ * ++ * Copyright (C) 2006--2009 Nokia Corporation ++ * Copyright (C) 2007--2009 Texas Instruments ++ * ++ * Contact: Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * ++ * Originally based on the OMAP 2 camera driver. ++ * ++ * Written by Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Mohit Jalori ++ * Sameer Venkatraman ++ * Leonides Martinez ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include <linux/io.h> ++#include <linux/clk.h> ++#include <linux/pci.h> /* needed for videobufs */ ++#include <linux/delay.h> ++#include <linux/kernel.h> ++#include <linux/interrupt.h> ++#include <linux/videodev2.h> ++#include <linux/version.h> ++#include <linux/platform_device.h> ++ ++#include <media/v4l2-common.h> ++#include <media/v4l2-ioctl.h> ++ ++#include "omap34xxcam.h" ++#include "isp/isp.h" ++#include "isp/ispmmu.h" ++#include "isp/ispreg.h" ++#include "isp/ispccdc.h" ++#include "isp/isph3a.h" ++#include "isp/isp_af.h" ++#include "isp/isphist.h" ++#include "isp/isppreview.h" ++#include "isp/ispresizer.h" ++ ++#define OMAP34XXCAM_VERSION KERNEL_VERSION(0, 0, 0) ++ ++/* global variables */ ++static struct omap34xxcam_device *omap34xxcam; ++ ++/* ++ * ++ * Sensor handling. ++ * ++ */ ++ ++/** ++ * omap34xxcam_slave_power_set - set slave power state ++ * @vdev: per-video device data structure ++ * @power: new power state ++ */ ++static int omap34xxcam_slave_power_set(struct omap34xxcam_videodev *vdev, ++ enum v4l2_power power, ++ int mask) ++{ ++ int rval = 0, i = 0; ++ ++ BUG_ON(!mutex_is_locked(&vdev->mutex)); ++ ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++ vdev->power_state_wish = -1; ++#endif ++ ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ if (vdev->slave[i] == v4l2_int_device_dummy()) ++ continue; ++ ++ if (!(mask & (1 << i)) ++ || power == vdev->power_state[i]) ++ continue; ++ ++ rval = vidioc_int_s_power(vdev->slave[i], power); ++ ++ if (rval && power != V4L2_POWER_OFF) { ++ power = V4L2_POWER_OFF; ++ goto out; ++ } ++ ++ vdev->power_state[i] = power; ++ } ++ ++ return 0; ++ ++out: ++ for (i--; i >= 0; i--) { ++ if (vdev->slave[i] == v4l2_int_device_dummy()) ++ continue; ++ ++ if (!(mask & (1 << i))) ++ continue; ++ ++ vidioc_int_s_power(vdev->slave[i], power); ++ vdev->power_state[i] = power; ++ } ++ ++ return rval; ++} ++ ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++static void omap34xxcam_slave_power_work(struct work_struct *work) ++{ ++ struct omap34xxcam_videodev *vdev = ++ container_of(work, struct omap34xxcam_videodev, poweroff_work); ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->power_state_wish != -1) ++ omap34xxcam_slave_power_set(vdev, vdev->power_state_wish, ++ vdev->power_state_mask); ++ ++ mutex_unlock(&vdev->mutex); ++} ++ ++static void omap34xxcam_slave_power_timer(unsigned long ptr) ++{ ++ struct omap34xxcam_videodev *vdev = (void *)ptr; ++ ++ schedule_work(&vdev->poweroff_work); ++} ++ ++/** ++ * omap34xxcam_slave_power_suggest - delayed power state change ++ * ++ * @vdev: per-video device data structure ++ * @power: new power state ++ */ ++static void omap34xxcam_slave_power_suggest(struct omap34xxcam_videodev *vdev, ++ enum v4l2_power power, ++ int mask) ++{ ++ BUG_ON(!mutex_is_locked(&vdev->mutex)); ++ ++ del_timer(&vdev->poweroff_timer); ++ ++ vdev->power_state_wish = power; ++ vdev->power_state_mask = mask; ++ ++ mod_timer(&vdev->poweroff_timer, jiffies + OMAP34XXCAM_POWEROFF_DELAY); ++} ++#else /* OMAP34XXCAM_POWEROFF_DELAY */ ++#define omap34xxcam_slave_power_suggest(a, b, c) do {} while (0) ++#endif /* OMAP34XXCAM_POWEROFF_DELAY */ ++ ++/** ++ * omap34xxcam_update_vbq - Updates VBQ with completed input buffer ++ * @vb: ptr. to standard V4L2 video buffer structure ++ * ++ * Updates video buffer queue with completed buffer passed as ++ * input parameter. Also updates ISP H3A timestamp and field count ++ * statistics. ++ */ ++void omap34xxcam_vbq_complete(struct videobuf_buffer *vb, void *priv) ++{ ++ struct omap34xxcam_fh *fh = priv; ++ ++ do_gettimeofday(&vb->ts); ++ vb->field_count = atomic_add_return(2, &fh->field_count); ++ ++ wake_up(&vb->done); ++} ++ ++/** ++ * omap34xxcam_vbq_setup - Calcs size and num of buffs allowed in queue ++ * @vbq: ptr. to standard V4L2 video buffer queue structure ++ * @cnt: ptr to location to hold the count of buffers to be in the queue ++ * @size: ptr to location to hold the size of a frame ++ * ++ * Calculates the number of buffers of current image size that can be ++ * supported by the available capture memory. ++ */ ++static int omap34xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt, ++ unsigned int *size) ++{ ++ struct omap34xxcam_fh *fh = vbq->priv_data; ++ struct omap34xxcam_videodev *vdev = fh->vdev; ++ ++ if (*cnt <= 0) ++ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ ++ ++ if (*cnt > VIDEO_MAX_FRAME) ++ *cnt = VIDEO_MAX_FRAME; ++ ++ *size = vdev->pix.sizeimage; ++ ++ while (*size * *cnt > fh->vdev->vdev_sensor_config.capture_mem) ++ (*cnt)--; ++ ++ return isp_vbq_setup(vbq, cnt, size); ++} ++ ++/** ++ * omap34xxcam_vbq_release - Free resources for input VBQ and VB ++ * @vbq: ptr. to standard V4L2 video buffer queue structure ++ * @vb: ptr to standard V4L2 video buffer structure ++ * ++ * Unmap and free all memory associated with input VBQ and VB, also ++ * unmap the address in ISP MMU. Reset the VB state. ++ */ ++static void omap34xxcam_vbq_release(struct videobuf_queue *vbq, ++ struct videobuf_buffer *vb) ++{ ++ if (!vbq->streaming) { ++ isp_vbq_release(vbq, vb); ++ videobuf_dma_unmap(vbq, videobuf_to_dma(vb)); ++ videobuf_dma_free(videobuf_to_dma(vb)); ++ vb->state = VIDEOBUF_NEEDS_INIT; ++ } ++ return; ++} ++ ++/** ++ * omap34xxcam_vbq_prepare - V4L2 video ops buf_prepare handler ++ * @vbq: ptr. to standard V4L2 video buffer queue structure ++ * @vb: ptr to standard V4L2 video buffer structure ++ * @field: standard V4L2 field enum ++ * ++ * Verifies there is sufficient locked memory for the requested ++ * buffer, or if there is not, allocates, locks and initializes ++ * it. ++ */ ++static int omap34xxcam_vbq_prepare(struct videobuf_queue *vbq, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct omap34xxcam_fh *fh = vbq->priv_data; ++ struct omap34xxcam_videodev *vdev = fh->vdev; ++ int err = 0; ++ ++ /* ++ * Accessing pix here is okay since it's constant while ++ * streaming is on (and we only get called then). ++ */ ++ if (vb->baddr) { ++ /* This is a userspace buffer. */ ++ if (vdev->pix.sizeimage > vb->bsize) ++ /* The buffer isn't big enough. */ ++ return -EINVAL; ++ } else { ++ if (vb->state != VIDEOBUF_NEEDS_INIT ++ && vdev->pix.sizeimage > vb->bsize) ++ /* ++ * We have a kernel bounce buffer that has ++ * already been allocated. ++ */ ++ omap34xxcam_vbq_release(vbq, vb); ++ } ++ ++ vb->size = vdev->pix.bytesperline * vdev->pix.height; ++ vb->width = vdev->pix.width; ++ vb->height = vdev->pix.height; ++ vb->field = field; ++ ++ if (vb->state == VIDEOBUF_NEEDS_INIT) { ++ err = videobuf_iolock(vbq, vb, NULL); ++ if (!err) { ++ /* isp_addr will be stored locally inside isp code */ ++ err = isp_vbq_prepare(vbq, vb, field); ++ } ++ } ++ ++ if (!err) ++ vb->state = VIDEOBUF_PREPARED; ++ else ++ omap34xxcam_vbq_release(vbq, vb); ++ ++ return err; ++} ++ ++/** ++ * omap34xxcam_vbq_queue - V4L2 video ops buf_queue handler ++ * @vbq: ptr. to standard V4L2 video buffer queue structure ++ * @vb: ptr to standard V4L2 video buffer structure ++ * ++ * Maps the video buffer to sgdma and through the isp, sets ++ * the isp buffer done callback and sets the video buffer state ++ * to active. ++ */ ++static void omap34xxcam_vbq_queue(struct videobuf_queue *vbq, ++ struct videobuf_buffer *vb) ++{ ++ struct omap34xxcam_fh *fh = vbq->priv_data; ++ ++ vb->state = VIDEOBUF_ACTIVE; ++ ++ isp_buf_queue(vb, omap34xxcam_vbq_complete, (void *)fh); ++} ++ ++static struct videobuf_queue_ops omap34xxcam_vbq_ops = { ++ .buf_setup = omap34xxcam_vbq_setup, ++ .buf_prepare = omap34xxcam_vbq_prepare, ++ .buf_queue = omap34xxcam_vbq_queue, ++ .buf_release = omap34xxcam_vbq_release, ++}; ++ ++/* ++ * ++ * IOCTL interface. ++ * ++ */ ++ ++/** ++ * vidioc_querycap - V4L2 query capabilities IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @cap: ptr to standard V4L2 capability structure ++ * ++ * Fill in the V4L2 capabliity structure for the camera device ++ */ ++static int vidioc_querycap(struct file *file, void *fh, ++ struct v4l2_capability *cap) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ ++ strlcpy(cap->driver, CAM_SHORT_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, vdev->vfd->name, sizeof(cap->card)); ++ cap->version = OMAP34XXCAM_VERSION; ++ if (vdev->vdev_sensor != v4l2_int_device_dummy()) ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ ++ return 0; ++} ++ ++/** ++ * vidioc_enum_fmt_vid_cap - V4L2 enumerate format capabilities IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @f: ptr to standard V4L2 format description structure ++ * ++ * Fills in enumerate format capabilities information for sensor (if SOC ++ * sensor attached) or ISP (if raw sensor attached). ++ */ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_fmtdesc *f) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ if (vdev->vdev_sensor_config.sensor_isp) ++ rval = vidioc_int_enum_fmt_cap(vdev->vdev_sensor, f); ++ else ++ rval = isp_enum_fmt_cap(f); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_g_fmt_vid_cap - V4L2 get format capabilities IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @f: ptr to standard V4L2 format structure ++ * ++ * Fills in format capabilities for sensor (if SOC sensor attached) or ISP ++ * (if raw sensor attached). ++ */ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ f->fmt.pix = vdev->pix; ++ mutex_unlock(&vdev->mutex); ++ ++ return 0; ++} ++ ++static int try_pix_parm(struct omap34xxcam_videodev *vdev, ++ struct v4l2_pix_format *best_pix_in, ++ struct v4l2_pix_format *wanted_pix_out, ++ struct v4l2_fract *best_ival) ++{ ++ int fps; ++ int fmtd_index; ++ int rval; ++ struct v4l2_pix_format best_pix_out; ++ ++ if (best_ival->numerator == 0 ++ || best_ival->denominator == 0) ++ *best_ival = vdev->vdev_sensor_config.ival_default; ++ ++ fps = best_ival->denominator / best_ival->numerator; ++ ++ best_ival->denominator = 0; ++ best_pix_out.height = INT_MAX >> 1; ++ best_pix_out.width = best_pix_out.height; ++ ++ for (fmtd_index = 0; ; fmtd_index++) { ++ int size_index; ++ struct v4l2_fmtdesc fmtd; ++ ++ fmtd.index = fmtd_index; ++ fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ rval = vidioc_int_enum_fmt_cap(vdev->vdev_sensor, &fmtd); ++ if (rval) ++ break; ++ dev_info(&vdev->vfd->dev, "trying fmt %8.8x (%d)\n", ++ fmtd.pixelformat, fmtd_index); ++ /* ++ * Get supported resolutions. ++ */ ++ for (size_index = 0; ; size_index++) { ++ struct v4l2_frmsizeenum frms; ++ struct v4l2_pix_format pix_tmp_in, pix_tmp_out; ++ int ival_index; ++ ++ frms.index = size_index; ++ frms.pixel_format = fmtd.pixelformat; ++ ++ rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, ++ &frms); ++ if (rval) ++ break; ++ ++ pix_tmp_in.pixelformat = frms.pixel_format; ++ pix_tmp_in.width = frms.discrete.width; ++ pix_tmp_in.height = frms.discrete.height; ++ pix_tmp_out = *wanted_pix_out; ++ /* Don't do upscaling. */ ++ if (pix_tmp_out.width > pix_tmp_in.width) ++ pix_tmp_out.width = pix_tmp_in.width; ++ if (pix_tmp_out.height > pix_tmp_in.height) ++ pix_tmp_out.height = pix_tmp_in.height; ++ rval = isp_try_fmt_cap(&pix_tmp_in, &pix_tmp_out); ++ if (rval) ++ return rval; ++ ++ dev_info(&vdev->vfd->dev, "this w %d\th %d\tfmt %8.8x\t" ++ "-> w %d\th %d\t fmt %8.8x" ++ "\twanted w %d\th %d\t fmt %8.8x\n", ++ pix_tmp_in.width, pix_tmp_in.height, ++ pix_tmp_in.pixelformat, ++ pix_tmp_out.width, pix_tmp_out.height, ++ pix_tmp_out.pixelformat, ++ wanted_pix_out->width, wanted_pix_out->height, ++ wanted_pix_out->pixelformat); ++ ++#define IS_SMALLER_OR_EQUAL(pix1, pix2) \ ++ ((pix1)->width + (pix1)->height \ ++ < (pix2)->width + (pix2)->height) ++#define SIZE_DIFF(pix1, pix2) \ ++ (abs((pix1)->width - (pix2)->width) \ ++ + abs((pix1)->height - (pix2)->height)) ++ ++ /* ++ * Don't use modes that are farther from wanted size ++ * that what we already got. ++ */ ++ if (SIZE_DIFF(&pix_tmp_out, wanted_pix_out) ++ > SIZE_DIFF(&best_pix_out, wanted_pix_out)) { ++ dev_info(&vdev->vfd->dev, "size diff bigger: " ++ "w %d\th %d\tw %d\th %d\n", ++ pix_tmp_out.width, pix_tmp_out.height, ++ best_pix_out.width, ++ best_pix_out.height); ++ continue; ++ } ++ ++ /* ++ * There's an input mode that can provide output ++ * closer to wanted. ++ */ ++ if (SIZE_DIFF(&pix_tmp_out, wanted_pix_out) ++ < SIZE_DIFF(&best_pix_out, wanted_pix_out)) { ++ /* Force renegotation of fps etc. */ ++ best_ival->denominator = 0; ++ dev_info(&vdev->vfd->dev, "renegotiate: " ++ "w %d\th %d\tw %d\th %d\n", ++ pix_tmp_out.width, pix_tmp_out.height, ++ best_pix_out.width, ++ best_pix_out.height); ++ } ++ ++ for (ival_index = 0; ; ival_index++) { ++ struct v4l2_frmivalenum frmi; ++ ++ frmi.index = ival_index; ++ frmi.pixel_format = frms.pixel_format; ++ frmi.width = frms.discrete.width; ++ frmi.height = frms.discrete.height; ++ /* FIXME: try to fix standard... */ ++ frmi.reserved[0] = 0xdeafbeef; ++ ++ rval = vidioc_int_enum_frameintervals( ++ vdev->vdev_sensor, &frmi); ++ if (rval) ++ break; ++ ++ dev_info(&vdev->vfd->dev, "fps %d\n", ++ frmi.discrete.denominator ++ / frmi.discrete.numerator); ++ ++ if (best_ival->denominator == 0) ++ goto do_it_now; ++ ++ /* ++ * We aim to use maximum resolution ++ * from the sensor, provided that the ++ * fps is at least as close as on the ++ * current mode. ++ */ ++#define FPS_ABS_DIFF(fps, ival) abs(fps - (ival).denominator / (ival).numerator) ++ ++ /* Select mode with closest fps. */ ++ if (FPS_ABS_DIFF(fps, frmi.discrete) ++ < FPS_ABS_DIFF(fps, *best_ival)) { ++ dev_info(&vdev->vfd->dev, "closer fps: " ++ "fps %d\t fps %d\n", ++ FPS_ABS_DIFF(fps, ++ frmi.discrete), ++ FPS_ABS_DIFF(fps, *best_ival)); ++ goto do_it_now; ++ } ++ ++ /* ++ * Select bigger resolution if it's available ++ * at same fps. ++ */ ++ if (frmi.width + frmi.height ++ > best_pix_in->width + best_pix_in->height ++ && FPS_ABS_DIFF(fps, frmi.discrete) ++ <= FPS_ABS_DIFF(fps, *best_ival)) { ++ dev_info(&vdev->vfd->dev, "bigger res, " ++ "same fps: " ++ "w %d\th %d\tw %d\th %d\n", ++ frmi.width, frmi.height, ++ best_pix_in->width, ++ best_pix_in->height); ++ goto do_it_now; ++ } ++ ++ dev_info(&vdev->vfd->dev, "falling through\n"); ++ ++ continue; ++ ++do_it_now: ++ *best_ival = frmi.discrete; ++ best_pix_out = pix_tmp_out; ++ best_pix_in->width = frmi.width; ++ best_pix_in->height = frmi.height; ++ best_pix_in->pixelformat = frmi.pixel_format; ++ ++ dev_info(&vdev->vfd->dev, ++ "best_pix_in: w %d\th %d\tfmt %8.8x" ++ "\tival %d/%d\n", ++ best_pix_in->width, ++ best_pix_in->height, ++ best_pix_in->pixelformat, ++ best_ival->numerator, ++ best_ival->denominator); ++ } ++ } ++ } ++ ++ if (best_ival->denominator == 0) ++ return -EINVAL; ++ ++ *wanted_pix_out = best_pix_out; ++ ++ dev_info(&vdev->vfd->dev, "w %d, h %d, fmt %8.8x -> w %d, h %d\n", ++ best_pix_in->width, best_pix_in->height, ++ best_pix_in->pixelformat, ++ best_pix_out.width, best_pix_out.height); ++ ++ return isp_try_fmt_cap(best_pix_in, wanted_pix_out); ++} ++ ++static int s_pix_parm(struct omap34xxcam_videodev *vdev, ++ struct v4l2_pix_format *best_pix, ++ struct v4l2_pix_format *pix, ++ struct v4l2_fract *best_ival) ++{ ++ struct v4l2_streamparm a; ++ struct v4l2_format fmt; ++ int rval; ++ ++ rval = try_pix_parm(vdev, best_pix, pix, best_ival); ++ if (rval) ++ return rval; ++ ++ rval = isp_s_fmt_cap(best_pix, pix); ++ if (rval) ++ return rval; ++ ++ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ fmt.fmt.pix = *best_pix; ++ rval = vidioc_int_s_fmt_cap(vdev->vdev_sensor, &fmt); ++ if (rval) ++ return rval; ++ ++ a.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a.parm.capture.timeperframe = *best_ival; ++ rval = vidioc_int_s_parm(vdev->vdev_sensor, &a); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_s_fmt_vid_cap - V4L2 set format capabilities IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @f: ptr to standard V4L2 format structure ++ * ++ * Attempts to set input format with the sensor driver (first) and then the ++ * ISP. Returns the return code from vidioc_g_fmt_vid_cap(). ++ */ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ struct v4l2_pix_format pix_tmp; ++ struct v4l2_fract timeperframe; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming) { ++ rval = -EBUSY; ++ goto out; ++ } ++ ++ vdev->want_pix = f->fmt.pix; ++ ++ timeperframe = vdev->want_timeperframe; ++ ++ rval = s_pix_parm(vdev, &pix_tmp, &f->fmt.pix, &timeperframe); ++ if (!rval) ++ vdev->pix = f->fmt.pix; ++ ++out: ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_try_fmt_vid_cap - V4L2 try format capabilities IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @f: ptr to standard V4L2 format structure ++ * ++ * Checks if the given format is supported by the sensor driver and ++ * by the ISP. ++ */ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ struct v4l2_pix_format pix_tmp; ++ struct v4l2_fract timeperframe; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ ++ timeperframe = vdev->want_timeperframe; ++ ++ rval = try_pix_parm(vdev, &pix_tmp, &f->fmt.pix, &timeperframe); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_reqbufs - V4L2 request buffers IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @b: ptr to standard V4L2 request buffers structure ++ * ++ * Attempts to get a buffer from the buffer queue associated with the ++ * fh through the video buffer library API. ++ */ ++static int vidioc_reqbufs(struct file *file, void *fh, ++ struct v4l2_requestbuffers *b) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming) { ++ mutex_unlock(&vdev->mutex); ++ return -EBUSY; ++ } ++ ++ rval = videobuf_reqbufs(&ofh->vbq, b); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ /* ++ * Either videobuf_reqbufs failed or the buffers are not ++ * memory-mapped (which would need special attention). ++ */ ++ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP) ++ goto out; ++ ++out: ++ return rval; ++} ++ ++/** ++ * vidioc_querybuf - V4L2 query buffer IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @b: ptr to standard V4L2 buffer structure ++ * ++ * Attempts to fill in the v4l2_buffer structure for the buffer queue ++ * associated with the fh through the video buffer library API. ++ */ ++static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ ++ return videobuf_querybuf(&ofh->vbq, b); ++} ++ ++/** ++ * vidioc_qbuf - V4L2 queue buffer IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @b: ptr to standard V4L2 buffer structure ++ * ++ * Attempts to queue the v4l2_buffer on the buffer queue ++ * associated with the fh through the video buffer library API. ++ */ ++static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ ++ return videobuf_qbuf(&ofh->vbq, b); ++} ++ ++/** ++ * vidioc_dqbuf - V4L2 dequeue buffer IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @b: ptr to standard V4L2 buffer structure ++ * ++ * Attempts to dequeue the v4l2_buffer from the buffer queue ++ * associated with the fh through the video buffer library API. If the ++ * buffer is a user space buffer, then this function will also requeue it, ++ * as user does not expect to do this. ++ */ ++static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ int rval; ++ ++videobuf_dqbuf_again: ++ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); ++ ++ /* ++ * This is a hack. We don't want to show -EIO to the user ++ * space. Requeue the buffer and try again if we're not doing ++ * this in non-blocking mode. ++ */ ++ if (rval == -EIO) { ++ videobuf_qbuf(&ofh->vbq, b); ++ if (!(file->f_flags & O_NONBLOCK)) ++ goto videobuf_dqbuf_again; ++ /* ++ * We don't have a videobuf_buffer now --- maybe next ++ * time... ++ */ ++ rval = -EAGAIN; ++ } ++ ++ return rval; ++} ++ ++/** ++ * vidioc_streamon - V4L2 streamon IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @i: V4L2 buffer type ++ * ++ * Attempts to start streaming by enabling the sensor interface and turning ++ * on video buffer streaming through the video buffer library API. Upon ++ * success the function returns 0, otherwise an error code is returned. ++ */ ++static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming) { ++ rval = -EBUSY; ++ goto out; ++ } ++ ++ rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS); ++ if (rval) { ++ dev_dbg(&vdev->vfd->dev, ++ "omap34xxcam_slave_power_set failed\n"); ++ goto out; ++ } ++ ++ rval = videobuf_streamon(&ofh->vbq); ++ if (rval) ++ omap34xxcam_slave_power_set( ++ vdev, V4L2_POWER_OFF, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS); ++ else ++ vdev->streaming = file; ++ ++out: ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_streamoff - V4L2 streamoff IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @i: V4L2 buffer type ++ * ++ * Attempts to stop streaming by flushing all scheduled work, waiting on ++ * any queued buffers to complete and then stopping the ISP and turning ++ * off video buffer streaming through the video buffer library API. Upon ++ * success the function returns 0, otherwise an error code is returned. ++ */ ++static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ struct videobuf_queue *q = &ofh->vbq; ++ int rval; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->streaming == file) ++ isp_stop(); ++ ++ rval = videobuf_streamoff(q); ++ if (!rval) { ++ vdev->streaming = NULL; ++ ++ omap34xxcam_slave_power_set(vdev, V4L2_POWER_STANDBY, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR); ++ omap34xxcam_slave_power_suggest(vdev, V4L2_POWER_STANDBY, ++ OMAP34XXCAM_SLAVE_POWER_LENS); ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_enum_input - V4L2 enumerate input IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @inp: V4L2 input type information structure ++ * ++ * Fills in v4l2_input structure. Returns 0. ++ */ ++static int vidioc_enum_input(struct file *file, void *fh, ++ struct v4l2_input *inp) ++{ ++ if (inp->index > 0) ++ return -EINVAL; ++ ++ strlcpy(inp->name, "camera", sizeof(inp->name)); ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ ++ return 0; ++} ++ ++/** ++ * vidioc_g_input - V4L2 get input IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @i: address to hold index of input supported ++ * ++ * Sets index to 0. ++ */ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) ++{ ++ *i = 0; ++ ++ return 0; ++} ++ ++/** ++ * vidioc_s_input - V4L2 set input IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @i: index of input selected ++ * ++ * 0 is only index supported. ++ */ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int i) ++{ ++ if (i > 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/** ++ * vidioc_queryctrl - V4L2 query control IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 query control ioctl structure ++ * ++ * If the requested control is supported, returns the control information ++ * in the v4l2_queryctrl structure. Otherwise, returns -EINVAL if the ++ * control is not supported. If the sensor being used is a "smart sensor", ++ * this request is passed to the sensor driver, otherwise the ISP is ++ * queried and if it does not support the requested control, the request ++ * is forwarded to the "raw" sensor driver to see if it supports it. ++ */ ++static int vidioc_queryctrl(struct file *file, void *fh, ++ struct v4l2_queryctrl *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ struct v4l2_queryctrl a_tmp; ++ int best_slave = -1; ++ u32 best_ctrl = (u32)-1; ++ int i; ++ ++ if (vdev->vdev_sensor_config.sensor_isp) ++ return vidioc_int_queryctrl(vdev->vdev_sensor, a); ++ ++ /* No next flags: try slaves directly. */ ++ if (!(a->id & V4L2_CTRL_FLAG_NEXT_CTRL)) { ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ if (!vidioc_int_queryctrl(vdev->slave[i], a)) ++ return 0; ++ } ++ return isp_queryctrl(a); ++ } ++ ++ /* Find slave with smallest next control id. */ ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ a_tmp = *a; ++ ++ if (vidioc_int_queryctrl(vdev->slave[i], &a_tmp)) ++ continue; ++ ++ if (a_tmp.id < best_ctrl) { ++ best_slave = i; ++ best_ctrl = a_tmp.id; ++ } ++ } ++ ++ a_tmp = *a; ++ if (!isp_queryctrl(&a_tmp)) { ++ if (a_tmp.id < best_ctrl) { ++ *a = a_tmp; ++ ++ return 0; ++ } ++ } ++ ++ if (best_slave == -1) ++ return -EINVAL; ++ ++ a->id = best_ctrl; ++ return vidioc_int_queryctrl(vdev->slave[best_slave], a); ++} ++ ++/** ++ * vidioc_querymenu - V4L2 query menu IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 query menu ioctl structure ++ * ++ * If the requested control is supported, returns the menu information ++ * in the v4l2_querymenu structure. Otherwise, returns -EINVAL if the ++ * control is not supported or is not a menu. If the sensor being used ++ * is a "smart sensor", this request is passed to the sensor driver, ++ * otherwise the ISP is queried and if it does not support the requested ++ * menu control, the request is forwarded to the "raw" sensor driver to ++ * see if it supports it. ++ */ ++static int vidioc_querymenu(struct file *file, void *fh, ++ struct v4l2_querymenu *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int i; ++ ++ if (vdev->vdev_sensor_config.sensor_isp) ++ return vidioc_int_querymenu(vdev->vdev_sensor, a); ++ ++ /* Try slaves directly. */ ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ if (!vidioc_int_querymenu(vdev->slave[i], a)) ++ return 0; ++ } ++ return isp_querymenu(a); ++} ++ ++static int vidioc_g_ext_ctrls(struct file *file, void *fh, ++ struct v4l2_ext_controls *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int i, ctrl_idx, rval = 0; ++ ++ mutex_lock(&vdev->mutex); ++ ++ for (ctrl_idx = 0; ctrl_idx < a->count; ctrl_idx++) { ++ struct v4l2_control ctrl; ++ ++ ctrl.id = a->controls[ctrl_idx].id; ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = vidioc_int_g_ctrl(vdev->vdev_sensor, &ctrl); ++ } else { ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ rval = vidioc_int_g_ctrl(vdev->slave[i], &ctrl); ++ if (!rval) ++ break; ++ } ++ } ++ ++ if (rval) ++ rval = isp_g_ctrl(&ctrl); ++ ++ if (rval) { ++ a->error_idx = ctrl_idx; ++ break; ++ } ++ ++ a->controls[ctrl_idx].value = ctrl.value; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++static int vidioc_s_ext_ctrls(struct file *file, void *fh, ++ struct v4l2_ext_controls *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int i, ctrl_idx, rval = 0; ++ ++ mutex_lock(&vdev->mutex); ++ ++ for (ctrl_idx = 0; ctrl_idx < a->count; ctrl_idx++) { ++ struct v4l2_control ctrl; ++ ++ ctrl.id = a->controls[ctrl_idx].id; ++ ctrl.value = a->controls[ctrl_idx].value; ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = vidioc_int_s_ctrl(vdev->vdev_sensor, &ctrl); ++ } else { ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ rval = vidioc_int_s_ctrl(vdev->slave[i], &ctrl); ++ if (!rval) ++ break; ++ } ++ } ++ ++ if (rval) ++ rval = isp_s_ctrl(&ctrl); ++ ++ if (rval) { ++ a->error_idx = ctrl_idx; ++ break; ++ } ++ ++ a->controls[ctrl_idx].value = ctrl.value; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_g_parm - V4L2 get parameters IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 stream parameters structure ++ * ++ * If request is for video capture buffer type, handles request by ++ * forwarding to sensor driver. ++ */ ++static int vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ rval = vidioc_int_g_parm(vdev->vdev_sensor, a); ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_s_parm - V4L2 set parameters IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 stream parameters structure ++ * ++ * If request is for video capture buffer type, handles request by ++ * first getting current stream parameters from sensor, then forwarding ++ * request to set new parameters to sensor driver. It then attempts to ++ * enable the sensor interface with the new parameters. If this fails, it ++ * reverts back to the previous parameters. ++ */ ++static int vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ struct v4l2_pix_format pix_tmp_sensor, pix_tmp; ++ int rval; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming) { ++ rval = -EBUSY; ++ goto out; ++ } ++ ++ vdev->want_timeperframe = a->parm.capture.timeperframe; ++ ++ pix_tmp = vdev->want_pix; ++ ++ rval = s_pix_parm(vdev, &pix_tmp_sensor, &pix_tmp, ++ &a->parm.capture.timeperframe); ++ ++out: ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_cropcap - V4L2 crop capture IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 crop capture structure ++ * ++ * If using a "smart" sensor, just forwards request to the sensor driver, ++ * otherwise fills in the v4l2_cropcap values locally. ++ */ ++static int vidioc_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ struct v4l2_cropcap *cropcap = a; ++ int rval; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ ++ rval = vidioc_int_cropcap(vdev->vdev_sensor, a); ++ ++ if (rval && !vdev->vdev_sensor_config.sensor_isp) { ++ struct v4l2_format f; ++ ++ /* cropcap failed, try to do this via g_fmt_cap */ ++ rval = vidioc_int_g_fmt_cap(vdev->vdev_sensor, &f); ++ if (!rval) { ++ cropcap->bounds.top = 0; ++ cropcap->bounds.left = 0; ++ cropcap->bounds.width = f.fmt.pix.width; ++ cropcap->bounds.height = f.fmt.pix.height; ++ cropcap->defrect = cropcap->bounds; ++ cropcap->pixelaspect.numerator = 1; ++ cropcap->pixelaspect.denominator = 1; ++ } ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_g_crop - V4L2 get capture crop IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 crop structure ++ * ++ * If using a "smart" sensor, just forwards request to the sensor driver, ++ * otherwise calls the isp functions to fill in current crop values. ++ */ ++static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval = 0; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->vdev_sensor_config.sensor_isp) ++ rval = vidioc_int_g_crop(vdev->vdev_sensor, a); ++ else ++ rval = isp_g_crop(a); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++/** ++ * vidioc_s_crop - V4L2 set capture crop IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @a: standard V4L2 crop structure ++ * ++ * If using a "smart" sensor, just forwards request to the sensor driver, ++ * otherwise calls the isp functions to set the current crop values. ++ */ ++static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *a) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval = 0; ++ ++ if (vdev->vdev_sensor == v4l2_int_device_dummy()) ++ return -EINVAL; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->vdev_sensor_config.sensor_isp) ++ rval = vidioc_int_s_crop(vdev->vdev_sensor, a); ++ else ++ rval = isp_s_crop(a, &vdev->pix); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return rval; ++} ++ ++static int vidioc_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *frms) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ u32 pixel_format; ++ int rval; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, frms); ++ } else { ++ pixel_format = frms->pixel_format; ++ frms->pixel_format = -1; /* ISP does format conversion */ ++ rval = vidioc_int_enum_framesizes(vdev->vdev_sensor, frms); ++ frms->pixel_format = pixel_format; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ return rval; ++} ++ ++static int vidioc_enum_frameintervals(struct file *file, void *fh, ++ struct v4l2_frmivalenum *frmi) ++{ ++ struct omap34xxcam_fh *ofh = fh; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ u32 pixel_format; ++ int rval; ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = vidioc_int_enum_frameintervals(vdev->vdev_sensor, frmi); ++ } else { ++ pixel_format = frmi->pixel_format; ++ frmi->pixel_format = -1; /* ISP does format conversion */ ++ rval = vidioc_int_enum_frameintervals(vdev->vdev_sensor, frmi); ++ frmi->pixel_format = pixel_format; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ return rval; ++} ++ ++/** ++ * vidioc_default - private IOCTL handler ++ * @file: ptr. to system file structure ++ * @fh: ptr to hold address of omap34xxcam_fh struct (per-filehandle data) ++ * @cmd: ioctl cmd value ++ * @arg: ioctl arg value ++ * ++ * If the sensor being used is a "smart sensor", this request is returned to ++ * caller with -EINVAL err code. Otherwise if the control id is the private ++ * VIDIOC_PRIVATE_ISP_AEWB_REQ to update the analog gain or exposure, ++ * then this request is forwared directly to the sensor to incorporate the ++ * feedback. The request is then passed on to the ISP private IOCTL handler, ++ * isp_handle_private() ++ */ ++static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) ++{ ++ struct omap34xxcam_fh *ofh = file->private_data; ++ struct omap34xxcam_videodev *vdev = ofh->vdev; ++ int rval; ++ ++ if (vdev->vdev_sensor_config.sensor_isp) { ++ rval = -EINVAL; ++ } else { ++ switch (cmd) { ++ case VIDIOC_PRIVATE_ISP_AEWB_REQ: ++ { ++ /* Need to update sensor first */ ++ struct isph3a_aewb_data *data; ++ struct v4l2_control vc; ++ ++ data = (struct isph3a_aewb_data *) arg; ++ if (data->update & SET_EXPOSURE) { ++ dev_info(&vdev->vfd->dev, "using " ++ "VIDIOC_PRIVATE_ISP_AEWB_REQ to set " ++ "exposure is deprecated!\n"); ++ vc.id = V4L2_CID_EXPOSURE; ++ vc.value = data->shutter; ++ mutex_lock(&vdev->mutex); ++ rval = vidioc_int_s_ctrl(vdev->vdev_sensor, ++ &vc); ++ mutex_unlock(&vdev->mutex); ++ if (rval) ++ goto out; ++ } ++ if (data->update & SET_ANALOG_GAIN) { ++ dev_info(&vdev->vfd->dev, "using " ++ "VIDIOC_PRIVATE_ISP_AEWB_REQ to set " ++ "gain is deprecated!\n"); ++ vc.id = V4L2_CID_GAIN; ++ vc.value = data->gain; ++ mutex_lock(&vdev->mutex); ++ rval = vidioc_int_s_ctrl(vdev->vdev_sensor, ++ &vc); ++ mutex_unlock(&vdev->mutex); ++ if (rval) ++ goto out; ++ } ++ } ++ break; ++ case VIDIOC_PRIVATE_ISP_AF_REQ: { ++ /* Need to update lens first */ ++ struct isp_af_data *data; ++ struct v4l2_control vc; ++ ++ if (!vdev->vdev_lens) { ++ rval = -EINVAL; ++ goto out; ++ } ++ data = (struct isp_af_data *) arg; ++ if (data->update & LENS_DESIRED_POSITION) { ++ dev_info(&vdev->vfd->dev, "using " ++ "VIDIOC_PRIVATE_ISP_AF_REQ to set " ++ "lens position is deprecated!\n"); ++ vc.id = V4L2_CID_FOCUS_ABSOLUTE; ++ vc.value = data->desired_lens_direction; ++ mutex_lock(&vdev->mutex); ++ rval = vidioc_int_s_ctrl(vdev->vdev_lens, &vc); ++ mutex_unlock(&vdev->mutex); ++ if (rval) ++ goto out; ++ } ++ } ++ break; ++ } ++ ++ mutex_lock(&vdev->mutex); ++ rval = isp_handle_private(cmd, arg); ++ mutex_unlock(&vdev->mutex); ++ } ++out: ++ return rval; ++} ++ ++/* ++ * ++ * File operations. ++ * ++ */ ++ ++/** ++ * omap34xxcam_poll - file operations poll handler ++ * @file: ptr. to system file structure ++ * @wait: system poll table structure ++ * ++ */ ++static unsigned int omap34xxcam_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct omap34xxcam_fh *fh = file->private_data; ++ struct omap34xxcam_videodev *vdev = fh->vdev; ++ struct videobuf_buffer *vb; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming != file) { ++ mutex_unlock(&vdev->mutex); ++ return POLLERR; ++ } ++ mutex_unlock(&vdev->mutex); ++ ++ mutex_lock(&fh->vbq.vb_lock); ++ if (list_empty(&fh->vbq.stream)) { ++ mutex_unlock(&fh->vbq.vb_lock); ++ return POLLERR; ++ } ++ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream); ++ mutex_unlock(&fh->vbq.vb_lock); ++ ++ poll_wait(file, &vb->done, wait); ++ ++ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR) ++ return POLLIN | POLLRDNORM; ++ ++ return 0; ++} ++ ++/** ++ * omap34xxcam_mmap - file operations mmap handler ++ * @file: ptr. to system file structure ++ * @vma: system virt. mem. area structure ++ * ++ * Maps a virtual memory area via the video buffer API ++ */ ++static int omap34xxcam_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct omap34xxcam_fh *fh = file->private_data; ++ return videobuf_mmap_mapper(&fh->vbq, vma); ++} ++ ++/** ++ * omap34xxcam_open - file operations open handler ++ * @inode: ptr. to system inode structure ++ * @file: ptr. to system file structure ++ * ++ * Allocates and initializes the per-filehandle data (omap34xxcam_fh), ++ * enables the sensor, opens/initializes the ISP interface and the ++ * video buffer queue. Note that this function will allow multiple ++ * file handles to be open simultaneously, however only the first ++ * handle opened will initialize the ISP. It is the application ++ * responsibility to only use one handle for streaming and the others ++ * for control only. ++ * This function returns 0 upon success and -ENODEV upon error. ++ */ ++static int omap34xxcam_open(struct file *file) ++{ ++ int rval = 0; ++ struct omap34xxcam_videodev *vdev = NULL; ++ struct omap34xxcam_device *cam = omap34xxcam; ++ struct omap34xxcam_fh *fh; ++ struct v4l2_format format; ++ int i; ++ ++ for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { ++ if (cam->vdevs[i].vfd ++ && cam->vdevs[i].vfd->minor == ++ iminor(file->f_dentry->d_inode)) { ++ vdev = &cam->vdevs[i]; ++ break; ++ } ++ } ++ ++ if (!vdev || !vdev->vfd) ++ return -ENODEV; ++ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (fh == NULL) ++ return -ENOMEM; ++ ++ mutex_lock(&vdev->mutex); ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ if (vdev->slave[i] != v4l2_int_device_dummy() ++ && !try_module_get(vdev->slave[i]->module)) { ++ mutex_unlock(&vdev->mutex); ++ dev_err(&vdev->vfd->dev, "can't try_module_get %s\n", ++ vdev->slave[i]->name); ++ rval = -ENODEV; ++ goto out_try_module_get; ++ } ++ } ++ ++ if (atomic_inc_return(&vdev->users) == 1) { ++ rval = isp_get(); ++ if (rval < 0) { ++ dev_err(&vdev->vfd->dev, "can't get isp\n"); ++ goto out_isp_get; ++ } ++ if (omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, ++ OMAP34XXCAM_SLAVE_POWER_ALL)) { ++ dev_err(&vdev->vfd->dev, "can't power up slaves\n"); ++ rval = -EBUSY; ++ goto out_slave_power_set_standby; ++ } ++ omap34xxcam_slave_power_set( ++ vdev, V4L2_POWER_STANDBY, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR); ++ omap34xxcam_slave_power_suggest( ++ vdev, V4L2_POWER_STANDBY, ++ OMAP34XXCAM_SLAVE_POWER_LENS); ++ } ++ ++ fh->vdev = vdev; ++ ++ if (!vdev->pix.width ++ && vdev->vdev_sensor != v4l2_int_device_dummy()) { ++ memset(&format, 0, sizeof(format)); ++ if (vidioc_int_g_fmt_cap(vdev->vdev_sensor, &format)) { ++ dev_err(&vdev->vfd->dev, ++ "can't get current pix from sensor!\n"); ++ goto out_vidioc_int_g_fmt_cap; ++ } ++ if (!vdev->vdev_sensor_config.sensor_isp) { ++ struct v4l2_pix_format pix = format.fmt.pix; ++ if (isp_s_fmt_cap(&pix, &format.fmt.pix)) { ++ dev_err(&vdev->vfd->dev, ++ "isp doesn't like the sensor!\n"); ++ goto out_isp_s_fmt_cap; ++ } ++ } ++ vdev->pix = format.fmt.pix; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ ++ file->private_data = fh; ++ ++ spin_lock_init(&fh->vbq_lock); ++ ++ videobuf_queue_sg_init(&fh->vbq, &omap34xxcam_vbq_ops, NULL, ++ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_NONE, ++ sizeof(struct videobuf_buffer), fh); ++ ++ return 0; ++ ++out_isp_s_fmt_cap: ++out_vidioc_int_g_fmt_cap: ++ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, ++ OMAP34XXCAM_SLAVE_POWER_ALL); ++out_slave_power_set_standby: ++ isp_put(); ++ ++out_isp_get: ++ atomic_dec(&vdev->users); ++ mutex_unlock(&vdev->mutex); ++ ++out_try_module_get: ++ for (i--; i >= 0; i--) ++ if (vdev->slave[i] != v4l2_int_device_dummy()) ++ module_put(vdev->slave[i]->module); ++ ++ kfree(fh); ++ ++ return rval; ++} ++ ++/** ++ * omap34xxcam_release - file operations release handler ++ * @inode: ptr. to system inode structure ++ * @file: ptr. to system file structure ++ * ++ * Complement of omap34xxcam_open. This function will flush any scheduled ++ * work, disable the sensor, close the ISP interface, stop the ++ * video buffer queue from streaming and free the per-filehandle data ++ * (omap34xxcam_fh). Note that because multiple open file handles ++ * are allowed, this function will only close the ISP and disable the ++ * sensor when the last open file handle (by count) is closed. ++ * This function returns 0. ++ */ ++static int omap34xxcam_release(struct file *file) ++{ ++ struct omap34xxcam_fh *fh = file->private_data; ++ struct omap34xxcam_videodev *vdev = fh->vdev; ++ int i; ++ ++ mutex_lock(&vdev->mutex); ++ if (vdev->streaming == file) { ++ isp_stop(); ++ videobuf_streamoff(&fh->vbq); ++ omap34xxcam_slave_power_set( ++ vdev, V4L2_POWER_STANDBY, ++ OMAP34XXCAM_SLAVE_POWER_SENSOR); ++ omap34xxcam_slave_power_suggest( ++ vdev, V4L2_POWER_STANDBY, ++ OMAP34XXCAM_SLAVE_POWER_LENS); ++ vdev->streaming = NULL; ++ } ++ ++ if (atomic_dec_return(&vdev->users) == 0) { ++ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, ++ OMAP34XXCAM_SLAVE_POWER_ALL); ++ isp_put(); ++ } ++ mutex_unlock(&vdev->mutex); ++ ++ file->private_data = NULL; ++ ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) ++ if (vdev->slave[i] != v4l2_int_device_dummy()) ++ module_put(vdev->slave[i]->module); ++ ++ kfree(fh); ++ ++ return 0; ++} ++ ++static struct v4l2_file_operations omap34xxcam_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = video_ioctl2, ++ .poll = omap34xxcam_poll, ++ .mmap = omap34xxcam_mmap, ++ .open = omap34xxcam_open, ++ .release = omap34xxcam_release, ++}; ++ ++static void omap34xxcam_vfd_name_update(struct omap34xxcam_videodev *vdev) ++{ ++ struct video_device *vfd = vdev->vfd; ++ int i; ++ ++ strlcpy(vfd->name, CAM_SHORT_NAME, sizeof(vfd->name)); ++ for (i = 0; i <= OMAP34XXCAM_SLAVE_FLASH; i++) { ++ strlcat(vfd->name, "/", sizeof(vfd->name)); ++ if (vdev->slave[i] == v4l2_int_device_dummy()) ++ continue; ++ strlcat(vfd->name, vdev->slave[i]->name, sizeof(vfd->name)); ++ } ++ dev_info(&vdev->vfd->dev, "video%d is now %s\n", vfd->num, vfd->name); ++} ++ ++/** ++ * omap34xxcam_device_unregister - V4L2 detach handler ++ * @s: ptr. to standard V4L2 device information structure ++ * ++ * Detach sensor and unregister and release the video device. ++ */ ++static void omap34xxcam_device_unregister(struct v4l2_int_device *s) ++{ ++ struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; ++ struct omap34xxcam_hw_config hwc; ++ ++ BUG_ON(vidioc_int_g_priv(s, &hwc) < 0); ++ ++ mutex_lock(&vdev->mutex); ++ ++ if (vdev->slave[hwc.dev_type] != v4l2_int_device_dummy()) { ++ vdev->slave[hwc.dev_type] = v4l2_int_device_dummy(); ++ vdev->slaves--; ++ omap34xxcam_vfd_name_update(vdev); ++ } ++ ++ if (vdev->slaves == 0 && vdev->vfd) { ++ if (vdev->vfd->minor == -1) { ++ /* ++ * The device was never registered, so release the ++ * video_device struct directly. ++ */ ++ video_device_release(vdev->vfd); ++ } else { ++ /* ++ * The unregister function will release the ++ * video_device struct as well as ++ * unregistering it. ++ */ ++ video_unregister_device(vdev->vfd); ++ } ++ vdev->vfd = NULL; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++} ++ ++static const struct v4l2_ioctl_ops omap34xxcam_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_querymenu = vidioc_querymenu, ++ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, ++ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_s_parm = vidioc_s_parm, ++ .vidioc_cropcap = vidioc_cropcap, ++ .vidioc_g_crop = vidioc_g_crop, ++ .vidioc_s_crop = vidioc_s_crop, ++ .vidioc_enum_framesizes = vidioc_enum_framesizes, ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .vidioc_default = vidioc_default, ++}; ++ ++/** ++ * omap34xxcam_device_register - V4L2 attach handler ++ * @s: ptr. to standard V4L2 device information structure ++ * ++ * Allocates and initializes the V4L2 video_device structure, initializes ++ * the sensor, and finally ++ registers the device with V4L2 based on the ++ * video_device structure. ++ * ++ * Returns 0 on success, otherwise an appropriate error code on ++ * failure. ++ */ ++static int omap34xxcam_device_register(struct v4l2_int_device *s) ++{ ++ struct omap34xxcam_videodev *vdev = s->u.slave->master->priv; ++ struct omap34xxcam_hw_config hwc; ++ int rval; ++ ++ /* We need to check rval just once. The place is here. */ ++ if (vidioc_int_g_priv(s, &hwc)) ++ return -ENODEV; ++ ++ if (vdev->index != hwc.dev_index) ++ return -ENODEV; ++ ++ if (hwc.dev_type < 0 || hwc.dev_type > OMAP34XXCAM_SLAVE_FLASH) ++ return -EINVAL; ++ ++ if (vdev->slave[hwc.dev_type] != v4l2_int_device_dummy()) ++ return -EBUSY; ++ ++ mutex_lock(&vdev->mutex); ++ if (atomic_read(&vdev->users)) { ++ printk(KERN_ERR "%s: we're open (%d), can't register\n", ++ __func__, atomic_read(&vdev->users)); ++ mutex_unlock(&vdev->mutex); ++ return -EBUSY; ++ } ++ ++ vdev->slaves++; ++ vdev->slave[hwc.dev_type] = s; ++ vdev->slave_config[hwc.dev_type] = hwc; ++ ++ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) { ++ rval = isp_get(); ++ if (rval < 0) { ++ printk(KERN_ERR "%s: can't get ISP, " ++ "sensor init failed\n", __func__); ++ goto err; ++ } ++ } ++ rval = omap34xxcam_slave_power_set(vdev, V4L2_POWER_ON, ++ 1 << hwc.dev_type); ++ if (rval) ++ goto err_omap34xxcam_slave_power_set; ++ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) { ++ struct v4l2_format format; ++ ++ format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ rval = vidioc_int_g_fmt_cap(vdev->vdev_sensor, &format); ++ if (rval) ++ rval = -EBUSY; ++ ++ vdev->want_pix = format.fmt.pix; ++ } ++ omap34xxcam_slave_power_set(vdev, V4L2_POWER_OFF, 1 << hwc.dev_type); ++ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) ++ isp_put(); ++ ++ if (rval) ++ goto err; ++ ++ /* Are we the first slave? */ ++ if (vdev->slaves == 1) { ++ /* initialize the video_device struct */ ++ vdev->vfd = video_device_alloc(); ++ if (!vdev->vfd) { ++ printk(KERN_ERR "%s: could not allocate " ++ "video device struct\n", __func__); ++ rval = -ENOMEM; ++ goto err; ++ } ++ vdev->vfd->release = video_device_release; ++ vdev->vfd->minor = -1; ++ vdev->vfd->fops = &omap34xxcam_fops; ++ vdev->vfd->ioctl_ops = &omap34xxcam_ioctl_ops; ++ video_set_drvdata(vdev->vfd, vdev); ++ ++ if (video_register_device(vdev->vfd, VFL_TYPE_GRABBER, ++ hwc.dev_minor) < 0) { ++ printk(KERN_ERR "%s: could not register V4L device\n", ++ __func__); ++ vdev->vfd->minor = -1; ++ rval = -EBUSY; ++ goto err; ++ } ++ } ++ ++ omap34xxcam_vfd_name_update(vdev); ++ ++ mutex_unlock(&vdev->mutex); ++ ++ return 0; ++ ++err_omap34xxcam_slave_power_set: ++ if (hwc.dev_type == OMAP34XXCAM_SLAVE_SENSOR) ++ isp_put(); ++ ++err: ++ if (s == vdev->slave[hwc.dev_type]) { ++ vdev->slave[hwc.dev_type] = v4l2_int_device_dummy(); ++ vdev->slaves--; ++ } ++ ++ mutex_unlock(&vdev->mutex); ++ omap34xxcam_device_unregister(s); ++ ++ return rval; ++} ++ ++static struct v4l2_int_master omap34xxcam_master = { ++ .attach = omap34xxcam_device_register, ++ .detach = omap34xxcam_device_unregister, ++}; ++ ++/* ++ * ++ * Module initialisation and deinitialisation ++ * ++ */ ++ ++static void omap34xxcam_exit(void) ++{ ++ struct omap34xxcam_device *cam = omap34xxcam; ++ int i; ++ ++ if (!cam) ++ return; ++ ++ for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { ++ if (cam->vdevs[i].cam == NULL) ++ continue; ++ ++ v4l2_int_device_unregister(&cam->vdevs[i].master); ++ cam->vdevs[i].cam = NULL; ++ } ++ ++ omap34xxcam = NULL; ++ ++ kfree(cam); ++} ++ ++static int __init omap34xxcam_init(void) ++{ ++ struct omap34xxcam_device *cam; ++ int i; ++ ++ cam = kzalloc(sizeof(*cam), GFP_KERNEL); ++ if (!cam) { ++ printk(KERN_ERR "%s: could not allocate memory\n", __func__); ++ return -ENOMEM; ++ } ++ ++ omap34xxcam = cam; ++ ++ for (i = 0; i < OMAP34XXCAM_VIDEODEVS; i++) { ++ struct omap34xxcam_videodev *vdev = &cam->vdevs[i]; ++ struct v4l2_int_device *m = &vdev->master; ++ ++ m->module = THIS_MODULE; ++ strlcpy(m->name, CAM_NAME, sizeof(m->name)); ++ m->type = v4l2_int_type_master; ++ m->u.master = &omap34xxcam_master; ++ m->priv = vdev; ++ ++ mutex_init(&vdev->mutex); ++ vdev->index = i; ++ vdev->cam = cam; ++ vdev->vdev_sensor = ++ vdev->vdev_lens = ++ vdev->vdev_flash = v4l2_int_device_dummy(); ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++ setup_timer(&vdev->poweroff_timer, ++ omap34xxcam_slave_power_timer, (unsigned long)vdev); ++ INIT_WORK(&vdev->poweroff_work, omap34xxcam_slave_power_work); ++#endif /* OMAP34XXCAM_POWEROFF_DELAY */ ++ ++ if (v4l2_int_device_register(m)) ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ omap34xxcam_exit(); ++ return -ENODEV; ++} ++ ++MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>"); ++MODULE_DESCRIPTION("OMAP34xx Video for Linux camera driver"); ++MODULE_LICENSE("GPL"); ++ ++late_initcall(omap34xxcam_init); ++module_exit(omap34xxcam_exit); +diff --git a/drivers/media/video/omap34xxcam.h b/drivers/media/video/omap34xxcam.h +new file mode 100644 +index 0000000..9859d15 +--- /dev/null ++++ b/drivers/media/video/omap34xxcam.h +@@ -0,0 +1,207 @@ ++/* ++ * omap34xxcam.h ++ * ++ * Copyright (C) 2006--2009 Nokia Corporation ++ * Copyright (C) 2007--2009 Texas Instruments ++ * ++ * Contact: Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * ++ * Originally based on the OMAP 2 camera driver. ++ * ++ * Written by Sakari Ailus <sakari.ailus@nokia.com> ++ * Tuukka Toivonen <tuukka.o.toivonen@nokia.com> ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Mohit Jalori ++ * Sameer Venkatraman ++ * Leonides Martinez ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#ifndef OMAP34XXCAM_H ++#define OMAP34XXCAM_H ++ ++#include <media/v4l2-int-device.h> ++#include "isp/isp.h" ++ ++#define CAM_NAME "omap34xxcam" ++#define CAM_SHORT_NAME "omap3" ++ ++#define OMAP_ISP_AF (1 << 4) ++#define OMAP_ISP_HIST (1 << 5) ++#define OMAP34XXCAM_XCLK_NONE -1 ++#define OMAP34XXCAM_XCLK_A 0 ++#define OMAP34XXCAM_XCLK_B 1 ++ ++#define OMAP34XXCAM_SLAVE_SENSOR 0 ++#define OMAP34XXCAM_SLAVE_LENS 1 ++#define OMAP34XXCAM_SLAVE_FLASH 2 /* This is the last slave! */ ++ ++/* mask for omap34xxcam_slave_power_set */ ++#define OMAP34XXCAM_SLAVE_POWER_SENSOR (1 << OMAP34XXCAM_SLAVE_SENSOR) ++#define OMAP34XXCAM_SLAVE_POWER_LENS (1 << OMAP34XXCAM_SLAVE_LENS) ++#define OMAP34XXCAM_SLAVE_POWER_SENSOR_LENS \ ++ (OMAP34XXCAM_SLAVE_POWER_SENSOR | OMAP34XXCAM_SLAVE_POWER_LENS) ++#define OMAP34XXCAM_SLAVE_POWER_FLASH (1 << OMAP34XXCAM_SLAVE_FLASH) ++#define OMAP34XXCAM_SLAVE_POWER_ALL -1 ++ ++#define OMAP34XXCAM_VIDEODEVS 4 ++ ++/* #define OMAP34XXCAM_POWEROFF_DELAY (2 * HZ) */ ++ ++struct omap34xxcam_device; ++struct omap34xxcam_videodev; ++ ++struct omap34xxcam_sensor_config { ++ int xclk; ++ int sensor_isp; ++ u32 capture_mem; ++ struct v4l2_fract ival_default; ++}; ++ ++struct omap34xxcam_lens_config { ++}; ++ ++struct omap34xxcam_flash_config { ++}; ++ ++/** ++ * struct omap34xxcam_hw_config - struct for vidioc_int_g_priv ioctl ++ * @xclk: OMAP34XXCAM_XCLK_A or OMAP34XXCAM_XCLK_B ++ * @sensor_isp: Is sensor smart/SOC or raw ++ * @s_pix_sparm: Access function to set pix and sparm. ++ * Pix will override sparm ++ */ ++struct omap34xxcam_hw_config { ++ int dev_index; /* Index in omap34xxcam_sensors */ ++ int dev_minor; /* Video device minor number */ ++ int dev_type; /* OMAP34XXCAM_SLAVE_* */ ++ union { ++ struct omap34xxcam_sensor_config sensor; ++ struct omap34xxcam_lens_config lens; ++ struct omap34xxcam_flash_config flash; ++ } u; ++}; ++ ++/** ++ * struct omap34xxcam_videodev - per /dev/video* structure ++ * @mutex: serialises access to this structure ++ * @cam: pointer to cam hw structure ++ * @master: we are v4l2_int_device master ++ * @sensor: sensor device ++ * @lens: lens device ++ * @flash: flash device ++ * @slaves: how many slaves we have at the moment ++ * @vfd: our video device ++ * @capture_mem: maximum kernel-allocated capture memory ++ * @if_u: sensor interface stuff ++ * @index: index of this structure in cam->vdevs ++ * @users: how many users we have ++ * @power_state: Current power state ++ * @power_state_wish: New power state when poweroff_timer expires ++ * @power_state_mask: Bitmask of devices to set the new power state ++ * @poweroff_timer: Timer for dispatching poweroff_work ++ * @poweroff_work: Work for slave power state change ++ * @sensor_config: ISP-speicific sensor configuration ++ * @lens_config: ISP-speicific lens configuration ++ * @flash_config: ISP-speicific flash configuration ++ * @want_timeperframe: Desired timeperframe ++ * @want_pix: Desired pix ++ * @pix: Current pix ++ * @streaming: streaming file handle, if streaming is enabled ++ */ ++struct omap34xxcam_videodev { ++ struct mutex mutex; /* serialises access to this structure */ ++ ++ struct omap34xxcam_device *cam; ++ struct v4l2_int_device master; ++ ++#define vdev_sensor slave[OMAP34XXCAM_SLAVE_SENSOR] ++#define vdev_lens slave[OMAP34XXCAM_SLAVE_LENS] ++#define vdev_flash slave[OMAP34XXCAM_SLAVE_FLASH] ++ struct v4l2_int_device *slave[OMAP34XXCAM_SLAVE_FLASH + 1]; ++ ++ /* number of slaves attached */ ++ int slaves; ++ ++ /*** video device parameters ***/ ++ struct video_device *vfd; ++ int capture_mem; ++ ++ /*** general driver state information ***/ ++ int index; ++ atomic_t users; ++ enum v4l2_power power_state[OMAP34XXCAM_SLAVE_FLASH + 1]; ++#ifdef OMAP34XXCAM_POWEROFF_DELAY ++ enum v4l2_power power_state_wish; ++ int power_state_mask; ++ struct timer_list poweroff_timer; ++ struct work_struct poweroff_work; ++#endif /* OMAP34XXCAM_POWEROFF_DELAY */ ++ ++#define vdev_sensor_config slave_config[OMAP34XXCAM_SLAVE_SENSOR].u.sensor ++#define vdev_lens_config slave_config[OMAP34XXCAM_SLAVE_LENS].u.lens ++#define vdev_flash_config slave_config[OMAP34XXCAM_SLAVE_FLASH].u.flash ++ struct omap34xxcam_hw_config slave_config[OMAP34XXCAM_SLAVE_FLASH + 1]; ++ ++ /*** capture data ***/ ++ struct file *streaming; ++ struct v4l2_fract want_timeperframe; ++ struct v4l2_pix_format want_pix; ++ spinlock_t pix_lock; ++ struct v4l2_pix_format pix; ++}; ++ ++/** ++ * struct omap34xxcam_device - per-device data structure ++ * @mutex: mutex serialises access to this structure ++ * @sgdma_in_queue: Number or sgdma requests in scatter-gather queue, ++ * protected by the lock above. ++ * @sgdma: ISP sgdma subsystem information structure ++ * @dma_notify: DMA notify flag ++ * @dev: device structure ++ * @vdevs: /dev/video specific structures ++ * @fck: camera module fck clock information ++ * @ick: camera module ick clock information ++ */ ++struct omap34xxcam_device { ++ struct mutex mutex; /* serialises access to this structure */ ++ ++ /*** interfaces and device ***/ ++ struct omap34xxcam_videodev vdevs[OMAP34XXCAM_VIDEODEVS]; ++ ++ /*** camera module clocks ***/ ++ struct clk *fck; ++ struct clk *ick; ++ bool sensor_if_enabled; ++}; ++ ++/** ++ * struct omap34xxcam_fh - per-filehandle data structure ++ * @vbq_lock: spinlock for the videobuf queue ++ * @vbq: V4L2 video buffer queue structure ++ * @field_count: field counter for videobuf_buffer ++ * @vdev: our /dev/video specific structure ++ */ ++struct omap34xxcam_fh { ++ spinlock_t vbq_lock; /* spinlock for the videobuf queue */ ++ struct videobuf_queue vbq; ++ atomic_t field_count; ++ struct omap34xxcam_videodev *vdev; ++}; ++ ++#endif /* ifndef OMAP34XXCAM_H */ +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0023-OMAP-Resizer-Basic-Resizer-refreshed-with-latest-gi.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0023-OMAP-Resizer-Basic-Resizer-refreshed-with-latest-gi.patch new file mode 100644 index 0000000000..3390599292 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0023-OMAP-Resizer-Basic-Resizer-refreshed-with-latest-gi.patch @@ -0,0 +1,1890 @@ +From 38f3cd5564a466e5251fc2ff47e0504148922304 Mon Sep 17 00:00:00 2001 +From: Vaibhav Hiremath <vaibhav@psp-nfs-02.india.ti.com> +Date: Wed, 29 Apr 2009 17:18:56 +0530 +Subject: [PATCH 23/26] OMAP-Resizer: Basic Resizer refreshed with latest gitorious tree + +This is same resizer driver patch posted by Sergio onto +mailing list quite a some time back. This commit refreshes +the same patch on top of latest gitorious.org tree. + +List of New/Modified files: + modified: drivers/media/video/Kconfig + modified: drivers/media/video/isp/Makefile + modified: drivers/media/video/isp/isp.c + modified: drivers/media/video/isp/ispreg.h + new file: drivers/media/video/isp/omap_resizer.c + new file: include/linux/omap_resizer.h + +TODO: + - Resizer driver needs to be independent from camera and ISP + - Custom patches implemented ontop of PSP1.0.2 release + to fix some V4L2-buf layer issues. + - +--- + drivers/media/video/Kconfig | 7 + + drivers/media/video/isp/Makefile | 4 + + drivers/media/video/isp/isp.c | 15 + + drivers/media/video/isp/omap_resizer.c | 1634 ++++++++++++++++++++++++++++++++ + include/linux/omap_resizer.h | 136 +++ + 5 files changed, 1796 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/omap_resizer.c + create mode 100644 include/linux/omap_resizer.h + +diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig +index 3cdb5a4..d2b4ae1 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -720,6 +720,13 @@ config VIDEO_OMAP3 + ---help--- + Driver for an OMAP 3 camera controller. + ++config VIDEO_OMAP34XX_ISP_RESIZER ++ tristate "OMAP ISP Resizer" ++ depends on VIDEO_V4L2 && ARCH_OMAP34XX ++ select VIDEOBUF_GEN ++ select VIDEOBUF_DMA_SG ++ select OMAP_IOMMU ++ + config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 && HAS_DMA +diff --git a/drivers/media/video/isp/Makefile b/drivers/media/video/isp/Makefile +index f14d617..d171fb9 100644 +--- a/drivers/media/video/isp/Makefile ++++ b/drivers/media/video/isp/Makefile +@@ -7,6 +7,10 @@ else + isp-mod-objs += \ + isp.o ispccdc.o ispmmu.o \ + isppreview.o ispresizer.o isph3a.o isphist.o isp_af.o ispcsi2.o ++ ++obj-$(CONFIG_VIDEO_OMAP34XX_ISP_RESIZER) += \ ++ omap_resizer.o ++ + endif + + obj-$(CONFIG_VIDEO_OMAP3) += isp-mod.o +diff --git a/drivers/media/video/isp/isp.c b/drivers/media/video/isp/isp.c +index 54c839b..f1f92b4 100644 +--- a/drivers/media/video/isp/isp.c ++++ b/drivers/media/video/isp/isp.c +@@ -505,6 +505,12 @@ int isp_set_callback(enum isp_callback_type type, isp_callback_t callback, + isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + IRQ0ENABLE_PRV_DONE_IRQ); + break; ++ case CBK_RESZ_DONE: ++ isp_reg_writel(IRQ0ENABLE_RSZ_DONE_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ IRQ0ENABLE_RSZ_DONE_IRQ); ++ break; + default: + break; + } +@@ -556,6 +562,10 @@ int isp_unset_callback(enum isp_callback_type type) + isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, + ~IRQ0ENABLE_PRV_DONE_IRQ); + break; ++ case CBK_RESZ_DONE: ++ isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, ++ ~IRQ0ENABLE_RSZ_DONE_IRQ); ++ break; + default: + break; + } +@@ -938,6 +948,11 @@ static irqreturn_t omap34xx_isp_isr(int irq, void *_isp) + if (!ispresizer_busy()) + ispresizer_config_shadow_registers(); + isp_buf_process(bufs); ++ } else { ++ if (irqdis->isp_callbk[CBK_RESZ_DONE]) ++ irqdis->isp_callbk[CBK_RESZ_DONE](RESZ_DONE, ++ irqdis->isp_callbk_arg1[CBK_RESZ_DONE], ++ irqdis->isp_callbk_arg2[CBK_RESZ_DONE]); + } + } + +diff --git a/drivers/media/video/isp/omap_resizer.c b/drivers/media/video/isp/omap_resizer.c +new file mode 100644 +index 0000000..54bc425 +--- /dev/null ++++ b/drivers/media/video/isp/omap_resizer.c +@@ -0,0 +1,1634 @@ ++/* ++ * drivers/media/video/isp/omap_resizer.c ++ * ++ * Wrapper for Resizer module in TI's OMAP3430 ISP ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * Contributors: ++ * Sergio Aguirre <saaguirre@ti.com> ++ * Troy Laramy <t-laramy@ti.com> ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#include <linux/mutex.h> ++#include <linux/cdev.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/fs.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/uaccess.h> ++#include <media/v4l2-dev.h> ++#include <asm/cacheflush.h> ++ ++#include "isp.h" ++#include "ispmmu.h" ++#include "ispreg.h" ++#include "ispresizer.h" ++#include <linux/omap_resizer.h> ++ ++#define OMAP_REZR_NAME "omap-resizer" ++ ++/* Defines and Constants*/ ++#define MAX_CHANNELS 16 ++#define MAX_IMAGE_WIDTH 2047 ++#define MAX_IMAGE_WIDTH_HIGH 2047 ++#define ALIGNMENT 16 ++#define CHANNEL_BUSY 1 ++#define CHANNEL_FREE 0 ++#define PIXEL_EVEN 2 ++#define RATIO_MULTIPLIER 256 ++/* Bit position Macro */ ++/* macro for bit set and clear */ ++#define BITSET(variable, bit) ((variable) | (1 << bit)) ++#define BITRESET(variable, bit) ((variable) & ~(0x00000001 << (bit))) ++#define SET_BIT_INPUTRAM 28 ++#define SET_BIT_CBLIN 29 ++#define SET_BIT_INPTYP 27 ++#define SET_BIT_YCPOS 26 ++#define INPUT_RAM 1 ++#define UP_RSZ_RATIO 64 ++#define DOWN_RSZ_RATIO 512 ++#define UP_RSZ_RATIO1 513 ++#define DOWN_RSZ_RATIO1 1024 ++#define RSZ_IN_SIZE_VERT_SHIFT 16 ++#define MAX_HORZ_PIXEL_8BIT 31 ++#define MAX_HORZ_PIXEL_16BIT 15 ++#define NUM_PHASES 8 ++#define NUM_TAPS 4 ++#define NUM_D2PH 4 /* for downsampling * 2+x ~ 4x, ++ * number of phases ++ */ ++#define NUM_D2TAPS 7 /* for downsampling * 2+x ~ 4x, ++ * number of taps ++ */ ++#define ALIGN32 32 ++#define MAX_COEF_COUNTER 16 ++#define COEFF_ADDRESS_OFFSET 0x04 ++ ++/* Global structure which contains information about number of channels ++ and protection variables */ ++struct device_params { ++ ++ unsigned char opened; /* state of the device */ ++ struct completion compl_isr; /* Completion for interrupt */ ++ struct mutex reszwrap_mutex; /* Semaphore for array */ ++ ++ struct videobuf_queue_ops vbq_ops; /* videobuf queue operations */ ++}; ++ ++/* Register mapped structure which contains the every register ++ information */ ++struct resizer_config { ++ u32 rsz_pcr; /* pcr register mapping ++ * variable. ++ */ ++ u32 rsz_in_start; /* in_start register mapping ++ * variable. ++ */ ++ u32 rsz_in_size; /* in_size register mapping ++ * variable. ++ */ ++ u32 rsz_out_size; /* out_size register mapping ++ * variable. ++ */ ++ u32 rsz_cnt; /* rsz_cnt register mapping ++ * variable. ++ */ ++ u32 rsz_sdr_inadd; /* sdr_inadd register mapping ++ * variable. ++ */ ++ u32 rsz_sdr_inoff; /* sdr_inoff register mapping ++ * variable. ++ */ ++ u32 rsz_sdr_outadd; /* sdr_outadd register mapping ++ * variable. ++ */ ++ u32 rsz_sdr_outoff; /* sdr_outbuff register ++ * mapping variable. ++ */ ++ u32 rsz_coeff_horz[16]; /* horizontal coefficients ++ * mapping array. ++ */ ++ u32 rsz_coeff_vert[16]; /* vertical coefficients ++ * mapping array. ++ */ ++ u32 rsz_yehn; /* yehn(luma)register mapping ++ * variable. ++ */ ++}; ++ ++struct rsz_mult { ++ int in_hsize; /* input frame horizontal ++ * size. ++ */ ++ int in_vsize; /* input frame vertical size. ++ */ ++ int out_hsize; /* output frame horizontal ++ * size. ++ */ ++ int out_vsize; /* output frame vertical ++ * size. ++ */ ++ int in_pitch; /* offset between two rows of ++ * input frame. ++ */ ++ int out_pitch; /* offset between two rows of ++ * output frame. ++ */ ++ int end_hsize; ++ int end_vsize; ++ int num_htap; /* 0 = 7tap; 1 = 4tap */ ++ int num_vtap; /* 0 = 7tap; 1 = 4tap */ ++ int active; ++ int inptyp; ++ int vrsz; ++ int hrsz; ++ int hstph; /* for specifying horizontal ++ * starting phase. ++ */ ++ int vstph; ++ int pix_fmt; /* # defined, UYVY or YUYV. */ ++ int cbilin; /* # defined, filter with luma ++ * or bi-linear. ++ */ ++ u16 tap4filt_coeffs[32]; /* horizontal filter ++ * coefficients. ++ */ ++ u16 tap7filt_coeffs[32]; /* vertical filter ++ * coefficients. ++ */ ++}; ++/* Channel specific structure contains information regarding ++ the every channel */ ++struct channel_config { ++ struct resizer_config register_config; /* Instance of register set ++ * mapping structure ++ */ ++ int status; /* Specifies whether the ++ * channel is busy or not ++ */ ++ struct mutex chanprotection_mutex; ++ enum config_done config_state; ++ u8 input_buf_index; ++ u8 output_buf_index; ++ ++}; ++ ++/* per-filehandle data structure */ ++struct rsz_fh { ++ struct rsz_params *params; ++ struct channel_config *config; ++ struct rsz_mult *multipass; /* Multipass to support ++ * resizing ration outside ++ * of 0.25x to 4x ++ */ ++ spinlock_t vbq_lock; /* spinlock for videobuf ++ * queues. ++ */ ++ enum v4l2_buf_type type; ++ struct videobuf_queue vbq; ++ struct device_params *device; ++ ++ dma_addr_t isp_addr_read; /* Input/Output address */ ++ dma_addr_t isp_addr_write; /* Input/Output address */ ++ u32 rsz_bufsize; /* channel specific buffersize ++ */ ++}; ++ ++static struct device_params *device_config; ++static struct device *rsz_device; ++static int rsz_major = -1; ++/* functions declaration */ ++static void rsz_hardware_setup(struct channel_config *rsz_conf_chan); ++static int rsz_set_params(struct rsz_mult *multipass, struct rsz_params *, ++ struct channel_config *); ++static int rsz_get_params(struct rsz_params *, struct channel_config *); ++static void rsz_copy_data(struct rsz_mult *multipass, ++ struct rsz_params *params); ++static void rsz_isr(unsigned long status, isp_vbq_callback_ptr arg1, ++ void *arg2); ++static void rsz_calculate_crop(struct channel_config *rsz_conf_chan, ++ struct rsz_cropsize *cropsize); ++static int rsz_set_multipass(struct rsz_mult *multipass, ++ struct channel_config *rsz_conf_chan); ++static int rsz_set_ratio(struct rsz_mult *multipass, ++ struct channel_config *rsz_conf_chan); ++static void rsz_config_ratio(struct rsz_mult *multipass, ++ struct channel_config *rsz_conf_chan); ++ ++/** ++ * rsz_hardware_setup - Sets hardware configuration registers ++ * @rsz_conf_chan: Structure containing channel configuration ++ * ++ * Set hardware configuration registers ++ **/ ++static void rsz_hardware_setup(struct channel_config *rsz_conf_chan) ++{ ++ int coeffcounter; ++ int coeffoffset = 0; ++ ++ omap_writel(rsz_conf_chan->register_config.rsz_cnt, ++ OMAP3ISP_RESZ_REG(ISPRSZ_CNT)); ++ ++ omap_writel(rsz_conf_chan->register_config.rsz_in_start, ++ OMAP3ISP_RESZ_REG(ISPRSZ_IN_START)); ++ omap_writel(rsz_conf_chan->register_config.rsz_in_size, ++ OMAP3ISP_RESZ_REG(ISPRSZ_IN_SIZE)); ++ ++ omap_writel(rsz_conf_chan->register_config.rsz_out_size, ++ OMAP3ISP_RESZ_REG(ISPRSZ_OUT_SIZE)); ++ omap_writel(rsz_conf_chan->register_config.rsz_sdr_inadd, ++ OMAP3ISP_RESZ_REG(ISPRSZ_SDR_INADD)); ++ omap_writel(rsz_conf_chan->register_config.rsz_sdr_inoff, ++ OMAP3ISP_RESZ_REG(ISPRSZ_SDR_INOFF)); ++ omap_writel(rsz_conf_chan->register_config.rsz_sdr_outadd, ++ OMAP3ISP_RESZ_REG(ISPRSZ_SDR_OUTADD)); ++ omap_writel(rsz_conf_chan->register_config.rsz_sdr_outoff, ++ OMAP3ISP_RESZ_REG(ISPRSZ_SDR_OUTOFF)); ++ omap_writel(rsz_conf_chan->register_config.rsz_yehn, OMAP3ISP_RESZ_REG(ISPRSZ_YENH)); ++ ++ for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; ++ coeffcounter++) { ++ omap_writel(rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter], ++ OMAP3ISP_RESZ_REG(ISPRSZ_HFILT10 ++ + coeffoffset)); ++ ++ omap_writel(rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter], ++ OMAP3ISP_RESZ_REG(ISPRSZ_VFILT10 ++ + coeffoffset)); ++ coeffoffset = coeffoffset + COEFF_ADDRESS_OFFSET; ++ } ++} ++ ++/** ++ * rsz_start - Enables Resizer Wrapper ++ * @arg: Currently not used. ++ * @device: Structure containing ISP resizer wrapper global information ++ * ++ * Submits a resizing task specified by the rsz_resize structure. The call can ++ * either be blocked until the task is completed or returned immediately based ++ * on the value of the blocking argument in the rsz_resize structure. If it is ++ * blocking, the status of the task can be checked by calling ioctl ++ * RSZ_G_STATUS. Only one task can be outstanding for each logical channel. ++ * ++ * Returns 0 if successful, or -EINVAL if could not set callback for RSZR IRQ ++ * event or the state of the channel is not configured. ++ **/ ++int rsz_start(int *arg, struct rsz_fh *fh) ++{ ++ struct channel_config *rsz_conf_chan = fh->config; ++ struct rsz_mult *multipass = fh->multipass; ++ struct videobuf_queue *q = &fh->vbq; ++ int ret; ++ ++ if (rsz_conf_chan->config_state) { ++ dev_err(rsz_device, "State not configured \n"); ++ goto err_einval; ++ } ++ ++ rsz_conf_chan->status = CHANNEL_BUSY; ++ ++ rsz_hardware_setup(rsz_conf_chan); ++ ++ if (isp_set_callback(CBK_RESZ_DONE, rsz_isr, (void *) NULL, ++ (void *)NULL)) { ++ dev_err(rsz_device, "No callback for RSZR\n"); ++ goto err_einval; ++ } ++mult: ++ device_config->compl_isr.done = 0; ++ ++ ispresizer_enable(1); ++ ++ ret = wait_for_completion_interruptible(&device_config->compl_isr); ++ if (ret != 0) { ++ dev_dbg(rsz_device, "Unexpected exit from " ++ "wait_for_completion_interruptible\n"); ++ wait_for_completion(&device_config->compl_isr); ++ } ++ ++ if (multipass->active) { ++ rsz_set_multipass(multipass, rsz_conf_chan); ++ goto mult; ++ } ++ ++ if (fh->isp_addr_read) { ++ ispmmu_unmap(fh->isp_addr_read); ++ fh->isp_addr_read = 0; ++ } ++ if (fh->isp_addr_write) { ++ ispmmu_unmap(fh->isp_addr_write); ++ fh->isp_addr_write = 0; ++ } ++ ++ rsz_conf_chan->status = CHANNEL_FREE; ++ q->bufs[rsz_conf_chan->input_buf_index]->state = VIDEOBUF_NEEDS_INIT; ++ q->bufs[rsz_conf_chan->output_buf_index]->state = VIDEOBUF_NEEDS_INIT; ++ rsz_conf_chan->register_config.rsz_sdr_outadd = 0; ++ rsz_conf_chan->register_config.rsz_sdr_inadd = 0; ++ ++ /* Unmap and free the DMA memory allocated for buffers */ ++ videobuf_dma_unmap(q, videobuf_to_dma( ++ q->bufs[rsz_conf_chan->input_buf_index])); ++ videobuf_dma_unmap(q, videobuf_to_dma( ++ q->bufs[rsz_conf_chan->output_buf_index])); ++ videobuf_dma_free(videobuf_to_dma( ++ q->bufs[rsz_conf_chan->input_buf_index])); ++ videobuf_dma_free(videobuf_to_dma( ++ q->bufs[rsz_conf_chan->output_buf_index])); ++ ++ isp_unset_callback(CBK_RESZ_DONE); ++ ++ return 0; ++err_einval: ++ return -EINVAL; ++} ++ ++/** ++ * rsz_set_multipass - Set resizer multipass ++ * @rsz_conf_chan: Structure containing channel configuration ++ * ++ * Returns always 0 ++ **/ ++static int rsz_set_multipass(struct rsz_mult *multipass, ++ struct channel_config *rsz_conf_chan) ++{ ++ multipass->in_hsize = multipass->out_hsize; ++ multipass->in_vsize = multipass->out_vsize; ++ multipass->out_hsize = multipass->end_hsize; ++ multipass->out_vsize = multipass->end_vsize; ++ ++ multipass->out_pitch = (multipass->inptyp ? multipass->out_hsize ++ : (multipass->out_hsize * 2)); ++ multipass->in_pitch = (multipass->inptyp ? multipass->in_hsize ++ : (multipass->in_hsize * 2)); ++ ++ rsz_set_ratio(multipass, rsz_conf_chan); ++ rsz_config_ratio(multipass, rsz_conf_chan); ++ rsz_hardware_setup(rsz_conf_chan); ++ return 0; ++} ++ ++/** ++ * rsz_copy_data - Copy data ++ * @params: Structure containing the Resizer Wrapper parameters ++ * ++ * Copy data ++ **/ ++static void rsz_copy_data(struct rsz_mult *multipass, struct rsz_params *params) ++{ ++ int i; ++ multipass->in_hsize = params->in_hsize; ++ multipass->in_vsize = params->in_vsize; ++ multipass->out_hsize = params->out_hsize; ++ multipass->out_vsize = params->out_vsize; ++ multipass->end_hsize = params->out_hsize; ++ multipass->end_vsize = params->out_vsize; ++ multipass->in_pitch = params->in_pitch; ++ multipass->out_pitch = params->out_pitch; ++ multipass->hstph = params->hstph; ++ multipass->vstph = params->vstph; ++ multipass->inptyp = params->inptyp; ++ multipass->pix_fmt = params->pix_fmt; ++ multipass->cbilin = params->cbilin; ++ ++ for (i = 0; i < 32; i++) { ++ multipass->tap4filt_coeffs[i] = params->tap4filt_coeffs[i]; ++ multipass->tap7filt_coeffs[i] = params->tap7filt_coeffs[i]; ++ } ++} ++ ++/** ++ * rsz_set_params - Set parameters for resizer wrapper ++ * @params: Structure containing the Resizer Wrapper parameters ++ * @rsz_conf_chan: Structure containing channel configuration ++ * ++ * Used to set the parameters of the Resizer hardware, including input and ++ * output image size, horizontal and vertical poly-phase filter coefficients, ++ * luma enchancement filter coefficients, etc. ++ **/ ++static int rsz_set_params(struct rsz_mult *multipass, struct rsz_params *params, ++ struct channel_config *rsz_conf_chan) ++{ ++ int mul = 1; ++ if ((params->yenh_params.type < 0) || (params->yenh_params.type > 2)) { ++ dev_err(rsz_device, "rsz_set_params: Wrong yenh type\n"); ++ return -EINVAL; ++ } ++ if ((params->in_vsize <= 0) || (params->in_hsize <= 0) || ++ (params->out_vsize <= 0) || (params->out_hsize <= 0) || ++ (params->in_pitch <= 0) || (params->out_pitch <= 0)) { ++ dev_err(rsz_device, "rsz_set_params: Invalid size params\n"); ++ return -EINVAL; ++ } ++ if ((params->inptyp != RSZ_INTYPE_YCBCR422_16BIT) && ++ (params->inptyp != RSZ_INTYPE_PLANAR_8BIT)) { ++ dev_err(rsz_device, "rsz_set_params: Invalid input type\n"); ++ return -EINVAL; ++ } ++ if ((params->pix_fmt != RSZ_PIX_FMT_UYVY) && ++ (params->pix_fmt != RSZ_PIX_FMT_YUYV)) { ++ dev_err(rsz_device, "rsz_set_params: Invalid pixel format\n"); ++ return -EINVAL; ++ } ++ if (params->inptyp == RSZ_INTYPE_YCBCR422_16BIT) ++ mul = 2; ++ else ++ mul = 1; ++ if (params->in_pitch < (params->in_hsize * mul)) { ++ dev_err(rsz_device, "rsz_set_params: Pitch is incorrect\n"); ++ return -EINVAL; ++ } ++ if (params->out_pitch < (params->out_hsize * mul)) { ++ dev_err(rsz_device, "rsz_set_params: Out pitch cannot be less" ++ " than out hsize\n"); ++ return -EINVAL; ++ } ++ /* Output H size should be even */ ++ if ((params->out_hsize % PIXEL_EVEN) != 0) { ++ dev_err(rsz_device, "rsz_set_params: Output H size should" ++ " be even\n"); ++ return -EINVAL; ++ } ++ if (params->horz_starting_pixel < 0) { ++ dev_err(rsz_device, "rsz_set_params: Horz start pixel cannot" ++ " be less than zero\n"); ++ return -EINVAL; ++ } ++ ++ rsz_copy_data(multipass, params); ++ if (0 != rsz_set_ratio(multipass, rsz_conf_chan)) ++ goto err_einval; ++ ++ if (params->yenh_params.type) { ++ if ((multipass->num_htap && multipass->out_hsize > ++ 1280) || ++ (!multipass->num_htap && multipass->out_hsize > ++ 640)) ++ goto err_einval; ++ } ++ ++ if (INPUT_RAM) ++ params->vert_starting_pixel = 0; ++ ++ rsz_conf_chan->register_config.rsz_in_start = ++ (params->vert_starting_pixel ++ << ISPRSZ_IN_SIZE_VERT_SHIFT) ++ & ISPRSZ_IN_SIZE_VERT_MASK; ++ ++ if (params->inptyp == RSZ_INTYPE_PLANAR_8BIT) { ++ if (params->horz_starting_pixel > MAX_HORZ_PIXEL_8BIT) ++ goto err_einval; ++ } ++ if (params->inptyp == RSZ_INTYPE_YCBCR422_16BIT) { ++ if (params->horz_starting_pixel > MAX_HORZ_PIXEL_16BIT) ++ goto err_einval; ++ } ++ ++ rsz_conf_chan->register_config.rsz_in_start |= ++ params->horz_starting_pixel ++ & ISPRSZ_IN_START_HORZ_ST_MASK; ++ ++ rsz_conf_chan->register_config.rsz_yehn = ++ (params->yenh_params.type ++ << ISPRSZ_YENH_ALGO_SHIFT) ++ & ISPRSZ_YENH_ALGO_MASK; ++ ++ if (params->yenh_params.type) { ++ rsz_conf_chan->register_config.rsz_yehn |= ++ params->yenh_params.core ++ & ISPRSZ_YENH_CORE_MASK; ++ ++ rsz_conf_chan->register_config.rsz_yehn |= ++ (params->yenh_params.gain ++ << ISPRSZ_YENH_GAIN_SHIFT) ++ & ISPRSZ_YENH_GAIN_MASK; ++ ++ rsz_conf_chan->register_config.rsz_yehn |= ++ (params->yenh_params.slop ++ << ISPRSZ_YENH_SLOP_SHIFT) ++ & ISPRSZ_YENH_SLOP_MASK; ++ } ++ ++ rsz_config_ratio(multipass, rsz_conf_chan); ++ ++ rsz_conf_chan->config_state = STATE_CONFIGURED; ++ ++ return 0; ++err_einval: ++ return -EINVAL; ++} ++ ++/** ++ * rsz_set_ratio - Set ratio ++ * @rsz_conf_chan: Structure containing channel configuration ++ * ++ * Returns 0 if successful, -EINVAL if invalid output size, upscaling ratio is ++ * being requested, or other ratio configuration value is out of bounds ++ **/ ++static int rsz_set_ratio(struct rsz_mult *multipass, ++ struct channel_config *rsz_conf_chan) ++{ ++ int alignment = 0; ++ ++ rsz_conf_chan->register_config.rsz_cnt = 0; ++ ++ if ((multipass->out_hsize > MAX_IMAGE_WIDTH) || ++ (multipass->out_vsize > MAX_IMAGE_WIDTH)) { ++ dev_err(rsz_device, "Invalid output size!"); ++ goto err_einval; ++ } ++ if (multipass->cbilin) { ++ rsz_conf_chan->register_config.rsz_cnt = ++ BITSET(rsz_conf_chan->register_config.rsz_cnt, ++ SET_BIT_CBLIN); ++ } ++ if (INPUT_RAM) { ++ rsz_conf_chan->register_config.rsz_cnt = ++ BITSET(rsz_conf_chan->register_config.rsz_cnt, ++ SET_BIT_INPUTRAM); ++ } ++ if (multipass->inptyp == RSZ_INTYPE_PLANAR_8BIT) { ++ rsz_conf_chan->register_config.rsz_cnt = ++ BITSET(rsz_conf_chan->register_config.rsz_cnt, ++ SET_BIT_INPTYP); ++ } else { ++ rsz_conf_chan->register_config.rsz_cnt = ++ BITRESET(rsz_conf_chan->register_config. ++ rsz_cnt, SET_BIT_INPTYP); ++ ++ if (multipass->pix_fmt == RSZ_PIX_FMT_UYVY) { ++ rsz_conf_chan->register_config.rsz_cnt = ++ BITRESET(rsz_conf_chan->register_config. ++ rsz_cnt, SET_BIT_YCPOS); ++ } else if (multipass->pix_fmt == RSZ_PIX_FMT_YUYV) { ++ rsz_conf_chan->register_config.rsz_cnt = ++ BITSET(rsz_conf_chan->register_config. ++ rsz_cnt, SET_BIT_YCPOS); ++ } ++ ++ } ++ multipass->vrsz = ++ (multipass->in_vsize * RATIO_MULTIPLIER) / multipass->out_vsize; ++ multipass->hrsz = ++ (multipass->in_hsize * RATIO_MULTIPLIER) / multipass->out_hsize; ++ if (UP_RSZ_RATIO > multipass->vrsz || UP_RSZ_RATIO > multipass->hrsz) { ++ dev_err(rsz_device, "Upscaling ratio not supported!"); ++ goto err_einval; ++ } ++ multipass->vrsz = (multipass->in_vsize - NUM_D2TAPS) * RATIO_MULTIPLIER ++ / (multipass->out_vsize - 1); ++ multipass->hrsz = ((multipass->in_hsize - NUM_D2TAPS) ++ * RATIO_MULTIPLIER) / ++ (multipass->out_hsize - 1); ++ ++ if (multipass->hrsz <= 512) { ++ multipass->hrsz = (multipass->in_hsize - NUM_TAPS) ++ * RATIO_MULTIPLIER ++ / (multipass->out_hsize - 1); ++ if (multipass->hrsz < 64) ++ multipass->hrsz = 64; ++ if (multipass->hrsz > 512) ++ multipass->hrsz = 512; ++ if (multipass->hstph > NUM_PHASES) ++ goto err_einval; ++ multipass->num_htap = 1; ++ } else if (multipass->hrsz >= 513 && multipass->hrsz <= 1024) { ++ if (multipass->hstph > NUM_D2PH) ++ goto err_einval; ++ multipass->num_htap = 0; ++ } ++ ++ if (multipass->vrsz <= 512) { ++ multipass->vrsz = (multipass->in_vsize - NUM_TAPS) ++ * RATIO_MULTIPLIER ++ / (multipass->out_vsize - 1); ++ if (multipass->vrsz < 64) ++ multipass->vrsz = 64; ++ if (multipass->vrsz > 512) ++ multipass->vrsz = 512; ++ if (multipass->vstph > NUM_PHASES) ++ goto err_einval; ++ multipass->num_vtap = 1; ++ } else if (multipass->vrsz >= 513 && multipass->vrsz <= 1024) { ++ if (multipass->vstph > NUM_D2PH) ++ goto err_einval; ++ multipass->num_vtap = 0; ++ } ++ ++ if ((multipass->in_pitch) % ALIGN32) { ++ dev_err(rsz_device, "Invalid input pitch: %d \n", ++ multipass->in_pitch); ++ goto err_einval; ++ } ++ if ((multipass->out_pitch) % ALIGN32) { ++ dev_err(rsz_device, "Invalid output pitch %d \n", ++ multipass->out_pitch); ++ goto err_einval; ++ } ++ ++ if (multipass->vrsz < 256 && ++ (multipass->in_vsize < multipass->out_vsize)) { ++ if (multipass->inptyp == RSZ_INTYPE_PLANAR_8BIT) ++ alignment = ALIGNMENT; ++ else if (multipass->inptyp == RSZ_INTYPE_YCBCR422_16BIT) ++ alignment = (ALIGNMENT / 2); ++ else ++ dev_err(rsz_device, "Invalid input type\n"); ++ ++ if (!(((multipass->out_hsize % PIXEL_EVEN) == 0) ++ && (multipass->out_hsize % alignment) == 0)) { ++ dev_err(rsz_device, "wrong hsize\n"); ++ goto err_einval; ++ } ++ } ++ if (multipass->hrsz >= 64 && multipass->hrsz <= 1024) { ++ if (multipass->out_hsize > MAX_IMAGE_WIDTH) { ++ dev_err(rsz_device, "wrong width\n"); ++ goto err_einval; ++ } ++ multipass->active = 0; ++ ++ } else if (multipass->hrsz > 1024) { ++ if (multipass->out_hsize > MAX_IMAGE_WIDTH) { ++ dev_err(rsz_device, "wrong width\n"); ++ goto err_einval; ++ } ++ if (multipass->hstph > NUM_D2PH) ++ goto err_einval; ++ multipass->num_htap = 0; ++ multipass->out_hsize = multipass->in_hsize * 256 / 1024; ++ if (multipass->out_hsize % ALIGN32) { ++ multipass->out_hsize += ++ abs((multipass->out_hsize % ALIGN32) - ALIGN32); ++ } ++ multipass->out_pitch = ((multipass->inptyp) ? ++ multipass->out_hsize : ++ (multipass->out_hsize * 2)); ++ multipass->hrsz = ((multipass->in_hsize - NUM_D2TAPS) ++ * RATIO_MULTIPLIER) ++ / (multipass->out_hsize - 1); ++ multipass->active = 1; ++ ++ } ++ ++ if (multipass->vrsz > 1024) { ++ if (multipass->out_vsize > MAX_IMAGE_WIDTH_HIGH) { ++ dev_err(rsz_device, "wrong width\n"); ++ goto err_einval; ++ } ++ ++ multipass->out_vsize = multipass->in_vsize * 256 / 1024; ++ multipass->vrsz = ((multipass->in_vsize - NUM_D2TAPS) ++ * RATIO_MULTIPLIER) ++ / (multipass->out_vsize - 1); ++ multipass->active = 1; ++ multipass->num_vtap = 0; ++ ++ } ++ rsz_conf_chan->register_config.rsz_out_size = ++ multipass->out_hsize ++ & ISPRSZ_OUT_SIZE_HORZ_MASK; ++ ++ rsz_conf_chan->register_config.rsz_out_size |= ++ (multipass->out_vsize ++ << ISPRSZ_OUT_SIZE_VERT_SHIFT) ++ & ISPRSZ_OUT_SIZE_VERT_MASK; ++ ++ rsz_conf_chan->register_config.rsz_sdr_inoff = ++ multipass->in_pitch ++ & ISPRSZ_SDR_INOFF_OFFSET_MASK; ++ ++ rsz_conf_chan->register_config.rsz_sdr_outoff = ++ multipass->out_pitch ++ & ISPRSZ_SDR_OUTOFF_OFFSET_MASK; ++ ++ if (multipass->hrsz >= 64 && multipass->hrsz <= 512) { ++ if (multipass->hstph > NUM_PHASES) ++ goto err_einval; ++ } else if (multipass->hrsz >= 64 && multipass->hrsz <= 512) { ++ if (multipass->hstph > NUM_D2PH) ++ goto err_einval; ++ } ++ ++ rsz_conf_chan->register_config.rsz_cnt |= ++ (multipass->hstph ++ << ISPRSZ_CNT_HSTPH_SHIFT) ++ & ISPRSZ_CNT_HSTPH_MASK; ++ ++ if (multipass->vrsz >= 64 && multipass->hrsz <= 512) { ++ if (multipass->vstph > NUM_PHASES) ++ goto err_einval; ++ } else if (multipass->vrsz >= 64 && multipass->vrsz <= 512) { ++ if (multipass->vstph > NUM_D2PH) ++ goto err_einval; ++ } ++ ++ rsz_conf_chan->register_config.rsz_cnt |= ++ (multipass->vstph ++ << ISPRSZ_CNT_VSTPH_SHIFT) ++ & ISPRSZ_CNT_VSTPH_MASK; ++ ++ rsz_conf_chan->register_config.rsz_cnt |= ++ (multipass->hrsz - 1) ++ & ISPRSZ_CNT_HRSZ_MASK; ++ ++ rsz_conf_chan->register_config.rsz_cnt |= ++ ((multipass->vrsz - 1) ++ << ISPRSZ_CNT_VRSZ_SHIFT) ++ & ISPRSZ_CNT_VRSZ_MASK; ++ ++ return 0; ++err_einval: ++ return -EINVAL; ++} ++ ++/** ++ * rsz_config_ratio - Configure ratio ++ * @rsz_conf_chan: Structure containing channel configuration ++ * ++ * Configure ratio ++ **/ ++static void rsz_config_ratio(struct rsz_mult *multipass, ++ struct channel_config *rsz_conf_chan) ++{ ++ int hsize; ++ int vsize; ++ int coeffcounter; ++ ++ if (multipass->hrsz <= 512) { ++ hsize = ((32 * multipass->hstph + (multipass->out_hsize - 1) ++ * multipass->hrsz + 16) >> 8) + 7; ++ } else { ++ hsize = ((64 * multipass->hstph + (multipass->out_hsize - 1) ++ * multipass->hrsz + 32) >> 8) + 7; ++ } ++ if (multipass->vrsz <= 512) { ++ vsize = ((32 * multipass->vstph + (multipass->out_vsize - 1) ++ * multipass->vrsz + 16) >> 8) + 4; ++ } else { ++ vsize = ((64 * multipass->vstph + (multipass->out_vsize - 1) ++ * multipass->vrsz + 32) >> 8) + 7; ++ } ++ rsz_conf_chan->register_config.rsz_in_size = hsize; ++ ++ rsz_conf_chan->register_config.rsz_in_size |= ++ ((vsize << ISPRSZ_IN_SIZE_VERT_SHIFT) ++ & ISPRSZ_IN_SIZE_VERT_MASK); ++ ++ for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; ++ coeffcounter++) { ++ if (multipass->num_htap) { ++ rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter] = ++ (multipass->tap4filt_coeffs[2 ++ * coeffcounter] ++ & ISPRSZ_HFILT10_COEF0_MASK); ++ rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter] |= ++ ((multipass->tap4filt_coeffs[2 ++ * coeffcounter + 1] ++ << ISPRSZ_HFILT10_COEF1_SHIFT) ++ & ISPRSZ_HFILT10_COEF1_MASK); ++ } else { ++ rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter] = ++ (multipass->tap7filt_coeffs[2 ++ * coeffcounter] ++ & ISPRSZ_HFILT10_COEF0_MASK); ++ ++ rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter] |= ++ ((multipass->tap7filt_coeffs[2 ++ * coeffcounter + 1] ++ << ISPRSZ_HFILT10_COEF1_SHIFT) ++ & ISPRSZ_HFILT10_COEF1_MASK); ++ } ++ ++ if (multipass->num_vtap) { ++ rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter] = ++ (multipass->tap4filt_coeffs[2 ++ * coeffcounter] ++ & ISPRSZ_VFILT10_COEF0_MASK); ++ ++ rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter] |= ++ ((multipass->tap4filt_coeffs[2 ++ * coeffcounter + 1] ++ << ISPRSZ_VFILT10_COEF1_SHIFT) & ++ ISPRSZ_VFILT10_COEF1_MASK); ++ } else { ++ rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter] = ++ (multipass->tap7filt_coeffs[2 ++ * coeffcounter] ++ & ISPRSZ_VFILT10_COEF0_MASK); ++ rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter] |= ++ ((multipass->tap7filt_coeffs[2 ++ * coeffcounter + 1] ++ << ISPRSZ_VFILT10_COEF1_SHIFT) ++ & ISPRSZ_VFILT10_COEF1_MASK); ++ } ++ } ++} ++ ++/** ++ * rsz_get_params - Gets the parameter values ++ * @params: Structure containing the Resizer Wrapper parameters ++ * @rsz_conf_chan: Structure containing channel configuration ++ * ++ * Used to get the Resizer hardware settings associated with the ++ * current logical channel represented by fd. ++ **/ ++static int rsz_get_params(struct rsz_params *params, ++ struct channel_config *rsz_conf_chan) ++{ ++ int coeffcounter; ++ ++ if (rsz_conf_chan->config_state) { ++ dev_err(rsz_device, "state not configured\n"); ++ return -EINVAL; ++ } ++ ++ params->in_hsize = rsz_conf_chan->register_config.rsz_in_size ++ & ISPRSZ_IN_SIZE_HORZ_MASK; ++ params->in_vsize = (rsz_conf_chan->register_config.rsz_in_size ++ & ISPRSZ_IN_SIZE_VERT_MASK) ++ >> ISPRSZ_IN_SIZE_VERT_SHIFT; ++ ++ params->in_pitch = rsz_conf_chan->register_config.rsz_sdr_inoff ++ & ISPRSZ_SDR_INOFF_OFFSET_MASK; ++ ++ params->out_hsize = rsz_conf_chan->register_config.rsz_out_size ++ & ISPRSZ_OUT_SIZE_HORZ_MASK; ++ ++ params->out_vsize = (rsz_conf_chan->register_config.rsz_out_size ++ & ISPRSZ_OUT_SIZE_VERT_MASK) ++ >> ISPRSZ_OUT_SIZE_VERT_SHIFT; ++ ++ params->out_pitch = rsz_conf_chan->register_config.rsz_sdr_outoff ++ & ISPRSZ_SDR_OUTOFF_OFFSET_MASK; ++ ++ params->cbilin = (rsz_conf_chan->register_config.rsz_cnt ++ & SET_BIT_CBLIN) >> SET_BIT_CBLIN; ++ ++ params->inptyp = (rsz_conf_chan->register_config.rsz_cnt ++ & ISPRSZ_CNT_INPTYP_MASK) ++ >> SET_BIT_INPTYP; ++ params->horz_starting_pixel = ((rsz_conf_chan->register_config. ++ rsz_in_start ++ & ISPRSZ_IN_START_HORZ_ST_MASK)); ++ params->vert_starting_pixel = ((rsz_conf_chan->register_config. ++ rsz_in_start ++ & ISPRSZ_IN_START_VERT_ST_MASK) ++ >> ISPRSZ_IN_START_VERT_ST_SHIFT); ++ ++ params->hstph = ((rsz_conf_chan->register_config.rsz_cnt ++ & ISPRSZ_CNT_HSTPH_MASK ++ >> ISPRSZ_CNT_HSTPH_SHIFT)); ++ params->vstph = ((rsz_conf_chan->register_config.rsz_cnt ++ & ISPRSZ_CNT_VSTPH_MASK ++ >> ISPRSZ_CNT_VSTPH_SHIFT)); ++ ++ for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; ++ coeffcounter++) { ++ params->tap4filt_coeffs[2 * coeffcounter] = ++ rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter] ++ & ISPRSZ_HFILT10_COEF0_MASK; ++ ++ params->tap4filt_coeffs[2 * coeffcounter + 1] = ++ (rsz_conf_chan->register_config. ++ rsz_coeff_horz[coeffcounter] ++ & ISPRSZ_HFILT10_COEF1_MASK) ++ >> ISPRSZ_HFILT10_COEF1_SHIFT; ++ ++ params->tap7filt_coeffs[2 * coeffcounter] = ++ rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter] ++ & ISPRSZ_VFILT10_COEF0_MASK; ++ ++ params->tap7filt_coeffs[2 * coeffcounter + 1] = ++ (rsz_conf_chan->register_config. ++ rsz_coeff_vert[coeffcounter] ++ & ISPRSZ_VFILT10_COEF1_MASK) ++ >> ISPRSZ_VFILT10_COEF1_SHIFT; ++ ++ } ++ ++ params->yenh_params.type = (rsz_conf_chan->register_config.rsz_yehn ++ & ISPRSZ_YENH_ALGO_MASK) ++ >> ISPRSZ_YENH_ALGO_SHIFT; ++ ++ params->yenh_params.core = rsz_conf_chan->register_config.rsz_yehn ++ & ISPRSZ_YENH_CORE_MASK; ++ ++ params->yenh_params.gain = (rsz_conf_chan->register_config.rsz_yehn ++ & ISPRSZ_YENH_GAIN_MASK) ++ >> ISPRSZ_YENH_GAIN_SHIFT; ++ ++ params->yenh_params.slop = (rsz_conf_chan->register_config.rsz_yehn ++ & ISPRSZ_YENH_SLOP_MASK) ++ >> ISPRSZ_YENH_SLOP_SHIFT; ++ ++ params->pix_fmt = ((rsz_conf_chan->register_config.rsz_cnt ++ & ISPRSZ_CNT_PIXFMT_MASK) ++ >> SET_BIT_YCPOS); ++ ++ if (params->pix_fmt) ++ params->pix_fmt = RSZ_PIX_FMT_UYVY; ++ else ++ params->pix_fmt = RSZ_PIX_FMT_YUYV; ++ ++ return 0; ++} ++ ++/** ++ * rsz_calculate_crop - Calculate Crop values ++ * @rsz_conf_chan: Structure containing channel configuration ++ * @cropsize: Structure containing crop parameters ++ * ++ * Calculate Crop values ++ **/ ++static void rsz_calculate_crop(struct channel_config *rsz_conf_chan, ++ struct rsz_cropsize *cropsize) ++{ ++ int luma_enable; ++ ++ cropsize->hcrop = 0; ++ cropsize->vcrop = 0; ++ ++ luma_enable = (rsz_conf_chan->register_config.rsz_yehn ++ & ISPRSZ_YENH_ALGO_MASK) ++ >> ISPRSZ_YENH_ALGO_SHIFT; ++ ++ if (luma_enable) ++ cropsize->hcrop += 2; ++} ++ ++/** ++ * rsz_vbq_release - Videobuffer queue release ++ * @q: Structure containing the videobuffer queue file handle, and device ++ * structure which contains the actual configuration. ++ * @vb: Structure containing the videobuffer used for resizer processing. ++ **/ ++static void rsz_vbq_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ int i; ++ struct rsz_fh *fh = q->priv_data; ++ ++ for (i = 0; i < VIDEO_MAX_FRAME; i++) { ++ struct videobuf_dmabuf *dma = NULL; ++ if (!q->bufs[i]) ++ continue; ++ if (q->bufs[i]->memory != V4L2_MEMORY_MMAP) ++ continue; ++ dma = videobuf_to_dma(q->bufs[i]); ++ videobuf_dma_unmap(q, dma); ++ videobuf_dma_free(dma); ++ } ++ ++ ispmmu_unmap(fh->isp_addr_read); ++ ispmmu_unmap(fh->isp_addr_write); ++ fh->isp_addr_read = 0; ++ fh->isp_addr_write = 0; ++ spin_lock(&fh->vbq_lock); ++ vb->state = VIDEOBUF_NEEDS_INIT; ++ spin_unlock(&fh->vbq_lock); ++ ++} ++ ++/** ++ * rsz_vbq_setup - Sets up the videobuffer size and validates count. ++ * @q: Structure containing the videobuffer queue file handle, and device ++ * structure which contains the actual configuration. ++ * @cnt: Number of buffers requested ++ * @size: Size in bytes of the buffer used for previewing ++ * ++ * Always returns 0. ++ **/ ++static int rsz_vbq_setup(struct videobuf_queue *q, unsigned int *cnt, ++ unsigned int *size) ++{ ++ struct rsz_fh *fh = q->priv_data; ++ struct rsz_mult *multipass = fh->multipass; ++ u32 insize, outsize; ++ ++ spin_lock(&fh->vbq_lock); ++ if (*cnt <= 0) ++ *cnt = VIDEO_MAX_FRAME; ++ ++ if (*cnt > VIDEO_MAX_FRAME) ++ *cnt = VIDEO_MAX_FRAME; ++ ++ outsize = multipass->out_pitch * multipass->out_vsize; ++ insize = multipass->in_pitch * multipass->in_vsize; ++ if (*cnt == 1 && (outsize > insize)) { ++ dev_err(rsz_device, "2 buffers are required for Upscaling " ++ "mode\n"); ++ goto err_einval; ++ } ++ if (!fh->params->in_hsize || !fh->params->in_vsize) { ++ dev_err(rsz_device, "Can't setup buffer size\n"); ++ goto err_einval; ++ } else { ++ if (outsize > insize) ++ *size = outsize; ++ else ++ *size = insize; ++ ++ fh->rsz_bufsize = *size; ++ } ++ spin_unlock(&fh->vbq_lock); ++ ++ return 0; ++err_einval: ++ spin_unlock(&fh->vbq_lock); ++ return -EINVAL; ++} ++ ++/** ++ * rsz_vbq_prepare - Videobuffer is prepared and mmapped. ++ * @q: Structure containing the videobuffer queue file handle, and device ++ * structure which contains the actual configuration. ++ * @vb: Structure containing the videobuffer used for resizer processing. ++ * @field: Type of field to set in videobuffer device. ++ * ++ * Returns 0 if successful, or -EINVAL if buffer couldn't get allocated, or ++ * -EIO if the ISP MMU mapping fails ++ **/ ++static int rsz_vbq_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct rsz_fh *fh = q->priv_data; ++ struct channel_config *rsz_conf_chan = fh->config; ++ struct rsz_mult *multipass = fh->multipass; ++ int err = 0; ++ unsigned int isp_addr, insize, outsize; ++ struct videobuf_dmabuf *dma = videobuf_to_dma(vb); ++ ++ spin_lock(&fh->vbq_lock); ++ if (vb->baddr) { ++ vb->size = fh->rsz_bufsize; ++ vb->bsize = fh->rsz_bufsize; ++ } else { ++ spin_unlock(&fh->vbq_lock); ++ dev_err(rsz_device, "No user buffer allocated\n"); ++ goto out; ++ } ++ if (vb->i) { ++ vb->width = fh->params->out_hsize; ++ vb->height = fh->params->out_vsize; ++ } else { ++ vb->width = fh->params->in_hsize; ++ vb->height = fh->params->in_vsize; ++ } ++ ++ vb->field = field; ++ spin_unlock(&fh->vbq_lock); ++ ++ if (vb->state == VIDEOBUF_NEEDS_INIT) { ++ err = videobuf_iolock(q, vb, NULL); ++ if (!err) { ++ isp_addr = ispmmu_map_sg(dma->sglist, dma->sglen); ++ if (!isp_addr) ++ err = -EIO; ++ else { ++ if (vb->i) { ++ rsz_conf_chan->register_config. ++ rsz_sdr_outadd ++ = isp_addr; ++ fh->isp_addr_write = isp_addr; ++ rsz_conf_chan->output_buf_index = vb->i; ++ } else { ++ rsz_conf_chan->register_config. ++ rsz_sdr_inadd ++ = isp_addr; ++ rsz_conf_chan->input_buf_index = vb->i; ++ outsize = multipass->out_pitch * ++ multipass->out_vsize; ++ insize = multipass->in_pitch * ++ multipass->in_vsize; ++ if (outsize < insize) { ++ rsz_conf_chan->register_config. ++ rsz_sdr_outadd ++ = isp_addr; ++ rsz_conf_chan-> ++ output_buf_index = ++ vb->i; ++ } ++ ++ fh->isp_addr_read = isp_addr; ++ } ++ } ++ } ++ ++ } ++ ++ if (!err) { ++ spin_lock(&fh->vbq_lock); ++ vb->state = VIDEOBUF_PREPARED; ++ spin_unlock(&fh->vbq_lock); ++ flush_cache_user_range(NULL, vb->baddr, (vb->baddr ++ + vb->bsize)); ++ } else ++ rsz_vbq_release(q, vb); ++ ++out: ++ return err; ++} ++ ++static void rsz_vbq_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ return; ++} ++ ++/** ++ * rsz_open - Initializes and opens the Resizer Wrapper ++ * @inode: Inode structure associated with the Resizer Wrapper ++ * @filp: File structure associated with the Resizer Wrapper ++ * ++ * Returns 0 if successful, -EBUSY if its already opened or the ISP module is ++ * not available, or -ENOMEM if its unable to allocate the device in kernel ++ * space memory. ++ **/ ++static int rsz_open(struct inode *inode, struct file *filp) ++{ ++ int ret = 0; ++ struct channel_config *rsz_conf_chan; ++ struct rsz_fh *fh; ++ struct device_params *device = device_config; ++ struct rsz_params *params; ++ struct rsz_mult *multipass; ++ ++ if ((filp->f_flags & O_NONBLOCK) == O_NONBLOCK) { ++ printk(KERN_DEBUG "omap-resizer: Device is opened in " ++ "non blocking mode\n"); ++ } else { ++ printk(KERN_DEBUG "omap-resizer: Device is opened in blocking " ++ "mode\n"); ++ } ++ fh = kzalloc(sizeof(struct rsz_fh), GFP_KERNEL); ++ if (NULL == fh) ++ return -ENOMEM; ++ ++ isp_get(); ++ ++ rsz_conf_chan = kzalloc(sizeof(struct channel_config), GFP_KERNEL); ++ if (rsz_conf_chan == NULL) { ++ dev_err(rsz_device, "\n cannot allocate memory to config"); ++ ret = -ENOMEM; ++ goto err_enomem0; ++ } ++ params = kzalloc(sizeof(struct rsz_params), GFP_KERNEL); ++ if (params == NULL) { ++ dev_err(rsz_device, "\n cannot allocate memory to params"); ++ ret = -ENOMEM; ++ goto err_enomem1; ++ } ++ multipass = kzalloc(sizeof(struct rsz_mult), GFP_KERNEL); ++ if (multipass == NULL) { ++ dev_err(rsz_device, "\n cannot allocate memory to multipass"); ++ ret = -ENOMEM; ++ goto err_enomem2; ++ } ++ ++ fh->multipass = multipass; ++ fh->params = params; ++ fh->config = rsz_conf_chan; ++ ++ if (mutex_lock_interruptible(&device->reszwrap_mutex)) { ++ ret = -EINTR; ++ goto err_enomem2; ++ } ++ device->opened++; ++ mutex_unlock(&device->reszwrap_mutex); ++ ++ rsz_conf_chan->config_state = STATE_NOT_CONFIGURED; ++ rsz_conf_chan->status = CHANNEL_FREE; ++ ++ filp->private_data = fh; ++ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ fh->device = device; ++ ++ videobuf_queue_sg_init(&fh->vbq, &device->vbq_ops, NULL, ++ &fh->vbq_lock, fh->type, ++ V4L2_FIELD_NONE, ++ sizeof(struct videobuf_buffer), fh); ++ ++ spin_lock_init(&fh->vbq_lock); ++ mutex_init(&rsz_conf_chan->chanprotection_mutex); ++ ++ return 0; ++err_enomem2: ++ kfree(params); ++err_enomem1: ++ kfree(rsz_conf_chan); ++err_enomem0: ++ kfree(fh); ++ return ret; ++} ++ ++/** ++ * rsz_release - Releases Resizer Wrapper and frees up allocated memory ++ * @inode: Inode structure associated with the Resizer Wrapper ++ * @filp: File structure associated with the Resizer Wrapper ++ * ++ * Returns 0 if successful, or -EBUSY if channel is being used. ++ **/ ++static int rsz_release(struct inode *inode, struct file *filp) ++{ ++ u32 timeout = 0; ++ struct rsz_fh *fh = filp->private_data; ++ struct channel_config *rsz_conf_chan = fh->config; ++ struct rsz_params *params = fh->params; ++ struct rsz_mult *multipass = fh->multipass; ++ struct videobuf_queue *q = &fh->vbq; ++ ++ while ((rsz_conf_chan->status != CHANNEL_FREE) && (timeout < 20)) { ++ timeout++; ++ schedule(); ++ } ++ if (mutex_lock_interruptible(&device_config->reszwrap_mutex)) ++ return -EINTR; ++ device_config->opened--; ++ mutex_unlock(&device_config->reszwrap_mutex); ++ /* This will Free memory allocated to the buffers, ++ * and flushes the queue ++ */ ++ videobuf_queue_cancel(q); ++ fh->params = NULL; ++ fh->config = NULL; ++ ++ fh->rsz_bufsize = 0; ++ filp->private_data = NULL; ++ ++ kfree(rsz_conf_chan); ++ kfree(params); ++ kfree(multipass); ++ kfree(fh); ++ ++ isp_put(); ++ ++ return 0; ++} ++ ++/** ++ * rsz_mmap - Memory maps the Resizer Wrapper module. ++ * @file: File structure associated with the Resizer Wrapper ++ * @vma: Virtual memory area structure. ++ * ++ * Returns 0 if successful, or returned value by the videobuf_mmap_mapper() ++ * function. ++ **/ ++static int rsz_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct rsz_fh *fh = file->private_data; ++ ++ return videobuf_mmap_mapper(&fh->vbq, vma); ++} ++ ++/** ++ * rsz_ioctl - I/O control function for Resizer Wrapper ++ * @inode: Inode structure associated with the Resizer Wrapper. ++ * @file: File structure associated with the Resizer Wrapper. ++ * @cmd: Type of command to execute. ++ * @arg: Argument to send to requested command. ++ * ++ * Returns 0 if successful, -EBUSY if channel is being used, -1 if bad command ++ * passed or access is denied, -EFAULT if copy_from_user() or copy_to_user() ++ * fails, -EINVAL if parameter validation fails or parameter structure is not ++ * present. ++ **/ ++static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret = 0; ++ struct rsz_fh *fh = file->private_data; ++ struct device_params *device = fh->device; ++ struct channel_config *rsz_conf_chan = fh->config; ++ ++ if ((_IOC_TYPE(cmd) != RSZ_IOC_BASE) ++ || (_IOC_NR(cmd) > RSZ_IOC_MAXNR)) { ++ dev_err(rsz_device, "Bad command value \n"); ++ return -1; ++ } ++ ++ if (_IOC_DIR(cmd) & _IOC_READ) ++ ret = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); ++ else if (_IOC_DIR(cmd) & _IOC_WRITE) ++ ret = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); ++ ++ if (ret) { ++ dev_err(rsz_device, "Access denied\n"); ++ return -1; ++ } ++ ++ switch (cmd) { ++ case RSZ_REQBUF: ++ { ++ struct v4l2_requestbuffers req_buf; ++ if (copy_from_user(&req_buf, (struct v4l2_requestbuffers *)arg, ++ sizeof(struct v4l2_requestbuffers))) { ++ return -EFAULT; ++ } ++ if (mutex_lock_interruptible(&rsz_conf_chan-> ++ chanprotection_mutex)) ++ return -EINTR; ++ ret = videobuf_reqbufs(&fh->vbq, (void *)&req_buf); ++ mutex_unlock(&rsz_conf_chan->chanprotection_mutex); ++ break; ++ } ++ case RSZ_QUERYBUF: ++ { ++ struct v4l2_buffer buf; ++ if (copy_from_user(&buf, (struct v4l2_buffer *)arg, ++ sizeof(struct v4l2_buffer))) { ++ return -EFAULT; ++ } ++ if (mutex_lock_interruptible(&rsz_conf_chan-> ++ chanprotection_mutex)) ++ return -EINTR; ++ ret = videobuf_querybuf(&fh->vbq, (void *)&buf); ++ mutex_unlock(&rsz_conf_chan->chanprotection_mutex); ++ if (copy_to_user((struct v4l2_buffer *)arg, &buf, ++ sizeof(struct v4l2_buffer))) ++ return -EFAULT; ++ break; ++ } ++ case RSZ_QUEUEBUF: ++ { ++ struct v4l2_buffer buf; ++ if (copy_from_user(&buf, (struct v4l2_buffer *)arg, ++ sizeof(struct v4l2_buffer))) { ++ return -EFAULT; ++ } ++ if (mutex_lock_interruptible(&rsz_conf_chan-> ++ chanprotection_mutex)) ++ return -EINTR; ++ ret = videobuf_qbuf(&fh->vbq, (void *)&buf); ++ mutex_unlock(&rsz_conf_chan->chanprotection_mutex); ++ break; ++ } ++ case RSZ_S_PARAM: ++ { ++ struct rsz_params *params = fh->params; ++ if (copy_from_user(params, (struct rsz_params *)arg, ++ sizeof(struct rsz_params))) { ++ return -EFAULT; ++ } ++ if (mutex_lock_interruptible(&rsz_conf_chan-> ++ chanprotection_mutex)) ++ return -EINTR; ++ ret = rsz_set_params(fh->multipass, params, rsz_conf_chan); ++ mutex_unlock(&rsz_conf_chan->chanprotection_mutex); ++ break; ++ } ++ case RSZ_G_PARAM: ++ ret = rsz_get_params((struct rsz_params *)arg, rsz_conf_chan); ++ break; ++ ++ case RSZ_G_STATUS: ++ { ++ struct rsz_status *status; ++ status = (struct rsz_status *)arg; ++ status->chan_busy = rsz_conf_chan->status; ++ status->hw_busy = ispresizer_busy(); ++ status->src = INPUT_RAM; ++ break; ++ } ++ case RSZ_RESIZE: ++ if (file->f_flags & O_NONBLOCK) { ++ if (ispresizer_busy()) ++ return -EBUSY; ++ else { ++ if (!mutex_trylock(&device->reszwrap_mutex)) ++ return -EBUSY; ++ } ++ } else { ++ if (mutex_lock_interruptible(&device->reszwrap_mutex)) ++ return -EINTR; ++ } ++ ret = rsz_start((int *)arg, fh); ++ mutex_unlock(&device->reszwrap_mutex); ++ break; ++ case RSZ_GET_CROPSIZE: ++ rsz_calculate_crop(rsz_conf_chan, (struct rsz_cropsize *)arg); ++ break; ++ ++ default: ++ dev_err(rsz_device, "resizer_ioctl: Invalid Command Value"); ++ return -EINVAL; ++ } ++ ++ return (long)ret; ++} ++ ++static struct file_operations rsz_fops = { ++ .owner = THIS_MODULE, ++ .open = rsz_open, ++ .release = rsz_release, ++ .mmap = rsz_mmap, ++ .unlocked_ioctl = rsz_unlocked_ioctl, ++}; ++ ++/** ++ * rsz_isr - Interrupt Service Routine for Resizer wrapper ++ * @status: ISP IRQ0STATUS register value ++ * @arg1: Currently not used ++ * @arg2: Currently not used ++ * ++ * Interrupt Service Routine for Resizer wrapper ++ **/ ++static void rsz_isr(unsigned long status, isp_vbq_callback_ptr arg1, void *arg2) ++{ ++ ++ if ((status & RESZ_DONE) != RESZ_DONE) ++ return; ++ ++ complete(&(device_config->compl_isr)); ++ ++} ++ ++/** ++ * resizer_platform_release - Acts when Reference count is zero ++ * @device: Structure containing ISP resizer wrapper global information ++ * ++ * This is called when the reference count goes to zero. ++ **/ ++static void resizer_platform_release(struct device *device) ++{ ++} ++ ++/** ++ * resizer_probe - Checks for device presence ++ * @device: Structure containing details of the current device. ++ * ++ * Always returns 0. ++ **/ ++static int __init resizer_probe(struct platform_device *device) ++{ ++ return 0; ++} ++ ++/** ++ * resizer_remove - Handles the removal of the driver ++ * @omap_resizer_device: Structure containing details of the current device. ++ * ++ * Always returns 0. ++ **/ ++static int resizer_remove(struct platform_device *omap_resizer_device) ++{ ++ return 0; ++} ++ ++static struct class *rsz_class; ++static struct cdev c_dev; ++static dev_t dev; ++static struct platform_device omap_resizer_device = { ++ .name = OMAP_REZR_NAME, ++ .id = 2, ++ .dev = { ++ .release = resizer_platform_release,} ++}; ++ ++static struct platform_driver omap_resizer_driver = { ++ .probe = resizer_probe, ++ .remove = resizer_remove, ++ .driver = { ++ .bus = &platform_bus_type, ++ .name = OMAP_REZR_NAME, ++ }, ++}; ++ ++/** ++ * omap_rsz_init - Initialization of Resizer Wrapper ++ * ++ * Returns 0 if successful, -ENOMEM if could not allocate memory, -ENODEV if ++ * could not register the wrapper as a character device, or other errors if the ++ * device or driver can't register. ++ **/ ++static int __init omap_rsz_init(void) ++{ ++ int ret = 0; ++ struct device_params *device; ++ device = kzalloc(sizeof(struct device_params), GFP_KERNEL); ++ if (!device) { ++ dev_err(rsz_device, OMAP_REZR_NAME ": could not allocate " ++ "memory\n"); ++ return -ENOMEM; ++ } ++ ++ ret = alloc_chrdev_region(&dev, 0, 1, OMAP_REZR_NAME); ++ if (ret < 0) { ++ dev_err(rsz_device, OMAP_REZR_NAME ": intialization failed. " ++ "Could not allocate region " ++ "for character device\n"); ++ kfree(device); ++ return -ENODEV; ++ } ++ ++ /* Register the driver in the kernel */ ++ /* Initialize of character device */ ++ cdev_init(&c_dev, &rsz_fops); ++ c_dev.owner = THIS_MODULE; ++ c_dev.ops = &rsz_fops; ++ ++ /* Addding character device */ ++ ret = cdev_add(&c_dev, dev, 1); ++ if (ret) { ++ dev_err(rsz_device, OMAP_REZR_NAME ": Error adding " ++ "device - %d\n", ret); ++ goto fail2; ++ } ++ rsz_major = MAJOR(dev); ++ ++ /* register driver as a platform driver */ ++ ret = platform_driver_register(&omap_resizer_driver); ++ if (ret) { ++ dev_err(rsz_device, OMAP_REZR_NAME ++ ": Failed to register platform driver!\n"); ++ goto fail3; ++ } ++ ++ /* Register the drive as a platform device */ ++ ret = platform_device_register(&omap_resizer_device); ++ if (ret) { ++ dev_err(rsz_device, OMAP_REZR_NAME ++ ": Failed to register platform device!\n"); ++ goto fail4; ++ } ++ ++ rsz_class = class_create(THIS_MODULE, OMAP_REZR_NAME); ++ if (!rsz_class) { ++ dev_err(rsz_device, OMAP_REZR_NAME ++ ": Failed to create class!\n"); ++ goto fail5; ++ } ++ ++ /* make entry in the devfs */ ++ rsz_device = device_create(rsz_class, rsz_device, ++ MKDEV(rsz_major, 0), NULL, ++ OMAP_REZR_NAME); ++ dev_dbg(rsz_device, OMAP_REZR_NAME ": Registered Resizer Wrapper\n"); ++ device->opened = 0; ++ ++ device->vbq_ops.buf_setup = rsz_vbq_setup; ++ device->vbq_ops.buf_prepare = rsz_vbq_prepare; ++ device->vbq_ops.buf_release = rsz_vbq_release; ++ device->vbq_ops.buf_queue = rsz_vbq_queue; ++ init_completion(&device->compl_isr); ++ mutex_init(&device->reszwrap_mutex); ++ ++ device_config = device; ++ return 0; ++ ++fail5: ++ platform_device_unregister(&omap_resizer_device); ++fail4: ++ platform_driver_unregister(&omap_resizer_driver); ++fail3: ++ cdev_del(&c_dev); ++fail2: ++ unregister_chrdev_region(dev, 1); ++ kfree(device); ++ return ret; ++} ++ ++/** ++ * omap_rsz_exit - Close of Resizer Wrapper ++ **/ ++void __exit omap_rsz_exit(void) ++{ ++ device_destroy(rsz_class, dev); ++ class_destroy(rsz_class); ++ platform_device_unregister(&omap_resizer_device); ++ platform_driver_unregister(&omap_resizer_driver); ++ cdev_del(&c_dev); ++ unregister_chrdev_region(dev, 1); ++ kfree(device_config); ++} ++ ++module_init(omap_rsz_init) ++module_exit(omap_rsz_exit) ++ ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_DESCRIPTION("OMAP ISP Resizer"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/omap_resizer.h b/include/linux/omap_resizer.h +new file mode 100644 +index 0000000..5ac0c88 +--- /dev/null ++++ b/include/linux/omap_resizer.h +@@ -0,0 +1,136 @@ ++/* ++ * drivers/media/video/isp/omap_resizer.h ++ * ++ * Include file for Resizer module wrapper in TI's OMAP3430 ISP ++ * ++ * Copyright (C) 2008 Texas Instruments, Inc. ++ * ++ * This package 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. ++ * ++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ++ */ ++ ++#ifndef OMAP_RESIZER_H ++#define OMAP_RESIZER_H ++ ++#include <linux/types.h> ++ ++/* ioctls definition */ ++#define RSZ_IOC_BASE 'R' ++#define RSZ_IOC_MAXNR 8 ++ ++/*Ioctl options which are to be passed while calling the ioctl*/ ++#define RSZ_REQBUF _IOWR(RSZ_IOC_BASE, 1,\ ++ struct v4l2_requestbuffers) ++#define RSZ_QUERYBUF _IOWR(RSZ_IOC_BASE, 2, struct v4l2_buffer) ++#define RSZ_S_PARAM _IOWR(RSZ_IOC_BASE, 3, struct rsz_params) ++#define RSZ_G_PARAM _IOWR(RSZ_IOC_BASE, 4, struct rsz_params) ++#define RSZ_RESIZE _IOWR(RSZ_IOC_BASE, 5, __s32) ++#define RSZ_G_STATUS _IOWR(RSZ_IOC_BASE, 6, struct rsz_status) ++#define RSZ_QUEUEBUF _IOWR(RSZ_IOC_BASE, 7, struct v4l2_buffer) ++#define RSZ_GET_CROPSIZE _IOWR(RSZ_IOC_BASE, 8, struct rsz_cropsize) ++ ++#define RSZ_INTYPE_YCBCR422_16BIT 0 ++#define RSZ_INTYPE_PLANAR_8BIT 1 ++#define RSZ_PIX_FMT_UYVY 1 /* cb:y:cr:y */ ++#define RSZ_PIX_FMT_YUYV 0 /* y:cb:y:cr */ ++ ++enum config_done { ++ STATE_CONFIGURED, /* Resizer driver configured ++ * by application. ++ */ ++ STATE_NOT_CONFIGURED /* Resizer driver not ++ * configured by application. ++ */ ++}; ++ ++/* Structure Definitions */ ++ ++/* used to luma enhancement options */ ++ ++struct rsz_yenh { ++ __s32 type; /* represents luma enable or ++ * disable. ++ */ ++ __u8 gain; /* represents gain. */ ++ __u8 slop; /* represents slop. */ ++ __u8 core; /* Represents core value. */ ++}; ++ ++/* Conatins all the parameters for resizing. This structure ++ * is used to configure resiser parameters ++ */ ++struct rsz_params { ++ __s32 in_hsize; /* input frame horizontal ++ * size. ++ */ ++ __s32 in_vsize; /* input frame vertical size */ ++ __s32 in_pitch; /* offset between two rows of ++ * input frame. ++ */ ++ __s32 inptyp; /* for determining 16 bit or ++ * 8 bit data. ++ */ ++ __s32 vert_starting_pixel; /* for specifying vertical ++ * starting pixel in input. ++ */ ++ __s32 horz_starting_pixel; /* for specyfing horizontal ++ * starting pixel in input. ++ */ ++ __s32 cbilin; /* # defined, filter with luma ++ * or bi-linear interpolation. ++ */ ++ __s32 pix_fmt; /* # defined, UYVY or YUYV */ ++ __s32 out_hsize; /* output frame horizontal ++ * size. ++ */ ++ __s32 out_vsize; /* output frame vertical ++ * size. ++ */ ++ __s32 out_pitch; /* offset between two rows of ++ * output frame. ++ */ ++ __s32 hstph; /* for specifying horizontal ++ * starting phase. ++ */ ++ __s32 vstph; /* for specifying vertical ++ * starting phase. ++ */ ++ __u16 tap4filt_coeffs[32]; /* horizontal filter ++ * coefficients. ++ */ ++ __u16 tap7filt_coeffs[32]; /* vertical filter ++ * coefficients. ++ */ ++ struct rsz_yenh yenh_params; ++}; ++ ++/* Contains the status of hardware and channel */ ++struct rsz_status { ++ __s32 chan_busy; /* 1: channel is busy, ++ * 0: channel is not busy ++ */ ++ __s32 hw_busy; /* 1: hardware is busy, ++ * 0: hardware is not busy ++ */ ++ __s32 src; /* # defined, can be either ++ * SD-RAM or CCDC/PREVIEWER ++ */ ++}; ++ ++/* Passed by application for getting crop size */ ++struct rsz_cropsize { ++ __u32 hcrop; /* Number of pixels per line ++ * cropped in output image. ++ */ ++ ++ __u32 vcrop; /* Number of lines cropped ++ * in output image. ++ */ ++}; ++ ++#endif +-- +1.6.2.4 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0024-OMAP3-Resizer-V4L2-buf-layer-issues-fixed.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0024-OMAP3-Resizer-V4L2-buf-layer-issues-fixed.patch new file mode 100644 index 0000000000..2c3023643e --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0024-OMAP3-Resizer-V4L2-buf-layer-issues-fixed.patch @@ -0,0 +1,729 @@ +From ad3bbadb7fc39a946dfd0cdac19e2ec8647b2c2c Mon Sep 17 00:00:00 2001 +From: Vaibhav Hiremath <vaibhav@psp-nfs-02.india.ti.com> +Date: Wed, 29 Apr 2009 17:20:27 +0530 +Subject: [PATCH 24/26] OMAP3-Resizer: V4L2-buf layer issues fixed + +V4L2-Buffer layer issues fixed under this commit. +This patch is same as available with PSP1.0.2 release + +The discussion is initiated on this with V4L2 mailing list. + +Please note that this patch is not being tested. +--- + drivers/media/video/isp/omap_resizer.c | 417 ++++++++++++++++++++++++-------- + include/linux/omap_resizer.h | 3 +- + 2 files changed, 321 insertions(+), 99 deletions(-) + +diff --git a/drivers/media/video/isp/omap_resizer.c b/drivers/media/video/isp/omap_resizer.c +index 54bc425..8059c70 100644 +--- a/drivers/media/video/isp/omap_resizer.c ++++ b/drivers/media/video/isp/omap_resizer.c +@@ -28,6 +28,7 @@ + #include <linux/platform_device.h> + #include <linux/io.h> + #include <linux/uaccess.h> ++#include <linux/pci.h> + #include <media/v4l2-dev.h> + #include <asm/cacheflush.h> + +@@ -76,6 +77,10 @@ + #define MAX_COEF_COUNTER 16 + #define COEFF_ADDRESS_OFFSET 0x04 + ++#define RSZ_DEF_REQ_EXP 0xE /* Default read operation expand ++ * for the Resizer driver; value ++ * taken from Davinci. ++ */ + /* Global structure which contains information about number of channels + and protection variables */ + struct device_params { +@@ -85,6 +90,7 @@ struct device_params { + struct mutex reszwrap_mutex; /* Semaphore for array */ + + struct videobuf_queue_ops vbq_ops; /* videobuf queue operations */ ++ unsigned long extra_page_addr; + }; + + /* Register mapped structure which contains the every register +@@ -126,6 +132,9 @@ struct resizer_config { + u32 rsz_yehn; /* yehn(luma)register mapping + * variable. + */ ++ u32 sdr_req_exp; /* Configuration for Non ++ * real time read expand ++ */ + }; + + struct rsz_mult { +@@ -179,6 +188,7 @@ struct channel_config { + * channel is busy or not + */ + struct mutex chanprotection_mutex; ++ int buf_address[VIDEO_MAX_FRAME]; + enum config_done config_state; + u8 input_buf_index; + u8 output_buf_index; +@@ -200,8 +210,6 @@ struct rsz_fh { + struct videobuf_queue vbq; + struct device_params *device; + +- dma_addr_t isp_addr_read; /* Input/Output address */ +- dma_addr_t isp_addr_write; /* Input/Output address */ + u32 rsz_bufsize; /* channel specific buffersize + */ + }; +@@ -227,6 +235,10 @@ static int rsz_set_ratio(struct rsz_mult *multipass, + static void rsz_config_ratio(struct rsz_mult *multipass, + struct channel_config *rsz_conf_chan); + ++static void inline rsz_set_exp(unsigned int exp) ++{ ++ omap_writel(((exp & 0x3FF) << 10), OMAP3ISP_SBL_REG(0xF8)); ++} + /** + * rsz_hardware_setup - Sets hardware configuration registers + * @rsz_conf_chan: Structure containing channel configuration +@@ -271,12 +283,15 @@ static void rsz_hardware_setup(struct channel_config *rsz_conf_chan) + + coeffoffset)); + coeffoffset = coeffoffset + COEFF_ADDRESS_OFFSET; + } ++ /* Configure the read expand register */ ++ rsz_set_exp(rsz_conf_chan->register_config.sdr_req_exp); + } + + /** + * rsz_start - Enables Resizer Wrapper + * @arg: Currently not used. +- * @device: Structure containing ISP resizer wrapper global information ++ * @fh: File structure containing ISP resizer information specific to ++ * channel opened. + * + * Submits a resizing task specified by the rsz_resize structure. The call can + * either be blocked until the task is completed or returned immediately based +@@ -292,12 +307,18 @@ int rsz_start(int *arg, struct rsz_fh *fh) + struct channel_config *rsz_conf_chan = fh->config; + struct rsz_mult *multipass = fh->multipass; + struct videobuf_queue *q = &fh->vbq; ++ struct videobuf_buffer *buf; + int ret; + + if (rsz_conf_chan->config_state) { + dev_err(rsz_device, "State not configured \n"); + goto err_einval; + } ++ if (!rsz_conf_chan->register_config.rsz_sdr_inadd || ++ !rsz_conf_chan->register_config.rsz_sdr_outadd) { ++ dev_err(rsz_device, "address is null\n"); ++ goto err_einval; ++ } + + rsz_conf_chan->status = CHANNEL_BUSY; + +@@ -325,33 +346,22 @@ mult: + goto mult; + } + +- if (fh->isp_addr_read) { +- ispmmu_unmap(fh->isp_addr_read); +- fh->isp_addr_read = 0; +- } +- if (fh->isp_addr_write) { +- ispmmu_unmap(fh->isp_addr_write); +- fh->isp_addr_write = 0; +- } +- + rsz_conf_chan->status = CHANNEL_FREE; +- q->bufs[rsz_conf_chan->input_buf_index]->state = VIDEOBUF_NEEDS_INIT; +- q->bufs[rsz_conf_chan->output_buf_index]->state = VIDEOBUF_NEEDS_INIT; + rsz_conf_chan->register_config.rsz_sdr_outadd = 0; + rsz_conf_chan->register_config.rsz_sdr_inadd = 0; + +- /* Unmap and free the DMA memory allocated for buffers */ +- videobuf_dma_unmap(q, videobuf_to_dma( +- q->bufs[rsz_conf_chan->input_buf_index])); +- videobuf_dma_unmap(q, videobuf_to_dma( +- q->bufs[rsz_conf_chan->output_buf_index])); +- videobuf_dma_free(videobuf_to_dma( +- q->bufs[rsz_conf_chan->input_buf_index])); +- videobuf_dma_free(videobuf_to_dma( +- q->bufs[rsz_conf_chan->output_buf_index])); +- + isp_unset_callback(CBK_RESZ_DONE); + ++ /* Empty the Videobuf queue which was filled during the qbuf */ ++ buf = q->bufs[rsz_conf_chan->input_buf_index]; ++ buf->state = VIDEOBUF_IDLE; ++ list_del(&buf->stream); ++ if (rsz_conf_chan->input_buf_index != rsz_conf_chan->output_buf_index) { ++ buf = q->bufs[rsz_conf_chan->output_buf_index]; ++ buf->state = VIDEOBUF_IDLE; ++ list_del(&buf->stream); ++ } ++ + return 0; + err_einval: + return -EINVAL; +@@ -359,6 +369,8 @@ err_einval: + + /** + * rsz_set_multipass - Set resizer multipass ++ * @multipass: Structure containing channel configuration ++ for multipass support + * @rsz_conf_chan: Structure containing channel configuration + * + * Returns always 0 +@@ -384,6 +396,8 @@ static int rsz_set_multipass(struct rsz_mult *multipass, + + /** + * rsz_copy_data - Copy data ++ * @multipass: Structure containing channel configuration ++ for multipass support + * @params: Structure containing the Resizer Wrapper parameters + * + * Copy data +@@ -413,6 +427,8 @@ static void rsz_copy_data(struct rsz_mult *multipass, struct rsz_params *params) + + /** + * rsz_set_params - Set parameters for resizer wrapper ++ * @multipass: Structure containing channel configuration ++ for multipass support + * @params: Structure containing the Resizer Wrapper parameters + * @rsz_conf_chan: Structure containing channel configuration + * +@@ -524,6 +540,8 @@ static int rsz_set_params(struct rsz_mult *multipass, struct rsz_params *params, + } + + rsz_config_ratio(multipass, rsz_conf_chan); ++ /* Default value for read expand:Taken from Davinci */ ++ rsz_conf_chan->register_config.sdr_req_exp = RSZ_DEF_REQ_EXP; + + rsz_conf_chan->config_state = STATE_CONFIGURED; + +@@ -534,6 +552,8 @@ err_einval: + + /** + * rsz_set_ratio - Set ratio ++ * @multipass: Structure containing channel configuration ++ for multipass support + * @rsz_conf_chan: Structure containing channel configuration + * + * Returns 0 if successful, -EINVAL if invalid output size, upscaling ratio is +@@ -548,7 +568,8 @@ static int rsz_set_ratio(struct rsz_mult *multipass, + + if ((multipass->out_hsize > MAX_IMAGE_WIDTH) || + (multipass->out_vsize > MAX_IMAGE_WIDTH)) { +- dev_err(rsz_device, "Invalid output size!"); ++ dev_err(rsz_device, "Invalid output size! - %d", \ ++ multipass->out_hsize); + goto err_einval; + } + if (multipass->cbilin) { +@@ -758,6 +779,8 @@ err_einval: + + /** + * rsz_config_ratio - Configure ratio ++ * @multipass: Structure containing channel configuration ++ for multipass support + * @rsz_conf_chan: Structure containing channel configuration + * + * Configure ratio +@@ -789,6 +812,20 @@ static void rsz_config_ratio(struct rsz_mult *multipass, + ((vsize << ISPRSZ_IN_SIZE_VERT_SHIFT) + & ISPRSZ_IN_SIZE_VERT_MASK); + ++ /* This is another workaround for the ISP-MMU translation fault. ++ For the parameters whose image size comes exactly to PAGE_SIZE ++ generates ISP-MMU translation fault. The root-cause is the equation ++ input width = (32*sph + (ow - 1)*hrsz + 16) >> 8 + 7 ++ = (64*sph + (ow - 1)*hrsz + 32) >> 8 + 7 ++ input height = (32*spv + (oh - 1)*vrsz + 16) >> 8 + 4 ++ = (64*spv + (oh - 1)*vrsz + 32) >> 8 + 7 ++ ++ we are adjusting the input width to suit for Resizer module, ++ application should use this configuration henceforth. ++ */ ++ multipass->in_hsize = hsize; ++ multipass->in_vsize = vsize; ++ + for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER; + coeffcounter++) { + if (multipass->num_htap) { +@@ -990,24 +1027,15 @@ static void rsz_calculate_crop(struct channel_config *rsz_conf_chan, + static void rsz_vbq_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) + { +- int i; + struct rsz_fh *fh = q->priv_data; ++ struct videobuf_dmabuf *dma = NULL; + +- for (i = 0; i < VIDEO_MAX_FRAME; i++) { +- struct videobuf_dmabuf *dma = NULL; +- if (!q->bufs[i]) +- continue; +- if (q->bufs[i]->memory != V4L2_MEMORY_MMAP) +- continue; +- dma = videobuf_to_dma(q->bufs[i]); +- videobuf_dma_unmap(q, dma); +- videobuf_dma_free(dma); +- } ++ dma = videobuf_to_dma(q->bufs[vb->i]); ++ videobuf_dma_unmap(q, dma); ++ videobuf_dma_free(dma); ++ ispmmu_unmap(fh->config->buf_address[vb->i]); ++ fh->config->buf_address[vb->i] = 0; + +- ispmmu_unmap(fh->isp_addr_read); +- ispmmu_unmap(fh->isp_addr_write); +- fh->isp_addr_read = 0; +- fh->isp_addr_write = 0; + spin_lock(&fh->vbq_lock); + vb->state = VIDEOBUF_NEEDS_INIT; + spin_unlock(&fh->vbq_lock); +@@ -1062,7 +1090,105 @@ err_einval: + spin_unlock(&fh->vbq_lock); + return -EINVAL; + } ++/* ++ * This function is work around for the videobuf_iolock API, ++ * for User memory allocated with ioremap (VM_IO flag) the API ++ * get_user_pages fails. ++ * ++ * To fulfill this requirement, we have completely ignored VM layer of ++ * Linux, and configuring the ISP MMU with physical address. ++ */ ++static int omap_videobuf_dma_init_user(struct videobuf_buffer *vb, ++ unsigned long physp, unsigned long asize) ++{ ++ struct videobuf_dmabuf *dma; ++ struct scatterlist *sglist; ++ unsigned long data, first, last; ++ int len, i = 0; ++ ++ dma = videobuf_to_dma(vb); ++ data = vb->baddr; ++ ++ first = (data & PAGE_MASK) >> PAGE_SHIFT; ++ last = ((data+asize-1) & PAGE_MASK) >> PAGE_SHIFT; ++ dma->offset = data & ~PAGE_MASK; ++ dma->nr_pages = last-first+1; ++ ++ dma->direction = PCI_DMA_FROMDEVICE; ++ /* ++ * Allocate array of sglen + 1, to add entry of extra page ++ * for input buffer. Driver always uses 0th buffer as input buffer. ++ */ ++ len = dma->nr_pages + (vb->i ? 0 : 1); ++ sglist = kcalloc(len, sizeof(*sglist), GFP_KERNEL); ++ if (NULL == sglist) ++ return -ENOMEM; ++ ++ sglist[0].offset = 0; ++ sglist[0].length = PAGE_SIZE - dma->offset; ++ sglist[0].dma_address = (dma_addr_t)physp; ++ physp += sglist[0].length; ++ /* ++ * Iterate in a loop for the number of pages ++ */ ++ for (i = 1; i < (len - (vb->i ? 0 : 1)); i++) { ++ sglist[i].offset = 0; ++ sglist[i].length = PAGE_SIZE; ++ sglist[i].dma_address = (dma_addr_t)physp; ++ physp += PAGE_SIZE; ++ } ++ if (0 == vb->i) { ++ sglist[i].offset = 0; ++ sglist[i].length = PAGE_SIZE; ++ sglist[i].dma_address = ++ (dma_addr_t)device_config->extra_page_addr; ++ } ++ dma->sglist = sglist; ++ dma->sglen = len; ++ return 0; ++ ++ } ++/* ++ * This function is workaround for the issue, where ISP-MMU generated ++ * translation fault for specific params whose size is aligned to PAGE_SIZE. ++ ++ * As a workaround we are padding one extra page for input buffer. This page ++ * we are allocating during init time and will not be released through-out ++ * life time of resizer driver. Please note that Resizer module only reads ++ * from this extra page. ++ */ ++int omap_create_sg(struct videobuf_queue *q, struct videobuf_dmabuf *dma) ++{ ++ struct scatterlist *sglist; ++ int sglen; + ++ sglen = dma->sglen; ++ sglist = kcalloc(sglen + 1, sizeof(*sglist), GFP_KERNEL); ++ if (NULL == sglist) ++ return -ENOMEM; ++ /* ++ * Copy the sglist locally ++ */ ++ memcpy(sglist, dma->sglist, sglen * sizeof(*sglist)); ++ /* ++ * Release the old sglist, since we already copied it locally ++ */ ++ videobuf_dma_unmap(q, dma); ++ /* ++ * Add extra entry to sglist to work with specific params, whose ++ * buffer address alined to PAGE_SIZE. ++ */ ++ sglist[sglen].offset = 0; ++ sglist[sglen].length = PAGE_SIZE; ++ sglist[sglen].dma_address = (dma_addr_t)device_config->extra_page_addr; ++ sglen++; ++ /* ++ * Save the sglist for mapping to ISP-MMU space ++ */ ++ dma->sglist = sglist; ++ dma->sglen = sglen; ++ return 0; ++} + /** + * rsz_vbq_prepare - Videobuffer is prepared and mmapped. + * @q: Structure containing the videobuffer queue file handle, and device +@@ -1079,19 +1205,24 @@ static int rsz_vbq_prepare(struct videobuf_queue *q, + { + struct rsz_fh *fh = q->priv_data; + struct channel_config *rsz_conf_chan = fh->config; +- struct rsz_mult *multipass = fh->multipass; + int err = 0; + unsigned int isp_addr, insize, outsize; +- struct videobuf_dmabuf *dma = videobuf_to_dma(vb); +- ++ struct rsz_mult *multipass = fh->multipass; + spin_lock(&fh->vbq_lock); + if (vb->baddr) { ++ /* Check for 32 byte alignement */ ++ if (vb->baddr != (vb->baddr & ~0x1F)) { ++ spin_unlock(&fh->vbq_lock); ++ dev_err(rsz_device, "Buffer address should be aligned \ ++ to 32 byte\n"); ++ return -EINVAL; ++ } + vb->size = fh->rsz_bufsize; + vb->bsize = fh->rsz_bufsize; + } else { + spin_unlock(&fh->vbq_lock); + dev_err(rsz_device, "No user buffer allocated\n"); +- goto out; ++ return -EINVAL; + } + if (vb->i) { + vb->width = fh->params->out_hsize; +@@ -1103,55 +1234,128 @@ static int rsz_vbq_prepare(struct videobuf_queue *q, + + vb->field = field; + spin_unlock(&fh->vbq_lock); ++ /* ++ * Calculate input and output sizes, will be used while mapping ++ * user pages ++ */ ++ outsize = multipass->out_pitch * multipass->out_vsize; ++ insize = multipass->in_pitch * multipass->in_vsize; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { +- err = videobuf_iolock(q, vb, NULL); +- if (!err) { +- isp_addr = ispmmu_map_sg(dma->sglist, dma->sglen); +- if (!isp_addr) +- err = -EIO; +- else { +- if (vb->i) { +- rsz_conf_chan->register_config. +- rsz_sdr_outadd +- = isp_addr; +- fh->isp_addr_write = isp_addr; +- rsz_conf_chan->output_buf_index = vb->i; +- } else { ++ struct videobuf_dmabuf *dma; ++ struct vm_area_struct *vma; ++ spin_lock(&fh->vbq_lock); ++ dma = videobuf_to_dma(vb); ++ vma = find_vma(current->mm, vb->baddr); ++ if ((vma) && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { ++ /* This will catch ioremaped buffers to the kernel. ++ * It gives two possible scenarios - ++ * - Driver allocates buffer using either ++ * dma_alloc_coherent or get_free_pages, ++ * and maps to user space using ++ * io_remap_pfn_range/remap_pfn_range ++ * - Drivers maps memory outside from Linux using ++ * io_remap ++ */ ++ unsigned long physp = 0, asize; ++ asize = vb->i ? outsize : insize; ++ if ((vb->baddr + asize) > vma->vm_end) { ++ spin_unlock(&fh->vbq_lock); ++ dev_err(rsz_device, "User Buffer Allocation:" \ ++ "err=%lu[%lu]\n",\ ++ (vma->vm_end - vb->baddr), asize); ++ return -ENOMEM; ++ } ++ physp = (vma->vm_pgoff << PAGE_SHIFT) + ++ (vb->baddr - vma->vm_start); ++ err = omap_videobuf_dma_init_user(vb, physp, asize); ++ spin_unlock(&fh->vbq_lock); ++ if (0 != err) ++ return err; ++ } else { ++ err = videobuf_iolock(q, vb, NULL); ++ /* ++ * In case of user pointer mode, the get_user_pages ++ * will fail if user has allocated less memory than ++ * vb->size. But it is not error from resizer driver ++ * point of view. so handled seperately ++ */ ++ if ((err < 0) && (dma->nr_pages > 0)) ++ err = videobuf_dma_map(q, dma); ++ if (err) ++ goto buf_release; ++ /* ++ * Add one extra page for input buffer ++ */ ++ if (0 == vb->i) ++ err = omap_create_sg(q, dma); ++ if (err) ++ goto buf_release; ++ spin_unlock(&fh->vbq_lock); ++ } ++ isp_addr = ispmmu_map_sg(dma->sglist, dma->sglen); ++ if (!isp_addr) ++ err = -EIO; ++ else { ++ if (vb->i) { ++ rsz_conf_chan->buf_address[vb->i] = isp_addr; ++ rsz_conf_chan->register_config. ++ rsz_sdr_outadd ++ = isp_addr; ++ rsz_conf_chan->output_buf_index = vb->i; ++ } else { ++ rsz_conf_chan->buf_address[vb->i] = isp_addr; ++ rsz_conf_chan->register_config. ++ rsz_sdr_inadd ++ = isp_addr; ++ rsz_conf_chan->input_buf_index = vb->i; ++ if (outsize < insize && rsz_conf_chan-> ++ register_config. ++ rsz_sdr_outadd == 0) { + rsz_conf_chan->register_config. +- rsz_sdr_inadd +- = isp_addr; +- rsz_conf_chan->input_buf_index = vb->i; +- outsize = multipass->out_pitch * +- multipass->out_vsize; +- insize = multipass->in_pitch * +- multipass->in_vsize; +- if (outsize < insize) { +- rsz_conf_chan->register_config. +- rsz_sdr_outadd +- = isp_addr; +- rsz_conf_chan-> +- output_buf_index = +- vb->i; +- } +- +- fh->isp_addr_read = isp_addr; ++ rsz_sdr_outadd ++ = isp_addr; ++ rsz_conf_chan-> ++ output_buf_index = ++ vb->i; + } + } + } + +- } ++ } else { ++ if(vb->i) { ++ rsz_conf_chan->register_config. ++ rsz_sdr_outadd = ++ rsz_conf_chan->buf_address[vb->i]; ++ rsz_conf_chan->output_buf_index = vb->i; ++ } else { ++ rsz_conf_chan->register_config. ++ rsz_sdr_inadd = ++ rsz_conf_chan->buf_address[vb->i]; ++ rsz_conf_chan->input_buf_index = vb->i; ++ if(outsize < insize && rsz_conf_chan-> ++ register_config. ++ rsz_sdr_outadd == 0) { ++ rsz_conf_chan->register_config. ++ rsz_sdr_outadd ++ = rsz_conf_chan->buf_address[vb->i]; ++ rsz_conf_chan->output_buf_index = vb->i; ++ } ++ ++ } + ++ } + if (!err) { + spin_lock(&fh->vbq_lock); + vb->state = VIDEOBUF_PREPARED; + spin_unlock(&fh->vbq_lock); +- flush_cache_user_range(NULL, vb->baddr, (vb->baddr +- + vb->bsize)); + } else + rsz_vbq_release(q, vb); + +-out: ++ return err; ++buf_release: ++ spin_unlock(&fh->vbq_lock); ++ rsz_vbq_release(q, vb); + return err; + } + +@@ -1255,7 +1459,8 @@ err_enomem0: + **/ + static int rsz_release(struct inode *inode, struct file *filp) + { +- u32 timeout = 0; ++ int i; ++ unsigned int timeout = 0; + struct rsz_fh *fh = filp->private_data; + struct channel_config *rsz_conf_chan = fh->config; + struct rsz_params *params = fh->params; +@@ -1266,17 +1471,17 @@ static int rsz_release(struct inode *inode, struct file *filp) + timeout++; + schedule(); + } +- if (mutex_lock_interruptible(&device_config->reszwrap_mutex)) +- return -EINTR; +- device_config->opened--; +- mutex_unlock(&device_config->reszwrap_mutex); +- /* This will Free memory allocated to the buffers, +- * and flushes the queue +- */ +- videobuf_queue_cancel(q); +- fh->params = NULL; +- fh->config = NULL; ++ /* Free memory allocated to the buffers */ ++ for (i = 0 ; i < VIDEO_MAX_FRAME ; i++) { ++ struct videobuf_dmabuf *dma = NULL; ++ if (!q->bufs[i]) ++ continue; ++ dma = videobuf_to_dma(q->bufs[i]); ++ videobuf_dma_unmap(q, dma); ++ videobuf_dma_free(dma); ++ } + ++ videobuf_mmap_free(q); + fh->rsz_bufsize = 0; + filp->private_data = NULL; + +@@ -1286,7 +1491,8 @@ static int rsz_release(struct inode *inode, struct file *filp) + kfree(fh); + + isp_put(); +- ++ fh->params = NULL; ++ fh->config = NULL; + return 0; + } + +@@ -1353,6 +1559,12 @@ static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd, + chanprotection_mutex)) + return -EINTR; + ret = videobuf_reqbufs(&fh->vbq, (void *)&req_buf); ++ if (ret >= 0) { ++ if (copy_to_user((struct v4l2_requestbuffers *)arg, ++ &req_buf, sizeof(struct ++ v4l2_requestbuffers))) ++ return -EFAULT; ++ } + mutex_unlock(&rsz_conf_chan->chanprotection_mutex); + break; + } +@@ -1394,11 +1606,7 @@ static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd, + sizeof(struct rsz_params))) { + return -EFAULT; + } +- if (mutex_lock_interruptible(&rsz_conf_chan-> +- chanprotection_mutex)) +- return -EINTR; +- ret = rsz_set_params(fh->multipass, params, rsz_conf_chan); +- mutex_unlock(&rsz_conf_chan->chanprotection_mutex); ++ ret = rsz_set_params(fh->multipass, fh->params, rsz_conf_chan); + break; + } + case RSZ_G_PARAM: +@@ -1433,6 +1641,12 @@ static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd, + rsz_calculate_crop(rsz_conf_chan, (struct rsz_cropsize *)arg); + break; + ++ case RSZ_S_EXP: ++ if (mutex_lock_interruptible(&rsz_conf_chan->chanprotection_mutex)) ++ return -EINTR; ++ rsz_conf_chan->register_config.sdr_req_exp = *((unsigned int *)arg); ++ mutex_unlock(&rsz_conf_chan->chanprotection_mutex); ++ break; + default: + dev_err(rsz_device, "resizer_ioctl: Invalid Command Value"); + return -EINVAL; +@@ -1535,14 +1749,18 @@ static int __init omap_rsz_init(void) + "memory\n"); + return -ENOMEM; + } +- ++ device->extra_page_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, 0); ++ if (!device->extra_page_addr) { ++ dev_err(rsz_device, OMAP_REZR_NAME ":Allocation failed. "); ++ kfree(device); ++ return -ENOMEM; ++ } + ret = alloc_chrdev_region(&dev, 0, 1, OMAP_REZR_NAME); + if (ret < 0) { + dev_err(rsz_device, OMAP_REZR_NAME ": intialization failed. " + "Could not allocate region " + "for character device\n"); +- kfree(device); +- return -ENODEV; ++ goto fail1; + } + + /* Register the driver in the kernel */ +@@ -1608,6 +1826,8 @@ fail3: + cdev_del(&c_dev); + fail2: + unregister_chrdev_region(dev, 1); ++fail1: ++ free_pages((unsigned long)device->extra_page_addr, 0); + kfree(device); + return ret; + } +@@ -1623,6 +1843,7 @@ void __exit omap_rsz_exit(void) + platform_driver_unregister(&omap_resizer_driver); + cdev_del(&c_dev); + unregister_chrdev_region(dev, 1); ++ free_pages((unsigned long)device_config->extra_page_addr, 0); + kfree(device_config); + } + +diff --git a/include/linux/omap_resizer.h b/include/linux/omap_resizer.h +index 5ac0c88..47b8dd8 100644 +--- a/include/linux/omap_resizer.h ++++ b/include/linux/omap_resizer.h +@@ -21,7 +21,7 @@ + + /* ioctls definition */ + #define RSZ_IOC_BASE 'R' +-#define RSZ_IOC_MAXNR 8 ++#define RSZ_IOC_MAXNR 9 + + /*Ioctl options which are to be passed while calling the ioctl*/ + #define RSZ_REQBUF _IOWR(RSZ_IOC_BASE, 1,\ +@@ -33,6 +33,7 @@ + #define RSZ_G_STATUS _IOWR(RSZ_IOC_BASE, 6, struct rsz_status) + #define RSZ_QUEUEBUF _IOWR(RSZ_IOC_BASE, 7, struct v4l2_buffer) + #define RSZ_GET_CROPSIZE _IOWR(RSZ_IOC_BASE, 8, struct rsz_cropsize) ++#define RSZ_S_EXP _IOWR(RSZ_IOC_BASE, 9, __s32) + + #define RSZ_INTYPE_YCBCR422_16BIT 0 + #define RSZ_INTYPE_PLANAR_8BIT 1 +-- +1.6.2.4 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0025-OMAP3-Resizer-Build-issues-fixed.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0025-OMAP3-Resizer-Build-issues-fixed.patch new file mode 100644 index 0000000000..143a846e3c --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/resizer/0025-OMAP3-Resizer-Build-issues-fixed.patch @@ -0,0 +1,36 @@ +From 9fec955e98b4ef7922f629e3a81d2d1af216e028 Mon Sep 17 00:00:00 2001 +From: Vaibhav Hiremath <vaibhav@psp-nfs-02.india.ti.com> +Date: Wed, 29 Apr 2009 18:12:42 +0530 +Subject: [PATCH 25/26] OMAP3-Resizer: Build issues fixed + +There were some building issues with latest gitorious tree, +fixed them. +--- + drivers/media/video/isp/omap_resizer.c | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/video/isp/omap_resizer.c b/drivers/media/video/isp/omap_resizer.c +index 8059c70..dd90b24 100644 +--- a/drivers/media/video/isp/omap_resizer.c ++++ b/drivers/media/video/isp/omap_resizer.c +@@ -1033,7 +1033,7 @@ static void rsz_vbq_release(struct videobuf_queue *q, + dma = videobuf_to_dma(q->bufs[vb->i]); + videobuf_dma_unmap(q, dma); + videobuf_dma_free(dma); +- ispmmu_unmap(fh->config->buf_address[vb->i]); ++ ispmmu_vunmap(fh->config->buf_address[vb->i]); + fh->config->buf_address[vb->i] = 0; + + spin_lock(&fh->vbq_lock); +@@ -1293,7 +1293,7 @@ static int rsz_vbq_prepare(struct videobuf_queue *q, + goto buf_release; + spin_unlock(&fh->vbq_lock); + } +- isp_addr = ispmmu_map_sg(dma->sglist, dma->sglen); ++ isp_addr = ispmmu_vmap(dma->sglist, dma->sglen); + if (!isp_addr) + err = -EIO; + else { +-- +1.6.2.4 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0001-V4L2-Add-COLORFX-user-control.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0001-V4L2-Add-COLORFX-user-control.patch new file mode 100644 index 0000000000..d9e4243b4a --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0001-V4L2-Add-COLORFX-user-control.patch @@ -0,0 +1,44 @@ +From ad422f476ce04636f911557bbfd066c516e9b472 Mon Sep 17 00:00:00 2001 +From: Aguirre Rodriguez, Sergio Alberto <saaguirre@ti.com> +Date: Tue, 20 Jan 2009 16:29:26 -0600 +Subject: [PATCH] V4L2: Add COLORFX user control + +From 07396d67b39bf7bcc81440d3e72d253ad6c54f11 Mon Sep 17 00:00:00 2001 +From: Sergio Aguirre <saaguirre@ti.com> +Date: Tue, 20 Jan 2009 15:34:43 -0600 +Subject: [PATCH v2] V4L2: Add COLORFX user control + +This is a common feature on many cameras. the options are: +Default colors, +B & W, +Sepia + +Signed-off-by: Sergio Aguirre <saaguirre@ti.com> +--- + include/linux/videodev2.h | 9 ++++++++- + 1 files changed, 8 insertions(+), 1 deletions(-) + +diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h +index 5571dbe..8e4e25e 100644 +--- a/include/linux/videodev2.h ++++ b/include/linux/videodev2.h +@@ -879,8 +879,15 @@ enum v4l2_power_line_frequency { + #define V4L2_CID_BACKLIGHT_COMPENSATION (V4L2_CID_BASE+28) + #define V4L2_CID_CHROMA_AGC (V4L2_CID_BASE+29) + #define V4L2_CID_COLOR_KILLER (V4L2_CID_BASE+30) ++#define V4L2_CID_COLORFX (V4L2_CID_BASE+31) ++enum v4l2_colorfx { ++ V4L2_COLORFX_NONE = 0, ++ V4L2_COLORFX_BW = 1, ++ V4L2_COLORFX_SEPIA = 2, ++}; ++ + /* last CID + 1 */ +-#define V4L2_CID_LASTP1 (V4L2_CID_BASE+31) ++#define V4L2_CID_LASTP1 (V4L2_CID_BASE+32) + + /* MPEG-class control IDs defined by V4L2 */ + #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0002-V4L-Int-if-v4l2_int_device_try_attach_all-requires.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0002-V4L-Int-if-v4l2_int_device_try_attach_all-requires.patch new file mode 100644 index 0000000000..45e27a2fda --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0002-V4L-Int-if-v4l2_int_device_try_attach_all-requires.patch @@ -0,0 +1,50 @@ +From 5b007183d51543624bc9f582966f245a64157b57 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@nokia.com> +Date: Fri, 31 Oct 2008 11:51:30 +0200 +Subject: [PATCH] V4L: Int if: v4l2_int_device_try_attach_all requires mutex + +Signed-off-by: Sakari Ailus <sakari.ailus@nokia.com> +--- + drivers/media/video/v4l2-int-device.c | 12 ++++++++++-- + 1 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c +index a935bae..eb8dc84 100644 +--- a/drivers/media/video/v4l2-int-device.c ++++ b/drivers/media/video/v4l2-int-device.c +@@ -32,7 +32,7 @@ + static DEFINE_MUTEX(mutex); + static LIST_HEAD(int_list); + +-void v4l2_int_device_try_attach_all(void) ++static void __v4l2_int_device_try_attach_all(void) + { + struct v4l2_int_device *m, *s; + +@@ -66,6 +66,14 @@ void v4l2_int_device_try_attach_all(void) + } + } + } ++ ++void v4l2_int_device_try_attach_all(void) ++{ ++ mutex_lock(&mutex); ++ __v4l2_int_device_try_attach_all(); ++ mutex_unlock(&mutex); ++} ++ + EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all); + + static int ioctl_sort_cmp(const void *a, const void *b) +@@ -89,7 +97,7 @@ int v4l2_int_device_register(struct v4l2_int_device *d) + &ioctl_sort_cmp, NULL); + mutex_lock(&mutex); + list_add(&d->head, &int_list); +- v4l2_int_device_try_attach_all(); ++ __v4l2_int_device_try_attach_all(); + mutex_unlock(&mutex); + + return 0; +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0003-V4L-Int-if-Dummy-slave.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0003-V4L-Int-if-Dummy-slave.patch new file mode 100644 index 0000000000..829810fab0 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0003-V4L-Int-if-Dummy-slave.patch @@ -0,0 +1,61 @@ +From cc1d76e0f50321e80f7f50e9e214de2c9a45628a Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@nokia.com> +Date: Wed, 22 Oct 2008 18:41:20 +0300 +Subject: [PATCH] V4L: Int if: Dummy slave + +This patch implements a dummy slave that has no functionality. Helps +managing slaves in the OMAP 3 camera driver; no need to check for NULL +pointers. + +Signed-off-by: Sakari Ailus <sakari.ailus@nokia.com> +--- + drivers/media/video/v4l2-int-device.c | 19 +++++++++++++++++++ + include/media/v4l2-int-device.h | 2 ++ + 2 files changed, 21 insertions(+), 0 deletions(-) + +diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c +index eb8dc84..483ee2e 100644 +--- a/drivers/media/video/v4l2-int-device.c ++++ b/drivers/media/video/v4l2-int-device.c +@@ -67,6 +67,25 @@ static void __v4l2_int_device_try_attach_all(void) + } + } + ++static struct v4l2_int_slave dummy_slave = { ++ /* Dummy pointer to avoid underflow in find_ioctl. */ ++ .ioctls = (void *)sizeof(struct v4l2_int_ioctl_desc), ++ .num_ioctls = 0, ++}; ++ ++static struct v4l2_int_device dummy = { ++ .type = v4l2_int_type_slave, ++ .u = { ++ .slave = &dummy_slave, ++ }, ++}; ++ ++struct v4l2_int_device *v4l2_int_device_dummy() ++{ ++ return &dummy; ++} ++EXPORT_SYMBOL_GPL(v4l2_int_device_dummy); ++ + void v4l2_int_device_try_attach_all(void) + { + mutex_lock(&mutex); +diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h +index fbf5855..5d254c4 100644 +--- a/include/media/v4l2-int-device.h ++++ b/include/media/v4l2-int-device.h +@@ -84,6 +84,8 @@ struct v4l2_int_device { + void *priv; + }; + ++struct v4l2_int_device *v4l2_int_device_dummy(void); ++ + void v4l2_int_device_try_attach_all(void); + + int v4l2_int_device_register(struct v4l2_int_device *d); +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0004-V4L-int-device-add-support-for-VIDIOC_QUERYMENU.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0004-V4L-int-device-add-support-for-VIDIOC_QUERYMENU.patch new file mode 100644 index 0000000000..b81b20419e --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0004-V4L-int-device-add-support-for-VIDIOC_QUERYMENU.patch @@ -0,0 +1,33 @@ +From e041e57cafca24cf92430cdf3cc091060a271e19 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@nokia.com> +Date: Fri, 31 Oct 2008 10:20:31 +0200 +Subject: [PATCH] V4L: int device: add support for VIDIOC_QUERYMENU + +Signed-off-by: Tuukka Toivonen <tuukka.o.toivonen@nokia.com> +--- + include/media/v4l2-int-device.h | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h +index 5d254c4..81f4863 100644 +--- a/include/media/v4l2-int-device.h ++++ b/include/media/v4l2-int-device.h +@@ -178,6 +178,7 @@ enum v4l2_int_ioctl_num { + vidioc_int_s_fmt_cap_num, + vidioc_int_try_fmt_cap_num, + vidioc_int_queryctrl_num, ++ vidioc_int_querymenu_num, + vidioc_int_g_ctrl_num, + vidioc_int_s_ctrl_num, + vidioc_int_cropcap_num, +@@ -282,6 +283,7 @@ V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *); + V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *); + V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *); + V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *); ++V4L2_INT_WRAPPER_1(querymenu, struct v4l2_querymenu, *); + V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *); + V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *); + V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *); +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0005-V4L-Int-if-Add-vidioc_int_querycap.patch b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0005-V4L-Int-if-Add-vidioc_int_querycap.patch new file mode 100644 index 0000000000..a9e06290fa --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/isp/v4l/0005-V4L-Int-if-Add-vidioc_int_querycap.patch @@ -0,0 +1,35 @@ +From dc05ee10583dca44e0f8d4109bd1397ee3c5ffae Mon Sep 17 00:00:00 2001 +From: Sakari Ailus <sakari.ailus@nokia.com> +Date: Thu, 2 Oct 2008 11:55:07 +0300 +Subject: [PATCH] V4L: Int if: Add vidioc_int_querycap + +Signed-off-by: Sakari Ailus <sakari.ailus@nokia.com> +--- + include/media/v4l2-int-device.h | 4 +++- + 1 files changed, 3 insertions(+), 1 deletions(-) + +diff --git a/include/media/v4l2-int-device.h b/include/media/v4l2-int-device.h +index 81f4863..2830ae1 100644 +--- a/include/media/v4l2-int-device.h ++++ b/include/media/v4l2-int-device.h +@@ -173,7 +173,8 @@ enum v4l2_int_ioctl_num { + * "Proper" V4L ioctls, as in struct video_device. + * + */ +- vidioc_int_enum_fmt_cap_num = 1, ++ vidioc_int_querycap_num = 1, ++ vidioc_int_enum_fmt_cap_num, + vidioc_int_g_fmt_cap_num, + vidioc_int_s_fmt_cap_num, + vidioc_int_try_fmt_cap_num, +@@ -278,6 +279,7 @@ enum v4l2_int_ioctl_num { + return desc; \ + } + ++V4L2_INT_WRAPPER_1(querycap, struct v4l2_capability, *); + V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *); + V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *); + V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *); +-- +1.5.6.5 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/make-alignment-visible.diff b/recipes/linux/linux-omap-pm-2.6.29/make-alignment-visible.diff new file mode 100644 index 0000000000..9b3958f82a --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/make-alignment-visible.diff @@ -0,0 +1,26 @@ +From: Mans Rullgard <mans@mansr.com> +Date: Mon, 13 Oct 2008 19:32:16 +0000 (+0100) +Subject: ARM: Add prompt for CONFIG_ALIGNMENT_TRAP +X-Git-Url: http://git.mansr.com/?p=linux-omap;a=commitdiff_plain;h=60d60f0ca47fcf4fbb649e45aa64f5a0a4c2f2c8 + +ARM: Add prompt for CONFIG_ALIGNMENT_TRAP + +This adds a prompt text for CONFIG_ALIGNMENT_TRAP, thus making it +visible in make *config. + +Signed-off-by: Mans Rullgard <mans@mansr.com> +--- + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 61314f6..18d3119 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -971,7 +971,7 @@ config LEDS_CPU + will overrule the CPU usage LED. + + config ALIGNMENT_TRAP +- bool ++ bool "Enable alignment trap" + depends on CPU_CP15_MMU + default y if !ARCH_EBSA110 + help diff --git a/recipes/linux/linux-omap-pm-2.6.29/mmctiming.patch b/recipes/linux/linux-omap-pm-2.6.29/mmctiming.patch new file mode 100644 index 0000000000..ec540ab3cb --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/mmctiming.patch @@ -0,0 +1,16 @@ +Index: git/drivers/mmc/core/core.c +=================================================================== +--- git.orig/drivers/mmc/core/core.c ++++ git/drivers/mmc/core/core.c +@@ -284,9 +284,9 @@ void mmc_set_data_timeout(struct mmc_dat + * The limit is really 250 ms, but that is + * insufficient for some crappy cards. + */ +- limit_us = 300000; ++ limit_us = 500000; + else +- limit_us = 100000; ++ limit_us = 200000; + + /* + * SDHC cards always use these fixed values. diff --git a/recipes/linux/linux-omap-pm-2.6.29/modedb-hd720.patch b/recipes/linux/linux-omap-pm-2.6.29/modedb-hd720.patch new file mode 100644 index 0000000000..0166de651a --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/modedb-hd720.patch @@ -0,0 +1,13 @@ +--- orig/drivers/video/modedb.c.orig 2009-04-07 11:40:10.000000000 +0200 ++++ git/drivers/video/modedb.c 2009-04-07 10:35:29.000000000 +0200 +@@ -44,6 +44,10 @@ + NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, + 0, FB_VMODE_NONINTERLACED + }, { ++ /* 1280x720 @ 60 Hz, 45 kHz hsync, CEA 681-E Format 4 */ ++ "hd720", 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5, ++ 0, FB_VMODE_NONINTERLACED ++ }, { + /* 800x600 @ 56 Hz, 35.15 kHz hsync */ + NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2, + 0, FB_VMODE_NONINTERLACED diff --git a/recipes/linux/linux-omap-pm-2.6.29/no-harry-potter.diff b/recipes/linux/linux-omap-pm-2.6.29/no-harry-potter.diff new file mode 100644 index 0000000000..2bb20ab9c0 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/no-harry-potter.diff @@ -0,0 +1,11 @@ +--- /tmp/Makefile 2008-04-24 14:36:20.509598016 +0200 ++++ git/arch/arm/Makefile 2008-04-24 14:36:31.949546584 +0200 +@@ -47,7 +47,7 @@ + # Note that GCC does not numerically define an architecture version + # macro, but instead defines a whole series of macros which makes + # testing for a specific architecture or later rather impossible. +-arch-$(CONFIG_CPU_32v7) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7a,-march=armv5t -Wa$(comma)-march=armv7a) ++arch-$(CONFIG_CPU_32v7) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) + arch-$(CONFIG_CPU_32v6) :=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) + # Only override the compiler option if ARMv6. The ARMv6K extensions are + # always available in ARMv7 diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap-2430-lcd.patch b/recipes/linux/linux-omap-pm-2.6.29/omap-2430-lcd.patch new file mode 100644 index 0000000000..8f8a687c06 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap-2430-lcd.patch @@ -0,0 +1,11 @@ +--- git/drivers/video/omap/lcd_2430sdp.c.orig 2007-08-13 14:35:17.000000000 -0700 ++++ git/drivers/video/omap/lcd_2430sdp.c 2007-08-13 14:35:55.000000000 -0700 +@@ -32,7 +32,7 @@ + #define LCD_PANEL_BACKLIGHT_GPIO 91 + #define LCD_PANEL_ENABLE_GPIO 154 + #define LCD_PIXCLOCK_MAX 5400 /* freq 5.4 MHz */ +-#define PM_RECEIVER TWL4030_MODULE_PM_RECIEVER ++#define PM_RECEIVER TWL4030_MODULE_PM_RECEIVER + #define ENABLE_VAUX2_DEDICATED 0x09 + #define ENABLE_VAUX2_DEV_GRP 0x20 + diff --git a/recipes/linux/linux-omap-pm-2.6.29/timer-suppression.patch b/recipes/linux/linux-omap-pm-2.6.29/timer-suppression.patch new file mode 100644 index 0000000000..04362c96e3 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/timer-suppression.patch @@ -0,0 +1,43 @@ +diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c +index b854a89..26f5569 100644 +--- a/kernel/time/tick-sched.c ++++ b/kernel/time/tick-sched.c +@@ -253,6 +253,16 @@ void tick_nohz_stop_sched_tick(void) + + /* Schedule the tick, if we are at least one jiffie off */ + if ((long)delta_jiffies >= 1) { ++ /* ++ * calculate the expiry time for the next timer wheel ++ * timer ++ */ ++ expires = ktime_add_ns(last_update, tick_period.tv64 * ++ delta_jiffies); ++ ++ /* Skip reprogram of event if its not changed */ ++ if(ts->tick_stopped && ktime_equal(expires, dev->next_event)) ++ goto out2; + + if (delta_jiffies > 1) + cpu_set(cpu, nohz_cpu_mask); +@@ -304,12 +314,7 @@ void tick_nohz_stop_sched_tick(void) + goto out; + } + +- /* +- * calculate the expiry time for the next timer wheel +- * timer +- */ +- expires = ktime_add_ns(last_update, tick_period.tv64 * +- delta_jiffies); ++ /* Mark expiries */ + ts->idle_expires = expires; + + if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { +@@ -328,6 +333,7 @@ void tick_nohz_stop_sched_tick(void) + tick_do_update_jiffies64(ktime_get()); + cpu_clear(cpu, nohz_cpu_mask); + } ++out2: + raise_softirq_irqoff(TIMER_SOFTIRQ); + out: + ts->next_jiffies = next_jiffies; diff --git a/recipes/linux/linux-omap-pm-2.6.29/touchscreen.patch b/recipes/linux/linux-omap-pm-2.6.29/touchscreen.patch new file mode 100644 index 0000000000..2325c401e4 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/touchscreen.patch @@ -0,0 +1,22 @@ +diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c +index d8109ae..f8ce669 100644 +--- a/arch/arm/mach-omap2/board-omap3evm.c ++++ b/arch/arm/mach-omap2/board-omap3evm.c +@@ -128,8 +128,16 @@ static int ads7846_get_pendown_state(void) + } + + struct ads7846_platform_data ads7846_config = { ++ .x_max = 0x0fff, ++ .y_max = 0x0fff, ++ .x_plate_ohms = 180, ++ .pressure_max = 255, ++ .debounce_max = 10, ++ .debounce_tol = 3, ++ .debounce_rep = 1, + .get_pendown_state = ads7846_get_pendown_state, + .keep_vref_on = 1, ++ .settle_delay_usecs = 150, + }; + + static struct omap2_mcspi_device_config ads7846_mcspi_config = { + diff --git a/recipes/linux/linux-omap-pm-2.6.29/usbttyfix.patch b/recipes/linux/linux-omap-pm-2.6.29/usbttyfix.patch new file mode 100644 index 0000000000..997705a31b --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/usbttyfix.patch @@ -0,0 +1,29 @@ +To get USB HOST mode working on USB OTG Port with USB TTY enabled U-boot + +Signed-off-by: Syed Mohammed Khasim <khasim@ti.com> +--- +--- linux-2.6.git/drivers/usb/musb/omap2430.c 2009-01-19 22:42:18.000000000 +0530 ++++ linux-2.6.git/drivers/usb/musb/omap2430.c 2009-02-19 12:45:22.000000000 +0530 +@@ -33,6 +33,7 @@ + #include <linux/list.h> + #include <linux/clk.h> + #include <linux/io.h> ++#include <linux/i2c/twl4030.h> + + #include <asm/mach-types.h> + #include <mach/hardware.h> +@@ -233,6 +234,14 @@ int __init musb_platform_init(struct mus + omap_cfg_reg(AE5_2430_USB0HS_STP); + #endif + ++ /* Reset MUSB Controller */ ++ omap_writel(SOFTRST,OTG_SYSCONFIG); ++ ++#if defined(CONFIG_TWL4030_USB) ++ /* Reset the TWL USB PHY */ ++ twl4030_i2c_write_u8(TWL4030_MODULE_USB, 0x60, 0x4); ++#endif ++ + musb->xceiv = *x; + musb_platform_resume(musb); + diff --git a/recipes/linux/linux-omap-pm-2.6.29/vfp/01-vfp-pm.patch b/recipes/linux/linux-omap-pm-2.6.29/vfp/01-vfp-pm.patch new file mode 100644 index 0000000000..70d4d1efc5 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/vfp/01-vfp-pm.patch @@ -0,0 +1,128 @@ +From: Ben Dooks <ben-linux@fluff.org> +Date: Thu, 18 Dec 2008 10:26:54 +0000 (+0100) +Subject: 5349/1: VFP: Add PM code to save and restore current VFP state +X-Git-Url: http://siarhei.siamashka.name/gitweb/?p=linux-omap-2.6.git;a=commitdiff_plain;h=d504d72ad38e54b5feda67e956834348d6a500e1 + +5349/1: VFP: Add PM code to save and restore current VFP state + +[ARM] 5349/1: VFP: Add PM code to save and restore current VFP state + +When CONFIG_PM is selected, the VFP code does not have any handler +installed to deal with either saving the VFP state of the current +task, nor does it do anything to try and restore the VFP after a +resume. + +On resume, the VFP will have been reset and the co-processor access +control registers are in an indeterminate state (very probably the +CP10 and CP11 the VFP uses will have been disabled by the ARM core +reset). When this happens, resume will break as soon as it tries to +unfreeze the tasks and restart scheduling. + +Add a sys device to allow us to hook the suspend call to save the +current thread state if the thread is using VFP and a resume hook +which restores the CP10/CP11 access and ensures the VFP is disabled +so that the lazy swapping will take place on next access. + +Signed-off-by: Ben Dooks <ben-linux@fluff.org> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +diff --git a/arch/arm/vfp/vfp.h b/arch/arm/vfp/vfp.h +index c85860b..8de86e4 100644 +--- a/arch/arm/vfp/vfp.h ++++ b/arch/arm/vfp/vfp.h +@@ -377,6 +377,6 @@ struct op { + u32 flags; + }; + +-#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP) || defined(CONFIG_PM) + extern void vfp_save_state(void *location, u32 fpexc); + #endif +diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S +index a62dcf7..b21f43f 100644 +--- a/arch/arm/vfp/vfphw.S ++++ b/arch/arm/vfp/vfphw.S +@@ -166,7 +166,7 @@ process_exception: + @ retry the faulted instruction + ENDPROC(vfp_support_entry) + +-#ifdef CONFIG_SMP ++#if defined(CONFIG_SMP) || defined(CONFIG_PM) + ENTRY(vfp_save_state) + @ Save the current VFP state + @ r0 - save location +diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c +index 67ca340..9f476a1 100644 +--- a/arch/arm/vfp/vfpmodule.c ++++ b/arch/arm/vfp/vfpmodule.c +@@ -322,6 +322,61 @@ static void vfp_enable(void *unused) + set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); + } + ++#ifdef CONFIG_PM ++#include <linux/sysdev.h> ++ ++static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state) ++{ ++ struct thread_info *ti = current_thread_info(); ++ u32 fpexc = fmrx(FPEXC); ++ ++ /* if vfp is on, then save state for resumption */ ++ if (fpexc & FPEXC_EN) { ++ printk(KERN_DEBUG "%s: saving vfp state\n", __func__); ++ vfp_save_state(&ti->vfpstate, fpexc); ++ ++ /* disable, just in case */ ++ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); ++ } ++ ++ /* clear any information we had about last context state */ ++ memset(last_VFP_context, 0, sizeof(last_VFP_context)); ++ ++ return 0; ++} ++ ++static int vfp_pm_resume(struct sys_device *dev) ++{ ++ /* ensure we have access to the vfp */ ++ vfp_enable(NULL); ++ ++ /* and disable it to ensure the next usage restores the state */ ++ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); ++ ++ return 0; ++} ++ ++static struct sysdev_class vfp_pm_sysclass = { ++ .name = "vfp", ++ .suspend = vfp_pm_suspend, ++ .resume = vfp_pm_resume, ++}; ++ ++static struct sys_device vfp_pm_sysdev = { ++ .cls = &vfp_pm_sysclass, ++}; ++ ++static void vfp_pm_init(void) ++{ ++ sysdev_class_register(&vfp_pm_sysclass); ++ sysdev_register(&vfp_pm_sysdev); ++} ++ ++ ++#else ++static inline void vfp_pm_init(void) { } ++#endif /* CONFIG_PM */ ++ + #include <linux/smp.h> + + /* +@@ -365,6 +420,7 @@ static int __init vfp_init(void) + vfp_vector = vfp_support_entry; + + thread_register_notifier(&vfp_notifier_block); ++ vfp_pm_init(); + + /* + * We detected VFP, and the support code is diff --git a/recipes/linux/linux-omap-pm-2.6.29/vfp/02-vfp-ptrace.patch b/recipes/linux/linux-omap-pm-2.6.29/vfp/02-vfp-ptrace.patch new file mode 100644 index 0000000000..feba206f95 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/vfp/02-vfp-ptrace.patch @@ -0,0 +1,231 @@ +From: Catalin Marinas <catalin.marinas@arm.com> +Date: Wed, 11 Feb 2009 11:12:56 +0000 (+0100) +Subject: 5387/1: Add ptrace VFP support on ARM +X-Git-Url: http://siarhei.siamashka.name/gitweb/?p=linux-omap-2.6.git;a=commitdiff_plain;h=4dd5beb2244f15c895aba46474bd89545327d1a6 + +5387/1: Add ptrace VFP support on ARM + +[ARM] 5387/1: Add ptrace VFP support on ARM + +This patch adds ptrace support for setting and getting the VFP registers +using PTRACE_SETVFPREGS and PTRACE_GETVFPREGS. The user_vfp structure +defined in asm/user.h contains 32 double registers (to cover VFPv3 and +Neon hardware) and the FPSCR register. + +Cc: Paul Brook <paul@codesourcery.com> +Cc: Daniel Jacobowitz <dan@codesourcery.com> +Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h +index 7319261..236a06b 100644 +--- a/arch/arm/include/asm/ptrace.h ++++ b/arch/arm/include/asm/ptrace.h +@@ -27,6 +27,8 @@ + /* PTRACE_SYSCALL is 24 */ + #define PTRACE_GETCRUNCHREGS 25 + #define PTRACE_SETCRUNCHREGS 26 ++#define PTRACE_GETVFPREGS 27 ++#define PTRACE_SETVFPREGS 28 + + /* + * PSR bits +diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h +index 68b9ec8..b9dc8a8 100644 +--- a/arch/arm/include/asm/thread_info.h ++++ b/arch/arm/include/asm/thread_info.h +@@ -113,6 +113,8 @@ extern void iwmmxt_task_restore(struct thread_info *, void *); + extern void iwmmxt_task_release(struct thread_info *); + extern void iwmmxt_task_switch(struct thread_info *); + ++extern void vfp_sync_state(struct thread_info *thread); ++ + #endif + + /* +diff --git a/arch/arm/include/asm/user.h b/arch/arm/include/asm/user.h +index 825c1e7..df95e05 100644 +--- a/arch/arm/include/asm/user.h ++++ b/arch/arm/include/asm/user.h +@@ -81,4 +81,13 @@ struct user{ + #define HOST_TEXT_START_ADDR (u.start_code) + #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) + ++/* ++ * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 ++ * are ignored by the ptrace system call. ++ */ ++struct user_vfp { ++ unsigned long long fpregs[32]; ++ unsigned long fpscr; ++}; ++ + #endif /* _ARM_USER_H */ +diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c +index df653ea..89882a1 100644 +--- a/arch/arm/kernel/ptrace.c ++++ b/arch/arm/kernel/ptrace.c +@@ -653,6 +653,54 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp) + } + #endif + ++#ifdef CONFIG_VFP ++/* ++ * Get the child VFP state. ++ */ ++static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data) ++{ ++ struct thread_info *thread = task_thread_info(tsk); ++ union vfp_state *vfp = &thread->vfpstate; ++ struct user_vfp __user *ufp = data; ++ ++ vfp_sync_state(thread); ++ ++ /* copy the floating point registers */ ++ if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs, ++ sizeof(vfp->hard.fpregs))) ++ return -EFAULT; ++ ++ /* copy the status and control register */ ++ if (put_user(vfp->hard.fpscr, &ufp->fpscr)) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/* ++ * Set the child VFP state. ++ */ ++static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) ++{ ++ struct thread_info *thread = task_thread_info(tsk); ++ union vfp_state *vfp = &thread->vfpstate; ++ struct user_vfp __user *ufp = data; ++ ++ vfp_sync_state(thread); ++ ++ /* copy the floating point registers */ ++ if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs, ++ sizeof(vfp->hard.fpregs))) ++ return -EFAULT; ++ ++ /* copy the status and control register */ ++ if (get_user(vfp->hard.fpscr, &ufp->fpscr)) ++ return -EFAULT; ++ ++ return 0; ++} ++#endif ++ + long arch_ptrace(struct task_struct *child, long request, long addr, long data) + { + int ret; +@@ -775,6 +823,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) + break; + #endif + ++#ifdef CONFIG_VFP ++ case PTRACE_GETVFPREGS: ++ ret = ptrace_getvfpregs(child, (void __user *)data); ++ break; ++ ++ case PTRACE_SETVFPREGS: ++ ret = ptrace_setvfpregs(child, (void __user *)data); ++ break; ++#endif ++ + default: + ret = ptrace_request(child, request, addr, data); + break; +diff --git a/arch/arm/vfp/vfp.h b/arch/arm/vfp/vfp.h +index 8de86e4..c8c98dd 100644 +--- a/arch/arm/vfp/vfp.h ++++ b/arch/arm/vfp/vfp.h +@@ -377,6 +377,4 @@ struct op { + u32 flags; + }; + +-#if defined(CONFIG_SMP) || defined(CONFIG_PM) + extern void vfp_save_state(void *location, u32 fpexc); +-#endif +diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S +index b21f43f..902d396 100644 +--- a/arch/arm/vfp/vfphw.S ++++ b/arch/arm/vfp/vfphw.S +@@ -166,7 +166,6 @@ process_exception: + @ retry the faulted instruction + ENDPROC(vfp_support_entry) + +-#if defined(CONFIG_SMP) || defined(CONFIG_PM) + ENTRY(vfp_save_state) + @ Save the current VFP state + @ r0 - save location +@@ -181,7 +180,6 @@ ENTRY(vfp_save_state) + stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 + mov pc, lr + ENDPROC(vfp_save_state) +-#endif + + last_VFP_context_address: + .word last_VFP_context +diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c +index 9f476a1..7e12390 100644 +--- a/arch/arm/vfp/vfpmodule.c ++++ b/arch/arm/vfp/vfpmodule.c +@@ -377,6 +377,55 @@ static void vfp_pm_init(void) + static inline void vfp_pm_init(void) { } + #endif /* CONFIG_PM */ + ++/* ++ * Synchronise the hardware VFP state of a thread other than current with the ++ * saved one. This function is used by the ptrace mechanism. ++ */ ++#ifdef CONFIG_SMP ++void vfp_sync_state(struct thread_info *thread) ++{ ++ /* ++ * On SMP systems, the VFP state is automatically saved at every ++ * context switch. We mark the thread VFP state as belonging to a ++ * non-existent CPU so that the saved one will be reloaded when ++ * needed. ++ */ ++ thread->vfpstate.hard.cpu = NR_CPUS; ++} ++#else ++void vfp_sync_state(struct thread_info *thread) ++{ ++ unsigned int cpu = get_cpu(); ++ u32 fpexc = fmrx(FPEXC); ++ ++ /* ++ * If VFP is enabled, the previous state was already saved and ++ * last_VFP_context updated. ++ */ ++ if (fpexc & FPEXC_EN) ++ goto out; ++ ++ if (!last_VFP_context[cpu]) ++ goto out; ++ ++ /* ++ * Save the last VFP state on this CPU. ++ */ ++ fmxr(FPEXC, fpexc | FPEXC_EN); ++ vfp_save_state(last_VFP_context[cpu], fpexc); ++ fmxr(FPEXC, fpexc); ++ ++ /* ++ * Set the context to NULL to force a reload the next time the thread ++ * uses the VFP. ++ */ ++ last_VFP_context[cpu] = NULL; ++ ++out: ++ put_cpu(); ++} ++#endif ++ + #include <linux/smp.h> + + /* diff --git a/recipes/linux/linux-omap-pm-2.6.29/vfp/03-vfp-corruption.patch b/recipes/linux/linux-omap-pm-2.6.29/vfp/03-vfp-corruption.patch new file mode 100644 index 0000000000..216d4f5eaa --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/vfp/03-vfp-corruption.patch @@ -0,0 +1,136 @@ +From: George G. Davis <gdavis@mvista.com> +Date: Wed, 1 Apr 2009 18:27:18 +0000 (+0100) +Subject: 5440/1: Fix VFP state corruption due to preemption during VFP exceptions +X-Git-Url: http://siarhei.siamashka.name/gitweb/?p=linux-omap-2.6.git;a=commitdiff_plain;h=4f7720780dc4a298a6d59c0ec39f7687022cc36f + +5440/1: Fix VFP state corruption due to preemption during VFP exceptions + +[ARM] 5440/1: Fix VFP state corruption due to preemption during VFP exceptions + +We've observed that ARM VFP state can be corrupted during VFP exception +handling when PREEMPT is enabled. The exact conditions are difficult +to reproduce but appear to occur during VFP exception handling when a +task causes a VFP exception which is handled via VFP_bounce and is then +preempted by yet another task which in turn causes yet another VFP +exception. Since the VFP_bounce code is not preempt safe, VFP state then +becomes corrupt. In order to prevent preemption from occuring while +handling a VFP exception, this patch disables preemption while handling +VFP exceptions. + +Signed-off-by: George G. Davis <gdavis@mvista.com> +Acked-by: Catalin Marinas <catalin.marinas@arm.com> +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + +diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S +index ba592a9..a2bed62 100644 +--- a/arch/arm/vfp/entry.S ++++ b/arch/arm/vfp/entry.S +@@ -15,13 +15,16 @@ + * r10 = thread_info structure + * lr = failure return + */ +-#include <linux/linkage.h> +-#include <linux/init.h> +-#include <asm/asm-offsets.h> +-#include <asm/assembler.h> ++#include <asm/thread_info.h> + #include <asm/vfpmacros.h> ++#include "../kernel/entry-header.S" + + ENTRY(do_vfp) ++#ifdef CONFIG_PREEMPT ++ ldr r4, [r10, #TI_PREEMPT] @ get preempt count ++ add r11, r4, #1 @ increment it ++ str r11, [r10, #TI_PREEMPT] ++#endif + enable_irq + ldr r4, .LCvfp + ldr r11, [r10, #TI_CPU] @ CPU number +@@ -30,6 +33,12 @@ ENTRY(do_vfp) + ENDPROC(do_vfp) + + ENTRY(vfp_null_entry) ++#ifdef CONFIG_PREEMPT ++ get_thread_info r10 ++ ldr r4, [r10, #TI_PREEMPT] @ get preempt count ++ sub r11, r4, #1 @ decrement it ++ str r11, [r10, #TI_PREEMPT] ++#endif + mov pc, lr + ENDPROC(vfp_null_entry) + +@@ -41,6 +50,12 @@ ENDPROC(vfp_null_entry) + + __INIT + ENTRY(vfp_testing_entry) ++#ifdef CONFIG_PREEMPT ++ get_thread_info r10 ++ ldr r4, [r10, #TI_PREEMPT] @ get preempt count ++ sub r11, r4, #1 @ decrement it ++ str r11, [r10, #TI_PREEMPT] ++#endif + ldr r0, VFP_arch_address + str r5, [r0] @ known non-zero value + mov pc, r9 @ we have handled the fault +diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S +index 902d396..ea0a156 100644 +--- a/arch/arm/vfp/vfphw.S ++++ b/arch/arm/vfp/vfphw.S +@@ -131,6 +131,12 @@ check_for_exception: + VFPFMXR FPEXC, r1 @ restore FPEXC last + sub r2, r2, #4 + str r2, [sp, #S_PC] @ retry the instruction ++#ifdef CONFIG_PREEMPT ++ get_thread_info r10 ++ ldr r4, [r10, #TI_PREEMPT] @ get preempt count ++ sub r11, r4, #1 @ decrement it ++ str r11, [r10, #TI_PREEMPT] ++#endif + mov pc, r9 @ we think we have handled things + + +@@ -149,6 +155,12 @@ look_for_VFP_exceptions: + @ not recognised by VFP + + DBGSTR "not VFP" ++#ifdef CONFIG_PREEMPT ++ get_thread_info r10 ++ ldr r4, [r10, #TI_PREEMPT] @ get preempt count ++ sub r11, r4, #1 @ decrement it ++ str r11, [r10, #TI_PREEMPT] ++#endif + mov pc, lr + + process_exception: +diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c +index 75457b3..01599c4 100644 +--- a/arch/arm/vfp/vfpmodule.c ++++ b/arch/arm/vfp/vfpmodule.c +@@ -266,7 +266,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + * on VFP subarch 1. + */ + vfp_raise_exceptions(VFP_EXCEPTION_ERROR, trigger, fpscr, regs); +- return; ++ goto exit; + } + + /* +@@ -297,7 +297,7 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + * the FPEXC.FP2V bit is valid only if FPEXC.EX is 1. + */ + if (fpexc ^ (FPEXC_EX | FPEXC_FP2V)) +- return; ++ goto exit; + + /* + * The barrier() here prevents fpinst2 being read +@@ -310,6 +310,8 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs) + exceptions = vfp_emulate_instruction(trigger, orig_fpscr, regs); + if (exceptions) + vfp_raise_exceptions(exceptions, trigger, orig_fpscr, regs); ++ exit: ++ preempt_enable(); + } + + static void vfp_enable(void *unused) diff --git a/recipes/linux/linux-omap-pm-2.6.29/vfp/04-vfp-threads.patch b/recipes/linux/linux-omap-pm-2.6.29/vfp/04-vfp-threads.patch new file mode 100644 index 0000000000..221b2774a1 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/vfp/04-vfp-threads.patch @@ -0,0 +1,98 @@ +From: Imre Deak <imre.deak@nokia.com> +Date: Wed, 1 Jul 2009 21:20:59 +0000 (+0300) +Subject: So far vfp_sync_state worked only for threads other than the current one. This worked... +X-Git-Url: http://siarhei.siamashka.name/gitweb/?p=linux-omap-2.6.git;a=commitdiff_plain;h=23671113997664ae55c8132fc4a56e676d5b46c7 + +So far vfp_sync_state worked only for threads other than the current one. This worked for tracing other threads, but not for PTRACE_TRACEME. Syncing for the current thread will also be needed by an upcoming patch adding support for VFP context save / restore around signal handlers. + +For SMP we need get_cpu now, since we have to protect the FPEXC +register, other than this things remained the same for threads other +than the current. + +Signed-off-by: Imre Deak <imre.deak@nokia.com> +--- + +diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c +index 01599c4..969b77a 100644 +--- a/arch/arm/vfp/vfpmodule.c ++++ b/arch/arm/vfp/vfpmodule.c +@@ -380,12 +380,19 @@ static inline void vfp_pm_init(void) { } + #endif /* CONFIG_PM */ + + /* +- * Synchronise the hardware VFP state of a thread other than current with the +- * saved one. This function is used by the ptrace mechanism. ++ * Synchronise the hardware VFP state of a thread with the saved one. ++ * This function is used by the ptrace mechanism and the signal handler path. + */ +-#ifdef CONFIG_SMP + void vfp_sync_state(struct thread_info *thread) + { ++ unsigned int cpu = get_cpu(); ++ u32 fpexc = fmrx(FPEXC); ++ int vfp_enabled; ++ int self; ++ ++ vfp_enabled = fpexc & FPEXC_EN; ++ self = thread == current_thread_info(); ++#ifdef CONFIG_SMP + /* + * On SMP systems, the VFP state is automatically saved at every + * context switch. We mark the thread VFP state as belonging to a +@@ -393,18 +400,22 @@ void vfp_sync_state(struct thread_info *thread) + * needed. + */ + thread->vfpstate.hard.cpu = NR_CPUS; +-} +-#else +-void vfp_sync_state(struct thread_info *thread) +-{ +- unsigned int cpu = get_cpu(); +- u32 fpexc = fmrx(FPEXC); +- + /* +- * If VFP is enabled, the previous state was already saved and +- * last_VFP_context updated. ++ * Only the current thread's saved VFP context can be out-of-date. ++ * For others there is nothing else to do, since we already ensured ++ * force loading above. + */ +- if (fpexc & FPEXC_EN) ++ if (!self) ++ goto out; ++#endif ++ /* ++ * If the VFP is enabled only the current thread's saved VFP ++ * context can get out-of-date. For other threads the context ++ * was updated when the current thread started to use the VFP. ++ * This also means that the context will be reloaded next time ++ * the thread uses the VFP, so no need to enforce it. ++ */ ++ if (vfp_enabled && !self) + goto out; + + if (!last_VFP_context[cpu]) +@@ -413,8 +424,14 @@ void vfp_sync_state(struct thread_info *thread) + /* + * Save the last VFP state on this CPU. + */ +- fmxr(FPEXC, fpexc | FPEXC_EN); ++ if (!vfp_enabled) ++ fmxr(FPEXC, fpexc | FPEXC_EN); + vfp_save_state(last_VFP_context[cpu], fpexc); ++ /* ++ * Disable VFP in case it was enabled so that the force reload ++ * can happen. ++ */ ++ fpexc &= ~FPEXC_EN; + fmxr(FPEXC, fpexc); + + /* +@@ -426,7 +443,6 @@ void vfp_sync_state(struct thread_info *thread) + out: + put_cpu(); + } +-#endif + + #include <linux/smp.h> + diff --git a/recipes/linux/linux-omap-pm-2.6.29/vfp/05-vfp-signal-handlers.patch b/recipes/linux/linux-omap-pm-2.6.29/vfp/05-vfp-signal-handlers.patch new file mode 100644 index 0000000000..f648188584 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/vfp/05-vfp-signal-handlers.patch @@ -0,0 +1,148 @@ +From: Imre Deak <imre.deak@nokia.com> +Date: Wed, 1 Jul 2009 21:21:06 +0000 (+0300) +Subject: Signal handlers can use floating point, so prevent them to corrupt the main thread... +X-Git-Url: http://siarhei.siamashka.name/gitweb/?p=linux-omap-2.6.git;a=commitdiff_plain;h=4daa6b5d833c401a9de6f136ea23daa971fc4fdc + +Signal handlers can use floating point, so prevent them to corrupt the main thread's VFP context. So far there were two signal stack frame formats defined based on the VFP implementation, but recently a new user struct was added for ptrace that covers all possibilities, so use it for the signal stack too. + +Signed-off-by: Imre Deak <imre.deak@nokia.com> +--- + +diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h +index bf65e9f..5534653 100644 +--- a/arch/arm/include/asm/ucontext.h ++++ b/arch/arm/include/asm/ucontext.h +@@ -59,23 +59,19 @@ struct iwmmxt_sigframe { + #endif /* CONFIG_IWMMXT */ + + #ifdef CONFIG_VFP +-#if __LINUX_ARM_ARCH__ < 6 +-/* For ARM pre-v6, we use fstmiax and fldmiax. This adds one extra +- * word after the registers, and a word of padding at the end for +- * alignment. */ + #define VFP_MAGIC 0x56465001 +-#define VFP_STORAGE_SIZE 152 +-#else +-#define VFP_MAGIC 0x56465002 +-#define VFP_STORAGE_SIZE 144 +-#endif + + struct vfp_sigframe + { + unsigned long magic; + unsigned long size; +- union vfp_state storage; +-}; ++ struct user_vfp ufp; ++ unsigned long reserved; ++} __attribute__((__aligned__(8))); ++ ++/* 8 byte for magic and size, 260 byte for ufp and 4 byte padding */ ++#define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe) ++ + #endif /* CONFIG_VFP */ + + /* +@@ -91,7 +87,7 @@ struct aux_sigframe { + #ifdef CONFIG_IWMMXT + struct iwmmxt_sigframe iwmmxt; + #endif +-#if 0 && defined CONFIG_VFP /* Not yet saved. */ ++#ifdef CONFIG_VFP + struct vfp_sigframe vfp; + #endif + /* Something that isn't a valid magic number for any coprocessor. */ +diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c +index 80b8b5c..a5ef7f5 100644 +--- a/arch/arm/kernel/signal.c ++++ b/arch/arm/kernel/signal.c +@@ -196,6 +196,67 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) + + #endif + ++#ifdef CONFIG_VFP ++ ++static int preserve_vfp_context(struct vfp_sigframe __user *frame) ++{ ++ struct thread_info *thread = current_thread_info(); ++ struct vfp_hard_struct *h = &thread->vfpstate.hard; ++ const unsigned long magic = VFP_MAGIC; ++ const unsigned long size = VFP_STORAGE_SIZE; ++ int err = 0; ++ ++ vfp_sync_state(thread); ++ __put_user_error(magic, &frame->magic, err); ++ __put_user_error(size, &frame->size, err); ++ ++ /* ++ * Copy the floating point registers. There can be unused ++ * registers see asm/hwcap.h for details. ++ */ ++ err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs, ++ sizeof(h->fpregs)); ++ /* ++ * Copy the status and control register. ++ */ ++ __put_user_error(h->fpscr, &frame->ufp.fpscr, err); ++ ++ return err ? -EFAULT : 0; ++} ++ ++static int restore_vfp_context(struct vfp_sigframe __user *frame) ++{ ++ struct thread_info *thread = current_thread_info(); ++ struct vfp_hard_struct *h = &thread->vfpstate.hard; ++ unsigned long magic; ++ unsigned long size; ++ int err = 0; ++ ++ vfp_sync_state(thread); ++ __get_user_error(magic, &frame->magic, err); ++ __get_user_error(size, &frame->size, err); ++ ++ if (err) ++ return -EFAULT; ++ if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) ++ return -EINVAL; ++ ++ /* ++ * Copy the floating point registers. There can be unused ++ * registers see asm/hwcap.h for details. ++ */ ++ err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs, ++ sizeof(h->fpregs)); ++ /* ++ * Copy the status and control register. ++ */ ++ __get_user_error(h->fpscr, &frame->ufp.fpscr, err); ++ ++ return err ? -EFAULT : 0; ++} ++ ++#endif ++ + /* + * Do a signal return; undo the signal stack. These are aligned to 64-bit. + */ +@@ -254,8 +315,8 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) + err |= restore_iwmmxt_context(&aux->iwmmxt); + #endif + #ifdef CONFIG_VFP +-// if (err == 0) +-// err |= vfp_restore_state(&sf->aux.vfp); ++ if (err == 0) ++ err |= restore_vfp_context(&aux->vfp); + #endif + + return err; +@@ -369,8 +430,8 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set) + err |= preserve_iwmmxt_context(&aux->iwmmxt); + #endif + #ifdef CONFIG_VFP +-// if (err == 0) +-// err |= vfp_save_state(&sf->aux.vfp); ++ if (err == 0) ++ err |= preserve_vfp_context(&aux->vfp); + #endif + __put_user_error(0, &aux->end_magic, err); + diff --git a/recipes/linux/linux-omap-pm_2.6.29.bb b/recipes/linux/linux-omap-pm_2.6.29.bb index 5eebc1e8fb..90e6fe2542 100644 --- a/recipes/linux/linux-omap-pm_2.6.29.bb +++ b/recipes/linux/linux-omap-pm_2.6.29.bb @@ -5,7 +5,7 @@ KERNEL_IMAGETYPE = "uImage" COMPATIBLE_MACHINE = "omap5912osk|omap1710h3|omap2430sdp|omap2420h4|beagleboard|omap3evm|omap3-pandora|overo" -SRCREV = "9c133058b929e738e6f28cb99e99c7fb5b35c59a" +SRCREV = "e63cf0710a4fb639d91d3e8b05aa485fbfa381b3" PV = "2.6.29" MACHINE_KERNEL_PR_append = "-pm2+gitr${SRCREV}" @@ -94,12 +94,44 @@ SRC_URI_append = " \ file://dss2/merge-fixups.diff;patch=1 \ file://overo-cpufreq.diff;patch=1 \ file://register-all-OPPs.diff;patch=1 \ - file://fix-audio-capture.patch;patch=1 \ + file://isp/v4l/0001-V4L2-Add-COLORFX-user-control.patch;patch=1 \ + file://isp/v4l/0002-V4L-Int-if-v4l2_int_device_try_attach_all-requires.patch;patch=1 \ + file://isp/v4l/0003-V4L-Int-if-Dummy-slave.patch;patch=1 \ + file://isp/v4l/0004-V4L-int-device-add-support-for-VIDIOC_QUERYMENU.patch;patch=1 \ + file://isp/v4l/0005-V4L-Int-if-Add-vidioc_int_querycap.patch;patch=1 \ + file://isp/iommu/0001-omap-iommu-tlb-and-pagetable-primitives.patch;patch=1 \ + file://isp/iommu/0002-omap-iommu-omap2-architecture-specific-functions.patch;patch=1 \ + file://isp/iommu/0003-omap-iommu-omap3-iommu-device-registration.patch;patch=1 \ + file://isp/iommu/0004-omap-iommu-simple-virtual-address-space-management.patch;patch=1 \ + file://isp/iommu/0005-omap-iommu-entries-for-Kconfig-and-Makefile.patch;patch=1 \ + file://isp/iommu/0006-omap-iommu-Don-t-try-BUG_ON-in_interrupt.patch;patch=1 \ + file://isp/iommu/0007-omap-iommu-We-support-chained-scatterlists-probabl.patch;patch=1 \ + file://isp/iommu/0008-omap2-iommu-entries-for-Kconfig-and-Makefile.patch;patch=1 \ + file://isp/omap3camera/0001-omap3isp-Add-ISP-main-driver-and-register-definitio.patch;patch=1 \ + file://isp/omap3camera/0002-omap3isp-Add-ISP-MMU-wrapper.patch;patch=1 \ + file://isp/omap3camera/0003-omap3isp-Add-userspace-header.patch;patch=1 \ + file://isp/omap3camera/0004-omap3isp-Add-ISP-frontend-CCDC.patch;patch=1 \ + file://isp/omap3camera/0005-omap3isp-Add-ISP-backend-PRV-and-RSZ.patch;patch=1 \ + file://isp/omap3camera/0006-omap3isp-Add-statistics-collection-modules-H3A-and.patch;patch=1 \ + file://isp/omap3camera/0007-omap3isp-Add-CSI2-interface-support.patch;patch=1 \ + file://isp/omap3camera/0008-omap3isp-Add-ISP-tables.patch;patch=1 \ + file://isp/omap3camera/0009-omap34xxcam-Add-camera-driver.patch;patch=1 \ + file://isp/resizer/0023-OMAP-Resizer-Basic-Resizer-refreshed-with-latest-gi.patch;patch=1 \ + file://isp/resizer/0024-OMAP3-Resizer-V4L2-buf-layer-issues-fixed.patch;patch=1 \ + file://isp/resizer/0025-OMAP3-Resizer-Build-issues-fixed.patch;patch=1 \ + file://modedb-hd720.patch;patch=1 \ + file://0001-implement-TIF_RESTORE_SIGMASK-support-and-enable-the.patch;patch=1 \ + file://vfp/02-vfp-ptrace.patch;patch=1 \ + file://vfp/03-vfp-corruption.patch;patch=1 \ + file://vfp/04-vfp-threads.patch;patch=1 \ + file://vfp/05-vfp-signal-handlers.patch;patch=1 \ " SRC_URI_append_beagleboard = " file://logo_linux_clut224.ppm \ - " + file://tincantools-puppy.diff;patch=1 \ + file://tincantools-zippy.diff;patch=1 \ +" SRC_URI_append_omap3evm = " \ file://evm-mcspi-ts.diff;patch=1 \ |