diff options
54 files changed, 9206 insertions, 114 deletions
diff --git a/arch/arm/configs/tegra_harmony_android_defconfig b/arch/arm/configs/tegra_harmony_android_defconfig index c24816c5b36b..b5e8bee4b8a6 100644 --- a/arch/arm/configs/tegra_harmony_android_defconfig +++ b/arch/arm/configs/tegra_harmony_android_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.29 -# Mon Jan 18 18:07:35 2010 +# Fri Feb 5 19:20:05 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -274,6 +274,7 @@ CONFIG_AEABI=y CONFIG_ARCH_FLATMEM_HAS_HOLES=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set @@ -939,7 +940,9 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=1 # # Non-8250 serial port support # -CONFIG_SERIAL_TEGRA_DDK=y +# CONFIG_SERIAL_TEGRA_DDK is not set +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_TEGRA_UARTS=3 CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_JSM is not set @@ -1460,6 +1463,7 @@ CONFIG_UDC_FSL_NR_ENDPOINTS=32 # # OTG and related infrastructure # +# CONFIG_USB_TEGRA_OTG is not set # CONFIG_USB_GPIO_VBUS is not set # CONFIG_UWB is not set CONFIG_MMC=y @@ -1482,9 +1486,9 @@ CONFIG_MMC_BLOCK_BOUNCE=y # MMC/SD/SDIO Host Controller Drivers # CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y # CONFIG_MMC_SDHCI_PCI is not set CONFIG_MMC_SDHCI_TEGRA=y -CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y # CONFIG_MMC_TIFM_SD is not set # CONFIG_MMC_SPI is not set # CONFIG_MEMSTICK is not set diff --git a/arch/arm/configs/tegra_harmony_gnu_linux_defconfig b/arch/arm/configs/tegra_harmony_gnu_linux_defconfig index 8883a75d00e3..a6c64116d1cb 100644 --- a/arch/arm/configs/tegra_harmony_gnu_linux_defconfig +++ b/arch/arm/configs/tegra_harmony_gnu_linux_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.29 -# Thu Dec 31 02:53:37 2009 +# Fri Feb 5 19:08:54 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -200,6 +200,7 @@ CONFIG_TEGRA_SYSTEM_DMA=y CONFIG_TEGRA_ODM_RFKILL=y CONFIG_TEGRA_NVEC=y CONFIG_TEGRA_NVEC_USER=y +# CONFIG_TEGRA_PCI is not set CONFIG_TEGRA_ODM_HARMONY=y # CONFIG_TEGRA_ODM_WHISTLER is not set # CONFIG_TEGRA_ODM_CONCORDE is not set @@ -272,6 +273,7 @@ CONFIG_AEABI=y CONFIG_ARCH_FLATMEM_HAS_HOLES=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set @@ -369,8 +371,6 @@ CONFIG_XFRM_IPCOMP=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_ASK_IP_FIB_HASH is not set -# CONFIG_IP_FIB_TRIE is not set CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y @@ -393,12 +393,6 @@ CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y # CONFIG_TCP_CONG_ADVANCED is not set CONFIG_TCP_CONG_CUBIC=y -# CONFIG_DEFAULT_BIC is not set -# CONFIG_DEFAULT_CUBIC is not set -# CONFIG_DEFAULT_HTCP is not set -# CONFIG_DEFAULT_VEGAS is not set -# CONFIG_DEFAULT_WESTWOOD is not set -# CONFIG_DEFAULT_RENO is not set CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_TCP_MD5SIG is not set # CONFIG_IPV6 is not set @@ -407,9 +401,6 @@ CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_NETFILTER is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set -# CONFIG_SCTP_HMAC_NONE is not set -# CONFIG_SCTP_HMAC_SHA1 is not set -# CONFIG_SCTP_HMAC_MD5 is not set # CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set @@ -465,8 +456,6 @@ CONFIG_WIRELESS_EXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set # CONFIG_MAC80211 is not set -# CONFIG_MAC80211_RC_DEFAULT_PID is not set -# CONFIG_MAC80211_RC_DEFAULT_MINSTREL is not set # CONFIG_WIMAX is not set CONFIG_RFKILL=y # CONFIG_RFKILL_PM is not set @@ -696,9 +685,6 @@ CONFIG_MII=y # CONFIG_WLAN_PRE80211 is not set # CONFIG_WLAN_80211 is not set # CONFIG_IWLWIFI_LEDS is not set -# CONFIG_B43LEGACY_DMA_AND_PIO_MODE is not set -# CONFIG_B43LEGACY_DMA_MODE is not set -# CONFIG_B43LEGACY_PIO_MODE is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -819,8 +805,9 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=1 # # Non-8250 serial port support # -CONFIG_SERIAL_TEGRA_DDK=y -# CONFIG_SERIAL_TEGRA is not set +# CONFIG_SERIAL_TEGRA_DDK is not set +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_TEGRA_UARTS=3 CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y @@ -972,8 +959,6 @@ CONFIG_FB_TILEBLITTING=y # # CONFIG_FB_S1D13XXX is not set CONFIG_FB_TEGRA=y -# CONFIG_FB_CARMINE_DRAM_EVAL is not set -# CONFIG_CARMINE_DRAM_CUSTOM is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set # CONFIG_FB_MB862XX is not set @@ -1016,7 +1001,39 @@ CONFIG_FONT_8x16=y # CONFIG_FONT_SUN12x22 is not set # CONFIG_FONT_10x18 is not set # CONFIG_LOGO is not set -# CONFIG_SOUND is not set +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y +CONFIG_TEGRA_SND_SOC=y +CONFIG_TEGRA_PCM=y +CONFIG_TEGRA_I2S=y +CONFIG_TEGRA_SND_HARMONY=y +CONFIG_SND_SOC_WM8903=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SOUND_PRIME is not set CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HID_DEBUG is not set @@ -1032,6 +1049,7 @@ CONFIG_USB_HID=y # # Special HID drivers # +# CONFIG_USB_KEYBOARD_USES_COMMON_NAME is not set CONFIG_HID_COMPAT=y CONFIG_HID_A4TECH=y CONFIG_HID_APPLE=y @@ -1199,6 +1217,7 @@ CONFIG_UDC_FSL_NR_ENDPOINTS=32 # # OTG and related infrastructure # +# CONFIG_USB_TEGRA_OTG is not set # CONFIG_USB_GPIO_VBUS is not set CONFIG_MMC=y CONFIG_EMBEDDED_MMC_START_OFFSET=y @@ -1220,6 +1239,7 @@ CONFIG_MMC_BLOCK_BOUNCE=y # MMC/SD/SDIO Host Controller Drivers # CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y CONFIG_MMC_SDHCI_TEGRA=y # CONFIG_MMC_SPI is not set # CONFIG_MEMSTICK is not set @@ -1374,10 +1394,6 @@ CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED=y CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y # CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set # CONFIG_JFFS2_FS is not set -# CONFIG_JFFS2_CMODE_NONE is not set -# CONFIG_JFFS2_CMODE_PRIORITY is not set -# CONFIG_JFFS2_CMODE_SIZE is not set -# CONFIG_JFFS2_CMODE_FAVOURLZO is not set # CONFIG_CRAMFS is not set # CONFIG_SQUASHFS is not set # CONFIG_VXFS_FS is not set diff --git a/arch/arm/configs/tegra_whistler_android_defconfig b/arch/arm/configs/tegra_whistler_android_defconfig index 6e341676dba1..5139b9bb6ba0 100644 --- a/arch/arm/configs/tegra_whistler_android_defconfig +++ b/arch/arm/configs/tegra_whistler_android_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.29 -# Mon Jan 18 12:46:38 2010 +# Fri Feb 5 19:16:55 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -268,6 +268,7 @@ CONFIG_AEABI=y CONFIG_ARCH_FLATMEM_HAS_HOLES=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set @@ -798,7 +799,9 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=1 # # Non-8250 serial port support # -CONFIG_SERIAL_TEGRA_DDK=y +# CONFIG_SERIAL_TEGRA_DDK is not set +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_TEGRA_UARTS=3 CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y @@ -871,7 +874,26 @@ CONFIG_SPI_TEGRA=y # # CONFIG_SPI_SPIDEV is not set # CONFIG_SPI_TLE62X0 is not set -# CONFIG_W1 is not set +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_DS2482 is not set +CONFIG_W1_MASTER_TEGRA=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set @@ -1198,8 +1220,8 @@ CONFIG_MMC_BLOCK_BOUNCE=y # MMC/SD/SDIO Host Controller Drivers # CONFIG_MMC_SDHCI=y -CONFIG_MMC_SDHCI_TEGRA=y CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y +CONFIG_MMC_SDHCI_TEGRA=y # CONFIG_MMC_SPI is not set # CONFIG_MEMSTICK is not set # CONFIG_ACCESSIBILITY is not set diff --git a/arch/arm/configs/tegra_whistler_gnu_linux_defconfig b/arch/arm/configs/tegra_whistler_gnu_linux_defconfig index 5dffc7af956a..5cc64a754c29 100644 --- a/arch/arm/configs/tegra_whistler_gnu_linux_defconfig +++ b/arch/arm/configs/tegra_whistler_gnu_linux_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.29 -# Thu Jan 7 15:12:17 2010 +# Fri Feb 5 19:09:59 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -272,6 +272,7 @@ CONFIG_AEABI=y CONFIG_ARCH_FLATMEM_HAS_HOLES=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y # CONFIG_DISCONTIGMEM_MANUAL is not set @@ -807,8 +808,9 @@ CONFIG_SERIAL_8250_RUNTIME_UARTS=1 # # Non-8250 serial port support # -CONFIG_SERIAL_TEGRA_DDK=y -# CONFIG_SERIAL_TEGRA is not set +# CONFIG_SERIAL_TEGRA_DDK is not set +CONFIG_SERIAL_TEGRA=y +CONFIG_SERIAL_TEGRA_UARTS=3 CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y @@ -1001,7 +1003,41 @@ CONFIG_FONT_8x16=y # CONFIG_FONT_SUN12x22 is not set # CONFIG_FONT_10x18 is not set # CONFIG_LOGO is not set -# CONFIG_SOUND is not set +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +CONFIG_SND_VERBOSE_PRINTK=y +CONFIG_SND_DEBUG=y +CONFIG_SND_DEBUG_VERBOSE=y +CONFIG_SND_PCM_XRUN_DEBUG=y +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y +CONFIG_TEGRA_SND_SOC=y +CONFIG_TEGRA_PCM=y +CONFIG_TEGRA_I2S=y +CONFIG_TEGRA_SND_WHISTLER=y +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SOUND_PRIME is not set CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HID_DEBUG is not set @@ -1185,6 +1221,7 @@ CONFIG_UDC_FSL_NR_ENDPOINTS=32 # # OTG and related infrastructure # +# CONFIG_USB_TEGRA_OTG is not set # CONFIG_USB_GPIO_VBUS is not set CONFIG_MMC=y CONFIG_EMBEDDED_MMC_START_OFFSET=y @@ -1206,6 +1243,7 @@ CONFIG_MMC_BLOCK_BOUNCE=y # MMC/SD/SDIO Host Controller Drivers # CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_DYNAMIC_SDMEM_CLOCK=y CONFIG_MMC_SDHCI_TEGRA=y # CONFIG_MMC_SPI is not set # CONFIG_MEMSTICK is not set diff --git a/arch/arm/mach-tegra/idle-t2.c b/arch/arm/mach-tegra/idle-t2.c index 0f3958d90f8d..5b5dad442911 100644 --- a/arch/arm/mach-tegra/idle-t2.c +++ b/arch/arm/mach-tegra/idle-t2.c @@ -36,12 +36,13 @@ extern NvRmDeviceHandle s_hRmGlobal; extern void cpu_ap20_do_lp2(void); +extern void cpu_ap20_do_lp0(void); extern void resume(unsigned int state); extern uintptr_t g_resume, g_contextSavePA, g_contextSaveVA; extern NvU32 g_NumActiveCPUs, g_ArmPerif; extern NvU32 g_enterLP2PA; extern volatile void *g_pPMC, *g_pAHB, *g_pCLK_RST_CONTROLLER; -extern volatile void *g_pEMC, *g_pMC, *g_pAPB_MISC; +extern volatile void *g_pEMC, *g_pMC, *g_pAPB_MISC, *g_pIRAM, *g_pTimerus; #ifdef CONFIG_WAKELOCK extern struct wake_lock main_wake_lock; #endif @@ -69,6 +70,7 @@ extern void power_lp0_init(void); extern void NvSpareTimerTrigger(unsigned long); /* timer.c */ NvRmMemHandle s_hWarmboot = NULL; NvU32 g_AvpWarmbootEntry; +NvU32 g_IramPA = 0; NvU32 lp2count = 0, lp3count = 0, lp2safe = 0; @@ -174,6 +176,30 @@ void __init NvAp20InitFlowController(void) return; } + NvRmModuleGetBaseAddress(s_hRmGlobal, + NVRM_MODULE_ID(NvRmPrivModuleID_Iram, 0), &g_IramPA, &len); + + if (NvRmPhysicalMemMap(g_IramPA, len, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, + (void**)&g_pIRAM)!=NvSuccess) + { + printk(KERN_INFO "failed to map iram; DVFS will not function" + " correctly as a result\n"); + return; + } + + NvRmModuleGetBaseAddress(s_hRmGlobal, + NVRM_MODULE_ID(NvRmModuleID_TimerUs, 0), &pa, &len); + + if (NvRmPhysicalMemMap(pa, len, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, + (void**)&g_pTimerus)!=NvSuccess) + { + printk(KERN_INFO "failed to map iram; DVFS will not function" + " correctly as a result\n"); + return; + } + s_pFlowCtrl = pTempFc; g_ArmPerif = (NvU32)pTempArmPerif; @@ -244,7 +270,7 @@ void cpu_ap20_do_idle(void) // Wait for any interrupt __asm__ volatile ("wfi"); - if (likely(s_pFlowCtrl)) + if (addr) { /* * Signal "stats monitor" to stop counting the idle cycles. diff --git a/arch/arm/mach-tegra/include/ap15/ararb_sema.h b/arch/arm/mach-tegra/include/ap15/ararb_sema.h new file mode 100644 index 000000000000..adc9b4af14b0 --- /dev/null +++ b/arch/arm/mach-tegra/include/ap15/ararb_sema.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +// +// DO NOT EDIT - generated by simspec! +// + +#ifndef ___ARARB_SEMA_H_INC_ +#define ___ARARB_SEMA_H_INC_ +// Arbitration semaphores provide a mechanism by which the two processors can arbitrate +// for the use of various resources. These semaphores provide a hardware locking mechanism, +// so that when a processor is already using a resource, the second processor is not +// granted that resource. There are 32 bits of Arbitration semaphores provided in the system. +// The hardware does not enforce any resource association to these bits. It is left to the +// firmware to assign and use these bits. +// Any processor that needs to access a particular resource will request for the +// corresponding bit in the Arbitration semaphores by writing a one to that bit in the +// Arbitration Semaphore Request register (SMP_GET register). Firmware will then +// check the corresponding bit in the Semaphore Granted Status register (SMP_GNT_ST register) +// If the requesting processor has been granted the resource, then the status returned will +// be a one. +// Alternately, the processor can configure the interrupt controller to generate an +// interrupt when the resource becomes available. Refer to the arictlr_arbgnt specfile for details. +// When the processor has finished using the resource, it releases the resource by writing a one +// to the corresponding bit in the Arbitration Semaphore Put Request register +// (SMP_PUT register). Additionally, pending request status is provided through the +// Arbitration Request Pending Status register (SMP_REQ_ST register). +// Semaphore Granted Status Register + +// Register ARB_SEMA_SMP_GNT_ST_0 +#define ARB_SEMA_SMP_GNT_ST_0 _MK_ADDR_CONST(0x0) +#define ARB_SEMA_SMP_GNT_ST_0_WORD_COUNT 0x1 +#define ARB_SEMA_SMP_GNT_ST_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GNT_ST_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_GNT_ST_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GNT_ST_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GNT_ST_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_GNT_ST_0_WRITE_MASK _MK_MASK_CONST(0x0) +// A one in any bit indicates that the processor reading this register as granted status for that bit. A zero indicates semaphore not granted. +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_SHIFT _MK_SHIFT_CONST(0) +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_FIELD (_MK_MASK_CONST(0xffffffff) << ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_SHIFT) +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_RANGE 31:0 +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_WOFFSET 0x0 +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GNT_ST_0_ARB_31_ARB_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// Request Arbitration Semaphore Register + +// Register ARB_SEMA_SMP_GET_0 +#define ARB_SEMA_SMP_GET_0 _MK_ADDR_CONST(0x4) +#define ARB_SEMA_SMP_GET_0_WORD_COUNT 0x1 +#define ARB_SEMA_SMP_GET_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GET_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_GET_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GET_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GET_0_READ_MASK _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GET_0_WRITE_MASK _MK_MASK_CONST(0xffffffff) +// Writing a one in any bit is a request for that semaphore bit by the processor performing the register write. +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_SHIFT _MK_SHIFT_CONST(0) +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_FIELD (_MK_MASK_CONST(0xffffffff) << ARB_SEMA_SMP_GET_0_GET_31_GET_0_SHIFT) +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_RANGE 31:0 +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_WOFFSET 0x0 +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_GET_0_GET_31_GET_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// Arbitration Semaphore Put Request Register + +// Register ARB_SEMA_SMP_PUT_0 +#define ARB_SEMA_SMP_PUT_0 _MK_ADDR_CONST(0x8) +#define ARB_SEMA_SMP_PUT_0_WORD_COUNT 0x1 +#define ARB_SEMA_SMP_PUT_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_PUT_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_PUT_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_PUT_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_PUT_0_READ_MASK _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_PUT_0_WRITE_MASK _MK_MASK_CONST(0x0) +// Writing a one in any bit will clear the corresponding semaphore bit by the processor performing the register write. +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_SHIFT _MK_SHIFT_CONST(0) +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_FIELD (_MK_MASK_CONST(0xffffffff) << ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_SHIFT) +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_RANGE 31:0 +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_WOFFSET 0x0 +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_PUT_0_PUT_31_PUT_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// Arbitration Request Pending Status (1=PENDING) Register + +// Register ARB_SEMA_SMP_REQ_ST_0 +#define ARB_SEMA_SMP_REQ_ST_0 _MK_ADDR_CONST(0xc) +#define ARB_SEMA_SMP_REQ_ST_0_WORD_COUNT 0x1 +#define ARB_SEMA_SMP_REQ_ST_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_REQ_ST_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_REQ_ST_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_REQ_ST_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_REQ_ST_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_REQ_ST_0_WRITE_MASK _MK_MASK_CONST(0x0) +// A one in any bit indicates a request pending status. The corresponding bits are set when the request for the individual resource is pending. The read by CPU of this register shows the pending status for CPU and a read of this register by AVP (COP) shows the pending status for AVP. +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_SHIFT _MK_SHIFT_CONST(0) +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_FIELD (_MK_MASK_CONST(0xffffffff) << ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_SHIFT) +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_RANGE 31:0 +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_WOFFSET 0x0 +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARB_SEMA_SMP_REQ_ST_0_REQ_31_REQ_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + + +// +// REGISTER LIST +// +#define LIST_ARARB_SEMA_REGS(_op_) \ +_op_(ARB_SEMA_SMP_GNT_ST_0) \ +_op_(ARB_SEMA_SMP_GET_0) \ +_op_(ARB_SEMA_SMP_PUT_0) \ +_op_(ARB_SEMA_SMP_REQ_ST_0) + + +// +// ADDRESS SPACES +// + +#define BASE_ADDRESS_ARB_SEMA 0x00000000 + +// +// ARARB_SEMA REGISTER BANKS +// + +#define ARB_SEMA0_FIRST_REG 0x0000 // ARB_SEMA_SMP_GNT_ST_0 +#define ARB_SEMA0_LAST_REG 0x000c // ARB_SEMA_SMP_REQ_ST_0 + +#ifndef _MK_SHIFT_CONST + #define _MK_SHIFT_CONST(_constant_) _constant_ +#endif +#ifndef _MK_MASK_CONST + #define _MK_MASK_CONST(_constant_) _constant_ +#endif +#ifndef _MK_ENUM_CONST + #define _MK_ENUM_CONST(_constant_) (_constant_ ## UL) +#endif +#ifndef _MK_ADDR_CONST + #define _MK_ADDR_CONST(_constant_) _constant_ +#endif + +#endif // ifndef ___ARARB_SEMA_H_INC_ diff --git a/arch/arm/mach-tegra/include/ap15/arictlr_arbgnt.h b/arch/arm/mach-tegra/include/ap15/arictlr_arbgnt.h new file mode 100644 index 000000000000..2d9790b7cc58 --- /dev/null +++ b/arch/arm/mach-tegra/include/ap15/arictlr_arbgnt.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +// +// DO NOT EDIT - generated by simspec! +// + +#ifndef ___ARICTLR_ARBGNT_H_INC_ +#define ___ARICTLR_ARBGNT_H_INC_ +// +// this spec file is for sw header generation +// +// hw should use headers generated from: +// arintr_ctlr.spec +// +// +// arb_gnt specific interrupt controller registers +// +// Arbitration semaphores provide a mechanism by which the two processors can arbitrate +// for the use of various resources. These semaphores provide a hardware locking mechanism, +// so that when a processor is already using a resource, the second processor is not +// granted that resource. There are 32 bits of Arbitration semaphores provided in the system. +// The hardware does not enforce any resource association to these bits. It is left to the +// firmware to assign and use these bits. +// The setup/usage of the Arbitration Semaphores is described in the ararb_sema specfile. +// +// The Arbitration Semaphores can also generate an interrupt when a hardware resource +// becomes available. The registers in this module configure these interrupts. +// When a 1 is set in the corresponding bit position of the Arbitration Semaphore Interrupt +// Source Register (CPU_enable or COP_enable), an interrupt will be generated when the +// processor achieves Grant Status for that resource. +// The current Grant status can be viewed in the CPU_STATUS or COP_STATUS registers. +// +// CPU Arbitration Semaphore Interrupt Status Register + +// Register ARBGNT_CPU_STATUS_0 +#define ARBGNT_CPU_STATUS_0 _MK_ADDR_CONST(0x0) +#define ARBGNT_CPU_STATUS_0_WORD_COUNT 0x1 +#define ARBGNT_CPU_STATUS_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_STATUS_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_CPU_STATUS_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_STATUS_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_STATUS_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_CPU_STATUS_0_WRITE_MASK _MK_MASK_CONST(0x0) +// Each bit is set by hardware when the corresponding arbitration semaphore ownership is granted to CPU. Interrupt is cleared when the CPU writes the ARB_SMP.PUT register with the corresponding bit set. +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_SHIFT _MK_SHIFT_CONST(0) +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_FIELD (_MK_MASK_CONST(0xffffffff) << ARBGNT_CPU_STATUS_0_GNT31_GNG0_SHIFT) +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_RANGE 31:0 +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_WOFFSET 0x0 +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_STATUS_0_GNT31_GNG0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// CPU Arbitration Semaphore Interrupt Enable Register + +// Register ARBGNT_CPU_ENABLE_0 +#define ARBGNT_CPU_ENABLE_0 _MK_ADDR_CONST(0x4) +#define ARBGNT_CPU_ENABLE_0_WORD_COUNT 0x1 +#define ARBGNT_CPU_ENABLE_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_ENABLE_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_CPU_ENABLE_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_ENABLE_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_ENABLE_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_CPU_ENABLE_0_WRITE_MASK _MK_MASK_CONST(0xffffffff) +// Writing a 1 in any bit position will enable the corresponding arbitration semaphore interrupt. +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_SHIFT _MK_SHIFT_CONST(0) +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_FIELD (_MK_MASK_CONST(0xffffffff) << ARBGNT_CPU_ENABLE_0_GER31_GER0_SHIFT) +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_RANGE 31:0 +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_WOFFSET 0x0 +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_CPU_ENABLE_0_GER31_GER0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// COP Arbitration Semaphore Interrupt Status Register + +// Register ARBGNT_COP_STATUS_0 +#define ARBGNT_COP_STATUS_0 _MK_ADDR_CONST(0x8) +#define ARBGNT_COP_STATUS_0_WORD_COUNT 0x1 +#define ARBGNT_COP_STATUS_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_COP_STATUS_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_COP_STATUS_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_COP_STATUS_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARBGNT_COP_STATUS_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_COP_STATUS_0_WRITE_MASK _MK_MASK_CONST(0x0) +// Each bit is set by hardware when the corresponding arbitration semaphore ownership is granted to COP. Interrupt is cleared when the COP writes the ARB_SMP.PUT register with the corresponding bit set. +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_SHIFT _MK_SHIFT_CONST(0) +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_FIELD (_MK_MASK_CONST(0xffffffff) << ARBGNT_COP_STATUS_0_GNT31_GNG0_SHIFT) +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_RANGE 31:0 +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_WOFFSET 0x0 +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_COP_STATUS_0_GNT31_GNG0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// COP Arbitration Semaphore Interrupt Enable Register + +// Register ARBGNT_COP_ENABLE_0 +#define ARBGNT_COP_ENABLE_0 _MK_ADDR_CONST(0xc) +#define ARBGNT_COP_ENABLE_0_WORD_COUNT 0x1 +#define ARBGNT_COP_ENABLE_0_RESET_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_COP_ENABLE_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_COP_ENABLE_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define ARBGNT_COP_ENABLE_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define ARBGNT_COP_ENABLE_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_COP_ENABLE_0_WRITE_MASK _MK_MASK_CONST(0xffffffff) +// Writing a 1 in any bit position will enable the corresponding arbitration semaphore interrupt. +#define ARBGNT_COP_ENABLE_0_GER31_GER0_SHIFT _MK_SHIFT_CONST(0) +#define ARBGNT_COP_ENABLE_0_GER31_GER0_FIELD (_MK_MASK_CONST(0xffffffff) << ARBGNT_COP_ENABLE_0_GER31_GER0_SHIFT) +#define ARBGNT_COP_ENABLE_0_GER31_GER0_RANGE 31:0 +#define ARBGNT_COP_ENABLE_0_GER31_GER0_WOFFSET 0x0 +#define ARBGNT_COP_ENABLE_0_GER31_GER0_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_COP_ENABLE_0_GER31_GER0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define ARBGNT_COP_ENABLE_0_GER31_GER0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define ARBGNT_COP_ENABLE_0_GER31_GER0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + + +// +// REGISTER LIST +// +#define LIST_ARICTLR_ARBGNT_REGS(_op_) \ +_op_(ARBGNT_CPU_STATUS_0) \ +_op_(ARBGNT_CPU_ENABLE_0) \ +_op_(ARBGNT_COP_STATUS_0) \ +_op_(ARBGNT_COP_ENABLE_0) + + +// +// ADDRESS SPACES +// + +#define BASE_ADDRESS_ARBGNT 0x00000000 + +// +// ARICTLR_ARBGNT REGISTER BANKS +// + +#define ARBGNT0_FIRST_REG 0x0000 // ARBGNT_CPU_STATUS_0 +#define ARBGNT0_LAST_REG 0x000c // ARBGNT_COP_ENABLE_0 + +#ifndef _MK_SHIFT_CONST + #define _MK_SHIFT_CONST(_constant_) _constant_ +#endif +#ifndef _MK_MASK_CONST + #define _MK_MASK_CONST(_constant_) _constant_ +#endif +#ifndef _MK_ENUM_CONST + #define _MK_ENUM_CONST(_constant_) (_constant_ ## UL) +#endif +#ifndef _MK_ADDR_CONST + #define _MK_ADDR_CONST(_constant_) _constant_ +#endif + +#endif // ifndef ___ARICTLR_ARBGNT_H_INC_ diff --git a/arch/arm/mach-tegra/include/ap15/arres_sema.h b/arch/arm/mach-tegra/include/ap15/arres_sema.h new file mode 100644 index 000000000000..093439d20511 --- /dev/null +++ b/arch/arm/mach-tegra/include/ap15/arres_sema.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +// +// DO NOT EDIT - generated by simspec! +// + +#ifndef ___ARRES_SEMA_H_INC_ +#define ___ARRES_SEMA_H_INC_ +// Shared Resource Semaphore Status + +// Register RES_SEMA_SHRD_SMP_STA_0 +#define RES_SEMA_SHRD_SMP_STA_0 _MK_ADDR_CONST(0x0) +#define RES_SEMA_SHRD_SMP_STA_0_WORD_COUNT 0x1 +#define RES_SEMA_SHRD_SMP_STA_0_RESET_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_STA_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_STA_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_STA_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_STA_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_STA_0_WRITE_MASK _MK_MASK_CONST(0x0) +// SMP.27:SMP.24: Available in APB_DMA.REQUESTORS register +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_SHIFT _MK_SHIFT_CONST(0) +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_FIELD (_MK_MASK_CONST(0xffffffff) << RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_SHIFT) +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_RANGE 31:0 +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_WOFFSET 0x0 +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_STA_0_SMP_31_SMP_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// Shared Resource Semaphore Set-bit Request + +// Register RES_SEMA_SHRD_SMP_SET_0 +#define RES_SEMA_SHRD_SMP_SET_0 _MK_ADDR_CONST(0x4) +#define RES_SEMA_SHRD_SMP_SET_0_WORD_COUNT 0x1 +#define RES_SEMA_SHRD_SMP_SET_0_RESET_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_SET_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_SET_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_SET_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_SET_0_READ_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_SET_0_WRITE_MASK _MK_MASK_CONST(0xffffffff) +// Semaphore set register. Writing a one to any bit will set the corresponding semaphore bit. Shared resource set-bit requests +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_SHIFT _MK_SHIFT_CONST(0) +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_FIELD (_MK_MASK_CONST(0xffffffff) << RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_SHIFT) +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_RANGE 31:0 +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_WOFFSET 0x0 +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_SET_0_SET_31_SET_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// Shared Resource Semaphore Clr-bit Request Register + +// Register RES_SEMA_SHRD_SMP_CLR_0 +#define RES_SEMA_SHRD_SMP_CLR_0 _MK_ADDR_CONST(0x8) +#define RES_SEMA_SHRD_SMP_CLR_0_WORD_COUNT 0x1 +#define RES_SEMA_SHRD_SMP_CLR_0_RESET_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_CLR_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_CLR_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_CLR_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_CLR_0_READ_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_CLR_0_WRITE_MASK _MK_MASK_CONST(0x0) +// corresponding semaphore bit +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_SHIFT _MK_SHIFT_CONST(0) +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_FIELD (_MK_MASK_CONST(0xffffffff) << RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_SHIFT) +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_RANGE 31:0 +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_WOFFSET 0x0 +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_DEFAULT_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_SMP_CLR_0_CLR_31_CLR_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + + +// Reserved address 12 [0xc] +// Shared Resource Inbox (messages from COP to CPU) + +// Register RES_SEMA_SHRD_INBOX_0 +#define RES_SEMA_SHRD_INBOX_0 _MK_ADDR_CONST(0x10) +#define RES_SEMA_SHRD_INBOX_0_WORD_COUNT 0x1 +#define RES_SEMA_SHRD_INBOX_0_RESET_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_INBOX_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_INBOX_0_WRITE_MASK _MK_MASK_CONST(0x0) +// Interrupt CPU on INBOX Full (TAG=1) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_SHIFT _MK_SHIFT_CONST(31) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_INBOX_0_IE_IBF_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_RANGE 31:31 +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_EMPTY _MK_ENUM_CONST(0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBF_FULL _MK_ENUM_CONST(1) + +// Interrupt COP on INBOX Empty (TAG=0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_SHIFT _MK_SHIFT_CONST(30) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_INBOX_0_IE_IBE_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_RANGE 30:30 +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_EMPTY _MK_ENUM_CONST(0) +#define RES_SEMA_SHRD_INBOX_0_IE_IBE_FULL _MK_ENUM_CONST(1) + +// Read-only. Set when COP writes this register and cleared when CPU Reads this register. +#define RES_SEMA_SHRD_INBOX_0_TAG_SHIFT _MK_SHIFT_CONST(29) +#define RES_SEMA_SHRD_INBOX_0_TAG_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_INBOX_0_TAG_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_TAG_RANGE 29:29 +#define RES_SEMA_SHRD_INBOX_0_TAG_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_TAG_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_TAG_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_INBOX_0_TAG_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_TAG_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_TAG_INVALID _MK_ENUM_CONST(0) +#define RES_SEMA_SHRD_INBOX_0_TAG_VALID _MK_ENUM_CONST(1) + +// Reserved = 0 +#define RES_SEMA_SHRD_INBOX_0_N_A1_SHIFT _MK_SHIFT_CONST(28) +#define RES_SEMA_SHRD_INBOX_0_N_A1_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_INBOX_0_N_A1_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_N_A1_RANGE 28:28 +#define RES_SEMA_SHRD_INBOX_0_N_A1_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_N_A1_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_N_A1_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_INBOX_0_N_A1_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_N_A1_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// definition) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_SHIFT _MK_SHIFT_CONST(24) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_FIELD (_MK_MASK_CONST(0xf) << RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_RANGE 27:24 +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_DEFAULT_MASK _MK_MASK_CONST(0xf) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_STAT_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// General purpose data bits, suggested usage is for INBOX command (SW can change definition) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_SHIFT _MK_SHIFT_CONST(17) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_FIELD (_MK_MASK_CONST(0x7f) << RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_RANGE 23:17 +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_DEFAULT_MASK _MK_MASK_CONST(0x7f) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_CMD_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// definition) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_SHIFT _MK_SHIFT_CONST(0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_FIELD (_MK_MASK_CONST(0x1ffff) << RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_SHIFT) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_RANGE 16:0 +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_WOFFSET 0x0 +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_DEFAULT_MASK _MK_MASK_CONST(0x1ffff) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_INBOX_0_IN_BOX_DATA_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + + +// Reserved address 20 [0x14] + +// Reserved address 24 [0x18] + +// Reserved address 28 [0x1c] +// Shared Resource Outbox (messages from CPU to COP) + +// Register RES_SEMA_SHRD_OUTBOX_0 +#define RES_SEMA_SHRD_OUTBOX_0 _MK_ADDR_CONST(0x20) +#define RES_SEMA_SHRD_OUTBOX_0_WORD_COUNT 0x1 +#define RES_SEMA_SHRD_OUTBOX_0_RESET_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_RESET_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_OUTBOX_0_SW_DEFAULT_VAL _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_READ_MASK _MK_MASK_CONST(0xffffffff) +#define RES_SEMA_SHRD_OUTBOX_0_WRITE_MASK _MK_MASK_CONST(0x0) +// Interrupt COP on OUTBOX Full (TAG=1) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_SHIFT _MK_SHIFT_CONST(31) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_OUTBOX_0_IE_OBF_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_RANGE 31:31 +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_EMPTY _MK_ENUM_CONST(0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBF_FULL _MK_ENUM_CONST(1) + +// Interrupt CPU on OUTBOX Empty (TAG=0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_SHIFT _MK_SHIFT_CONST(30) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_OUTBOX_0_IE_OBE_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_RANGE 30:30 +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_EMPTY _MK_ENUM_CONST(0) +#define RES_SEMA_SHRD_OUTBOX_0_IE_OBE_FULL _MK_ENUM_CONST(1) + +// HW clears this bit when COP Reads the Outbox Register. Read-only. Set when CPU writes this register and cleared when COP reads this register. +#define RES_SEMA_SHRD_OUTBOX_0_TAG_SHIFT _MK_SHIFT_CONST(29) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_OUTBOX_0_TAG_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_RANGE 29:29 +#define RES_SEMA_SHRD_OUTBOX_0_TAG_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_TAG_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_INVALID _MK_ENUM_CONST(0) +#define RES_SEMA_SHRD_OUTBOX_0_TAG_VALID _MK_ENUM_CONST(1) + +// Reserved = 0 +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_SHIFT _MK_SHIFT_CONST(28) +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_FIELD (_MK_MASK_CONST(0x1) << RES_SEMA_SHRD_OUTBOX_0_N_A1_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_RANGE 28:28 +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_DEFAULT_MASK _MK_MASK_CONST(0x1) +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_N_A1_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// General purpose data bits, suggested usage is for Out Box OUTBOX status (SW can change definition) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_SHIFT _MK_SHIFT_CONST(24) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_FIELD (_MK_MASK_CONST(0xf) << RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_RANGE 27:24 +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_DEFAULT_MASK _MK_MASK_CONST(0xf) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_STAT_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// General purpose data bits, suggested usage is for Out Box OUTBOX command (SW can change definition) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_SHIFT _MK_SHIFT_CONST(17) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_FIELD (_MK_MASK_CONST(0x7f) << RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_RANGE 23:17 +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_DEFAULT_MASK _MK_MASK_CONST(0x7f) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_CMD_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + +// General purpose Out Box data bits, suggested usage is for OUTBOX data (SW can change definition) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_SHIFT _MK_SHIFT_CONST(0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_FIELD (_MK_MASK_CONST(0x1ffff) << RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_SHIFT) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_RANGE 16:0 +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_WOFFSET 0x0 +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_DEFAULT_MASK _MK_MASK_CONST(0x1ffff) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_SW_DEFAULT _MK_MASK_CONST(0x0) +#define RES_SEMA_SHRD_OUTBOX_0_OUT_BOX_DATA_SW_DEFAULT_MASK _MK_MASK_CONST(0x0) + + +// +// REGISTER LIST +// +#define LIST_ARRES_SEMA_REGS(_op_) \ +_op_(RES_SEMA_SHRD_SMP_STA_0) \ +_op_(RES_SEMA_SHRD_SMP_SET_0) \ +_op_(RES_SEMA_SHRD_SMP_CLR_0) \ +_op_(RES_SEMA_SHRD_INBOX_0) \ +_op_(RES_SEMA_SHRD_OUTBOX_0) + + +// +// ADDRESS SPACES +// + +#define BASE_ADDRESS_RES_SEMA 0x00000000 + +// +// ARRES_SEMA REGISTER BANKS +// + +#define RES_SEMA0_FIRST_REG 0x0000 // RES_SEMA_SHRD_SMP_STA_0 +#define RES_SEMA0_LAST_REG 0x0008 // RES_SEMA_SHRD_SMP_CLR_0 +#define RES_SEMA1_FIRST_REG 0x0010 // RES_SEMA_SHRD_INBOX_0 +#define RES_SEMA1_LAST_REG 0x0010 // RES_SEMA_SHRD_INBOX_0 +#define RES_SEMA2_FIRST_REG 0x0020 // RES_SEMA_SHRD_OUTBOX_0 +#define RES_SEMA2_LAST_REG 0x0020 // RES_SEMA_SHRD_OUTBOX_0 + +#ifndef _MK_SHIFT_CONST + #define _MK_SHIFT_CONST(_constant_) _constant_ +#endif +#ifndef _MK_MASK_CONST + #define _MK_MASK_CONST(_constant_) _constant_ +#endif +#ifndef _MK_ENUM_CONST + #define _MK_ENUM_CONST(_constant_) (_constant_ ## UL) +#endif +#ifndef _MK_ADDR_CONST + #define _MK_ADDR_CONST(_constant_) _constant_ +#endif + +#endif // ifndef ___ARRES_SEMA_H_INC_ diff --git a/arch/arm/mach-tegra/include/avp.h b/arch/arm/mach-tegra/include/avp.h new file mode 100644 index 000000000000..249a28cd44db --- /dev/null +++ b/arch/arm/mach-tegra/include/avp.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_AVP_H +#define INCLUDED_AVP_H + +#include "ap15/arictlr.h" +#include "ap15/artimer.h" +// FIXME: get the ararmev header + +// 3 controllers in contiguous memory starting at INTERRUPT_BASE, each +// controller's aperture is INTERRUPT_SIZE large +#define INTERRUPT_BASE 0x60004000 +#define INTERRUPT_SIZE 0x100 +#define INTERRUPT_NUM_CONTROLLERS 3 + +#define INTERRUPT_PENDING( ctlr ) \ + (INTERRUPT_BASE + ((ctlr) * INTERRUPT_SIZE) + ICTLR_VIRQ_COP_0) + +#define INTERRUPT_SET( ctlr ) \ + (INTERRUPT_BASE + ((ctlr) * INTERRUPT_SIZE) + ICTLR_COP_IER_SET_0) + +#define INTERRUPT_CLR( ctlr ) \ + (INTERRUPT_BASE + ((ctlr) * INTERRUPT_SIZE) + ICTLR_COP_IER_CLR_0) + +#define OSC_CTRL ( 0x60006000 + 0x50 ) +#define OSC_FREQ_DET ( 0x60006000 + 0x58 ) +#define OSC_DET_STATUS ( 0x60006000 + 0x5C ) + +#define TIMER_USEC ( 0x60005010 ) +#define TIMER_CFG ( 0x60005014 ) +#define TIMER_0_BASE ( 0x60005000 ) +#define TIMER_0 ( TIMER_0_BASE + TIMER_TMR_PTV_0 ) +#define TIMER_0_CLEAR ( TIMER_0_BASE + TIMER_TMR_PCR_0 ) +#define TIMER_1_BASE ( 0x60005008 ) +#define TIMER_1 ( TIMER_1_BASE + TIMER_TMR_PTV_0 ) +#define TIMER_1_CLEAR ( TIMER_1_BASE + TIMER_TMR_PCR_0 ) + +#define CLOCK_RST_LO (0x60006004) +#define CLOCK_CTLR_HI (0x60006014) +#define CLOCK_CTLR_LO (0x60006010) + +#define CACHE_CTLR (0x6000C000) +#define CACHE_CONTROL_0 (0x0) + +#define PPI_INTR_ID_TIMER_0 (0) +#define PPI_INTR_ID_TIMER_1 (1) +#define PPI_INTR_ID_TIMER_2 (9) +#define PPI_INTR_ID_TIMER_3 (10) + +/* flow controller */ +#define FLOW_CONTROLLER (0x60007004) + +/* exception vectors */ +#define VECTOR_BASE ( 0x6000F200 ) +#define VECTOR_RESET ( VECTOR_BASE + 0 ) +#define VECTOR_UNDEF ( VECTOR_BASE + 4 ) +#define VECTOR_SWI ( VECTOR_BASE + 8 ) +#define VECTOR_PREFETCH_ABORT ( VECTOR_BASE + 12 ) +#define VECTOR_DATA_ABORT ( VECTOR_BASE + 16 ) +#define VECTOR_IRQ ( VECTOR_BASE + 24 ) +#define VECTOR_FIQ ( VECTOR_BASE + 28 ) + +#define MODE_DISABLE_INTR 0xc0 +#define MODE_USR 0x10 +#define MODE_FIQ 0x11 +#define MODE_IRQ 0x12 +#define MODE_SVC 0x13 +#define MODE_ABT 0x17 +#define MODE_UND 0x1B +#define MODE_SYS 0x1F + +#define AP15_CACHE_LINE_SIZE 32 + +#define AP15_APB_L2_CACHE_BASE 0x7000e800 +#define AP15_APB_CLK_RST_BASE 0x60006000 +#define AP15_APB_MISC_BASE 0x70000000 + +#define AP10_APB_CLK_RST_BASE 0x60006000 +#define AP10_APB_MISC_BASE 0x70000000 + +#define MMU_TLB_BASE 0xf000f000 +#define MMU_TLB_CACHE_WINDOW_0 0x40 +#define MMU_TLB_CACHE_OPTIONS_0 0x44 + +#define AP15_PINMUX_CFG_CTL_0 0x70000024 +#define AP15_AVP_JTAG_ENABLE 0xC0 + +#define PMC_SCRATCH22_REG_LP0 0x7000e4a8 + +#define AVP_WDT_RESET 0x2F00BAD0 + +/* Cached to uncached offset for AVP + * + * Hardware has uncached remap aperture for AVP as AVP doesn't have MMU + * but still has cache (named COP cache). + * + * This aperture moved between AP15 and AP20. + */ +#define AP15_CACHED_TO_UNCACHED_OFFSET 0x90000000 +#define AP20_CACHED_TO_UNCACHED_OFFSET 0x80000000 + +#define APXX_EXT_MEM_START 0x00000000 +#define APXX_EXT_MEM_END 0x40000000 + +#define APXX_MMIO_START 0x40000000 +#define APXX_MMIO_END 0xFFF00000 + +#define TXX_EXT_MEM_START 0x80000000 +#define TXX_EXT_MEM_END 0xc0000000 + +#define TXX_MMIO_START 0x40000000 +#define TXX_MMIO_END 0x80000000 + +#endif diff --git a/arch/arm/mach-tegra/include/mach/nvrm_linux.h b/arch/arm/mach-tegra/include/mach/nvrm_linux.h index 035de018f01d..cdf00221dc48 100644 --- a/arch/arm/mach-tegra/include/mach/nvrm_linux.h +++ b/arch/arm/mach-tegra/include/mach/nvrm_linux.h @@ -48,6 +48,7 @@ #include <nvrm_init.h> #include <nvrm_i2c.h> +#include <nvrm_owr.h> #include <nvrm_gpio.h> #include <nvodm_query_pinmux.h> #include <nvodm_query.h> diff --git a/arch/arm/mach-tegra/include/nvodm_query_pinmux.h b/arch/arm/mach-tegra/include/nvodm_query_pinmux.h index c0b2e48cb134..5365201b8457 100644 --- a/arch/arm/mach-tegra/include/nvodm_query_pinmux.h +++ b/arch/arm/mach-tegra/include/nvodm_query_pinmux.h @@ -194,6 +194,27 @@ typedef enum } NvOdmHsmmcPinMap; /** + * Defines the OWR pin-mux configurations. + */ +typedef enum +{ + NvOdmOwrPinMap_Config1 = 1, + NvOdmOwrPinMap_Config2, + NvOdmOwrPinMap_Config3, + + /** + * This configuration disables (tristates) OWR pins. This option may be + * used to change which pins an attached OWR device is using at runtime. + * In some cases, one device might set up OWR, communicate across this bus, + * and then set the OWR bus configuration to "multiplexed" so that another + * device can opt to use OWR with its own configurations at a later time. + */ + NvOdmOwrPinMap_Multiplexed = NVODM_QUERY_PINMAP_MULTIPLEXED, + /** Ignore -- Forces compilers to make 32-bit enums. */ + NvOdmOwrPinMap_Force32 = 0x7FFFFFFF, +} NvOdmOwrPinMap; + +/** * Defines I2C pin-mux configurations. */ typedef enum diff --git a/arch/arm/mach-tegra/include/nvrm_avp_shrd_interrupt.h b/arch/arm/mach-tegra/include/nvrm_avp_shrd_interrupt.h new file mode 100644 index 000000000000..579aab3c240d --- /dev/null +++ b/arch/arm/mach-tegra/include/nvrm_avp_shrd_interrupt.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_nvrm_avp_shrd_interrupt_H +#define INCLUDED_nvrm_avp_shrd_interrupt_H + +#include "nvos.h" +#include "nvrm_init.h" +#include "nvrm_module.h" +#include "nvrm_interrupt.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* Max number of clients with shared interrupt handler */ +enum {MAX_SHRDINT_CLIENTS = 32}; + +/* Now AP15 support only VDE interrupts 6 */ +enum {MAX_SHRDINT_INTERRUPTS = 6}; + /* VDE Sync Token Interrupt */ +enum {AP15_SYNC_TOKEN_INTERRUPT_INDEX = 0}; + /* VDE BSE-V Interrupt */ +enum {AP15_BSE_V_INTERRUPT_INDEX = 1}; + /* VDE BSE-A Interrupt */ +enum {AP15_BSE_A_INTERRUPT_INDEX = 2}; + /* VDE SXE Interrupt */ +enum {AP15_SXE_INTERRUPT_INDEX = 3}; + /* VDE UCQ Error Interrupt */ +enum {AP15_UCQ_INTERRUPT_INDEX = 4}; + /* VDE Interrupt */ +enum {AP15_VDE_INTERRUPT_INDEX = 5}; + +/* Now AP20 support only VDE interrupts 5 */ +enum {AP20_MAX_SHRDINT_INTERRUPTS = 5}; + /* VDE Sync Token Interrupt */ +enum {AP20_SYNC_TOKEN_INTERRUPT_INDEX = 0}; + /* VDE BSE-V Interrupt */ +enum {AP20_BSE_V_INTERRUPT_INDEX = 1}; + /* VDE SXE Interrupt */ +enum {AP20_SXE_INTERRUPT_INDEX = 2}; + /* VDE UCQ Error Interrupt */ +enum {AP20_UCQ_INTERRUPT_INDEX = 3}; + /* VDE Interrupt */ +enum {AP20_VDE_INTERRUPT_INDEX = 4}; + +enum +{ + NvRmArbSema_Vde = 0, + NvRmArbSema_Bsea, + //This should be last + NvRmArbSema_Num, +}; + +/* Shared interrupt private init , init done during RM init on AVP */ +NvError NvRmAvpShrdInterruptPrvInit(NvRmDeviceHandle hRmDevice); + +/* Shared interrupt private de-init , de-init done during RM close on AVP */ +void NvRmAvpShrdInterruptPrvDeinit(NvRmDeviceHandle hRmDevice); + +/* Get logical interrupt for a module*/ +NvU32 NvRmAvpShrdInterruptGetIrqForLogicalInterrupt(NvRmDeviceHandle hRmDevice, + NvRmModuleID ModuleID, + NvU32 Index); +/* Register for shared interrpt */ +NvError NvRmAvpShrdInterruptRegister(NvRmDeviceHandle hRmDevice, + NvU32 IrqListSize, + const NvU32 *pIrqList, + const NvOsInterruptHandler *pIrqHandlerList, + void *pContext, + NvOsInterruptHandle *handle, + NvU32 *ClientIndex); +/* Un-register a shared interrpt */ +void NvRmAvpShrdInterruptUnregister(NvRmDeviceHandle hRmDevice, + NvOsInterruptHandle handle, + NvU32 ClientIndex); +/* Get exclisive access to a hardware(VDE) block */ +NvError NvRmAvpShrdInterruptAquireHwBlock(NvRmModuleID ModuleID, NvU32 ClientId); + +/* Release exclisive access to a hardware(VDE) block */ +NvError NvRmAvpShrdInterruptReleaseHwBlock(NvRmModuleID ModuleID, NvU32 ClientId); + +#if defined(__cplusplus) +} +#endif + +#endif //INCLUDED_nvrm_avp_shrd_interrupt_H diff --git a/arch/arm/mach-tegra/include/nvrm_transport.h b/arch/arm/mach-tegra/include/nvrm_transport.h new file mode 100644 index 000000000000..179f63223ed5 --- /dev/null +++ b/arch/arm/mach-tegra/include/nvrm_transport.h @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_nvrm_transport_H +#define INCLUDED_nvrm_transport_H + + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#include "nvrm_module.h" +#include "nvrm_init.h" + +/** @file + * @brief <b>NVIDIA Driver Development Kit: + * Resource Manager Transport APIs</b> + * + * @b Description: This is the Transport API, which defines a simple means to + * pass messages across a lower level connection (generally between + * processors). + * + */ + +/** @defgroup nvrm_transport RM Transport API + * + * The Transport API defines a simple protocol through which clients and + * services may connect and communicate--normally, though not necessarily, + * across separate processors. Clients to this interface mostly include + * audio-visual applications whose code may reside on either the MPCore or AVP + * processors. These applications (and there could be many concurrently) may + * utilize this transport API to synchronize their operations. How the + * Transport API shepherds messages through these connections is not visible to + * the client. + * + * To setup a new connection, both the client and the service must open a port + * (whose name is agreed upon before compile-time). The service waits for a + * client to connect; this "handshake" allows a connection to be established. + * Once a client has established a connection with the service, they may send + * and receive messages. + * + * @ingroup nvddk_rm + * @{ + */ + +#include "nvos.h" + +/** + * A type-safe handle for the transport connection. + */ + +typedef struct NvRmTransportRec *NvRmTransportHandle; + +/** + * Creates one end of a transport connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port (whose name is agreed upon before compile-time). + * A connection is not established between the service and client until a + * handshake is completed (via calls to NvRmTransportWaitForConnect() and + * NvRmTransportConnect() respectively). + * + * Assert in debug mode encountered if PortName is too long or does not exist + * + * @see NvRmTransportWaitForConnect() + * @see NvRmTransportConnect() + * @see NvRmTransportClose() + * + * @param hRmDevice Handle to RM device + * @param pPortName A character string that identifies the name of the port. + * This value must be 16 bytes or less, otherwise the caller receives an error. + * You can optionally pass NULL for this parameter, in which case a unique + * name will be assigned. And you can call NvRmTransporGetPortName to retrieve + * the name. + * @param RecvMessageSemaphore The externally created semaphore that the + * transport connection will signal upon receipt of a message. + * @param phTransport Points to the location where the transport handle shall + * be stored + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + + NvError NvRmTransportOpen( + NvRmDeviceHandle hRmDevice, + char * pPortName, + NvOsSemaphoreHandle RecvMessageSemaphore, + NvRmTransportHandle * phTransport ); + +/** + * Retrieve the name associated with a port. + * + * Assert in debug mode encountered if PortName is too long or does not exist + * + * @see NvRmTransportOpen() + * + * @param hTransport Handle to the port that you want the name of. + * @param PortName A character string that identifies the name of the port. + * @param PortNameSize Length of the PortName buffer. + * + */ + + void NvRmTransportGetPortName( + NvRmTransportHandle hTransport, + NvU8 * PortName, + NvU32 PortNameSize ); + +/** + * Closes a transport connection. Proper closure of this connection requires + * that both the client and service call this API. Therefore, it is expected + * that the client and service message one another to coordinate the close. + * + * @see NvRmTransportOpen() + * + * @param hTransport Specifies the transport connection to close. If hTransport + * is NULL, this API does nothing. + */ + + void NvRmTransportClose( + NvRmTransportHandle hTransport ); + +/** + * Initializes the transport. + * + * @param hRmDevice Handle to RM device + * + */ + + NvError NvRmTransportInit( + NvRmDeviceHandle hRmDevice ); + +/** + * Deinitializes the transport. + * + * @param hRmDevice Handle to RM device + * + */ + + void NvRmTransportDeInit( + NvRmDeviceHandle hRmDevice ); + +/** + * This handshake API is called by the service, which waits for a client to + * establish a connection via a call to NvRmTransportConnect(). Messages + * cannot be sent and received until this handshake is completed. + * + * To ensure a client has sufficient opportunity to establish a connection + * from the other end, a large timeout value (such as NV_WAIT_INFINITE) is + * recommended here. + * + * @see NvRmTransportConnect() + * + * @param hTransport Specifies the transport connection + * @param TimeoutMS Specifies the amount of time (in milliseconds) to wait for + * connection to be established. A value of NV_WAIT_INFINITE means "wait + * indefinitely." A value of zero (0) will timeout immediately, which is + * not recommended for this function call. + * + * @retval NvSuccess Service is waiting to receive a "connect" from client + * @retval NvError_NotInitialized hTransport is not open + * @retval NvError_Timeout Timed out waiting for service to respond + */ + + NvError NvRmTransportWaitForConnect( + NvRmTransportHandle hTransport, + NvU32 TimeoutMS ); + +/** + * This blocking handshake API is called by the client, which seeks a + * service (as specified by a handle) to establish a connection. Messages + * cannot be sent and received until this handshake is completed. + * + * @see NvRmTransportWaitForConnect() + * + * @param hTransport Specifies the transport connection + * @param TimeoutMS Specifies the amount of time (in milliseconds) to wait for + * connection to be established. A value of NV_WAIT_INFINITE means "wait + * indefinitely." A value of zero (0) will timeout immediately, but + * this function will at least take time to check if the port is open and + * waiting for a connection--if so, a connection will be established. + * + * @retval NvSuccess Transport connection successfully established + * @retval NvError_NotInitialized hTransport is not open + * @retval NvError_Timeout Timed out waiting for service to respond. + */ + + NvError NvRmTransportConnect( + NvRmTransportHandle hTransport, + NvU32 TimeoutMS ); + +/** + * Set the max size of the message queue (FIFO) deptha nd length which can be + * send and receive from this port. The programmer must decide the + * queue depth that's appropriate for their design. If this function is not + * called, the queue depth is set to one (1) and message size is 256 bytes. + * + * + * @see NvRmTransportSendMsg() + * @see NvRmTransportRecvMsg() + * + * @param hTransport Specifies the transport connection + * @param MaxQueueDepth The maximum number of message which can be queued for + * this port for receiving and sending. The receive message can queue message + * till this count for this port. If receive queue is full for this port and + * if other port send the message to this port then receive queue error status + * will turn as overrun and ignore the incoming message. + * If send message queue is full and client request to send message then he + * will wait for time provided by the parameter. + * @param MaxMessageSize Specifies the maximum size of the message in bytes + * which client can receive and transmit through this port. + * + * @retval NvSuccess New queue depth is set + * @retval NvError_NotInitialized hTransport is not open. + * @retval NvError_BadValue The parameter passed is not correct. There is + * limitation for maximum message q and message length from the driver and if + * this parameter is larger than those value then it returns this error. + * + */ + + NvError NvRmTransportSetQueueDepth( + NvRmTransportHandle hTransport, + NvU32 MaxQueueDepth, + NvU32 MaxMessageSize ); + +/** + * Sends a message to the other port which is connected to this port. + * This will use the copy method to copy the client buffer message to + * transport message buffer. This function queue the message to the transmit + * queue. the data will be send later based on the physical transfer channel + * availablity. + * + * @see NvRmTransportOpen() + * @see NvRmTransportSetQueueDepth() + * @see NvRmTransportRecvMsg() + * + * @param hTransport Specifies the transport connection + * @param pMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * @param TimeoutMS Specifies the amount of time (in milliseconds) to wait for + * sent message to be queued for the transfer. If the transmit queue if full + * then this function will block the client till maximum of timeout to queue + * this message. If meesage queue is available before timeout then it will + * queue the message and comeout. If message queue is full and timeout happen + * the it will return the timeout error. + * if zero timeout is selecetd and the message queue is full then it will be + * return NvError_TransportMessageBoxFull error. + * Avalue of NV_WAIT_INFINITE means "wait indefinitely" for queueing the + * message. + * + * @retval NvSuccess Message is queued successfully. + * @retval NvError_NotInitialized hTransport is not open. + * @retval NvError_BadValue The parameter passed is not valid. + * @retval NvError_InvalidState The port is not connected to the other port and + * it is not ready for sending the message. + * @retval NvError_Timeout Timed out waiting for message to be queue if send + * message queue. + * @retval NvError_TransportMessageBoxFull Message box is full and it is not + * able to queue the message. + */ + + NvError NvRmTransportSendMsg( + NvRmTransportHandle hTransport, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS ); + +/** + * Sends a message to the other port which is connected to this port. + * This function is to be used ONLY when we're about to enter LP0! + * There is no synchronization in this function as only one person + * should be talking to the AVP at the time of LP0. The message is sent + * on the RPC_AVP_PORT. In the future, there might be instances where + * we need to talk on a different port in LP0. + * + * @retval NvSuccess Message is queued successfully. + * @retval NvError_TransportMessageBoxFull Message box is full and it is not + * able to queue the message. + */ + + NvError NvRmTransportSendMsgInLP0( + NvRmTransportHandle hPort, + void* message, + NvU32 MessageSize ); + +/** + * Receive the message from the port. This will read the message if it is + * available for this port otherwise it will return the + * NvError_TransportMessageBoxEmpty error. + * + * @see NvRmTransportOpen() + * @see NvRmTransportSetQueueDepth() + * @see NvRmTransportSendMsg() + * + * @param hTransport Specifies the transport connection + * @param pMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param MaxSize The maximum size in bytes that may be copied to the buffer + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * + * @retval NvSuccess Message received successfully. + * @retval NvError_NotInitialized hTransport is not open. + * @retval NvError_InvalidState The port is not connection state. + * @retval NvError_TransportMessageBoxEmpty The message box empty and not able + * to receive the message. + * @retval NvError_TransportIncompleteMessage The received message for this + * port is longer than the configured message length for this port. It copied + * the maximm size of the configured length of the message for this port and + * return the incomplete message buffer. + * @retval NvError_TransportMessageOverflow The port receives the message more + * than the configured queue depth count for this port and hence message + * overflow has been ocuured. + */ + + NvError NvRmTransportRecvMsg( + NvRmTransportHandle hTransport, + void* pMessageBuffer, + NvU32 MaxSize, + NvU32 * pMessageSize ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/arch/arm/mach-tegra/include/nvrm_xpc.h b/arch/arm/mach-tegra/include/nvrm_xpc.h new file mode 100644 index 000000000000..69b61d8d1147 --- /dev/null +++ b/arch/arm/mach-tegra/include/nvrm_xpc.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_nvrm_xpc_H +#define INCLUDED_nvrm_xpc_H + + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#include "nvrm_module.h" +#include "nvrm_init.h" + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "nvrm_interrupt.h" + +/** + * @brief 16 Byte allignment for the shared memory message transfer. + */ + +typedef enum +{ + XPC_MESSAGE_ALIGNMENT_SIZE = 0x10, + Xpc_Alignment_Num, + Xpc_Alignment_Force32 = 0x7FFFFFFF +} Xpc_Alignment; + +/** + * NvRmPrivXpcMessageHandle is an opaque handle to NvRmPrivXpcMessage. + * + * @ingroup nvrm_xpc + */ + +typedef struct NvRmPrivXpcMessageRec *NvRmPrivXpcMessageHandle; + +/** + * Create the xpc message handles for sending/receiving the message to/from + * target processor. + * This function allocates the memory (from multiprocessor shared memory + * region) and os resources for the message transfer and synchrnoisation. + * + * @see NvRmPrivXpcSendMessage() + * @see NvRmPrivXpcGetMessage() + * + * @param hDevice Handle to the Rm device which is required by Ddk to acquire + * the resources from RM. + * @param phXpcMessage Pointer to the handle to Xpc message where created + * Xpc message handle is stored. + * + * @retval NvSuccess Indicates the message queue is successfully created. + * @retval NvError_BadValue The parameter passed are incorrect. + * @retval NvError_InsufficientMemory Indicates that function fails to allocate the + * memory for message queue. + * @retval NvError_MemoryMapFailed Indicates that the memory mapping for xpc + * controller register failed. + * @retval NvError_NotSupported Indicates that the requested operation is not + * supported for the given target processor/Instance. + * + */ + + NvError NvRmPrivXpcCreate( + NvRmDeviceHandle hDevice, + NvRmPrivXpcMessageHandle * phXpcMessage ); + +/** + * Destroy the created Xpc message handle. This frees all the resources + * allocated for the xpc message handle. + * + * @note After calling this function client will not able to send/receive any + * message. + * + * @see NvRmPrivXpcMessageCreate() + * + * @param hXpcMessage Xpc message queue handle which need to be destroy. + * This cas created when function NvRmPrivXpcMessageCreate() was called. + * + */ + + void NvRmPrivXpcDestroy( + NvRmPrivXpcMessageHandle hXpcMessage ); + + NvError NvRmPrivXpcSendMessage( + NvRmPrivXpcMessageHandle hXpcMessage, + NvU32 data ); + + NvU32 NvRmPrivXpcGetMessage( + NvRmPrivXpcMessageHandle hXpcMessage ); + +/** + * Initializes the Arbitration semaphore system for cross processor synchronization. + * + * @param hDevice The RM handle. + * + * @retval "NvError_IrqRegistrationFailed" if interupt is already registred. + * @retval "NvSuccess" if successfull. + */ + + NvError NvRmXpcInitArbSemaSystem( + NvRmDeviceHandle hDevice ); + +/** + * Tries to obtain a hw arbitration semaphore. This API is used to + * synchronize access to hw blocks across processors. + * + * @param modId The module that we need to cross-processor safe access to. + */ + + void NvRmXpcModuleAcquire( + NvRmModuleID modId ); + +/** + * Releases the arbitration semaphore corresponding to the given module id. + * + * @param modId The module that we are releasing. + */ + + void NvRmXpcModuleRelease( + NvRmModuleID modId ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/arch/arm/mach-tegra/init_common.c b/arch/arm/mach-tegra/init_common.c index 97809fc0d086..b054a1bf40af 100755 --- a/arch/arm/mach-tegra/init_common.c +++ b/arch/arm/mach-tegra/init_common.c @@ -256,6 +256,42 @@ static void __init tegra_register_spi(void) } #endif +#if !defined(CONFIG_W1_MASTER_TEGRA) +#define tegra_register_w1() do { } while (0) +#else +static void __init tegra_register_w1(void) +{ + const NvU32 *pPinMuxes; + NvU32 NumPinMuxes, NumModules; + struct platform_device *pDev; + struct tegra_w1_platform_data W1Data; + NvU32 i; + + NumModules = NvRmModuleGetNumInstances(s_hRmGlobal, NvOdmIoModule_OneWire); + NvOdmQueryPinMux(NvOdmIoModule_OneWire, &pPinMuxes, &NumPinMuxes); + + for (i=0; i < NumModules && i < NumPinMuxes; i++) + { + if (!pPinMuxes[i]) + continue; + + pDev = platform_device_alloc("tegra_w1", i); + if (!pDev) + goto fail; + W1Data.Instance = i; + W1Data.PinMuxConfig = pPinMuxes[i]; + if (platform_device_add_data(pDev, &W1Data, sizeof(W1Data))) + goto fail; + if (platform_device_add(pDev)) + goto fail; + } + return; +fail: + if (pDev) + platform_device_del(pDev); +} +#endif + #if !defined(CONFIG_I2C_TEGRA) #define tegra_register_i2c() do { } while (0) #else @@ -873,6 +909,7 @@ void __init tegra_common_init(void) tegra_register_uart(); tegra_register_sdio(); tegra_register_usb(); + tegra_register_w1(); #ifdef CONFIG_PM /* FIXME : Uncomment this for actual suspend/resume tegra_set_suspend_ops(); */ diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/Makefile b/arch/arm/mach-tegra/nvrm/core/ap15/Makefile index 34a7465bd6f9..85675a55c0fa 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap15/Makefile +++ b/arch/arm/mach-tegra/nvrm/core/ap15/Makefile @@ -32,3 +32,5 @@ obj-y += ap16rm_reloctable.o obj-y += ap15rm_init.o obj-y += ap15rm_init_common.o obj-y += ap15rm_interrupt.o +obj-y += ap15rm_xpc.o +obj-y += ap15rm_xpc_hw_private.o diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c index c5f849c6da98..9392cdab7679 100644 --- a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_init.c @@ -42,6 +42,7 @@ #include "nvrm_heap.h" #include "nvrm_pmu_private.h" #include "nvrm_processor.h" +#include "nvrm_xpc.h" #include "ap15rm_private.h" #include "nvrm_structure.h" #include "ap15rm_private.h" @@ -428,6 +429,11 @@ NvRmOpenNew(NvRmDeviceHandle *pHandle) } } } + err = NvRmXpcInitArbSemaSystem(rm); + if( err != NvSuccess ) + { + goto fail; + } /* assign the handle pointer */ *pHandle = rm; @@ -730,3 +736,4 @@ void NvRmPrivMcErrorMonitorStop( NvRmDeviceHandle rm ) } } + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c new file mode 100644 index 000000000000..8b92ae4b5933 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief <b>nVIDIA Driver Development Kit: + * Cross Proc Communication driver </b> + * + * @b Description: Implements the interface to the NvDdk XPC. + * + */ + +#include "nvrm_xpc.h" +#include "nvrm_memmgr.h" +#include "ap15rm_xpc_hw_private.h" +#include "nvrm_hardware_access.h" +#include "nvassert.h" +#include "ap15/ararb_sema.h" +#include "ap15/arictlr_arbgnt.h" +#include "nvrm_avp_shrd_interrupt.h" + +// Minimum sdram offset required so that avp can access the address which is +// passed. +// AVP can not access the 0x0000:0000 to 0x0000:0040 +enum { MIN_SDRAM_OFFSET = 0x100}; + + +//There are only 32 arb semaphores +#define MAX_ARB_NUM 32 + +#define ARBSEMA_REG_READ(pArbSemaVirtAdd, reg) \ + NV_READ32(pArbSemaVirtAdd + (ARB_SEMA_##reg##_0)) + +#define ARBSEMA_REG_WRITE(pArbSemaVirtAdd, reg, data) \ + NV_WRITE32(pArbSemaVirtAdd + (ARB_SEMA_##reg##_0), (data)); + +#define ARBGNT_REG_READ(pArbGntVirtAdd, reg) \ + NV_READ32(pArbGntVirtAdd + (ARBGNT_##reg##_0)) + +#define ARBGNT_REG_WRITE(pArbGntVirtAdd, reg, data) \ + NV_WRITE32(pArbGntVirtAdd + (ARBGNT_##reg##_0), (data)); + +static NvOsInterruptHandle s_arbInterruptHandle = NULL; + +// Combines the Processor Xpc system details. This contains the details of the +// receive/send message queue and messaging system. +typedef struct NvRmPrivXpcMessageRec +{ + NvRmDeviceHandle hDevice; + + // Hw mail box register. + CpuAvpHwMailBoxReg HwMailBoxReg; + +} NvRmPrivXpcMessage; + +typedef struct NvRmPrivXpcArbSemaRec +{ + NvRmDeviceHandle hDevice; + NvU8 *pArbSemaVirtAddr; + NvU8 *pArbGntVirtAddr; + NvOsSemaphoreHandle semaphore[MAX_ARB_NUM]; + NvOsMutexHandle mutex[MAX_ARB_NUM]; + NvOsIntrMutexHandle hIntrMutex; + +} NvRmPrivXpcArbSema; + +static NvRmPrivXpcArbSema s_ArbSema; + +//Forward declarations +static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice); +static void ArbSemaIsr(void *args); +NvU32 GetArbIdFromRmModuleId(NvRmModuleID modId); +/** + * Initialize the cpu avp hw mail box address and map the hw register address + * to virtual address. + * Thread Safety: Caller responsibility + */ +static NvError +InitializeCpuAvpHwMailBoxRegister(NvRmPrivXpcMessageHandle hXpcMessage) +{ + NvError e; + NvRmPhysAddr ResourceSemaPhysAddr; + + // Get base address of the hw mail box register. This register is in the set + // of resource semaphore module Id. + NvRmModuleGetBaseAddress(hXpcMessage->hDevice, + NVRM_MODULE_ID(NvRmModuleID_ResourceSema, 0), + &ResourceSemaPhysAddr, &hXpcMessage->HwMailBoxReg.BankSize); + + // Map the base address to the virtual address. + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; + NV_CHECK_ERROR(NvRmPhysicalMemMap( + ResourceSemaPhysAddr, hXpcMessage->HwMailBoxReg.BankSize, + NVOS_MEM_READ_WRITE, NvOsMemAttribute_Uncached, + (void **)&hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr)); + + NvRmPrivXpcHwResetOutbox(&hXpcMessage->HwMailBoxReg); + + return NvSuccess; +} + +/** + * DeInitialize the cpu avp hw mail box address and unmap the hw register address + * virtual address. + * Thread Safety: Caller responsibility + */ +static void DeInitializeCpuAvpHwMailBoxRegister(NvRmPrivXpcMessageHandle hXpcMessage) +{ + // Unmap the hw register base virtual address + NvRmPhysicalMemUnmap(hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr, + hXpcMessage->HwMailBoxReg.BankSize); + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; +} + +/** + * Create the cpu-avp messaging system. + * This function will call other helper function to create the messaging technique + * used for cpu-avp communication. + * Thread Safety: Caller responsibility + */ +static NvError +CreateCpuAvpMessagingSystem(NvRmPrivXpcMessageHandle hXpcMessage) +{ + NvError Error = NvSuccess; + + Error = InitializeCpuAvpHwMailBoxRegister(hXpcMessage); + +#if NV_IS_AVP + hXpcMessage->HwMailBoxReg.IsCpu = NV_FALSE; +#else + hXpcMessage->HwMailBoxReg.IsCpu = NV_TRUE; +#endif + + // If error found then destroy all the allocation and initialization, + if (Error) + DeInitializeCpuAvpHwMailBoxRegister(hXpcMessage); + + return Error; +} + + +/** + * Destroy the cpu-avp messaging system. + * This function destroy all the allocation/initialization done for creating + * the cpu-avp messaging system. + * Thread Safety: Caller responsibility + */ +static void DestroyCpuAvpMessagingSystem(NvRmPrivXpcMessageHandle hXpcMessage) +{ + // Destroy the cpu-avp hw mail box registers. + DeInitializeCpuAvpHwMailBoxRegister(hXpcMessage); + hXpcMessage->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; + hXpcMessage->HwMailBoxReg.BankSize = 0; +} + + +NvError +NvRmPrivXpcCreate( + NvRmDeviceHandle hDevice, + NvRmPrivXpcMessageHandle *phXpcMessage) +{ + NvError Error = NvSuccess; + NvRmPrivXpcMessageHandle hNewXpcMsgHandle = NULL; + + *phXpcMessage = NULL; + + // Allocates the memory for the xpc message handle. + hNewXpcMsgHandle = NvOsAlloc(sizeof(*hNewXpcMsgHandle)); + if (!hNewXpcMsgHandle) + { + return NvError_InsufficientMemory; + } + + // Initialize all the members of the xpc message handle. + hNewXpcMsgHandle->hDevice = hDevice; + hNewXpcMsgHandle->HwMailBoxReg.pHwMailBoxRegBaseVirtAddr = NULL; + hNewXpcMsgHandle->HwMailBoxReg.BankSize = 0; + + // Create the messaging system between the processors. + Error = CreateCpuAvpMessagingSystem(hNewXpcMsgHandle); + + // if error the destroy all allocations done here. + if (Error) + { + NvOsFree(hNewXpcMsgHandle); + hNewXpcMsgHandle = NULL; + } + +#if NV_IS_AVP + Error = InitArbSemaSystem(hDevice); + if (Error) + { + NvOsFree(hNewXpcMsgHandle); + hNewXpcMsgHandle = NULL; + } +#endif + + // Copy the new xpc message handle into the passed parameter. + *phXpcMessage = hNewXpcMsgHandle; + return Error; +} + + +/** + * Destroy the Rm Xpc message handle. + * Thread Safety: It is provided inside the function. + */ +void NvRmPrivXpcDestroy(NvRmPrivXpcMessageHandle hXpcMessage) +{ + // If not a null pointer then destroy. + if (hXpcMessage) + { + // Destroy the messaging system between processor. + DestroyCpuAvpMessagingSystem(hXpcMessage); + + // Free the allocated memory for the xpc message handle. + NvOsFree(hXpcMessage); + } +} + + +// Set the outbound mailbox with the given data. We might have to spin until +// it's safe to send the message. +NvError +NvRmPrivXpcSendMessage(NvRmPrivXpcMessageHandle hXpcMessage, NvU32 data) +{ + NvRmPrivXpcHwSendMessageToTarget(&hXpcMessage->HwMailBoxReg, data); + return NvSuccess; +} + + +// Get the value currently in the inbox register. This read clears the incoming +// interrupt. +NvU32 +NvRmPrivXpcGetMessage(NvRmPrivXpcMessageHandle hXpcMessage) +{ + NvU32 data; + NvRmPrivXpcHwReceiveMessageFromTarget(&hXpcMessage->HwMailBoxReg, &data); + return data; +} + +NvError NvRmXpcInitArbSemaSystem(NvRmDeviceHandle hDevice) +{ +#if NV_IS_AVP + return NvSuccess; +#else + return InitArbSemaSystem(hDevice); +#endif +} + +static NvError InitArbSemaSystem(NvRmDeviceHandle hDevice) +{ + NvOsInterruptHandler ArbSemaHandler; + NvRmPhysAddr ArbSemaBase, ArbGntBase; + NvU32 ArbSemaSize, ArbGntSize; + NvU32 irq; + NvError e; + NvU32 i = 0; + + irq = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ArbitrationSema, 0); + + ArbSemaHandler = ArbSemaIsr; + + NV_CHECK_ERROR_CLEANUP( + NvRmInterruptRegister(hDevice, 1, &irq, &ArbSemaHandler, + hDevice, &s_arbInterruptHandle, NV_TRUE) + ); + + NvRmModuleGetBaseAddress(hDevice, + NVRM_MODULE_ID(NvRmModuleID_ArbitrationSema, 0), + &ArbSemaBase, &ArbSemaSize); + + NvRmModuleGetBaseAddress(hDevice, + NVRM_MODULE_ID(NvRmPrivModuleID_InterruptArbGnt, 0), + &ArbGntBase, &ArbGntSize); + + NV_CHECK_ERROR_CLEANUP( + NvRmPhysicalMemMap(ArbSemaBase, ArbSemaSize, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void**)&s_ArbSema.pArbSemaVirtAddr) + ); + + NV_CHECK_ERROR_CLEANUP( + NvRmPhysicalMemMap(ArbGntBase, ArbGntSize, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void**)&s_ArbSema.pArbGntVirtAddr) + ); + + //Initialize all the semaphores and mutexes + for (i=0;i<MAX_ARB_NUM;i++) + { + NV_CHECK_ERROR_CLEANUP( + NvOsSemaphoreCreate(&s_ArbSema.semaphore[i], 0) + ); + + NV_CHECK_ERROR_CLEANUP( + NvOsMutexCreate(&s_ArbSema.mutex[i]) + ); + } + + NV_CHECK_ERROR_CLEANUP( + NvOsIntrMutexCreate(&s_ArbSema.hIntrMutex) + ); + +fail: + + return e; +} + + +static void ArbSemaIsr(void *args) +{ + NvU32 int_mask, proc_int_enable, arb_gnt, i = 0; + + NvOsIntrMutexLock(s_ArbSema.hIntrMutex); + //Check which arb semaphores have been granted to this processor + arb_gnt = ARBSEMA_REG_READ(s_ArbSema.pArbSemaVirtAddr, SMP_GNT_ST); + + //Figure out which arb semaphores were signalled and then disable them. +#if NV_IS_AVP + proc_int_enable = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, COP_ENABLE); + int_mask = arb_gnt & proc_int_enable; + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, + COP_ENABLE, (proc_int_enable & ~int_mask)); +#else + proc_int_enable = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE); + int_mask = arb_gnt & proc_int_enable; + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, + CPU_ENABLE, (proc_int_enable & ~int_mask)); +#endif + + //Signal all the required semaphores + do + { + if (int_mask & 0x1) + { + NvOsSemaphoreSignal(s_ArbSema.semaphore[i]); + } + int_mask >>= 1; + i++; + + } while (int_mask); + + NvOsIntrMutexUnlock(s_ArbSema.hIntrMutex); + NvRmInterruptDone(s_arbInterruptHandle); +} + +NvU32 GetArbIdFromRmModuleId(NvRmModuleID modId) +{ + NvU32 arbId; + + switch(modId) + { + case NvRmModuleID_BseA: + arbId = NvRmArbSema_Bsea; + break; + case NvRmModuleID_Vde: + default: + arbId = NvRmArbSema_Vde; + break; + } + + return arbId; +} + +void NvRmXpcModuleAcquire(NvRmModuleID modId) +{ + NvU32 RequestedSemaNum; + NvU32 reg; + + RequestedSemaNum = GetArbIdFromRmModuleId(modId); + + NvOsMutexLock(s_ArbSema.mutex[RequestedSemaNum]); + NvOsIntrMutexLock(s_ArbSema.hIntrMutex); + + //Try to grab the lock + ARBSEMA_REG_WRITE(s_ArbSema.pArbSemaVirtAddr, SMP_GET, 1 << RequestedSemaNum); + + //Enable arb sema interrupt +#if NV_IS_AVP + reg = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, COP_ENABLE); + reg |= (1 << RequestedSemaNum); + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, COP_ENABLE, reg); +#else + reg = ARBGNT_REG_READ(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE); + reg |= (1 << RequestedSemaNum); + ARBGNT_REG_WRITE(s_ArbSema.pArbGntVirtAddr, CPU_ENABLE, reg); +#endif + + NvOsIntrMutexUnlock(s_ArbSema.hIntrMutex); + NvOsSemaphoreWait(s_ArbSema.semaphore[RequestedSemaNum]); +} + +void NvRmXpcModuleRelease(NvRmModuleID modId) +{ + NvU32 RequestedSemaNum; + + RequestedSemaNum = GetArbIdFromRmModuleId(modId); + + //Release the lock + ARBSEMA_REG_WRITE(s_ArbSema.pArbSemaVirtAddr, SMP_PUT, 1 << RequestedSemaNum); + + NvOsMutexUnlock(s_ArbSema.mutex[RequestedSemaNum]); +} diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c new file mode 100644 index 000000000000..ffd1dc5d6ebd --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief <b>nVIDIA Driver Development Kit: + * Cross Processor Communication driver </b> + * + * @b Description: Implements the cross processor communication Hw Access APIs + * + */ + +#include "nvcommon.h" +#include "nvassert.h" +#include "nvrm_drf.h" +#include "nvrm_hardware_access.h" +#include "ap15rm_xpc_hw_private.h" +#include "ap15/arres_sema.h" + +enum {MESSAGE_BOX_MESSAGE_LENGTH_BITS = 28}; +#define RESSEMA_REG_READ32(pResSemaHwRegVirtBaseAdd, reg) \ + NV_READ32((pResSemaHwRegVirtBaseAdd) + (RES_SEMA_##reg##_0)/4) + +#define RESSEMA_REG_WRITE32(pResSemaHwRegVirtBaseAdd, reg, val) \ + do { \ + NV_WRITE32(((pResSemaHwRegVirtBaseAdd) + ((RES_SEMA_##reg##_0)/4)), (val)); \ + } while(0) + +void NvRmPrivXpcHwResetOutbox(CpuAvpHwMailBoxReg *pHwMailBoxReg) +{ + NvU32 OutboxMessage; + NvU32 OutboxVal; + + OutboxMessage = 0; + + // Write Outbox in the message box + // Enable the Valid tag + // Enable interrupt +#if NV_IS_AVP + OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_DATA, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_CMD, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IE_IBE, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, TAG, 0, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, OutboxVal); +#else + OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_CMD, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_DATA, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, IE_OBE, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, TAG, 0, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, OutboxVal); +#endif +} + + +/** + * Send message to the target. + */ +void +NvRmPrivXpcHwSendMessageToTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr MessageAddress) +{ + NvU32 OutboxMessage; + NvU32 OutboxVal = 0; + + OutboxMessage = ((NvU32)(MessageAddress)) >> (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS); + + // Write Outbox in the message box + // Enable the Valid tag + // Enable interrupt +#if NV_IS_AVP + // !!! not sure why this would need to be read/modify/write +// OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_DATA, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_INBOX, IN_BOX_CMD, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, IE_IBF, FULL, OutboxVal); +// OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, IE_IBE, EMPTY, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_INBOX, TAG, VALID, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, OutboxVal); +#else + // !!! not sure why this would need to be read/modify/write +// OutboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_CMD, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_STAT, 0, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_NUM(RES_SEMA, SHRD_OUTBOX, OUT_BOX_DATA, 0, OutboxVal); + OutboxVal |= OutboxMessage; + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, IE_OBF, FULL, OutboxVal); +// OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, IE_OBE, EMPTY, OutboxVal); + OutboxVal = NV_FLD_SET_DRF_DEF(RES_SEMA, SHRD_OUTBOX, TAG, VALID, OutboxVal); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, OutboxVal); +#endif +} + + + +/** + * Receive message from the target. + */ +void +NvRmPrivXpcHwReceiveMessageFromTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr *pMessageAddress) +{ + NvU32 InboxMessage = 0; + NvU32 InboxVal; + + // Read the inbox. Lower 28 bit contains the message. +#if NV_IS_AVP + InboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_OUTBOX); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_OUTBOX, 0); +#else + InboxVal = RESSEMA_REG_READ32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr,SHRD_INBOX); + RESSEMA_REG_WRITE32(pHwMailBoxReg->pHwMailBoxRegBaseVirtAddr, SHRD_INBOX, 0); +#endif + if (InboxVal & NV_DRF_DEF(RES_SEMA, SHRD_INBOX, TAG, VALID)) + { + pHwMailBoxReg->MailBoxData = InboxVal; + } + + InboxVal = (pHwMailBoxReg->MailBoxData) & (0xFFFFFFFFUL >> (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS)); + InboxMessage = (InboxVal << (32 - MESSAGE_BOX_MESSAGE_LENGTH_BITS)); + + *pMessageAddress = InboxMessage; +} + + + + diff --git a/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h new file mode 100644 index 000000000000..c5822526b9c8 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/ap15/ap15rm_xpc_hw_private.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** + * @file + * @brief <b>nVIDIA Driver Development Kit: + * Priate Hw access function for XPC driver </b> + * + * @b Description: Defines the private interface functions for the xpc + * + */ + +#ifndef INCLUDED_RM_XPC_HW_PRIVATE_H +#define INCLUDED_RM_XPC_HW_PRIVATE_H + + +#include "nvcommon.h" +#include "nvrm_init.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// Combines the cpu avp hw mail baox system information. +typedef struct CpuAvpHwMailBoxRegRec +{ + // Hw mail box register virtual base address. + NvU32 *pHwMailBoxRegBaseVirtAddr; + + // Bank size of the hw regsiter. + NvU32 BankSize; + + // Tells whether this is on cpu or on Avp + NvBool IsCpu; + + // Mail box data which was read last time. + NvU32 MailBoxData; +} CpuAvpHwMailBoxReg; + +void NvRmPrivXpcHwResetOutbox(CpuAvpHwMailBoxReg *pHwMailBoxReg); + +/** + * Send message to the target. + */ +void +NvRmPrivXpcHwSendMessageToTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr MessageAddress); + +/** + * Receive message from the target. + */ +void +NvRmPrivXpcHwReceiveMessageFromTarget( + CpuAvpHwMailBoxReg *pHwMailBoxReg, + NvRmPhysAddr *pMessageAddress); + + +#if defined(__cplusplus) + } +#endif + +#endif // INCLUDED_RM_XPC_HW_PRIVATE_H diff --git a/arch/arm/mach-tegra/nvrm/core/common/Makefile b/arch/arm/mach-tegra/nvrm/core/common/Makefile index fcce9a5c566f..55c7f3042dbd 100644 --- a/arch/arm/mach-tegra/nvrm/core/common/Makefile +++ b/arch/arm/mach-tegra/nvrm/core/common/Makefile @@ -28,3 +28,4 @@ obj-y += nvrm_power.o obj-y += nvrm_power_dfs.o obj-y += nvrm_rmctrace.o obj-y += nvrm_relocation_table.o +obj-y += nvrm_transport.o diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h b/arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h new file mode 100644 index 000000000000..e32033f6aa05 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_message.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_NVRM_MESSAGE_H +#define INCLUDED_NVRM_MESSAGE_H + +#include "nvrm_memmgr.h" +#include "nvrm_module.h" +#include "nvrm_transport.h" +#include "nvrm_power.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +// Maximum message queue depth +enum {MAX_QUEUE_DEPTH = 5}; +// Maximum message length +enum {MAX_MESSAGE_LENGTH = 256}; +// Maximum argument size +enum {MAX_ARGS_SIZE = 220}; +// Max String length +enum {MAX_STRING_LENGTH = 200}; + +typedef struct NvRmRPCRec +{ + NvRmTransportHandle svcTransportHandle; + NvOsSemaphoreHandle TransportRecvSemId; + NvOsMutexHandle RecvLock; + NvRmDeviceHandle hRmDevice; + NvBool isConnected; +} NvRmRPC; + +typedef struct NvRmRPCRec *NvRmRPCHandle; + +void NvRmPrivProcessMessage(NvRmRPCHandle hRPCHandle, char *pRecvMessage, int messageLength); + +typedef enum +{ + NvRmMsg_MemHandleCreate = 0x0, + NvRmMsg_MemHandleCreate_Response, + NvRmMsg_MemHandleOpen, + NvRmMsg_MemHandleFree, + NvRmMsg_MemAlloc, + NvRmMsg_MemAlloc_Response, + NvRmMsg_MemPin, + NvRmMsg_MemPin_Response, + NvRmMsg_MemUnpin, + NvRmMsg_MemUnpin_Response, + NvRmMsg_MemGetAddress, + NvRmMsg_MemGetAddress_Response, + NvRmMsg_HandleFromId, + NvRmMsg_HandleFromId_Response, + NvRmMsg_PowerModuleClockControl, + NvRmMsg_PowerModuleClockControl_Response, + NvRmMsg_ModuleReset, + NvRmMsg_ModuleReset_Response, + NvRmMsg_PowerRegister, + NvRmMsg_PowerUnRegister, + NvRmMsg_PowerStarvationHint, + NvRmMsg_PowerBusyHint, + NvRmMsg_PowerBusyMultiHint, + NvRmMsg_PowerDfsGetState, + NvRmMsg_PowerDfsGetState_Response, + NvRmMsg_PowerResponse, + NvRmMsg_PowerModuleGetMaxFreq, + NvRmMsg_InitiateLP0, + NvRmMsg_InitiateLP0_Response, + NvRmMsg_RemotePrintf, + NvRmMsg_AttachModule, + NvRmMsg_AttachModule_Response, + NvRmMsg_DetachModule, + NvRmMsg_DetachModule_Response, + NvRmMsg_AVP_Reset, + NvRmMsg_Force32 = 0x7FFFFFFF +}NvRmMsg; + +typedef struct{ + NvRmMsg msg; + NvU32 size; +}NvRmMessage_HandleCreat; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; + NvError error; +}NvRmMessage_HandleCreatResponse; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; +}NvRmMessage_HandleFree; + +typedef struct{ + NvRmMsg msg; + NvError error; +}NvRmMessage_Response; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; + NvRmHeap Heaps[NvRmHeap_Num]; + NvU32 NumHeaps; + NvU32 Alignment; + NvOsMemAttribute Coherency; +}NvRmMessage_MemAlloc; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; + NvU32 Offset; +}NvRmMessage_GetAddress; + +typedef struct{ + NvRmMsg msg; + NvU32 address; +}NvRmMessage_GetAddressResponse; + +typedef struct{ + NvRmMsg msg; + NvU32 id; +}NvRmMessage_HandleFromId; + +typedef struct{ + NvRmMsg msg; + NvRmMemHandle hMem; +}NvRmMessage_Pin; + +typedef struct{ + NvRmMsg msg; + NvU32 address; +}NvRmMessage_PinResponse; + +typedef struct{ + NvRmMsg msg; + NvRmModuleID ModuleId; + NvU32 ClientId; + NvBool Enable; +}NvRmMessage_Module; + +typedef struct{ + NvRmMsg msg; + NvU32 clientId; + NvOsSemaphoreHandle eventSema; +}NvRmMessage_PowerRegister; + +typedef struct{ + NvRmMsg msg; + NvU32 clientId; +}NvRmMessage_PowerUnRegister; + +typedef struct{ + NvRmMsg msg; + NvRmDfsClockId clockId; + NvU32 clientId; + NvBool starving; +}NvRmMessage_PowerStarvationHint; + +typedef struct{ + NvRmMsg msg; + NvRmDfsClockId clockId; + NvU32 clientId; + NvU32 boostDurationMS; + NvRmFreqKHz boostKHz; +}NvRmMessage_PowerBusyHint; + +typedef struct{ + NvRmMsg msg; + NvU32 numHints; + NvU8 busyHints[MAX_STRING_LENGTH]; +}NvRmMessage_PowerBusyMultiHint; + +typedef struct{ + NvRmMsg msg; +}NvRmMessage_PowerDfsGetState; + +typedef struct{ + NvRmMsg msg; + NvError error; + NvU32 clientId; +}NvRmMessage_PowerRegister_Response; + +typedef struct{ + NvRmMsg msg; + NvRmDfsRunState state; +}NvRmMessage_PowerDfsGetState_Response; + +typedef struct{ + NvRmMsg msg; + NvRmModuleID moduleID; +}NvRmMessage_PowerModuleGetMaxFreq; + +typedef struct{ + NvRmMsg msg; + NvRmFreqKHz freqKHz; +}NvRmMessage_PowerModuleGetMaxFreq_Response; + +typedef struct{ + NvRmMsg msg; + NvU32 sourceAddr; + NvU32 bufferAddr; + NvU32 bufferSize; +} NvRmMessage_InitiateLP0; + +typedef struct{ + NvRmMsg msg; + const char string[MAX_STRING_LENGTH]; +} NvRmMessage_RemotePrintf; + + +typedef struct{ + NvRmMsg msg; + NvU32 entryAddress; + NvU32 size; + char args[MAX_ARGS_SIZE]; + NvU32 reason; +}NvRmMessage_AttachModule; + +typedef struct{ + NvRmMsg msg; + NvError error; +}NvRmMessage_AttachModuleResponse; + +typedef struct{ + NvRmMsg msg; + NvU32 reason; + NvU32 entryAddress; +}NvRmMessage_DetachModule; + +typedef struct{ + NvRmMsg msg; + NvError error; +}NvRmMessage_DetachModuleResponse; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c index 38b68621cd3f..756c806cc759 100644 --- a/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_power_dfs.c @@ -271,6 +271,22 @@ do\ /*****************************************************************************/ +#if NVRM_DTT_RANGE_CHANGE_PRINTF + +#define DttRangeReport(T, pDtt) \ +do\ +{\ + NvOsDebugPrintf("DTT: T = %d, Range = %d (%d : %d)\n", \ + (T), (pDtt)->TcorePolicy.PolicyRange, \ + (pDtt)->TcorePolicy.LowLimit, (pDtt)->TcorePolicy.HighLimit); \ +} while(0) + +#else +#define DttRangeReport(T, pDtt) +#endif + +/*****************************************************************************/ + // DFS object static NvRmDfs s_Dfs; @@ -1698,8 +1714,6 @@ DttPolicyUpdate( NvS32 TemperatureC, NvRmDtt* pDtt) { - NvU32 Range = pDtt->TcorePolicy.PolicyRange; - if (hRm->ChipId.Id == 0x20) { NvRmPrivAp20DttPolicyUpdate(hRm, TemperatureC, pDtt); @@ -1716,14 +1730,6 @@ DttPolicyUpdate( pDtt->TcorePolicy.UpdateIntervalUs = NV_WAIT_INFINITE; pDtt->TcorePolicy.PolicyRange = 0; } - if (pDtt->UseIntr || (pDtt->TcorePolicy.PolicyRange != Range)) - { -#if NVRM_DTT_RANGE_CHANGE_PRINTF - NvOsDebugPrintf("DTT: T = %d, Range = %d (%d : %d)\n", - TemperatureC, pDtt->TcorePolicy.PolicyRange, - pDtt->TcorePolicy.LowLimit, pDtt->TcorePolicy.HighLimit); -#endif - } } static NvBool @@ -1734,6 +1740,7 @@ DttClockUpdate( { NvS32 TemperatureC; NvS32 LowLimit, HighLimit; + NvU32 OldRange; NvRmTzonePolicy Policy; // Check if thermal throttling is supported @@ -1749,6 +1756,7 @@ DttClockUpdate( NvOdmTmonTemperatureGet(pDtt->hOdmTcore, &TemperatureC)) { DttPolicyUpdate(pDfs->hRm, TemperatureC, pDtt); + DttRangeReport(TemperatureC, pDtt); LowLimit = pDtt->TcorePolicy.LowLimit; HighLimit = pDtt->TcorePolicy.HighLimit; @@ -1769,6 +1777,7 @@ DttClockUpdate( } // Update temperature monitoring policy + OldRange = pDtt->TcorePolicy.PolicyRange; if (!pDtt->UseIntr && NvOdmTmonTemperatureGet(pDtt->hOdmTcore, &TemperatureC)) { @@ -1787,6 +1796,12 @@ DttClockUpdate( pDtt->TcorePolicy.UpdateFlag = NV_FALSE; } NvOsIntrMutexUnlock(pDfs->hIntrMutex); + + // Report range change + if (!pDtt->UseIntr && (OldRange != pDtt->TcorePolicy.PolicyRange)) + { + DttRangeReport(TemperatureC, pDtt); + } } else { @@ -1828,10 +1843,8 @@ static void DttIntrCallback(void* args) NvOdmTmonConfigParam_IntrLimitLow, &LowLimit); (void)NvOdmTmonParameterConfig(pDtt->hOdmTcore, NvOdmTmonConfigParam_IntrLimitHigh, &HighLimit); + DttRangeReport(TemperatureC, pDtt); } - - NVRM_DFS_PRINTF(("Dtt Intr: T = %d, LowLimit = %d, HighLimit = %d\n", - TemperatureC, LowLimit, HighLimit)); } /*****************************************************************************/ diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h b/arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h new file mode 100644 index 000000000000..d74ab4e30776 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_rpc.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef NVRM_RPC_H +#define NVRM_RPC_H + +/* + * nvrm_cpu_avp_rpc_private.h defines the private implementation functions to facilitate + * communication between processors (cpu and avp). + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvrm_init.h" +#include "nvrm_message.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** + * Initialize RPC + * + * Init the RPC. Both the service and client + * to the service must call this API before calling to create each endpoint of the connection + * via NvRmPrivRPCConnect + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hDeviceHandle rm device handle + * @param rpcPortName the port name + * @param hRPCHandle the RPC transport handle + * + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + */ + NvError NvRmPrivRPCInit( NvRmDeviceHandle hDeviceHandle, char* rpcPortName, NvRmRPCHandle *hRPCHandle ); +/** + * De-intialize the RPC and other resources. + * @param hRPCHandle the RPC transport handle + * + */ +void NvRmPrivRPCDeInit( NvRmRPCHandle hRPCHandle ); + +/** + * Connect to RPC port + * + * Creates one end of a RPC connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hRPCHandle the RPC transport handle + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + NvError NvRmPrivRPCConnect( NvRmRPCHandle hRPCHandle ); + + /** + * Connect to RPC port + * + * Creates one end of a RPC connection. Both the service and client + * to the service must call this API to create each endpoint of the connection + * through a specified port + * + * If PortName is too long or does not exist debug mode + * assert is encountered. + * + * @param hRPCHandle the RPC transport handle + * + * @retval NvSuccess Transport endpoint successfully allocated + * @retval NvError_InsufficientMemory Not enough memory to allocate endpoint + * @retval NvError_MutexCreateFailed Creaion of mutex failed. + * @retval NvError_SemaphoreCreateFailed Creaion of semaphore failed. + * @retval NvError_SharedMemAllocFailed Creaion of shared memory allocation + * failed. + * @retval NvError_NotInitialized The transport is not able to initialzed the + * threads. + */ + NvError NvRmPrivRPCWaitForConnect( NvRmRPCHandle hRPCHandle ); + /** + * Receive the message from the port. This will read the message if it is + * available for this port otherwise it will return the + * NvError_TransportMessageBoxEmpty error. + * + * @param hRPCHandle the RPC transport handle + * @param pMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * + * @retval NvSuccess Message received successfully. + * @retval NvError_NotInitialized hTransport is not open. + * @retval NvError_InvalidState The port is not connection state. + * @retval NvError_TransportMessageBoxEmpty The message box empty and not able + * to receive the message. + * @retval NvError_TransportIncompleteMessage The received message for this + * port is longer than the configured message length for this port. It copied + * the maximm size of the configured length of the message for this port and + * return the incomplete message buffer. + * @retval NvError_TransportMessageOverflow The port receives the message more + * than the configured queue depth count for this port and hence message + * overflow has been ocuured. + */ + + NvError NvRmPrivRPCRecvMsg( NvRmRPCHandle hRPCHandle, void* pMessageBuffer, NvU32 * pMessageSize ); + + /** + * Send Message. + * + * Sends a message to the other port which is connected to this port. + * Its a wrapper to rm transport send message + * + * @param hRPCHandle the RPC transport handle + * @param pMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * + */ +void +NvRmPrivRPCSendMsg(NvRmRPCHandle hRPCHandle, + void* pMessageBuffer, + NvU32 MessageSize); + +/** + * Send and Recieve message. + * + * Send and Recieve a message between port. + * Its a wrapper to rm transport send message with response + * + * @param hRPCHandle the RPC transport handle + * @param pRecvMessageBuffer The pointer to the receive message buffer where the + * received message will be copied. + * @param MaxSize The maximum size in bytes that may be copied to the buffer + * @param pMessageSize Pointer to the variable where the length of the message + * will be stored. + * @param pSendMessageBuffer The pointer to the message buffer where message which + * need to be send is available. + * @param MessageSize Specifies the size of the message. + * + */ +void +NvRmPrivRPCSendMsgWithResponse(NvRmRPCHandle hRPCHandle, + void* pRecvMessageBuffer, + NvU32 MaxSize, + NvU32 *pMessageSize, + void* pSendMessageBuffer, + NvU32 MessageSize); + + +/** + * Closes a transport connection. Proper closure of this connection requires + * that both the client and service call this API. Therefore, it is expected + * that the client and service message one another to coordinate the close. + * + */ +void NvRmPrivRPCClose(NvRmRPCHandle hRPCHandle); + +NvError NvRmPrivInitService(NvRmDeviceHandle hDeviceHandle); + +void NvRmPrivServiceDeInit(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c new file mode 100644 index 000000000000..ffc4f83196fc --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c @@ -0,0 +1,1655 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * @brief <b>NVIDIA Driver Development Kit: + * Transport API</b> + * + * @b Description: This is the implementation of Transport API, which + * implements a simple means to pass messages across a port name regardless of + * port exist in what processor (on same processor or other processor). + */ + +#include "nvrm_transport.h" +#include "nvrm_xpc.h" +#include "nvrm_interrupt.h" +#include "nvrm_rpc.h" +#include "nvutil.h" +#include "nvassert.h" +#include "nvcommon.h" +#include "avp.h" + +#define LOOPBACK_PROFILE 0 + +// indices where to save data for the loopback test +#define LOOP_CPU_SEND_INDEX 0 +#define LOOP_AVP_ISR_INDEX 1 +#define LOOP_AVP_RECV_INDEX 2 +#define LOOP_AVP_SEND_INDEX 3 +#define LOOP_CPU_ISR_INDEX 4 +#define LOOP_CPU_RECV_INDEX 5 + +#define SEMAPHORE_BASED_MUTUAL_EXCLUSION 0 + +enum {MAX_INT_FOR_TRANSPORT = 2}; + +// Interrupt bit index in the interrupt controller relocation table. +enum {CPU_TRANSPORT_INT_OBE = 1}; +enum {CPU_TRANSPORT_INT_IBF = 0}; +enum {AVP_TRANSPORT_INT_OBE = 0}; +enum {AVP_TRANSPORT_INT_IBF = 1}; + +// Some constraints parameter to develop the transport APIs. + +// Maximum port name length +enum {MAX_PORT_NAME_LENGTH = 16}; + +// Maximum possible message length between the ports +#define MAX_COMMAND_SIZE 16 + +// Message header size MessageCommand + port Name + message Length (24 Bytes) +enum {MESSAGE_HEADER_SIZE = 0x20}; + +// Maximum receive message queue depth +enum {MAX_MESSAGE_DEPTH = 30}; + +// Maximum time to wait for the response when open the port. +enum {MAX_OPEN_TIMEOUT_MS = 200}; + +// Try to resend the message after this time. +enum {MESSAGE_RETRY_AFTER_MS = 500 }; + +// Connection message transfer and response wait timeout. +enum {MAX_CONNECTION_TIMEOUT_MS = 500 }; + + + +// Transport Commands which uses to do the handshaking and message transfer +// between the processor. This commands are send to the remote processor +// when any type if transaction happens. +typedef enum +{ + TransportCmd_None = 0x0, + + // The first transport command from the cpu->avp will inform the + // avp of size of the buffer. + TransportCmd_SetBufferInfo, + + // Transport command for staring the connection process. + TransportCmd_Connect, + + // Transport command for disconnecting the port and deleting the port entry. + TransportCmd_Disconnect, + + // Transport command which used for normal message transfer to the port. + TransportCmd_Message, + + // When a command requires a response, the value in the command field will + // be changed by the called processor here to indicate that the response is ready. + TransportCmd_Response, + + TransportCmd_Force32 = 0x7FFFFFFF + +} TransportCmd; + + + +// Ports (endpoint) state. +typedef enum +{ + // Port is opened only. + PortState_Open = 0x1, + + // Port is waiting for connection. + PortState_Waiting, + + // Port is connected. + PortState_Connected, + + // Port has been disconnected from other side. You can pop out messages + // but you can't send anymore + PortState_Disconnected, + + // Set to destroy when there is someone waiting for a connection, but + // and a different thread calls to kill close the port. + PortState_Destroy, + + PortState_Force32 = 0x7FFFFFFF +} PortState; + + + +// Message list which will be queued in the port receive message queue. +typedef struct RmReceiveMessageRec +{ + // Length of message. + NvU32 MessageLength; + + // Fixed size message buffer where the receiving message will be store. + NvU8 MessageBuffer[MAX_MESSAGE_LENGTH]; +} RmReceiveMessage; + + +// Combines the information for keeping the received messages to the +// corresponding ports. +typedef struct MessageQueueRec +{ + // Receive message Q details to receive the message. We make the queue 1 extra bigger than the + // requested size, and then we can do lockless updates because only the Recv function modifies + // ReadIndex, and only the ISR modifies the WriteIndex + RmReceiveMessage *pReceiveMsg; + + volatile NvU16 ReadIndex; + volatile NvU16 WriteIndex; + + NvU16 QueueSize; + +} MessageQueue; + + + +// Combines all required information for the transport port. +// The port information contains the state, recv message q, message depth and +// message length. +typedef struct NvRmTransportRec +{ + // Name of the port, 1 exra byte for NULL termination + char PortName[MAX_PORT_NAME_LENGTH+1]; + + // The state of port whether this is open or connected or waiting for + // connection. + PortState State; + + // Receive message Box which contains the receive messages for this port. + MessageQueue RecvMessageQueue; + + // Semaphore which is signal after getting the message for that port. + // This is the client passed semaphore. + NvOsSemaphoreHandle hOnPushMsgSem; + + // Pointer to the partner port. If the connect is to a remote partner, + // then this pointer is NULL + NvRmTransportHandle hConnectedPort; + + // If this is a remote connection, this holds the remote ports "name" + NvU32 RemotePort; + + // save a copy of the rm handle. + NvRmDeviceHandle hRmDevice; + + struct NvRmTransportRec *pNext; + + // unlikely to be used members at the end + + // to be signalled when someone waits for a connector. + NvOsSemaphoreHandle hOnConnectSem; + +#if LOOPBACK_PROFILE + NvBool bLoopTest; +#endif + +} NvRmTransport; + + + +// Combines the common information for keeping the transport information and +// sending and receiving the messages. +typedef struct NvRmPrivPortsRec +{ + // Device handle. + NvRmDeviceHandle hDevice; + + // List of port names of the open ports in the system. + NvRmTransport *pPortHead; + + // Mutex for transport + NvOsMutexHandle mutex; + + NvRmMemHandle hMessageMem; + void *pTransmitMem; + void *pReceiveMem; + NvU32 MessageMemPhysAddr; + + NvRmPrivXpcMessageHandle hXpc; + + // if a message comes in, but the receiver's queue is full, + // then we don't clear the inbound message to allow another message + // and set this flag. We use 2 variables here, so we don't need a lock. + volatile NvU8 ReceiveBackPressureOn; + NvU8 ReceiveBackPressureOff; + +#if LOOPBACK_PROFILE + volatile NvU32 *pTimer; +#endif +} NvRmPrivPorts; + + +// !!! Fixme, this should be part of the rm handle. +static NvRmPrivPorts s_TransportInfo; + +extern NvU32 NvRmAvpPrivGetUncachedAddress(NvU32 addr); + +#define MESSAGE_QUEUE_SIZE_IN_BYTES ( sizeof(RmReceiveMessage) * (MAX_MESSAGE_DEPTH+1) ) +static NvU32 s_RpcAvpQueue[ (MESSAGE_QUEUE_SIZE_IN_BYTES + 3) / 4 ]; +static NvU32 s_RpcCpuQueue[ (MESSAGE_QUEUE_SIZE_IN_BYTES + 3) / 4 ]; +static struct NvRmTransportRec s_RpcAvpPortStruct; +static struct NvRmTransportRec s_RpcCpuPortStruct; + +static NvOsInterruptHandle s_TransportInterruptHandle = NULL; + +static NvRmTransportHandle +FindPort(NvRmDeviceHandle hDevice, char *pPortName); + +static NvError +NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, NvU32 *message, NvU32 MessageLength); + +static void HandleAVPResetMessage(NvRmDeviceHandle hDevice); + +// expect caller to handle mutex +static char *NvRmPrivTransportUniqueName(void) +{ + static char UniqueName[] = "aaaaaaaa+"; + NvU32 len = 8; + NvU32 i; + + // this will roll a new name until we hit zzzz:zzzz + // it's not unbounded, but it is a lot of names... + // Unique names end in a '+' which won't be allowed in supplied names, to avoid + // collision. + for (i=0; i < len; ++i) + { + ++UniqueName[i]; + if (UniqueName[i] != 'z') + { + break; + } + UniqueName[i] = 'a'; + + } + + return UniqueName; +} + + +/* Returns NV_TRUE if the message was inserted ok + * Returns NV_FALSE if message was not inserted because the queue is already full + + */static NvBool +InsertMessage(NvRmTransportHandle hPort, const NvU8 *message, const NvU32 MessageSize) +{ + NvU32 index; + NvU32 NextIndex; + + index = (NvU32)hPort->RecvMessageQueue.WriteIndex; + NextIndex = index + 1; + if (NextIndex == hPort->RecvMessageQueue.QueueSize) + NextIndex = 0; + + // check for full condition + if (NextIndex == hPort->RecvMessageQueue.ReadIndex) + return NV_FALSE; + + // copy in the message + NvOsMemcpy(hPort->RecvMessageQueue.pReceiveMsg[index].MessageBuffer, + message, + MessageSize); + hPort->RecvMessageQueue.pReceiveMsg[index].MessageLength = MessageSize; + + hPort->RecvMessageQueue.WriteIndex = (NvU16)NextIndex; + return NV_TRUE; +} + + +static void +ExtractMessage(NvRmTransportHandle hPort, NvU8 *message, NvU32 *pMessageSize, NvU32 MaxSize) +{ + NvU32 NextIndex; + NvU32 index = (NvU32)hPort->RecvMessageQueue.ReadIndex; + NvU32 size = hPort->RecvMessageQueue.pReceiveMsg[index].MessageLength; + + NextIndex = index + 1; + if (NextIndex == hPort->RecvMessageQueue.QueueSize) + NextIndex = 0; + + NV_ASSERT(index != hPort->RecvMessageQueue.WriteIndex); // assert on empty condition + NV_ASSERT(size <= MaxSize); + + *pMessageSize = size; + + // only do the copy and update if there is sufficient room, otherwise + // the caller will propogate an error up. + if (size > MaxSize) + { + return; + } + NvOsMemcpy(message, + hPort->RecvMessageQueue.pReceiveMsg[index].MessageBuffer, + size); + + hPort->RecvMessageQueue.ReadIndex = (NvU16)NextIndex; +} + + + +static void *s_TmpIsrMsgBuffer; + +/** + * Connect message + * [ Transport Command ] + * [ Remote Handle ] + * [ Port Name ] + * + * Response: + * [ Remote Handle ] <- [ Local Handle ] + */ + +static void +HandleConnectMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage) +{ + char PortName[MAX_PORT_NAME_LENGTH+1]; + NvU32 RemotePort; + NvRmTransportHandle hPort; + + RemotePort = pMessage[1]; + NvOsMemcpy(PortName, (void*)&pMessage[2], MAX_PORT_NAME_LENGTH); + PortName[MAX_PORT_NAME_LENGTH] = 0; + + // See if there is a local port with that name + hPort = FindPort(hDevice, PortName); + if (hPort && hPort->State == PortState_Waiting) + { + NvOsAtomicCompareExchange32((NvS32 *)&hPort->State, PortState_Waiting, PortState_Connected); + if (hPort->State == PortState_Connected) + { + hPort->RemotePort = RemotePort; + NvOsSemaphoreSignal(hPort->hOnConnectSem); + pMessage[1] = (NvU32)hPort; + } + else + { + pMessage[1] = 0; + } + } + else + { + pMessage[1] = 0; + } + pMessage[0] = TransportCmd_Response; +} + + + +/** + * Disconnect message + * [ Transport Command ] + * [ Local Handle ] + * + * Response: + * [ Local Handle ] <- 0 + */ +static void +HandleDisconnectMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage) +{ + NvRmTransportHandle hPort; + hPort = (NvRmTransportHandle)pMessage[1]; + + // !!! For sanity we should walk the list of open ports to make sure this is a valid port! + if (hPort && hPort->State == PortState_Connected) + { + hPort->State = PortState_Disconnected; + hPort->RemotePort = 0; + } + pMessage[1] = 0; + pMessage[0] = TransportCmd_None; +} + + +/** + * Disconnect message + * [ Transport Command ] + * [ Local Handle ] + * [ Message Length ] + * [ Message ] + * + * Response: + * [ Message Length ] <- NvSuccess + * [ Transport Command ] <- When we can accept a new message + */ + +static void +HandlePortMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage) +{ + NvRmTransportHandle hPort; + NvU32 MessageLength; + NvBool bSuccess; + + hPort = (NvRmTransportHandle)pMessage[1]; + MessageLength = pMessage[2]; + +#if LOOPBACK_PROFILE + if (hPort && hPort->bLoopTest) + { +# if NV_IS_AVP + pMessage[LOOP_AVP_ISR_INDEX + 3] = *s_TransportInfo.pTimer; +# else + pMessage[LOOP_CPU_ISR_INDEX + 3] = *s_TransportInfo.pTimer; +# endif + } +#endif + + + // !!! For sanity we should walk the list of open ports to make sure this is a valid port! + // Queue the message even if in the open state as presumably this should only have happened if + // due to a race condition with the transport connected messages. + if (hPort && (hPort->State == PortState_Connected || hPort->State == PortState_Open)) + { + bSuccess = InsertMessage(hPort, (NvU8*)&pMessage[3], MessageLength); + if (bSuccess) + { + if (hPort->hOnPushMsgSem) + NvOsSemaphoreSignal(hPort->hOnPushMsgSem); + pMessage[0] = TransportCmd_None; + } + else + { + ++s_TransportInfo.ReceiveBackPressureOn; + } + } +} + +static void +HandleAVPResetMessage(NvRmDeviceHandle hDevice) +{ + NvRmTransportHandle hPort; + + hPort = FindPort(hDevice,(char*)"RPC_CPU_PORT"); + if (hPort && (hPort->State == PortState_Connected || hPort->State == PortState_Open)) + { + NvU32 message; + message = NvRmMsg_AVP_Reset; + InsertMessage(hPort, (NvU8*)&message, sizeof(NvU32)); + if (hPort->hOnPushMsgSem) + NvOsSemaphoreSignal(hPort->hOnPushMsgSem); + else + NV_ASSERT(0); + } + else + NV_ASSERT(0); + +} + + +/** + * Handle the Inbox full interrupt. + */ +static void +InboxFullIsr(void *args) +{ + NvRmDeviceHandle hDevice = (NvRmDeviceHandle)args; + NvU32 MessageData; + NvU32 MessageCommand; + volatile NvU32 *pMessage; + + MessageData = NvRmPrivXpcGetMessage(s_TransportInfo.hXpc); + if(MessageData == AVP_WDT_RESET) + { + HandleAVPResetMessage(hDevice); + NvRmInterruptDone(s_TransportInterruptHandle); + return; + } + // if we're on the AVP, the first message we get will configure the message info + if (s_TransportInfo.MessageMemPhysAddr == 0) + { +#if NV_IS_AVP + MessageData = NvRmAvpPrivGetUncachedAddress(MessageData); +#else + MessageData = MessageData; +#endif + s_TransportInfo.MessageMemPhysAddr = MessageData; + s_TransportInfo.pReceiveMem = (void*)MessageData; + s_TransportInfo.pTransmitMem = (void *) (MessageData + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE); + // ack the message and return. + *(NvU32*)s_TransportInfo.pReceiveMem = TransportCmd_None; + return; + } + + // otherwise decode and dispatch the message. + + + if (s_TransportInfo.pReceiveMem == NULL) + { + /* QT/EMUTRANS takes this path. */ + NvRmMemRead(s_TransportInfo.hMessageMem, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, s_TmpIsrMsgBuffer, MAX_MESSAGE_LENGTH); + pMessage = s_TmpIsrMsgBuffer; + NvRmMemWrite(s_TransportInfo.hMessageMem, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, s_TmpIsrMsgBuffer, 2*sizeof(NvU32)); + } + else + { + pMessage = (NvU32*)s_TransportInfo.pReceiveMem; + } + + MessageCommand = pMessage[0]; + + switch (MessageCommand) + { + case TransportCmd_Connect: + HandleConnectMessage(hDevice, pMessage); + break; + + case TransportCmd_Disconnect: + HandleDisconnectMessage(hDevice, pMessage); + break; + + case TransportCmd_Message: + HandlePortMessage(hDevice, pMessage); + break; + + default: + NV_ASSERT(0); + } + + NvRmInterruptDone(s_TransportInterruptHandle); +} + + +/** + * Handle the outbox empty interrupt. + */ +static void +OutboxEmptyIsr(void *args) +{ + // !!! This is not currently used... ignore for now. Might be required if we find that we + // need to spin for long periods of time waiting for other end of the connection to consume + // messages. + // + NvRmInterruptDone(s_TransportInterruptHandle); +} + +static void +NvRmPrivProcIdGetProcessorInfo( + NvRmDeviceHandle hDevice, + NvRmModuleID *pProcModuleId) +{ +#if NV_IS_AVP + *pProcModuleId = NvRmModuleID_Avp; +#else + *pProcModuleId = NvRmModuleID_Cpu; +#endif +} + +/** + * Register for the transport interrupts. + */ +static NvError +RegisterTransportInterrupt(NvRmDeviceHandle hDevice) +{ + NvOsInterruptHandler DmaIntHandlers[MAX_INT_FOR_TRANSPORT]; + NvU32 IrqList[MAX_INT_FOR_TRANSPORT]; + NvRmModuleID ProcModuleId; + + if (s_TransportInterruptHandle) + { + return NvSuccess; + } + NvRmPrivProcIdGetProcessorInfo(hDevice, &ProcModuleId); + + if (ProcModuleId == NvRmModuleID_Cpu) + { + IrqList[0] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, CPU_TRANSPORT_INT_IBF); + IrqList[1] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, CPU_TRANSPORT_INT_OBE); + } + else + { + IrqList[0] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, AVP_TRANSPORT_INT_IBF); + IrqList[1] = NvRmGetIrqForLogicalInterrupt( + hDevice, NvRmModuleID_ResourceSema, AVP_TRANSPORT_INT_OBE); + } + + /* There is no need for registering the OutboxEmptyIsr, so we only register + * one interrupt i.e. InboxFullIsr */ + DmaIntHandlers[0] = InboxFullIsr; + DmaIntHandlers[1] = OutboxEmptyIsr; + return NvRmInterruptRegister(hDevice, 1, IrqList, DmaIntHandlers, + hDevice, &s_TransportInterruptHandle, NV_TRUE); +} + +// allocate buffers to be used for sending/receiving messages. +static void +NvRmPrivTransportAllocBuffers(NvRmDeviceHandle hRmDevice) +{ +#if !NV_IS_AVP + // These buffers are always allocated on the CPU side. We'll pass the address over the AVP + // + + NvError Error = NvSuccess; + NvRmMemHandle hNewMemHandle = NULL; + + // Create memory handle + Error = NvRmMemHandleCreate(hRmDevice, &hNewMemHandle, (MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE)*2); + if (Error) + goto fail; + + // Allocates the memory from the Heap + Error = NvRmMemAlloc(hNewMemHandle, NULL, 0, + XPC_MESSAGE_ALIGNMENT_SIZE, NvOsMemAttribute_Uncached); + if (Error) + goto fail; + + s_TransportInfo.MessageMemPhysAddr = NvRmMemPin(hNewMemHandle); + + // If it is success to create the memory handle. + // We have to be able to get a mapping to this, because it is used at interrupt time! + s_TransportInfo.hMessageMem = hNewMemHandle; + Error = NvRmMemMap(hNewMemHandle, 0, + (MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE)*2, + NVOS_MEM_READ_WRITE, + &s_TransportInfo.pTransmitMem); + if (Error) + { + s_TransportInfo.pTransmitMem = NULL; + s_TransportInfo.pReceiveMem = NULL; + } + else + { + s_TransportInfo.pReceiveMem = (void *) (((NvUPtr)s_TransportInfo.pTransmitMem) + + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE); + } + + s_TransportInfo.hMessageMem = hNewMemHandle; + NvRmMemWr32(hNewMemHandle, 0, 0xdeadf00d); // set this non-zero to throttle messages to the avp till avp is ready. + NvRmMemWr32(hNewMemHandle, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, 0); + + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return; + + +fail: + NvRmMemHandleFree(hNewMemHandle); + s_TransportInfo.hMessageMem = NULL; + return; +#else + return; +#endif +} + + +static void +NvRmPrivTransportFreeBuffers(NvRmDeviceHandle hRmDevice) +{ +#if !NV_IS_AVP + NvRmMemHandleFree(s_TransportInfo.hMessageMem); +#endif +} + +static volatile NvBool s_Transport_Inited = NV_FALSE; + +/** + * Initialize the transport structures, this is callled once + * at NvRmOpen time. + */ +NvError NvRmTransportInit(NvRmDeviceHandle hRmDevice) +{ + NvError err; + + NvOsMemset(&s_TransportInfo, 0, sizeof(s_TransportInfo)); + s_TransportInfo.hDevice = hRmDevice; + + err = NvOsMutexCreate(&s_TransportInfo.mutex); + if (err) + goto fail; + +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + err = NvRmPrivXpcCreate(hRmDevice, &s_TransportInfo.hXpc); + if (err) + goto fail; + + NvRmPrivTransportAllocBuffers(hRmDevice); +#endif + + if (1) // Used in EMUTRANS mode where the buffers cannot be mapped. + { + s_TmpIsrMsgBuffer = NvOsAlloc(MAX_MESSAGE_LENGTH); + if (!s_TmpIsrMsgBuffer) + goto fail; + } + +#if LOOPBACK_PROFILE + { + NvU32 TimerAddr; + NvU32 TimerSize; + + NvRmModuleGetBaseAddress(hRmDevice, NvRmModuleID_TimerUs, &TimerAddr, &TimerSize); + // map the us counter + err = NvRmPhysicalMemMap(TimerAddr, TimerSize, NVOS_MEM_READ_WRITE, + NvOsMemAttribute_Uncached, (void*)&s_TransportInfo.pTimer); + if (err) + goto fail; + } + +#endif + +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + err = RegisterTransportInterrupt(hRmDevice); + if (err) + goto fail; +#endif + s_Transport_Inited = NV_TRUE; + return NvSuccess; + + +fail: +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + NvRmPrivXpcDestroy(s_TransportInfo.hXpc); + NvRmPrivTransportFreeBuffers(hRmDevice); +#endif + NvOsFree(s_TmpIsrMsgBuffer); + NvOsMutexDestroy(s_TransportInfo.mutex); + return err; +} + +/** + * DeInitialize the transport structures. + */ +void NvRmTransportDeInit(NvRmDeviceHandle hRmDevice) +{ + // Unregister the interrupts. +#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE + NvRmPrivXpcDestroy(s_TransportInfo.hXpc); + NvRmPrivTransportFreeBuffers(hRmDevice); + NvRmInterruptUnregister(hRmDevice, s_TransportInterruptHandle); + s_TransportInterruptHandle = NULL; +#endif + NvOsFree(s_TmpIsrMsgBuffer); + NvOsMutexDestroy(s_TransportInfo.mutex); +} + + +static void +InsertPort(NvRmDeviceHandle hDevice, NvRmTransportHandle hPort) +{ + hPort->pNext = s_TransportInfo.pPortHead; + s_TransportInfo.pPortHead = hPort; +} + + +static NvRmTransportHandle +FindPort(NvRmDeviceHandle hDevice, char *pPortName) +{ + NvRmTransportHandle hPort = NULL; + NvRmTransportHandle hIter = NULL; + + hIter = s_TransportInfo.pPortHead; + while (hIter) + { + if ( NvOsStrcmp(pPortName, hIter->PortName) == 0) + { + hPort = hIter; + break; + } + hIter = hIter->pNext; + } + + return hPort; +} + + +// Remove the given hPort from the list of ports +static void +DeletePort(NvRmDeviceHandle hRmDevice, const NvRmTransportHandle hPort) +{ + // Pointer to the pointer alleviates all special cases in linked list walking. + // I wish I was clever enough to have figured this out myself. + + NvRmTransportHandle *hIter; + + hIter = &s_TransportInfo.pPortHead; + while (*hIter) + { + if ( *hIter == hPort ) + { + *hIter = (*hIter)->pNext; + break; + } + hIter = &(*hIter)->pNext; + } +} + + + + +/** + * Open the port handle with a given port name. With the same name, only two + * port can be open. + * Thread Safety: It is done inside the function. + */ + +NvError +NvRmTransportOpen( + NvRmDeviceHandle hRmDevice, + char *pPortName, + NvOsSemaphoreHandle RecvMessageSemaphore, + NvRmTransportHandle *phTransport) +{ + NvU32 PortNameLen; + NvRmTransportHandle hPartner = NULL; + NvRmTransportHandle hPort = NULL; + NvError err = NvError_InsufficientMemory; + char TmpName[MAX_PORT_NAME_LENGTH+1]; + + while (!s_Transport_Inited) { + // This can happen, if this API is called before avp init. + NvOsSleepMS(500); + } + // Look and see if this port exists anywhere. + if (pPortName == NULL) + { + NvOsMutexLock(s_TransportInfo.mutex); + + pPortName = NvRmPrivTransportUniqueName(); + PortNameLen = NvOsStrlen(pPortName); + NvOsStrncpy(TmpName, pPortName, sizeof(TmpName) ); + pPortName = TmpName; + + NvOsMutexUnlock(s_TransportInfo.mutex); + } + else + { + PortNameLen = NvOsStrlen(pPortName); + NV_ASSERT(PortNameLen <= MAX_PORT_NAME_LENGTH); + } + + NvOsMutexLock(s_TransportInfo.mutex); + hPartner = FindPort(hRmDevice, pPortName); + + if (hPartner && hPartner->hConnectedPort != NULL) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvError_TransportPortAlreadyExist; + } + + // check if this is one of the special RPC ports used by the rm + if ( NvOsStrcmp(pPortName, "RPC_AVP_PORT") == 0) + { + //If someone else wants to open this port + //just return the one already created. + if (hPartner) + { + hPort = hPartner; + goto success; + } + else + { + hPort = &s_RpcAvpPortStruct; + hPort->RecvMessageQueue.pReceiveMsg = (void *)&s_RpcAvpQueue[0]; + } + } + else if (NvOsStrcmp(pPortName, "RPC_CPU_PORT") == 0) + { + hPort = &s_RpcCpuPortStruct; + hPort->RecvMessageQueue.pReceiveMsg = (void *)&s_RpcCpuQueue[0]; + } + else + { + // Create a new TransportPort + hPort = NvOsAlloc( sizeof(*hPort) ); + if (!hPort) + goto fail; + + NvOsMemset(hPort, 0, sizeof(*hPort) ); + + // Allocate the receive queue + hPort->RecvMessageQueue.pReceiveMsg = NvOsAlloc( sizeof(RmReceiveMessage) * (MAX_MESSAGE_DEPTH+1)); + if (!hPort->RecvMessageQueue.pReceiveMsg) + goto fail; + } + + NvOsStrncpy(hPort->PortName, pPortName, PortNameLen); + hPort->State = PortState_Open; + hPort->hConnectedPort = hPartner; + + if (RecvMessageSemaphore) + { + err = NvOsSemaphoreClone(RecvMessageSemaphore, &hPort->hOnPushMsgSem); + if (err) + goto fail; + } + + hPort->RecvMessageQueue.QueueSize = MAX_MESSAGE_DEPTH+1; + hPort->hRmDevice = hRmDevice; + + if (hPort->hConnectedPort != NULL) + { + hPort->hConnectedPort->hConnectedPort = hPort; + } + InsertPort(hRmDevice, hPort); + + + // !!! loopback info +#if LOOPBACK_PROFILE + if (NvOsStrcmp(hPort->PortName, "LOOPTEST") == 0) + hPort->bLoopTest = 1; +#endif + +success: + NvOsMutexUnlock(s_TransportInfo.mutex); + *phTransport = hPort; + return NvSuccess; + +fail: + if (hPort) + { + NvOsFree(hPort->RecvMessageQueue.pReceiveMsg); + NvOsSemaphoreDestroy(hPort->hOnPushMsgSem); + NvOsFree(hPort); + hPort = NULL; + } + NvOsMutexUnlock(s_TransportInfo.mutex); + + return err; +} + + +/** + * Close the transport handle + * Thread Safety: It is done inside the function. + */ +void NvRmTransportClose(NvRmTransportHandle hPort) +{ + NvU32 RemoteMessage[4]; + + if (!hPort) + return; + + // Look and see if this port exists anywhere. + NV_ASSERT(hPort); + + + NvOsMutexLock(s_TransportInfo.mutex); + DeletePort(hPort->hRmDevice, hPort); // unlink this port + + // Check if there is already a port waiting to connect, and if there is + // switch the port state to _Destroy, and signal the waiters semaphore. + // The "State" member is not protected by the mutex because it can be + // updated by the ISR. + while (hPort->State == PortState_Waiting) + { + NvOsAtomicCompareExchange32((NvS32*)&hPort->State, PortState_Waiting, PortState_Destroy); + if (hPort->State == PortState_Destroy) + { + NvOsSemaphoreSignal(hPort->hOnConnectSem); + + // in this case, we can't complete the destroy, the signalled thread will + // have to complete. We just return now + NvOsMutexUnlock(s_TransportInfo.mutex); + return; + } + } + + if (hPort->hConnectedPort) + { + // unlink this port from the other side of the connection. + hPort->hConnectedPort->hConnectedPort = NULL; + } + + if (hPort->RemotePort) + { + RemoteMessage[0] = TransportCmd_Disconnect; + RemoteMessage[1] = hPort->RemotePort; + NvRmPrivTransportSendMessage(hPort->hRmDevice, RemoteMessage, 2*sizeof(NvU32)); + } + + NvOsSemaphoreDestroy(hPort->hOnPushMsgSem); + + + if (hPort == &s_RpcAvpPortStruct || + hPort == &s_RpcCpuPortStruct) + { + // don't free these.. + NvOsMemset(hPort, 0, sizeof(*hPort)); + } + else + { + NvOsFree(hPort->RecvMessageQueue.pReceiveMsg); + NvOsFree(hPort); + } + + NvOsMutexUnlock(s_TransportInfo.mutex); +} + + +/** + * Wait for the connection to the other end. + * Thread Safety: It is done inside the function. + */ +NvError +NvRmTransportWaitForConnect( + NvRmTransportHandle hPort, + NvU32 TimeoutMS) +{ + NvOsSemaphoreHandle hSem = NULL; + NvError err = NvSuccess; + + NvOsMutexLock(s_TransportInfo.mutex); + if (hPort->State != PortState_Open) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + err = NvError_TransportPortAlreadyExist; + goto exit_gracefully; + } + + err = NvOsSemaphoreCreate(&hSem, 0); + if (err) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + goto exit_gracefully; + } + + hPort->hOnConnectSem = hSem; + hPort->State = PortState_Waiting; + NvOsMutexUnlock(s_TransportInfo.mutex); + + err = NvOsSemaphoreWaitTimeout(hSem, TimeoutMS); + if (err) + { + // we have to be careful here, the ISR _might_ happen just after the semaphore + // times out. + NvOsAtomicCompareExchange32((NvS32 *)&hPort->State, PortState_Waiting, PortState_Open); + NV_ASSERT(hPort->State == PortState_Open || hPort->State == PortState_Connected); + if (hPort->State == PortState_Connected) + { + err = NvSuccess; + } + } + + NvOsMutexLock(s_TransportInfo.mutex); + hPort->hOnConnectSem = NULL; + NvOsMutexUnlock(s_TransportInfo.mutex); + + if (hPort->State == PortState_Destroy) + { + // finish the destroy process + NvRmTransportClose(hPort); + err = NvError_TransportConnectionFailed; + } + +exit_gracefully: + NvOsSemaphoreDestroy(hSem); + return err; +} + + + +static NvError +NvRmPrivTransportWaitResponse(NvRmDeviceHandle hDevice, NvU32 *response, NvU32 ResponseLength, NvU32 TimeoutMS) +{ + NvU32 CurrentTime; + NvU32 StartTime; + NvU32 Response; + NvBool GotResponse = NV_TRUE; + NvError err = NvError_Timeout; + volatile NvU32 *pXpcMessage = (volatile NvU32*)s_TransportInfo.pTransmitMem; + + if (pXpcMessage == NULL) + { + if (!NV_IS_AVP) + { + Response = NvRmMemRd32(s_TransportInfo.hMessageMem, 0); + } else + { + NV_ASSERT(0); + return NvSuccess; + } + } + else + { + Response = pXpcMessage[0]; + } + + if (Response != TransportCmd_Response) + { + GotResponse = NV_FALSE; + + // response is not back yet, so spin till its here. + StartTime = NvOsGetTimeMS(); + CurrentTime = StartTime; + while ( (CurrentTime - StartTime) < TimeoutMS ) + { + if ( pXpcMessage && (pXpcMessage[0] == TransportCmd_Response) ) + { + GotResponse = NV_TRUE; + break; + } + else if ( !pXpcMessage ) + { + NV_ASSERT(!"Invalid pXpcMessage pointer is accessed"); + } + CurrentTime = NvOsGetTimeMS(); + } + } + + if ( pXpcMessage && GotResponse ) + { + err = NvSuccess; + NvOsMemcpy(response, (void *)pXpcMessage, ResponseLength); + } + + return err; +} + + +static NvError +NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, NvU32 *message, NvU32 MessageLength) +{ + NvU32 ReadData; + + if (s_TransportInfo.pTransmitMem == NULL) + { + /* QT/EMUTRANS takes this code path */ + if (!NV_IS_AVP) + { + ReadData = NvRmMemRd32(s_TransportInfo.hMessageMem, 0); + } else + { + NV_ASSERT(0); + return NvSuccess; + } + } + else + { + ReadData = ((NvU32*)s_TransportInfo.pTransmitMem)[0]; + } + + // Check for clear to send + if ( ReadData != 0) + return NvError_TransportMessageBoxFull; // someone else is sending a message + + if (s_TransportInfo.pTransmitMem == NULL) + { + /* QT/EMUTRANS takes this code path */ + NvRmMemWrite(s_TransportInfo.hMessageMem, 0, message, MessageLength); + } + else + { + NvOsMemcpy(s_TransportInfo.pTransmitMem, message, MessageLength); + NvOsFlushWriteCombineBuffer(); + } + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return NvSuccess; +} + +NvError +NvRmTransportSendMsgInLP0(NvRmTransportHandle hPort, void *pMessageBuffer, NvU32 MessageSize) +{ + NvU32 ReadData; + NvU32 Message[3 + ((MAX_MESSAGE_LENGTH) / sizeof(NvU32))]; + + NV_ASSERT(pMessageBuffer); + + Message[0] = TransportCmd_Message; + Message[1] = hPort->RemotePort; + Message[2] = MessageSize; + + if (MessageSize) + NvOsMemcpy(&Message[3], pMessageBuffer, MessageSize); + + ReadData = ((NvU32*)s_TransportInfo.pTransmitMem)[0]; + + // Check for clear to send + if ( ReadData != 0) + return NvError_TransportMessageBoxFull; // someone else is sending a message + + NvOsMemcpy(s_TransportInfo.pTransmitMem, Message, MessageSize + 3*sizeof(NvU32)); + NvOsFlushWriteCombineBuffer(); + + NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr); + return NvSuccess; +} + +static void +NvRmPrivTransportClearSend(NvRmDeviceHandle hDevice) +{ + if (s_TransportInfo.pTransmitMem == NULL) + { + /* QT/EMUTRANS take this path */ + if (!NV_IS_AVP) + { + NvRmMemWr32(s_TransportInfo.hMessageMem, 0, TransportCmd_None); + } else + { + NV_ASSERT(0); + } + } + else + { + ((NvU32*)s_TransportInfo.pTransmitMem)[0] = TransportCmd_None; + } +} + +/** + * Make the connection to the other end. + * Thread Safety: It is done inside the function. + */ +NvError NvRmTransportConnect(NvRmTransportHandle hPort, NvU32 TimeoutMS) +{ + NvRmTransportHandle hPartnerPort; + NvU32 StartTime; + NvU32 CurrentTime; + NvU32 ConnectMessage[ MAX_PORT_NAME_LENGTH/4 + 3]; + NvError err; + + + // Look and see if there is a local port with the same name that is currently waiting, if there is + // mark both ports as connected. + + NV_ASSERT(hPort); + NV_ASSERT(hPort->hRmDevice); + NV_ASSERT(hPort->State == PortState_Open); + + + StartTime = NvOsGetTimeMS(); + for (;;) + { + // Someone is waiting for a connection here locally. + NvOsMutexLock(s_TransportInfo.mutex); + + hPartnerPort = hPort->hConnectedPort; + if (hPartnerPort) + { + // Found a local connection + if (hPartnerPort->State == PortState_Waiting) + { + + hPartnerPort->State = PortState_Connected; + hPartnerPort->hConnectedPort = hPort; + + hPort->State = PortState_Connected; + NvOsSemaphoreSignal(hPartnerPort->hOnConnectSem); + break; + } + } + else if (s_TransportInfo.hMessageMem || s_TransportInfo.pReceiveMem) // if no shared buffer, then we can't create a remote connection. + { + ConnectMessage[0] = TransportCmd_Connect; + ConnectMessage[1] = (NvU32)hPort; + NvOsMemcpy(&ConnectMessage[2], hPort->PortName, MAX_PORT_NAME_LENGTH); + + err = NvRmPrivTransportSendMessage(hPort->hRmDevice, ConnectMessage, sizeof(ConnectMessage)); + if (!err) + { + // should send back 2 words of data. Give remote side 1000ms to respond, which should be about 100x more + // than it needs. + NvU32 WaitTime = NV_MAX(1000, TimeoutMS); + if (TimeoutMS == NV_WAIT_INFINITE) + TimeoutMS = NV_WAIT_INFINITE; + + // !!! Note, we can do this without holding the mutex... + err = NvRmPrivTransportWaitResponse(hPort->hRmDevice, ConnectMessage, 2*sizeof(NvU32), WaitTime); + NvRmPrivTransportClearSend(hPort->hRmDevice); + if (err) + { + // the other side is not responding to messages, doh! + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvError_TransportConnectionFailed; + } + + // check the response + hPort->RemotePort = ConnectMessage[1]; + if (hPort->RemotePort != 0) + { + hPort->State = PortState_Connected; + break; + } + } + } + NvOsMutexUnlock(s_TransportInfo.mutex); + NV_ASSERT(hPort->State == PortState_Open); // it better still be open + + // Didn't find a connection, wait a few ms and then try again + CurrentTime = NvOsGetTimeMS(); + if ( (CurrentTime - StartTime) > TimeoutMS ) + return NvError_Timeout; + + NvOsSleepMS(10); + } + + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvSuccess; +} + + +/** + * Set the queue depth and message size of the transport handle. + * Thread Safety: It is done inside the function. + */ +NvError NvRmTransportSetQueueDepth( + NvRmTransportHandle hPort, + NvU32 MaxQueueDepth, + NvU32 MaxMessageSize) +{ + RmReceiveMessage *pNewReceiveMsg = NULL; + + NV_ASSERT(hPort != NULL); + NV_ASSERT(MaxQueueDepth != 0); + NV_ASSERT(MaxMessageSize != 0); + + // You cannot change the queue after a connection has been opened + NV_ASSERT(hPort->State == PortState_Open); + + // !!! FIXME + // Xpc does not allow changing the base message size, so we can't change the message size here (yet!) + // Once we have per port message buffers we can set this. + NV_ASSERT(MaxMessageSize <= MAX_MESSAGE_LENGTH); + + // These are statically allocated ports, they cannot be modified! + // !!! FIXME: this is just a sanity check. Remove this and make it so that + // cpu/avp rpc doesn't call this function and just knows that the + // transport will give it a port with a large enough queue to support + // rpc, since rpc ports and queue are statically allocated this has to be true. + if (hPort == &s_RpcAvpPortStruct || + hPort == &s_RpcCpuPortStruct) + { + if (MaxMessageSize <= MAX_MESSAGE_LENGTH && + MaxQueueDepth <= MAX_MESSAGE_DEPTH) + { + return NvSuccess; + } + + NV_ASSERT(!" Illegal meesage length or queue depth. "); + } + + // Freeing default allocated message queue. + NvOsFree(hPort->RecvMessageQueue.pReceiveMsg); + hPort->RecvMessageQueue.pReceiveMsg = NULL; + // create a new message queue struct, one longer than requested on purpose. + pNewReceiveMsg = NvOsAlloc( sizeof(RmReceiveMessage) * (MaxQueueDepth+1)); + if (pNewReceiveMsg == NULL) + return NvError_InsufficientMemory; + + hPort->RecvMessageQueue.pReceiveMsg = pNewReceiveMsg; + hPort->RecvMessageQueue.QueueSize = (NvU16)(MaxQueueDepth+1); + + return NvSuccess; +} + + +static NvError +NvRmPrivTransportSendRemoteMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvError err; + NvU32 StartTime; + NvU32 CurrentTime; + NvU32 Message[3 + ((MAX_MESSAGE_LENGTH) / sizeof(NvU32))]; + + NV_ASSERT((MAX_MESSAGE_LENGTH) >= MessageSize); + + StartTime = NvOsGetTimeMS(); + + Message[0] = TransportCmd_Message; + Message[1] = hPort->RemotePort; + Message[2] = MessageSize; + + NvOsMemcpy(&Message[3], pMessageBuffer, MessageSize); + + for (;;) + { + NvOsMutexLock(s_TransportInfo.mutex); + err = NvRmPrivTransportSendMessage(hPort->hRmDevice, Message, MessageSize + 3*sizeof(NvU32)); + NvOsMutexUnlock(s_TransportInfo.mutex); + if (err == NvSuccess) + { + return NvSuccess; + } + + // Sleep and then try again in a few ms to send again + CurrentTime = NvOsGetTimeMS(); + if ( TimeoutMS != NV_WAIT_INFINITE && (CurrentTime - StartTime) > TimeoutMS ) + return NvError_Timeout; + + NvOsSleepMS(1); // try again later... + } +} + + + +static NvError +NvRmPrivTransportSendLocalMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvU32 CurrentTime; + NvU32 StartTime; + NvError err = NvSuccess; + + NvRmTransportHandle hRemotePort; + + NvOsMutexLock(s_TransportInfo.mutex); + hRemotePort = hPort->hConnectedPort; + + + StartTime = NvOsGetTimeMS(); + + for (;;) + { + // try to insert into the message into the receivers queue. + NvBool bSuccess = InsertMessage(hRemotePort, (NvU8*)pMessageBuffer, MessageSize); + if (bSuccess) + { + if (hRemotePort->hOnPushMsgSem) + NvOsSemaphoreSignal(hRemotePort->hOnPushMsgSem); + break; + } + + // The destination port is full. + if (TimeoutMS == 0) + { + err = NvError_TransportMessageBoxFull; + break; + } + + // The user wants a timeout, so we just sleep a short time so the + // other thread can pop a message. It would be better to use another semaphore + // to indicate that the box is not full, but that just seems overkill since this + // should rarely happen anyhow. + // unlock the mutex, and wait a small amount of time. + NvOsMutexUnlock(s_TransportInfo.mutex); + + NvOsSleepMS(1); + NvOsMutexLock(s_TransportInfo.mutex); + if (TimeoutMS != NV_WAIT_INFINITE) + { + // check for a timeout condition. + CurrentTime = NvOsGetTimeMS(); + if ( (CurrentTime - StartTime) >= TimeoutMS) + { + err = NvError_Timeout; + break; + } + } + } + NvOsMutexUnlock(s_TransportInfo.mutex); + + return err; +} + + +/** + * Send the message to the other end port. + * Thread Safety: It is done inside the function. + */ +NvError +NvRmTransportSendMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MessageSize, + NvU32 TimeoutMS) +{ + NvError err; + + NV_ASSERT(hPort); + NV_ASSERT(hPort->State == PortState_Connected); + NV_ASSERT(pMessageBuffer); + +#if LOOPBACK_PROFILE + if (hPort->bLoopTest) + { +# if NV_IS_AVP + ((NvU32*)pMessageBuffer)[LOOP_AVP_SEND_INDEX] = *s_TransportInfo.pTimer; +# else + ((NvU32*)pMessageBuffer)[LOOP_CPU_SEND_INDEX] = *s_TransportInfo.pTimer; +# endif + } +#endif + + if (hPort->hConnectedPort) + { + err = NvRmPrivTransportSendLocalMsg(hPort, pMessageBuffer, MessageSize, TimeoutMS); + } + else if (hPort->State == PortState_Connected) + { + err = NvRmPrivTransportSendRemoteMsg(hPort, pMessageBuffer, MessageSize, TimeoutMS); + } + else + { + NV_ASSERT(0); // someone did something naughty + err = NvError_TransportNotConnected; + } + + return err; +} + + + +/** + * Receive the message from the other end port. + * Thread Safety: It is done inside the function. + */ +NvError +NvRmTransportRecvMsg( + NvRmTransportHandle hPort, + void* pMessageBuffer, + NvU32 MaxSize, + NvU32 *pMessageSize) +{ + NvU8 TmpMessage[MAX_MESSAGE_LENGTH]; + + NV_ASSERT(hPort); + NV_ASSERT( (hPort->State == PortState_Connected) || (hPort->State == PortState_Disconnected) ); + NV_ASSERT(pMessageBuffer); + NV_ASSERT(pMessageSize); + + + NvOsMutexLock(s_TransportInfo.mutex); + if (hPort->RecvMessageQueue.ReadIndex == hPort->RecvMessageQueue.WriteIndex) + { + NvOsMutexUnlock(s_TransportInfo.mutex); + return NvError_TransportMessageBoxEmpty; + } + + ExtractMessage(hPort, (NvU8*)pMessageBuffer, pMessageSize, MaxSize); + if (*pMessageSize > MaxSize) + { + // not enough room to copy the message + NvOsMutexUnlock(s_TransportInfo.mutex); + NV_ASSERT(!" RM Transport: Illegal message size. "); + } + + + // if there was backpressure asserted, try to handle the currently posted message, and re-enable messages + if (s_TransportInfo.ReceiveBackPressureOn != s_TransportInfo.ReceiveBackPressureOff) + { + NV_ASSERT( ((NvU8)s_TransportInfo.ReceiveBackPressureOn) == ((NvU8)(s_TransportInfo.ReceiveBackPressureOff+1)) ); + ++s_TransportInfo.ReceiveBackPressureOff; + + if (s_TransportInfo.pReceiveMem == NULL) + { + /* QT/EMUTRANS takes this path. */ + NvRmMemRead(s_TransportInfo.hMessageMem, + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, + TmpMessage, + MAX_MESSAGE_LENGTH); + HandlePortMessage(hPort->hRmDevice, (volatile void *)TmpMessage); + NvRmMemWrite(s_TransportInfo.hMessageMem, + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, + TmpMessage, + 2*sizeof(NvU32) ); + } + else + { + HandlePortMessage(hPort->hRmDevice, (NvU32*)s_TransportInfo.pReceiveMem); + } + } + +#if LOOPBACK_PROFILE + if (hPort->bLoopTest) + { +# if NV_IS_AVP + ((NvU32*)pMessageBuffer)[LOOP_AVP_RECV_INDEX] = *s_TransportInfo.pTimer; +# else + ((NvU32*)pMessageBuffer)[LOOP_CPU_RECV_INDEX] = *s_TransportInfo.pTimer; +# endif + } +#endif + + NvOsMutexUnlock(s_TransportInfo.mutex); + + return NvSuccess; +} + +void +NvRmTransportGetPortName( + NvRmTransportHandle hPort, + NvU8 *PortName, + NvU32 PortNameSize ) +{ + NvU32 len; + + NV_ASSERT(hPort); + NV_ASSERT(PortName); + + len = NvOsStrlen(hPort->PortName); + if (len >= PortNameSize) + { + NV_ASSERT(!" RM Transport: Port Name too long. "); + } + + NvOsStrncpy((char *)PortName, hPort->PortName, PortNameSize); +} + diff --git a/arch/arm/mach-tegra/nvrm/dispatch/Makefile b/arch/arm/mach-tegra/nvrm/dispatch/Makefile index 959155944f47..dd1710b2b750 100644 --- a/arch/arm/mach-tegra/nvrm/dispatch/Makefile +++ b/arch/arm/mach-tegra/nvrm/dispatch/Makefile @@ -26,3 +26,5 @@ obj-y += nvrm_pmu_dispatch.o obj-y += nvrm_keylist_dispatch.o obj-y += nvrm_pcie_dispatch.o obj-y += nvrm_memctrl_dispatch.o +obj-y += nvrm_transport_dispatch.o +obj-y += nvrm_xpc_dispatch.o diff --git a/arch/arm/mach-tegra/nvrm/dispatch/NvRm_Dispatch.c b/arch/arm/mach-tegra/nvrm/dispatch/NvRm_Dispatch.c index e8ea4ee5be9e..c321c393b6cf 100644 --- a/arch/arm/mach-tegra/nvrm/dispatch/NvRm_Dispatch.c +++ b/arch/arm/mach-tegra/nvrm/dispatch/NvRm_Dispatch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 NVIDIA Corporation. + * Copyright (c) 2009-2010 NVIDIA Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,6 +35,8 @@ #include "nvassert.h" #include "nvidlcmd.h" #include "nvreftrack.h" +#include "nvrm_xpc.h" +#include "nvrm_transport.h" #include "nvrm_memctrl.h" #include "nvrm_pcie.h" #include "nvrm_pwm.h" @@ -53,6 +55,8 @@ #include "nvrm_module.h" #include "nvrm_memmgr.h" #include "nvrm_init.h" +NvError nvrm_xpc_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); +NvError nvrm_transport_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); NvError nvrm_memctrl_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); NvError nvrm_pcie_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); NvError nvrm_pwm_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); @@ -76,6 +80,8 @@ NvError nvrm_init_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void * typedef enum { NvRm_Invalid = 0, + NvRm_nvrm_xpc, + NvRm_nvrm_transport, NvRm_nvrm_memctrl, NvRm_nvrm_pcie, NvRm_nvrm_pwm, @@ -108,6 +114,8 @@ typedef struct NvIdlDispatchTableRec static NvIdlDispatchTable gs_DispatchTable[] = { + { NvRm_nvrm_xpc, nvrm_xpc_Dispatch }, + { NvRm_nvrm_transport, nvrm_transport_Dispatch }, { NvRm_nvrm_memctrl, nvrm_memctrl_Dispatch }, { NvRm_nvrm_pcie, nvrm_pcie_Dispatch }, { NvRm_nvrm_pwm, nvrm_pwm_Dispatch }, @@ -149,3 +157,4 @@ NvError NvRm_Dispatch( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutS return table_[packid_ - 1].DispFunc( funcid_, InBuffer, InSize, OutBuffer, OutSize, Ctx ); } + diff --git a/arch/arm/mach-tegra/nvrm/dispatch/nvrm_transport_dispatch.c b/arch/arm/mach-tegra/nvrm/dispatch/nvrm_transport_dispatch.c new file mode 100644 index 000000000000..a37c39d2d8ae --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/dispatch/nvrm_transport_dispatch.c @@ -0,0 +1,657 @@ + +#define NV_IDL_IS_DISPATCH + +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvassert.h" +#include "nvreftrack.h" +#include "nvidlcmd.h" +#include "nvrm_transport.h" + +#define OFFSET( s, e ) (NvU32)(void *)(&(((s*)0)->e)) + + +typedef struct NvRmTransportRecvMsg_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; + void* pMessageBuffer; + NvU32 MaxSize; +} NV_ALIGN(4) NvRmTransportRecvMsg_in; + +typedef struct NvRmTransportRecvMsg_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportRecvMsg_inout; + +typedef struct NvRmTransportRecvMsg_out_t +{ + NvError ret_; + NvU32 pMessageSize; +} NV_ALIGN(4) NvRmTransportRecvMsg_out; + +typedef struct NvRmTransportRecvMsg_params_t +{ + NvRmTransportRecvMsg_in in; + NvRmTransportRecvMsg_inout inout; + NvRmTransportRecvMsg_out out; +} NvRmTransportRecvMsg_params; + +typedef struct NvRmTransportSendMsgInLP0_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hPort; + void* message; + NvU32 MessageSize; +} NV_ALIGN(4) NvRmTransportSendMsgInLP0_in; + +typedef struct NvRmTransportSendMsgInLP0_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportSendMsgInLP0_inout; + +typedef struct NvRmTransportSendMsgInLP0_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmTransportSendMsgInLP0_out; + +typedef struct NvRmTransportSendMsgInLP0_params_t +{ + NvRmTransportSendMsgInLP0_in in; + NvRmTransportSendMsgInLP0_inout inout; + NvRmTransportSendMsgInLP0_out out; +} NvRmTransportSendMsgInLP0_params; + +typedef struct NvRmTransportSendMsg_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; + void* pMessageBuffer; + NvU32 MessageSize; + NvU32 TimeoutMS; +} NV_ALIGN(4) NvRmTransportSendMsg_in; + +typedef struct NvRmTransportSendMsg_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportSendMsg_inout; + +typedef struct NvRmTransportSendMsg_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmTransportSendMsg_out; + +typedef struct NvRmTransportSendMsg_params_t +{ + NvRmTransportSendMsg_in in; + NvRmTransportSendMsg_inout inout; + NvRmTransportSendMsg_out out; +} NvRmTransportSendMsg_params; + +typedef struct NvRmTransportSetQueueDepth_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; + NvU32 MaxQueueDepth; + NvU32 MaxMessageSize; +} NV_ALIGN(4) NvRmTransportSetQueueDepth_in; + +typedef struct NvRmTransportSetQueueDepth_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportSetQueueDepth_inout; + +typedef struct NvRmTransportSetQueueDepth_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmTransportSetQueueDepth_out; + +typedef struct NvRmTransportSetQueueDepth_params_t +{ + NvRmTransportSetQueueDepth_in in; + NvRmTransportSetQueueDepth_inout inout; + NvRmTransportSetQueueDepth_out out; +} NvRmTransportSetQueueDepth_params; + +typedef struct NvRmTransportConnect_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; + NvU32 TimeoutMS; +} NV_ALIGN(4) NvRmTransportConnect_in; + +typedef struct NvRmTransportConnect_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportConnect_inout; + +typedef struct NvRmTransportConnect_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmTransportConnect_out; + +typedef struct NvRmTransportConnect_params_t +{ + NvRmTransportConnect_in in; + NvRmTransportConnect_inout inout; + NvRmTransportConnect_out out; +} NvRmTransportConnect_params; + +typedef struct NvRmTransportWaitForConnect_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; + NvU32 TimeoutMS; +} NV_ALIGN(4) NvRmTransportWaitForConnect_in; + +typedef struct NvRmTransportWaitForConnect_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportWaitForConnect_inout; + +typedef struct NvRmTransportWaitForConnect_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmTransportWaitForConnect_out; + +typedef struct NvRmTransportWaitForConnect_params_t +{ + NvRmTransportWaitForConnect_in in; + NvRmTransportWaitForConnect_inout inout; + NvRmTransportWaitForConnect_out out; +} NvRmTransportWaitForConnect_params; + +typedef struct NvRmTransportDeInit_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmDeviceHandle hRmDevice; +} NV_ALIGN(4) NvRmTransportDeInit_in; + +typedef struct NvRmTransportDeInit_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportDeInit_inout; + +typedef struct NvRmTransportDeInit_out_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportDeInit_out; + +typedef struct NvRmTransportDeInit_params_t +{ + NvRmTransportDeInit_in in; + NvRmTransportDeInit_inout inout; + NvRmTransportDeInit_out out; +} NvRmTransportDeInit_params; + +typedef struct NvRmTransportInit_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmDeviceHandle hRmDevice; +} NV_ALIGN(4) NvRmTransportInit_in; + +typedef struct NvRmTransportInit_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportInit_inout; + +typedef struct NvRmTransportInit_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmTransportInit_out; + +typedef struct NvRmTransportInit_params_t +{ + NvRmTransportInit_in in; + NvRmTransportInit_inout inout; + NvRmTransportInit_out out; +} NvRmTransportInit_params; + +typedef struct NvRmTransportClose_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; +} NV_ALIGN(4) NvRmTransportClose_in; + +typedef struct NvRmTransportClose_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportClose_inout; + +typedef struct NvRmTransportClose_out_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportClose_out; + +typedef struct NvRmTransportClose_params_t +{ + NvRmTransportClose_in in; + NvRmTransportClose_inout inout; + NvRmTransportClose_out out; +} NvRmTransportClose_params; + +typedef struct NvRmTransportGetPortName_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmTransportHandle hTransport; + NvU8 * PortName; + NvU32 PortNameSize; +} NV_ALIGN(4) NvRmTransportGetPortName_in; + +typedef struct NvRmTransportGetPortName_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportGetPortName_inout; + +typedef struct NvRmTransportGetPortName_out_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportGetPortName_out; + +typedef struct NvRmTransportGetPortName_params_t +{ + NvRmTransportGetPortName_in in; + NvRmTransportGetPortName_inout inout; + NvRmTransportGetPortName_out out; +} NvRmTransportGetPortName_params; + +typedef struct NvRmTransportOpen_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmDeviceHandle hRmDevice; + char * pPortName_data; + NvU32 pPortName_len; + NvOsSemaphoreHandle RecvMessageSemaphore; +} NV_ALIGN(4) NvRmTransportOpen_in; + +typedef struct NvRmTransportOpen_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmTransportOpen_inout; + +typedef struct NvRmTransportOpen_out_t +{ + NvError ret_; + NvRmTransportHandle phTransport; +} NV_ALIGN(4) NvRmTransportOpen_out; + +typedef struct NvRmTransportOpen_params_t +{ + NvRmTransportOpen_in in; + NvRmTransportOpen_inout inout; + NvRmTransportOpen_out out; +} NvRmTransportOpen_params; + +static NvError NvRmTransportRecvMsg_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportRecvMsg_in *p_in; + NvRmTransportRecvMsg_out *p_out; + void* pMessageBuffer = NULL; + + p_in = (NvRmTransportRecvMsg_in *)InBuffer; + p_out = (NvRmTransportRecvMsg_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportRecvMsg_params, out) - OFFSET(NvRmTransportRecvMsg_params, inout)); + + if( p_in->MaxSize && p_in->pMessageBuffer ) + { + pMessageBuffer = (void* )NvOsAlloc( p_in->MaxSize ); + if( !pMessageBuffer ) + { + err_ = NvError_InsufficientMemory; + goto clean; + } + } + + p_out->ret_ = NvRmTransportRecvMsg( p_in->hTransport, pMessageBuffer, p_in->MaxSize, &p_out->pMessageSize ); + + if(p_in->pMessageBuffer && pMessageBuffer) + { + err_ = NvOsCopyOut( p_in->pMessageBuffer, pMessageBuffer, p_in->MaxSize ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + } + } +clean: + NvOsFree( pMessageBuffer ); + return err_; +} + +static NvError NvRmTransportSendMsgInLP0_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportSendMsgInLP0_in *p_in; + NvRmTransportSendMsgInLP0_out *p_out; + void* message = NULL; + + p_in = (NvRmTransportSendMsgInLP0_in *)InBuffer; + p_out = (NvRmTransportSendMsgInLP0_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportSendMsgInLP0_params, out) - OFFSET(NvRmTransportSendMsgInLP0_params, inout)); + + if( p_in->MessageSize && p_in->message ) + { + message = (void* )NvOsAlloc( p_in->MessageSize ); + if( !message ) + { + err_ = NvError_InsufficientMemory; + goto clean; + } + if( p_in->message ) + { + err_ = NvOsCopyIn( message, p_in->message, p_in->MessageSize ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + goto clean; + } + } + } + + p_out->ret_ = NvRmTransportSendMsgInLP0( p_in->hPort, message, p_in->MessageSize ); + +clean: + NvOsFree( message ); + return err_; +} + +static NvError NvRmTransportSendMsg_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportSendMsg_in *p_in; + NvRmTransportSendMsg_out *p_out; + void* pMessageBuffer = NULL; + + p_in = (NvRmTransportSendMsg_in *)InBuffer; + p_out = (NvRmTransportSendMsg_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportSendMsg_params, out) - OFFSET(NvRmTransportSendMsg_params, inout)); + + if( p_in->MessageSize && p_in->pMessageBuffer ) + { + pMessageBuffer = (void* )NvOsAlloc( p_in->MessageSize ); + if( !pMessageBuffer ) + { + err_ = NvError_InsufficientMemory; + goto clean; + } + if( p_in->pMessageBuffer ) + { + err_ = NvOsCopyIn( pMessageBuffer, p_in->pMessageBuffer, p_in->MessageSize ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + goto clean; + } + } + } + + p_out->ret_ = NvRmTransportSendMsg( p_in->hTransport, pMessageBuffer, p_in->MessageSize, p_in->TimeoutMS ); + +clean: + NvOsFree( pMessageBuffer ); + return err_; +} + +static NvError NvRmTransportSetQueueDepth_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportSetQueueDepth_in *p_in; + NvRmTransportSetQueueDepth_out *p_out; + + p_in = (NvRmTransportSetQueueDepth_in *)InBuffer; + p_out = (NvRmTransportSetQueueDepth_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportSetQueueDepth_params, out) - OFFSET(NvRmTransportSetQueueDepth_params, inout)); + + + p_out->ret_ = NvRmTransportSetQueueDepth( p_in->hTransport, p_in->MaxQueueDepth, p_in->MaxMessageSize ); + + return err_; +} + +static NvError NvRmTransportConnect_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportConnect_in *p_in; + NvRmTransportConnect_out *p_out; + + p_in = (NvRmTransportConnect_in *)InBuffer; + p_out = (NvRmTransportConnect_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportConnect_params, out) - OFFSET(NvRmTransportConnect_params, inout)); + + + p_out->ret_ = NvRmTransportConnect( p_in->hTransport, p_in->TimeoutMS ); + + return err_; +} + +static NvError NvRmTransportWaitForConnect_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportWaitForConnect_in *p_in; + NvRmTransportWaitForConnect_out *p_out; + + p_in = (NvRmTransportWaitForConnect_in *)InBuffer; + p_out = (NvRmTransportWaitForConnect_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportWaitForConnect_params, out) - OFFSET(NvRmTransportWaitForConnect_params, inout)); + + + p_out->ret_ = NvRmTransportWaitForConnect( p_in->hTransport, p_in->TimeoutMS ); + + return err_; +} + +static NvError NvRmTransportDeInit_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportDeInit_in *p_in; + + p_in = (NvRmTransportDeInit_in *)InBuffer; + + + NvRmTransportDeInit( p_in->hRmDevice ); + + return err_; +} + +static NvError NvRmTransportInit_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportInit_in *p_in; + NvRmTransportInit_out *p_out; + + p_in = (NvRmTransportInit_in *)InBuffer; + p_out = (NvRmTransportInit_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportInit_params, out) - OFFSET(NvRmTransportInit_params, inout)); + + + p_out->ret_ = NvRmTransportInit( p_in->hRmDevice ); + + return err_; +} + +static NvError NvRmTransportClose_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportClose_in *p_in; + + p_in = (NvRmTransportClose_in *)InBuffer; + + + NvRmTransportClose( p_in->hTransport ); + + return err_; +} + +static NvError NvRmTransportGetPortName_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportGetPortName_in *p_in; + NvU8 *PortName = NULL; + + p_in = (NvRmTransportGetPortName_in *)InBuffer; + + if( p_in->PortNameSize && p_in->PortName ) + { + PortName = (NvU8 *)NvOsAlloc( p_in->PortNameSize * sizeof( NvU8 ) ); + if( !PortName ) + { + err_ = NvError_InsufficientMemory; + goto clean; + } + if( p_in->PortName ) + { + err_ = NvOsCopyIn( PortName, p_in->PortName, p_in->PortNameSize * sizeof( NvU8 ) ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + goto clean; + } + } + } + + NvRmTransportGetPortName( p_in->hTransport, PortName, p_in->PortNameSize ); + + if(p_in->PortName && PortName) + { + err_ = NvOsCopyOut( p_in->PortName, PortName, p_in->PortNameSize * sizeof( NvU8 ) ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + } + } +clean: + NvOsFree( PortName ); + return err_; +} + +static NvError NvRmTransportOpen_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmTransportOpen_in *p_in; + NvRmTransportOpen_out *p_out; + char *pPortName = NULL; + NvOsSemaphoreHandle RecvMessageSemaphore = NULL; + + p_in = (NvRmTransportOpen_in *)InBuffer; + p_out = (NvRmTransportOpen_out *)((NvU8 *)OutBuffer + OFFSET(NvRmTransportOpen_params, out) - OFFSET(NvRmTransportOpen_params, inout)); + + if( p_in->pPortName_len ) + { + pPortName = NvOsAlloc( p_in->pPortName_len ); + if( !pPortName ) + { + err_ = NvError_InsufficientMemory; + goto clean; + } + err_ = NvOsCopyIn( pPortName, p_in->pPortName_data, p_in->pPortName_len ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + goto clean; + } + if( pPortName[p_in->pPortName_len - 1] != 0 ) + { + err_ = NvError_BadParameter; + goto clean; + } + } + if( p_in->RecvMessageSemaphore ) + { + err_ = NvOsSemaphoreUnmarshal( p_in->RecvMessageSemaphore, &RecvMessageSemaphore ); + if( err_ != NvSuccess ) + { + err_ = NvError_BadParameter; + goto clean; + } + } + + p_out->ret_ = NvRmTransportOpen( p_in->hRmDevice, pPortName, RecvMessageSemaphore, &p_out->phTransport ); + +clean: + NvOsFree( pPortName ); + NvOsSemaphoreDestroy( RecvMessageSemaphore ); + return err_; +} + +NvError nvrm_transport_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); +NvError nvrm_transport_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + + switch( function ) { + case 10: + err_ = NvRmTransportRecvMsg_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 9: + err_ = NvRmTransportSendMsgInLP0_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 8: + err_ = NvRmTransportSendMsg_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 7: + err_ = NvRmTransportSetQueueDepth_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 6: + err_ = NvRmTransportConnect_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 5: + err_ = NvRmTransportWaitForConnect_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 4: + err_ = NvRmTransportDeInit_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 3: + err_ = NvRmTransportInit_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 2: + err_ = NvRmTransportClose_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 1: + err_ = NvRmTransportGetPortName_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 0: + err_ = NvRmTransportOpen_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + default: + err_ = NvError_BadParameter; + break; + } + + return err_; +} diff --git a/arch/arm/mach-tegra/nvrm/dispatch/nvrm_xpc_dispatch.c b/arch/arm/mach-tegra/nvrm/dispatch/nvrm_xpc_dispatch.c new file mode 100644 index 000000000000..16b98ede32e0 --- /dev/null +++ b/arch/arm/mach-tegra/nvrm/dispatch/nvrm_xpc_dispatch.c @@ -0,0 +1,348 @@ + +#define NV_IDL_IS_DISPATCH + +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nvcommon.h" +#include "nvos.h" +#include "nvassert.h" +#include "nvreftrack.h" +#include "nvidlcmd.h" +#include "nvrm_xpc.h" + +#define OFFSET( s, e ) (NvU32)(void *)(&(((s*)0)->e)) + + +typedef struct NvRmXpcModuleRelease_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmModuleID modId; +} NV_ALIGN(4) NvRmXpcModuleRelease_in; + +typedef struct NvRmXpcModuleRelease_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmXpcModuleRelease_inout; + +typedef struct NvRmXpcModuleRelease_out_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmXpcModuleRelease_out; + +typedef struct NvRmXpcModuleRelease_params_t +{ + NvRmXpcModuleRelease_in in; + NvRmXpcModuleRelease_inout inout; + NvRmXpcModuleRelease_out out; +} NvRmXpcModuleRelease_params; + +typedef struct NvRmXpcModuleAcquire_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmModuleID modId; +} NV_ALIGN(4) NvRmXpcModuleAcquire_in; + +typedef struct NvRmXpcModuleAcquire_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmXpcModuleAcquire_inout; + +typedef struct NvRmXpcModuleAcquire_out_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmXpcModuleAcquire_out; + +typedef struct NvRmXpcModuleAcquire_params_t +{ + NvRmXpcModuleAcquire_in in; + NvRmXpcModuleAcquire_inout inout; + NvRmXpcModuleAcquire_out out; +} NvRmXpcModuleAcquire_params; + +typedef struct NvRmXpcInitArbSemaSystem_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmDeviceHandle hDevice; +} NV_ALIGN(4) NvRmXpcInitArbSemaSystem_in; + +typedef struct NvRmXpcInitArbSemaSystem_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmXpcInitArbSemaSystem_inout; + +typedef struct NvRmXpcInitArbSemaSystem_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmXpcInitArbSemaSystem_out; + +typedef struct NvRmXpcInitArbSemaSystem_params_t +{ + NvRmXpcInitArbSemaSystem_in in; + NvRmXpcInitArbSemaSystem_inout inout; + NvRmXpcInitArbSemaSystem_out out; +} NvRmXpcInitArbSemaSystem_params; + +typedef struct NvRmPrivXpcGetMessage_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmPrivXpcMessageHandle hXpcMessage; +} NV_ALIGN(4) NvRmPrivXpcGetMessage_in; + +typedef struct NvRmPrivXpcGetMessage_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmPrivXpcGetMessage_inout; + +typedef struct NvRmPrivXpcGetMessage_out_t +{ + NvU32 ret_; +} NV_ALIGN(4) NvRmPrivXpcGetMessage_out; + +typedef struct NvRmPrivXpcGetMessage_params_t +{ + NvRmPrivXpcGetMessage_in in; + NvRmPrivXpcGetMessage_inout inout; + NvRmPrivXpcGetMessage_out out; +} NvRmPrivXpcGetMessage_params; + +typedef struct NvRmPrivXpcSendMessage_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmPrivXpcMessageHandle hXpcMessage; + NvU32 data; +} NV_ALIGN(4) NvRmPrivXpcSendMessage_in; + +typedef struct NvRmPrivXpcSendMessage_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmPrivXpcSendMessage_inout; + +typedef struct NvRmPrivXpcSendMessage_out_t +{ + NvError ret_; +} NV_ALIGN(4) NvRmPrivXpcSendMessage_out; + +typedef struct NvRmPrivXpcSendMessage_params_t +{ + NvRmPrivXpcSendMessage_in in; + NvRmPrivXpcSendMessage_inout inout; + NvRmPrivXpcSendMessage_out out; +} NvRmPrivXpcSendMessage_params; + +typedef struct NvRmPrivXpcDestroy_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmPrivXpcMessageHandle hXpcMessage; +} NV_ALIGN(4) NvRmPrivXpcDestroy_in; + +typedef struct NvRmPrivXpcDestroy_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmPrivXpcDestroy_inout; + +typedef struct NvRmPrivXpcDestroy_out_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmPrivXpcDestroy_out; + +typedef struct NvRmPrivXpcDestroy_params_t +{ + NvRmPrivXpcDestroy_in in; + NvRmPrivXpcDestroy_inout inout; + NvRmPrivXpcDestroy_out out; +} NvRmPrivXpcDestroy_params; + +typedef struct NvRmPrivXpcCreate_in_t +{ + NvU32 package_; + NvU32 function_; + NvRmDeviceHandle hDevice; +} NV_ALIGN(4) NvRmPrivXpcCreate_in; + +typedef struct NvRmPrivXpcCreate_inout_t +{ + NvU32 dummy_; +} NV_ALIGN(4) NvRmPrivXpcCreate_inout; + +typedef struct NvRmPrivXpcCreate_out_t +{ + NvError ret_; + NvRmPrivXpcMessageHandle phXpcMessage; +} NV_ALIGN(4) NvRmPrivXpcCreate_out; + +typedef struct NvRmPrivXpcCreate_params_t +{ + NvRmPrivXpcCreate_in in; + NvRmPrivXpcCreate_inout inout; + NvRmPrivXpcCreate_out out; +} NvRmPrivXpcCreate_params; + +static NvError NvRmXpcModuleRelease_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmXpcModuleRelease_in *p_in; + + p_in = (NvRmXpcModuleRelease_in *)InBuffer; + + + NvRmXpcModuleRelease( p_in->modId ); + + return err_; +} + +static NvError NvRmXpcModuleAcquire_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmXpcModuleAcquire_in *p_in; + + p_in = (NvRmXpcModuleAcquire_in *)InBuffer; + + + NvRmXpcModuleAcquire( p_in->modId ); + + return err_; +} + +static NvError NvRmXpcInitArbSemaSystem_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmXpcInitArbSemaSystem_in *p_in; + NvRmXpcInitArbSemaSystem_out *p_out; + + p_in = (NvRmXpcInitArbSemaSystem_in *)InBuffer; + p_out = (NvRmXpcInitArbSemaSystem_out *)((NvU8 *)OutBuffer + OFFSET(NvRmXpcInitArbSemaSystem_params, out) - OFFSET(NvRmXpcInitArbSemaSystem_params, inout)); + + + p_out->ret_ = NvRmXpcInitArbSemaSystem( p_in->hDevice ); + + return err_; +} + +static NvError NvRmPrivXpcGetMessage_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmPrivXpcGetMessage_in *p_in; + NvRmPrivXpcGetMessage_out *p_out; + + p_in = (NvRmPrivXpcGetMessage_in *)InBuffer; + p_out = (NvRmPrivXpcGetMessage_out *)((NvU8 *)OutBuffer + OFFSET(NvRmPrivXpcGetMessage_params, out) - OFFSET(NvRmPrivXpcGetMessage_params, inout)); + + + p_out->ret_ = NvRmPrivXpcGetMessage( p_in->hXpcMessage ); + + return err_; +} + +static NvError NvRmPrivXpcSendMessage_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmPrivXpcSendMessage_in *p_in; + NvRmPrivXpcSendMessage_out *p_out; + + p_in = (NvRmPrivXpcSendMessage_in *)InBuffer; + p_out = (NvRmPrivXpcSendMessage_out *)((NvU8 *)OutBuffer + OFFSET(NvRmPrivXpcSendMessage_params, out) - OFFSET(NvRmPrivXpcSendMessage_params, inout)); + + + p_out->ret_ = NvRmPrivXpcSendMessage( p_in->hXpcMessage, p_in->data ); + + return err_; +} + +static NvError NvRmPrivXpcDestroy_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmPrivXpcDestroy_in *p_in; + + p_in = (NvRmPrivXpcDestroy_in *)InBuffer; + + + NvRmPrivXpcDestroy( p_in->hXpcMessage ); + + return err_; +} + +static NvError NvRmPrivXpcCreate_dispatch_( void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + NvRmPrivXpcCreate_in *p_in; + NvRmPrivXpcCreate_out *p_out; + + p_in = (NvRmPrivXpcCreate_in *)InBuffer; + p_out = (NvRmPrivXpcCreate_out *)((NvU8 *)OutBuffer + OFFSET(NvRmPrivXpcCreate_params, out) - OFFSET(NvRmPrivXpcCreate_params, inout)); + + + p_out->ret_ = NvRmPrivXpcCreate( p_in->hDevice, &p_out->phXpcMessage ); + + return err_; +} + +NvError nvrm_xpc_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ); +NvError nvrm_xpc_Dispatch( NvU32 function, void *InBuffer, NvU32 InSize, void *OutBuffer, NvU32 OutSize, NvDispatchCtx* Ctx ) +{ + NvError err_ = NvSuccess; + + switch( function ) { + case 6: + err_ = NvRmXpcModuleRelease_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 5: + err_ = NvRmXpcModuleAcquire_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 4: + err_ = NvRmXpcInitArbSemaSystem_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 3: + err_ = NvRmPrivXpcGetMessage_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 2: + err_ = NvRmPrivXpcSendMessage_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 1: + err_ = NvRmPrivXpcDestroy_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + case 0: + err_ = NvRmPrivXpcCreate_dispatch_( InBuffer, InSize, OutBuffer, OutSize, Ctx ); + break; + default: + err_ = NvError_BadParameter; + break; + } + + return err_; +} diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c index 9108ae399152..dc76869f6dec 100644 --- a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c +++ b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c @@ -44,6 +44,8 @@ #define NVODM_ADT7461_PRINTF(x) #endif +#define ADT7461_ALERT_DEBOUNCE (1) + // ADT7461 Descrriptor static const ADT7461Info s_Adt7461Info = { @@ -347,6 +349,7 @@ Adt7461ConfigureSampleInterval( if(!Adt7461WriteReg(pPrivData, pReg, i)) return NV_FALSE; + pPrivData->ShadowRate = i; } *pTargetMs = s_Adt7461SampleIntervalsMS[i]; return NV_TRUE; @@ -366,7 +369,13 @@ static void Adt7461Isr(void* arg) { Callback(CallbackArg); } - +#if ADT7461_ALERT_DEBOUNCE + // New range limits set by callback are not guaranteed to take effect + // before the next temperature conversion is completed, and interrupt + // can not be cleared until then. Hence, the debounce delay below. + NvOdmOsSleepMS(s_Adt7461SampleIntervalsMS[pPrivData->ShadowRate] + + s_Adt7461ConversionTimesMS[pPrivData->ShadowRate] + 1); +#endif // Read status and ARA to finish clearing interrupt after callback pReg = &pPrivData->pDeviceInfo->Status; (void)Adt7461ReadReg(pPrivData, pReg, &Data); @@ -557,6 +566,7 @@ NvBool Adt7461Init(NvOdmTmonDeviceHandle hTmon) pReg = &pPrivData->pDeviceInfo->Rate; if(!Adt7461WriteReg(pPrivData, pReg, Data)) goto fail; + pPrivData->ShadowRate = Data; // Set remote channel offset (8-bit 2's complement value for any range) Data = ((NvU8)ADT7461_ODM_REMOTE_OFFSET_VALUE); diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.h b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.h index 485c82523a55..ffe4bf15dbec 100644 --- a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.h +++ b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.h @@ -125,6 +125,9 @@ typedef struct ADT7461PrivDataRec // Shadow of ADT7461 internal configuration register NvU8 ShadowConfig; + // Shadow of ADT7461 internal rate settings + NvU8 ShadowRate; + // Shadow of ADT7461 internal address pointer NvU8 ShadowRegPtr; diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461_reg.h b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461_reg.h index 290deeb9ebf3..e0e930e79fe7 100644 --- a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461_reg.h +++ b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461_reg.h @@ -154,7 +154,7 @@ typedef enum ADT7461ConfigBits_IntrAutoClear = (0x1 << 5), // If set - put device in stanby mode - // Ig cleared - put device in running mode + // If cleared - put device in running mode ADT7461ConfigBits_Standby = (0x1 << 6), // If set - interrupt from device is disabled @@ -165,10 +165,9 @@ typedef enum // ADT7461 initial configuration set by adaptation: // ADT7461 THERM1 output is dedicated for critical h/w shutdown, and ADT7461 // ALERT/THERM2 output is always configured as out of limit ALERT interrupt. -// Range and standby onfiguration options are selected per ODM policy macros. +// Monitor is in running mode, in the range selected per ODM policy. #define ADT7461_INITIAL_CONFIG \ ((ADT7461ConfigBits_IntrDisabled) | \ - (ADT7461_ODM_STANDBY_ENABLED ? ADT7461ConfigBits_Standby : 0) | \ (ADT7461_ODM_EXTENDED_RANGE ? ADT7461ConfigBits_ExtendedRange : 0)) diff --git a/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c b/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c index a751b818118e..dc0bfdfaf69a 100755 --- a/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c +++ b/arch/arm/mach-tegra/odm_kit/query/whistler/nvodm_query.c @@ -1126,7 +1126,7 @@ NvBool NvOdmQueryGetPmuProperty(NvOdmPmuProperty* pPmuProperty) // Not there yet, add it later ... //pPmuProperty->CpuPowerReqPolarity = ; - pPmuProperty->CorePowerReqPolarity = NvOdmCorePowerReqPolarity_Low; + pPmuProperty->CorePowerReqPolarity = NvOdmCorePowerReqPolarity_High; pPmuProperty->SysClockReqPolarity = NvOdmSysClockReqPolarity_High; pPmuProperty->CombinedPowerReq = NV_TRUE; pPmuProperty->CpuPowerGoodUs = 2000; diff --git a/arch/arm/mach-tegra/power-context-t2.c b/arch/arm/mach-tegra/power-context-t2.c index 70e21f1237d4..5cb579297682 100644 --- a/arch/arm/mach-tegra/power-context-t2.c +++ b/arch/arm/mach-tegra/power-context-t2.c @@ -58,7 +58,7 @@ static void update_registers_for_lp0(void) //SCRATCH0 : Warmbootflag Reg = NV_REGR(s_hRmGlobal, NvRmModuleID_Pmif, 0, APBDEV_PMC_SCRATCH0_0); - Reg = NV_FLD_SET_DRF_NUM (APBDEV_PMC, SCRATCH0, WARM_BOOT0_FLAG, 0, Reg); + Reg = NV_FLD_SET_DRF_NUM (APBDEV_PMC, SCRATCH0, WARM_BOOT0_FLAG, 1, Reg); NV_REGW(s_hRmGlobal, NvRmModuleID_Pmif, 0, APBDEV_PMC_SCRATCH0_0, Reg); @@ -217,7 +217,7 @@ static NvU32* save_clockreset_context( case PowerModuleContext_Save: //Register base address must have been set //by PowerModuleContext_Init. - if (pBase == NULL); + if (pBase == NULL) goto fail; //Anchor the starting point for this controller's context. @@ -252,7 +252,7 @@ static NvU32* save_clockreset_context( case PowerModuleContext_Restore: //Register base address must have been set //by PowerModuleContext_Init. - if (pBase == NULL); + if (pBase == NULL) goto fail; //We should be at the same place in the context diff --git a/arch/arm/mach-tegra/power-lp.S b/arch/arm/mach-tegra/power-lp.S index acc7645e34c1..ad21c95aee25 100644 --- a/arch/arm/mach-tegra/power-lp.S +++ b/arch/arm/mach-tegra/power-lp.S @@ -41,6 +41,7 @@ #define CSITE_CPUDBG0_LAR_0 0x10fb0 #define CSITE_CPUDBG1_LAR_0 0x12fb0 #define TEMP_AREA_SIZE 16 +#define DEBUG_FORCE_RTC_WAKEUP 5 #else #error "Unrecognized Tegra SoC Family" #endif @@ -52,6 +53,7 @@ ENTRY(enter_power_state) //with IRQs turned off mrs r2, CPSR stmfd sp!, {r0-r12, lr} + stmfd sp!, {r0} cmp r1, #0 bne save_arm_state @@ -92,10 +94,73 @@ ArmCortexA9Saved: ands r2, r2, #0x3 bne reset_slave + //Check which power state we want to enter + ldmfd sp!, {r0} + //Is it LP2? + cmp r0, #0 + ldreq r2, =g_enterLP2PA + ldreq r2, [r2] + beq transition_to_state + + ldr r4, =g_pIRAM + ldr r4, [r4] + + //Is it LP1? + cmp r0, #1 + ldreq r2, =g_enterLP2PA + ldreq r2, [r2] + + //Is it LP0? + cmp r0, #2 + ldr r5, =enter_lp0_end + ldr r6, =enter_lp0 + + //For LP0, the AVP stores it's continuation address at the first + //location in IRAM. Before we overwrite IRAM with the LP0 entry + //code, copy the AVP continuation and store it in the scratch + //register dedicated for this purposed. + + //R1 = *g_pIRAM + ldr r1, [r4] + //R3 = &(g_pPMC) + ldr r3, =g_pPMC + //R3 = g_pPMC + ldr r3, [r3] + //Store in scratch39 + str r1, [r3, #APBDEV_PMC_SCRATCH39_0] + +copy_to_iram: + //Copy the enter_lp0 function to IRAM using 8x4 block moves. + //It doesn't matter if we copy a few extra words. + //IRAM has already been safely saved by the AVP at this point + //R4 = destination address to copy code to + //R5 = size of code to copy in bytes + //R6 = source address to copy code from + + //r2 is the source address + cpy r2, r6 + //r3 is the size to copy + sub r3, r5, r6 + +copy_code: + //Load source + ldmia r2!, {r5-r12} + //Store at destination + stmia r4!, {r5-r12} + //Decrement count + subs r3, r3, #32 + bgt copy_code + + //Get the physical address of IRAM + //This is where we will jump to start LP0 + ldr r2, =g_IramPA + ldr r2, [r2] + //We are the master. We should //turn of MMUs and caches. //Write a value to unblock the slaves +transition_to_state: //Turn off caches and MMU mrc p15, 0, r3, c1, c0, 0 bic r3, r3, #(1<<12) //I-Cache @@ -108,9 +173,6 @@ ArmCortexA9Saved: ldr r1, =g_wakeupCcbp ldr r1, [r1] - ldr r2, =g_enterLP2PA - ldr r2, [r2] - mov r10, #0 mcr p15, 0, r10, c8, c7, 0 // invalidate TLB dsb @@ -119,7 +181,7 @@ ArmCortexA9Saved: //Disable L1 caches and MMU mcr p15, 0, r3, c1, c0, 0 - //Finish up LP2 by entering flow control state + //Jump to the appropriate LPx function //bl enter_lp2 bx r2 @@ -148,6 +210,7 @@ wait_for_master: str r3, [r2] finish_power_state: + ldmfd sp!, {r0} ldmfd sp!, {r0-r12, lr} bx lr ENDPROC(EnterPowerState) @@ -343,10 +406,11 @@ TempStoreArea: ENDPROC(exit_lp2) ENTRY(enter_lp0) - ldr r4, [pc, #0x84] //EMC base - ldr r5, [pc, #0x84] //PMC base - ldr r6, [pc, #0x84] //FLOW base - ldr r7, [pc, #0x84] //TIMERUS base + ldr r4, [pc, #0xC8] //EMC base + ldr r5, [pc, #0xC8] //PMC base + ldr r6, [pc, #0xC8] //FLOW base + ldr r7, [pc, #0xC8] //TIMERUS base + ldr r8, [pc, #0xC8] //RTC base //Flush the write buffer dmb @@ -388,6 +452,29 @@ is_self: orr r2, r2, #1 str r2, [r6, #8] + //r0 = RTC_BASE + mov r0, r8 + //setup rtc wake + ldr r2, [r0, #0x10] //milli + ldr r2, [r0, #0x8] //shadow + + add r2, r2, #DEBUG_FORCE_RTC_WAKEUP +rtc_idle1: + ldr r1, [r0, #0x4] + tst r1, #0x1 + bne rtc_idle1 + str r2, [r0, #0x14] +rtc_idle2: + ldr r1, [r0, #0x4] + tst r1, #0x1 + bne rtc_idle2 + //intr mask alarm0 + mov r2, #1 + str r2, [r0, #0x28] +rtc_idle3: + ldr r1, [r0, #0x4] + tst r1, #0x1 + bne rtc_idle3 //Save the microsecond count before LP0 ldr r2, [r7] str r2, [r5, #0x134] @@ -403,6 +490,8 @@ do_wfi: andvc lr, r0, r0, lsl #8 andvs r7, r0, r0 andvs r5, r0, r0, lsl r0 + .word 0x7000e000 +enter_lp0_end: ENDPROC(enter_lp0) ENTRY(exit_power_state) @@ -443,8 +532,10 @@ ArmCortexA9PhysicalRestored: //Check if power state is POWER_STATE_LP0 cmp r0, #2 bne skip_pll - ldr r0, =PMC_PA_BASE - ldr r1, =TIMERUS_PA_BASE + ldr r0, =g_pPMC + ldr r0, [r0] + ldr r1, =g_pTimerus + ldr r1, [r1] //Read from LP0 exit time from SCRATCH1 ldr r2, [r0, #0x54] @@ -455,7 +546,8 @@ pll_wait: blt pll_wait //Put CPU clock source on PLLX - ldr r0, =CLK_RST_PA_BASE + ldr r0, =g_pCLK_RST_CONTROLLER + ldr r0, [r0] ldr r1, =0x20008888 str r1, [r0, #0x20] @@ -482,6 +574,7 @@ ArmCortexA9VirtualRestored: skip_local_timer_restore: //Restore the stack registers + ldmfd sp!, {r0} ldmfd sp!, {r0-r12, lr} //Restore the CPSR stored in r2 diff --git a/arch/arm/mach-tegra/power-t2.c b/arch/arm/mach-tegra/power-t2.c index 25bee44d12a3..4559cb520778 100644 --- a/arch/arm/mach-tegra/power-t2.c +++ b/arch/arm/mach-tegra/power-t2.c @@ -50,7 +50,8 @@ NvU32 g_enterLP2PA = 0; NvU32 g_localTimerLoadRegister, g_localTimerCntrlRegister; NvU32 g_coreSightClock, g_currentCcbp; volatile void *g_pPMC, *g_pAHB, *g_pCLK_RST_CONTROLLER; -volatile void *g_pEMC, *g_pMC, *g_pAPB_MISC; +volatile void *g_pEMC, *g_pMC, *g_pAPB_MISC, *g_pTimerus; +volatile void *g_pIRAM; // Chip external specific wakeup events list static const struct wakeup_source s_WakeupSources[] = @@ -136,7 +137,9 @@ void cpu_ap20_do_lp0(void) //Enter low power LP0 mode prepare_for_wb0(); shadow_lp0_scratch_regs(); + printk("LP0: Entering...\n"); enter_power_state(POWER_STATE_LP0, 0); + printk("LP0: Exited...\n"); shadow_runstate_scratch_regs(); if (HasPmuProperty && PmuProperty.CombinedPowerReq) @@ -229,12 +232,10 @@ void power_lp0_init(void) LPStateInfo = NvOdmQueryLowestSocPowerState(); - //CPU power request must be already configured and enabled in early boot - //by now. Leave it enabled to be ready for LP2/LP1. + //Enable CPU power request. Leave it enabled to be ready for LP2/LP1. Reg = NV_PMC_REGR(g_pPMC, CNTRL); - Reg = NV_DRF_VAL(APBDEV_PMC, CNTRL, CPUPWRREQ_OE, Reg); - if (Reg != APBDEV_PMC_CNTRL_0_CPUPWRREQ_OE_ENABLE) - goto fail; + Reg = NV_FLD_SET_DRF_DEF(APBDEV_PMC, CNTRL, CPUPWRREQ_OE, ENABLE, Reg); + NV_PMC_REGW(g_pPMC, CNTRL, Reg); //If the system supports deep sleep (LP0), initialize PMC accordingly. if (LPStateInfo->LowestPowerState == NvOdmSocPowerState_DeepSleep) @@ -284,6 +285,7 @@ void power_lp0_init(void) //Create the list of wakeup IRQs. create_wakeup_irqs(); + return; fail: printk("lp0 init failed!\n"); } diff --git a/arch/arm/mach-tegra/power.h b/arch/arm/mach-tegra/power.h index 53e9e9568fd2..6903b961ec67 100644 --- a/arch/arm/mach-tegra/power.h +++ b/arch/arm/mach-tegra/power.h @@ -109,18 +109,6 @@ extern NvRmDeviceHandle s_hRmGlobal; //------------------------------------------------------------------------------ //Correct name \ Broken name from nvboot_pmc_scratch_map.h -#define APBDEV_PMC_SCRATCH2_0_CLK_RST_CONTROLLER_OSC_CTRL_0_XOBP_RANGE\ - APBDEV_PMC_SCRATCH2_0_CLK_RST_OSC_CTRL_XOBP_RANGE -#define APBDEV_PMC_SCRATCH2_0_CLK_RST_CONTROLLER_PLLM_BASE_0_PLLM_DIVM_RANGE\ - APBDEV_PMC_SCRATCH2_0_CLK_RST_PLLM_BASE_PLLM_DIVM_RANGE -#define APBDEV_PMC_SCRATCH2_0_CLK_RST_CONTROLLER_PLLM_BASE_0_PLLM_DIVN_RANGE\ - APBDEV_PMC_SCRATCH2_0_CLK_RST_PLLM_BASE_PLLM_DIVN_RANGE -#define APBDEV_PMC_SCRATCH2_0_CLK_RST_CONTROLLER_PLLM_BASE_0_PLLM_DIVP_RANGE\ - APBDEV_PMC_SCRATCH2_0_CLK_RST_PLLM_BASE_PLLM_DIVP_RANGE -#define APBDEV_PMC_SCRATCH2_0_CLK_RST_CONTROLLER_PLLM_MISC_0_PLLM_CPCON_RANGE\ - APBDEV_PMC_SCRATCH2_0_CLK_RST_PLLM_MISC_CPCON_RANGE -#define APBDEV_PMC_SCRATCH2_0_CLK_RST_CONTROLLER_PLLM_MISC_0_PLLM_LFCON_RANGE\ - APBDEV_PMC_SCRATCH2_0_CLK_RST_PLLM_MISC_LFCON_RANGE #define APBDEV_PMC_SCRATCH3_0_CLK_RST_CONTROLLER_PLLX_BASE_0_PLLX_DIVP_RANGE\ APBDEV_PMC_SCRATCH3_0_CLK_RST_PLLX_BASE_PLLX_DIVP_RANGE #define APBDEV_PMC_SCRATCH3_0_CLK_RST_CONTROLLER_PLLX_BASE_0_PLLX_DIVN_RANGE\ @@ -173,9 +161,7 @@ extern NvRmDeviceHandle s_hRmGlobal; @param s Scratch register name (APBDEV_PMC_s) */ #define SCRATCH_REGS() \ - SCRATCH_REG(SCRATCH2) \ SCRATCH_REG(SCRATCH3) \ - SCRATCH_REG(SCRATCH4) \ SCRATCH_REG(SCRATCH5) \ SCRATCH_REG(SCRATCH6) \ SCRATCH_REG(SCRATCH7) \ @@ -195,7 +181,6 @@ extern NvRmDeviceHandle s_hRmGlobal; SCRATCH_REG(SCRATCH21) \ SCRATCH_REG(SCRATCH22) \ SCRATCH_REG(SCRATCH23) \ - SCRATCH_REG(SCRATCH24) \ SCRATCH_REG(SCRATCH25) \ SCRATCH_REG(SCRATCH35) \ SCRATCH_REG(SCRATCH36) \ @@ -216,20 +201,12 @@ extern NvRmDeviceHandle s_hRmGlobal; REG(SCRATCH25, AHB, ARBITRATION_XBAR_CTRL, HOLD_DIS) \ REG(SCRATCH25, AHB, ARBITRATION_XBAR_CTRL, MEM_INIT_DONE) \ /* CLK_RST Group */ \ - REG(SCRATCH2, CLK_RST_CONTROLLER, OSC_CTRL, XOBP) \ - REG(SCRATCH2, CLK_RST_CONTROLLER, PLLM_BASE, PLLM_DIVM) \ - REG(SCRATCH2, CLK_RST_CONTROLLER, PLLM_BASE, PLLM_DIVN) \ - REG(SCRATCH2, CLK_RST_CONTROLLER, PLLM_BASE, PLLM_DIVP) \ - REG(SCRATCH2, CLK_RST_CONTROLLER, PLLM_MISC, PLLM_CPCON) \ - REG(SCRATCH2, CLK_RST_CONTROLLER, PLLM_MISC, PLLM_LFCON) \ - /**/ \ REG(SCRATCH3, CLK_RST_CONTROLLER, PLLX_BASE, PLLX_DIVP) \ REG(SCRATCH3, CLK_RST_CONTROLLER, PLLX_BASE, PLLX_DIVN) \ REG(SCRATCH3, CLK_RST_CONTROLLER, PLLX_BASE, PLLX_DIVM) \ REG(SCRATCH3, CLK_RST_CONTROLLER, PLLX_MISC, PLLX_CPCON) \ REG(SCRATCH3, CLK_RST_CONTROLLER, PLLX_MISC, PLLX_LFCON) \ /* EMC Group */ \ - REG(SCRATCH4, EMC, FBIO_SPARE, CFG_FBIO_SPARE_WB0) \ /**/ \ REG(SCRATCH5, EMC, R2W, R2W) \ REG(SCRATCH5, EMC, RAS, RAS) \ @@ -393,9 +370,6 @@ extern NvRmDeviceHandle s_hRmGlobal; /**/ \ REG(SCRATCH22, MC, LOWLATENCY_CONFIG, LL_DRAM_INTERLEAVE) \ /* APB_MISC Group */ \ - REG(SCRATCH2, APB_MISC, GP_XM2CFGAPADCTRL, CFG2TMC_XM2CFGA_PREEMP_EN) \ - REG(SCRATCH2, APB_MISC, GP_XM2CFGDPADCTRL, CFG2TMC_XM2CFGD_SCHMT_EN) \ - /**/ \ REG(SCRATCH3, APB_MISC, GP_XM2CFGCPADCTRL2, CFG2TMC_XM2CFGC_VREF_DQ) \ REG(SCRATCH3, APB_MISC, GP_XM2CFGCPADCTRL, CFG2TMC_XM2CFGC_SCHMT_EN) \ REG(SCRATCH3, APB_MISC, GP_XM2CLKCFGPADCTRL, CFG2TMC_XM2CLKCFG_PREEMP_EN) \ diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index f6581cb8f95c..a62eb48eef28 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -137,14 +137,23 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page) * page. This ensures that data in the physical page is mutually * coherent with the kernels mapping. */ + void *vaddr; + #ifdef CONFIG_HIGHMEM /* * kmap_atomic() doesn't set the page virtual address, and - * kunmap_atomic() takes care of cache flushing already. + * kunmap_atomic() takes care of cache flushing already; however, + * the background PKMAP zero-ref TLB shootdown can race with + * cache maintenance, so highmem mapping must be pinned in place. */ - if (page_address(page)) + if (PageHighMem(page)) + vaddr = kmap_high_get(page); + else #endif - __cpuc_flush_dcache_page(page_address(page)); + vaddr = page_address(page); + + if (vaddr) + __cpuc_flush_dcache_page(vaddr); /* * If this is a page cache page, and we have an aliasing VIPT cache, @@ -154,6 +163,11 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page) if (mapping && cache_is_vipt_aliasing()) flush_pfn_alias(page_to_pfn(page), page->index << PAGE_CACHE_SHIFT); + +#ifdef CONFIG_HIGHMEM + if (PageHighMem(page) && vaddr) + kunmap_high(page); +#endif } static void __flush_dcache_aliases(struct address_space *mapping, struct page *page) diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 489283a88402..e409b4ef8438 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -301,6 +301,12 @@ static void tegra_tx_dma_workqueue(struct work_struct *w) return; } + /* PIO is in flight. Just return */ + if (t->tx_pio_inflight == true) { + spin_unlock_irqrestore(&u->lock, flags); + return; + } + /* DMA just finished. Wait for the FIFO to drain. */ if (t->tx_dma_req.size) { /* FIXME: Do a better job on computing the delay */ @@ -324,16 +330,11 @@ static void tegra_tx_dma_workqueue(struct work_struct *w) u->icount.tx += t->tx_dma_req.size; t->tx_dma_req.size = 0; - /* PIO is in flight. Just return */ - if (t->tx_pio_inflight == true) { - spin_unlock_irqrestore(&u->lock, flags); - return; - } - if (uart_circ_empty(xmit)) { spin_unlock_irqrestore(&u->lock, flags); return; } + to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); to_send &= ~0x3; @@ -729,7 +730,7 @@ static int tegra_startup(struct uart_port *u) t->use_tx_dma = true; } if (t->use_tx_dma) { - t->tx_dma_virt = dma_alloc_writecombine(t->uport.dev, + t->tx_dma_virt = dma_alloc_coherent(t->uport.dev, UART_XMIT_SIZE, &t->tx_dma_phys, GFP_KERNEL); if (t->tx_dma_virt) { t->tx_dma_size = UART_XMIT_SIZE; diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 96d2f8e4c275..5bad3a3effac 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -40,6 +40,13 @@ config W1_MASTER_MXC help Say Y here to enable MXC 1-wire host +config W1_MASTER_TEGRA + boolean "NVIDIA Tegra internal 1-wire controller" + depends on W1 && ARCH_TEGRA + help + If you say yes to this option, support will be included for the + 1-wire controller embedded in NVIDIA Tegra SOCs + config W1_MASTER_DS1WM tristate "Maxim DS1WM 1-wire busmaster" depends on W1 && ARM && HAVE_CLK diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index c5a3e96fcbab..45eeae881ee7 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o obj-$(CONFIG_W1_MASTER_MXC) += mxc_w1.o +obj-$(CONFIG_W1_MASTER_TEGRA) += tegra_w1.o obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o diff --git a/drivers/w1/masters/tegra_w1.c b/drivers/w1/masters/tegra_w1.c new file mode 100644 index 000000000000..f0418da1c5b8 --- /dev/null +++ b/drivers/w1/masters/tegra_w1.c @@ -0,0 +1,222 @@ +/* + * drivers/w1/masters/tegra-w1.c + * + * ONE WIRE (OWR) bus driver for internal OWR controllers in NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/tegra_devices.h> +#include <asm/uaccess.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_log.h" + +#include <mach/nvrm_linux.h> +#include <nvrm_module.h> +#include <nvos.h> +#include <nvodm_query_discovery.h> + +struct tegra_w1_dev +{ + NvRmOwrHandle OwrHandle; + NvOdmOwrPinMap pin_map; + struct w1_bus_master bus_master; +}; + +static u8 tegra_w1_read_byte(void *data) +{ + struct tegra_w1_dev *dev = data; + NvRmOwrTransactionInfo tInfo; + NvError err; + u8 buffer[1]; + + tInfo.Flags = NvRmOwr_ReadByte; + tInfo.NumBytes = 1; + tInfo.Address = 0; + tInfo.Offset = 0; + + err = NvRmOwrTransaction(dev->OwrHandle, dev->pin_map, + buffer, tInfo.NumBytes, &tInfo, 1); + if (err != NvSuccess) + { + printk(KERN_ERR "tegra_w1_read_byte failed 0x%x\r\n", err); + err = -EIO; + } + + if (!err) + return buffer[0]; + else + return 0; +} + +static void tegra_w1_write_byte(void *data, u8 a_byte) +{ + struct tegra_w1_dev *dev = data; + NvRmOwrTransactionInfo tInfo; + NvError err; + u8 buffer[1]; + + tInfo.Flags = NvRmOwr_WriteByte; + tInfo.NumBytes = 1; + tInfo.Address = 0; + tInfo.Offset = 0; + buffer[0] = a_byte; + + err = NvRmOwrTransaction(dev->OwrHandle, dev->pin_map, + buffer, tInfo.NumBytes, &tInfo, 1); + if (err != NvSuccess) + { + printk(KERN_ERR "tegra_w1_write_byte failed 0x%x\r\n", err); + err = -EIO; + } +} + +static u8 tegra_w1_reset_bus(void *data) +{ + struct tegra_w1_dev *dev = data; + NvRmOwrTransactionInfo tInfo; + NvError err; + u8 buffer[1]; + + tInfo.Flags = NvRmOwr_CheckPresence; + tInfo.NumBytes = 1; + tInfo.Address = 0; + tInfo.Offset = 0; + + err = NvRmOwrTransaction(dev->OwrHandle, dev->pin_map, + buffer, tInfo.NumBytes, &tInfo, 1); + if (err != NvSuccess) + { + printk(KERN_ERR "tegra_w1_reset_bus failed 0x%x\r\n", err); + err = -EIO; + } + + if (!err) + { + /* Device present */ + return 0; + } + else + { + /* No Device present */ + return 1; + } +} + +static int tegra_w1_probe(struct platform_device *pdev) +{ + struct tegra_w1_dev *dev; + struct tegra_w1_platform_data *pdata = pdev->dev.platform_data; + int ret; + NvError err; + + printk(KERN_INFO "tegra_w1_probe\r\n"); + printk(KERN_INFO "Instance = %d, PinMuxConfig = %d\r\n", + pdata->Instance, pdata->PinMuxConfig); + + if (pdata == NULL) + return -ENODEV; + + dev = kzalloc(sizeof(struct tegra_w1_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + err = NvRmOwrOpen(s_hRmGlobal, pdata->Instance, &dev->OwrHandle); + if (err) + { + ret = -ENODEV; + printk(KERN_INFO "Failed to open NvRmOwrOpen - returned %d\n", err); + goto err_rmapi_failed; + } + + dev->pin_map = pdata->PinMuxConfig; + dev->bus_master.data = dev; + dev->bus_master.read_byte = tegra_w1_read_byte; + dev->bus_master.write_byte = tegra_w1_write_byte; + dev->bus_master.reset_bus = tegra_w1_reset_bus; + + ret = w1_add_master_device(&dev->bus_master); + if (ret) + { + printk(KERN_INFO "w1_add_master_device - failed %d\r\n", ret); + goto err_w1_add_master_device_failed; + } + + platform_set_drvdata(pdev, dev); + + return 0; + +err_rmapi_failed: +err_w1_add_master_device_failed: + kfree(dev); + return ret; +} + +static int +tegra_w1_remove(struct platform_device *pdev) +{ + struct tegra_w1_dev *dev = platform_get_drvdata(pdev); + + NvRmOwrClose(dev->OwrHandle); + w1_remove_master_device(&dev->bus_master); + platform_set_drvdata(pdev, NULL); + kfree(dev); + return 0; +} + +static int tegra_w1_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int tegra_w1_resume(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver tegra_w1_driver = { + .probe = tegra_w1_probe, + .remove = tegra_w1_remove, + .suspend = tegra_w1_suspend, + .resume = tegra_w1_resume, + .driver = + { + .name = "tegra_w1", + .owner = THIS_MODULE, + }, +}; + +static int __init +tegra_w1_init(void) +{ + return platform_driver_register(&tegra_w1_driver); +} +module_init(tegra_w1_init); + +static void __exit tegra_w1_exit(void) +{ + platform_driver_unregister(&tegra_w1_driver); +} +module_exit(tegra_w1_exit); diff --git a/include/linux/tegra_devices.h b/include/linux/tegra_devices.h index 143037a4a64e..adbfffe92f53 100755 --- a/include/linux/tegra_devices.h +++ b/include/linux/tegra_devices.h @@ -66,6 +66,13 @@ struct tegra_i2c_platform_data { NvOdmI2cPinMap PinMuxConfig; }; +/* Platfrom data for W1 bus driver */ +struct tegra_w1_platform_data { + NvU32 Instance; + NvOdmOwrPinMap PinMuxConfig; +}; + + /* Platfrom data for SPI bus driver */ struct tegra_spi_platform_data { NvU32 IoModuleID; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index ef025c66cc66..dd1458462870 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -32,6 +32,7 @@ source "sound/soc/omap/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/Kconfig" source "sound/soc/sh/Kconfig" +source "sound/soc/tegra/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 86a9b1f5b0f3..dfbeab6ffcd4 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += s3c24xx/ obj-$(CONFIG_SND_SOC) += sh/ +obj-$(CONFIG_SND_SOC) += tegra/ diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig new file mode 100644 index 000000000000..e41122bd05dd --- /dev/null +++ b/sound/soc/tegra/Kconfig @@ -0,0 +1,33 @@ +config TEGRA_SND_SOC + tristate "Soc alsa driver for Tegra" + depends on ARCH_TEGRA + select TEGRA_PCM + select TEGRA_I2S + help + Say Y to support ALSA SoC for TEGRA + +config TEGRA_PCM + depends on TEGRA_SND_SOC + tristate "Tegra pcm callbacks" + +config TEGRA_I2S + depends on TEGRA_SND_SOC + tristate "Tegra I2S" + +config TEGRA_SND_WHISTLER + tristate "SoC alsa driver for whistler" + depends on TEGRA_SND_SOC && TEGRA_ODM_WHISTLER + select SND_SOC_WM8753 + +config SND_SOC_WM8753 + depends on TEGRA_SND_WHISTLER + tristate "audio codec WM8753" + +config TEGRA_SND_HARMONY + tristate "SoC alsa driver for harmony" + depends on TEGRA_SND_SOC && TEGRA_ODM_HARMONY + select SND_SOC_WM8903 + +config SND_SOC_WM8903 + depends on TEGRA_SND_HARMONY + tristate "audio codec WM8903" diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile new file mode 100644 index 000000000000..53665b7552df --- /dev/null +++ b/sound/soc/tegra/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_TEGRA_PCM) += tegra_pcm_rpc.o +obj-$(CONFIG_TEGRA_I2S) += tegra_i2s.o tegra_transport.o +obj-$(CONFIG_TEGRA_SND_WHISTLER) += tegra_whistler.o +obj-$(CONFIG_TEGRA_SND_HARMONY) += tegra_harmony.o diff --git a/sound/soc/tegra/tegra_harmony.c b/sound/soc/tegra/tegra_harmony.c new file mode 100644 index 000000000000..8e6bc97bf5e8 --- /dev/null +++ b/sound/soc/tegra/tegra_harmony.c @@ -0,0 +1,181 @@ +/* + * sound/soc/tegra/tegra_harmony.c + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <linux/clk.h> + +#include <asm/mach-types.h> +#include <asm/hardware/scoop.h> +#include <linux/io.h> +#include "nvodm_query_discovery.h" + +#include "../codecs/wm8903.h" +#include "../codecs/wm8753.h" + +#include "tegra_transport.h" + +static struct platform_device *tegra_snd_device; +NvU64 codec_guid; + +#define NVODM_CODEC_MAX_CLOCKS 3 + +static unsigned int clock_frequencies[NVODM_CODEC_MAX_CLOCKS]; + +static int set_clock_source_on_codec(NvU64 codec_guid,int IsEnable) +{ + const NvOdmPeripheralConnectivity *p_connectivity = NULL; + unsigned int clock_instances[NVODM_CODEC_MAX_CLOCKS]; + unsigned int num_clocks; + + p_connectivity = NvOdmPeripheralGetGuid(codec_guid); + if (p_connectivity == NULL) + return NV_FALSE; + + if (IsEnable) { + if (!NvOdmExternalClockConfig(codec_guid, NV_FALSE, + clock_instances, + clock_frequencies, &num_clocks)) + return NV_FALSE; + } else { + if (!NvOdmExternalClockConfig(codec_guid, + NV_TRUE, + clock_instances, + clock_frequencies, + &num_clocks)); + return NV_FALSE; + } + return NV_TRUE; +} + +static int tegra_harmony_hifi_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; + int err; + + err = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | \ + SND_SOC_DAIFMT_NB_IF | \ + SND_SOC_DAIFMT_CBS_CFS); + if (err < 0) { + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, + 0, + clock_frequencies[0]*1000, + SND_SOC_CLOCK_IN); + + if (err<0) { + return err; + } + return 0; +} + +static struct snd_soc_ops tegra_harmony_hifi_ops = { + .hw_params = tegra_harmony_hifi_hw_params, +}; + +static int tegra_wm8903_init(struct snd_soc_codec *codec) +{ + return 0; +} + +extern struct snd_soc_dai tegra_i2s_rpc_dai; +extern struct snd_soc_platform tegra_soc_platform; + +static struct snd_soc_dai_link tegra_harmony_dai = { + /* Hifi Playback - for similatious use with voice below */ + .name = "WM8903", + .stream_name = "WM8903 HiFi", + .cpu_dai = &tegra_i2s_rpc_dai, + .codec_dai = &wm8903_dai, + .init = tegra_wm8903_init, + .ops = &tegra_harmony_hifi_ops, +}; + +static struct snd_soc_card tegra_harmony = { + .name = "tegra", + .platform = &tegra_soc_platform, + .dai_link = &tegra_harmony_dai, + .num_links = 1, +}; + +struct harmony_setup_data { + int i2c_bus; + unsigned short i2c_address; +}; + +static struct snd_soc_device tegra_harmony_snd_devdata = { + .card = &tegra_harmony, + .codec_dev = &soc_codec_dev_wm8903, +}; + +static int __init tegra_soc_init(void) +{ + int ret; + tegra_snd_device = platform_device_alloc("soc-audio", -1); + if (!tegra_snd_device) + return -ENOMEM; + + codec_guid = NV_ODM_GUID('w','o','l','f','8','9','0','3'); + platform_set_drvdata(tegra_snd_device, &tegra_harmony_snd_devdata); + tegra_harmony_snd_devdata.dev = &tegra_snd_device->dev; + + ret = platform_device_add(tegra_snd_device); + if (ret) { + snd_printk(KERN_ERR "tegra audio device could not be added \n"); + platform_device_put(tegra_snd_device); + return ret; + } + + set_clock_source_on_codec(codec_guid,NV_TRUE); + if (ret != 0) + platform_device_unregister(tegra_snd_device); + + return ret; +} + +static void __exit tegra_soc_exit(void) +{ + set_clock_source_on_codec(codec_guid,0); + platform_device_unregister(tegra_snd_device); +} + +module_init(tegra_soc_init); +module_exit(tegra_soc_exit); + +/* Module information */ +MODULE_DESCRIPTION("Tegra SoC Sound"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c new file mode 100644 index 000000000000..6e672347ae5e --- /dev/null +++ b/sound/soc/tegra/tegra_i2s.c @@ -0,0 +1,107 @@ +/* + * sound/soc/tegra/tegra_i2s.c + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/jiffies.h> +#include <linux/io.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "mach/nvrm_linux.h" +#include "nvrm_memmgr.h" +#include "nvassert.h" + +#include "tegra_transport.h" + +extern struct snd_soc_dai tegra_i2s_rpc_dai; + +static int tegra_i2s_rpc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + switch (params_rate(params)) { + case 8000: + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + return 0; + default: + return -EINVAL; + } +} + +static int tegra_i2s_rpc_probe(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + return 0; +} + +#define TEGRA_I2S_RATES (SNDRV_PCM_RATE_8000_96000) + +struct snd_soc_dai tegra_i2s_rpc_dai = { + .name = "tegra-i2s-rpc", + .id = 0, + .probe = tegra_i2s_rpc_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = TEGRA_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE,}, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = TEGRA_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE,}, + .ops = { + .hw_params = tegra_i2s_rpc_hw_params, + }, +}; +EXPORT_SYMBOL_GPL(tegra_i2s_rpc_dai); + +static int __init tegra_i2s_rpc_init(void) +{ + return snd_soc_register_dai(&tegra_i2s_rpc_dai); +} +module_init(tegra_i2s_rpc_init); + +static void __exit tegra_i2s_rpc_exit(void) +{ + snd_soc_unregister_dai(&tegra_i2s_rpc_dai); +} +module_exit(tegra_i2s_rpc_exit); + +/* Module information */ +MODULE_DESCRIPTION("Tegra I2S RPC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_pcm_rpc.c b/sound/soc/tegra/tegra_pcm_rpc.c new file mode 100644 index 000000000000..a913bbf9c12c --- /dev/null +++ b/sound/soc/tegra/tegra_pcm_rpc.c @@ -0,0 +1,850 @@ +/* + * sound/soc/tegra/tegra_pcm_rpc.c + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "tegra_transport.h" +static struct tegra_audio_data* tegra_snd_cx = NULL; + +static const struct snd_pcm_hardware tegra_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 16*1024, + .period_bytes_min = 4*1024, + .period_bytes_max = 4*1024, + .periods_min = 4, + .periods_max = 4, + .fifo_size = 4, +}; + + +static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + return 0; +} + +static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +} + +static int tegra_pcm_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int play_thread( void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = substream->runtime->private_data; + NvError e; + int size; + int offset = 0; + int period_offset = 0; + int rtbuffersize = 0; + int buffer_to_prime = 0, buffer_in_queue = 0; + NvAudioFxBufferDescriptor abd; + NvAudioFxState state = NVALSA_INVALID_STATE; + + wait_for_completion(&prtd->play_comp); + + rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); + buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE); + + while (1) { + switch (prtd->state) { + case SNDRV_PCM_TRIGGER_START: + state = NvAudioFxState_Run; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdoutpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + prtd->state = NVALSA_INVALID_STATE; + break; + case SNDRV_PCM_TRIGGER_STOP: + while (buffer_in_queue > 0) { + e = NvOsSemaphoreWaitTimeout( + prtd->play_sema, prtd->timeout); + if (e != NvSuccess) { + snd_printk(KERN_ERR "Sema Wait Fail\n"); + } + buffer_in_queue--; + } + state = NvAudioFxState_Stop; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdoutpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + prtd->state = NVALSA_INVALID_STATE; + break; + default: + ; + } + + if (kthread_should_stop()) + goto EXIT; + + memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor)); + + if(rtbuffersize > (offset + TEGRA_DEFAULT_BUFFER_SIZE)) { + size = TEGRA_DEFAULT_BUFFER_SIZE; + } else { + size = rtbuffersize - offset; + } + + abd.hMixBuffer = prtd->mixer_buffer; + abd.Offset = offset; + abd.Size = size; + abd.Format.FormatTag = 1; + abd.Format.SampleRate = runtime->rate; + abd.Format.BitsPerSample = runtime->sample_bits; + abd.Format.Channels = runtime->channels; + abd.Format.ChannelMask = 0; + + e = tegra_snd_cx->xrt_fxn.StreamAddBuffer( + (NvAudioFxStreamHandle)prtd->stdoutpath->Stream, &abd); + buffer_in_queue++; + offset += size; + if (offset >= rtbuffersize) + offset =0; + + if (buffer_to_prime == buffer_in_queue) { + e = NvOsSemaphoreWaitTimeout(prtd->play_sema, + prtd->timeout); + if (e != NvSuccess) { + snd_printk(KERN_ERR "sema wait Fail\n"); + return -ETIMEDOUT; + } + + buffer_in_queue--; + + prtd->cur_pos += bytes_to_frames(runtime, + TEGRA_DEFAULT_BUFFER_SIZE); + + if (prtd->cur_pos < prtd->last_pos) { + period_offset = (runtime->buffer_size + + prtd->cur_pos) - prtd->last_pos; + + } else { + period_offset = prtd->cur_pos - prtd->last_pos; + } + + if (period_offset >= runtime->period_size) { + prtd->last_pos = prtd->cur_pos; + snd_pcm_period_elapsed(substream); + } + + if (prtd->cur_pos >= runtime->buffer_size) { + prtd->cur_pos -= runtime->buffer_size; + } + + } + } + snd_printk(KERN_ERR "play_thread: return -EFAULT \n"); + return -EFAULT; +EXIT: + NvOsSemaphoreWaitTimeout(prtd->play_sema, prtd->timeout); + return 0; +} + +static int rec_thread( void *arg ) +{ + struct snd_pcm_substream *substream = arg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = substream->runtime->private_data; + NvError e; + int size; + int offset = 0; + int period_offset = 0; + int rtbuffersize = 0; + int buffer_to_prime = 0, buffer_in_queue = 0; + NvAudioFxBufferDescriptor abd; + NvAudioFxState state = NVALSA_INVALID_STATE; + NvAudioFxPinFormatDescriptor pin_format; + + wait_for_completion(&prtd->rec_comp); + rtbuffersize = frames_to_bytes(runtime, runtime->buffer_size); + buffer_to_prime = (rtbuffersize / TEGRA_DEFAULT_BUFFER_SIZE); + + while (1) { + switch (prtd->state) { + case SNDRV_PCM_TRIGGER_START: + pin_format.Format.FormatTag = 1; + pin_format.Format.SampleRate = runtime->rate; + pin_format.Format.BitsPerSample = runtime->sample_bits; + pin_format.Format.Channels = runtime->channels; + pin_format.Format.ChannelMask = 0; + pin_format.Pin = NvAudioFxSourcePin; + + e = tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdinpath->Convert, + NvAudioFxPinProperty_Format, + sizeof(NvAudioFxPinFormatDescriptor), + &pin_format); + if (e != NvSuccess) { + snd_printk(KERN_ERR"set_property failed!\n"); + } + + e = tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdinpath->Src, + NvAudioFxProperty_SampleRate, + sizeof(NvS32), + &pin_format.Format.SampleRate); + if (e != NvSuccess) { + snd_printk(KERN_ERR "set_property failed!\n"); + } + + state = NvAudioFxState_Run; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdinpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + prtd->state = NVALSA_INVALID_STATE; + break; + case SNDRV_PCM_TRIGGER_STOP: + while (buffer_in_queue) { + e = NvOsSemaphoreWaitTimeout( + prtd->rec_sema, prtd->timeout); + if (e != NvSuccess) { + snd_printk(KERN_ERR "Sema Wait Fail\n"); + } + buffer_in_queue--; + } + + state = NvAudioFxState_Stop; + tegra_snd_cx->xrt_fxn.SetProperty( + prtd->stdinpath->Stream, + NvAudioFxProperty_State, + sizeof(NvAudioFxState), + &state); + prtd->state = NVALSA_INVALID_STATE; + goto EXIT; + default: + ; + } + + memset(&abd, 0, sizeof(NvAudioFxBufferDescriptor)); + + if(rtbuffersize > (offset + TEGRA_DEFAULT_BUFFER_SIZE)) { + size = TEGRA_DEFAULT_BUFFER_SIZE; + } else { + size = rtbuffersize - offset; + } + + abd.hMixBuffer = prtd->mixer_buffer; + abd.Offset = offset; + abd.Size = size; + abd.Format.FormatTag = 1; + abd.Format.SampleRate = runtime->rate; + abd.Format.BitsPerSample = runtime->sample_bits; + abd.Format.Channels = runtime->channels; + abd.Format.ChannelMask = 0; + + e = tegra_snd_cx->xrt_fxn.StreamAddBuffer( + (NvAudioFxStreamHandle)prtd->stdinpath->Stream, &abd); + buffer_in_queue++; + offset += size; + + if (offset >= rtbuffersize) + offset =0; + + if (buffer_to_prime == buffer_in_queue) { + e = NvOsSemaphoreWaitTimeout(prtd->rec_sema, + prtd->timeout); + if (e != NvSuccess) { + snd_printk(KERN_ERR "sema wait Fail\n"); + return -ETIMEDOUT; + } + + buffer_in_queue--; + + prtd->cur_pos += bytes_to_frames( + runtime, + TEGRA_DEFAULT_BUFFER_SIZE); + + if (prtd->cur_pos < prtd->last_pos) { + period_offset = (runtime->buffer_size + + prtd->cur_pos) - prtd->last_pos; + } else { + period_offset = prtd->cur_pos - prtd->last_pos; + } + + if (period_offset >= runtime->period_size) { + prtd->last_pos = prtd->cur_pos; + snd_pcm_period_elapsed(substream); + } + + if (prtd->cur_pos >= runtime->buffer_size) { + prtd->cur_pos -= runtime->buffer_size; + } + } + } + snd_printk(KERN_ERR "rec_thread: return -EFAULT \n"); + return -EFAULT; +EXIT: + while(!kthread_should_stop()){ + } + + return 0; +} + +static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = substream->runtime->private_data; + int size; + int ret = 0; + int state = prtd->state; + + prtd->state = cmd; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + size=frames_to_bytes(runtime, runtime->period_size); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + prtd->timeout = PLAY_TIMEOUT; + if(state == NVALSA_INVALID_STATE) + complete(&prtd->play_comp); + else + NvOsSemaphoreSignal(prtd->play_sema); + } else { + prtd->timeout = REC_TIMEOUT; + if(state == NVALSA_INVALID_STATE) + complete(&prtd->rec_comp); + else + NvOsSemaphoreSignal(prtd->rec_sema); + } + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + prtd->timeout = PLAY_TIMEOUT; + }else{ + prtd->timeout = REC_TIMEOUT; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + prtd->timeout = NV_WAIT_INFINITE; + break; + default: + prtd->state = state; + ret = -EINVAL; + break; + } + return ret; +} + +static snd_pcm_uframes_t +tegra_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = runtime->private_data; + int size; + + size = prtd->last_pos; + if (size >= runtime->buffer_size) { + prtd->last_pos = size - runtime->buffer_size; + size = 0; + } + + return (size); +} + +static int init_mixer(struct snd_pcm_substream *substream) +{ + NvError e = NvSuccess; + int ret = 0; + + if (!tegra_snd_cx->mixer_handle) { + mutex_lock(&tegra_snd_cx->lock); + e = tegra_transport_init(&tegra_snd_cx->xrt_fxn); + mutex_unlock(&tegra_snd_cx->lock); + + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_init failed \n"); + return -EFAULT; + } + + tegra_snd_cx->mixer_handle = + tegra_snd_cx->xrt_fxn.MixerOpen(); + + if (!tegra_snd_cx->mixer_handle) { + ret = -EFAULT; + goto fail; + } + + e = tegra_audiofx_createfx(tegra_snd_cx); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_audiofx_createfx failed \n"); + ret = -EFAULT; + goto fail; + } + + tegra_snd_cx->mixer_buffer[0] = + tegra_snd_cx->xrt_fxn.MixerMapBuffer( + tegra_snd_cx->mixer_handle, + NvRmMemGetId(tegra_snd_cx->mem_handle[0]), + 0, + tegra_snd_cx->mapped_buf_size); + + if (!tegra_snd_cx->mixer_buffer[0]) { + snd_printk(KERN_ERR"TransportMixerMapBuffer failed!\n"); + } + + tegra_snd_cx->mixer_buffer[1] = + tegra_snd_cx->xrt_fxn.MixerMapBuffer( + tegra_snd_cx->mixer_handle, + NvRmMemGetId(tegra_snd_cx->mem_handle[1]), + 0, + tegra_snd_cx->mapped_buf_size); + + if (!tegra_snd_cx->mixer_buffer[1]) { + snd_printk(KERN_ERR"TransportMixerMapBuffer failed!\n"); + } + } + + return 0; +fail: + snd_printk(KERN_ERR "init mixer failed \n"); + if (tegra_snd_cx->mixer_handle) { + tegra_audiofx_destroyfx(tegra_snd_cx); + + if (tegra_snd_cx->mixer_handle) { + tegra_snd_cx->xrt_fxn.MixerClose( + tegra_snd_cx->mixer_handle); + } + } + mutex_lock(&tegra_snd_cx->lock); + tegra_transport_deinit(); + mutex_unlock(&tegra_snd_cx->lock); + + return ret; +} + +static int pcm_common_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd = runtime->private_data; + NvAudioFxMessage message; + NvError e; + + if (!prtd) + snd_printk(KERN_ERR "pcm_close called with prtd = NULL\n"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (prtd->play_sema) + NvOsSemaphoreSignal(prtd->play_sema); + + if (prtd->play_thread) + kthread_stop(prtd->play_thread); + + if (prtd->play_sema){ + NvOsSemaphoreDestroy(prtd->play_sema); + prtd->play_sema = NULL; + } + + } else { + + if (prtd->rec_sema) + NvOsSemaphoreSignal(prtd->rec_sema); + + if (prtd->rec_thread) + kthread_stop(prtd->rec_thread); + + if (prtd->rec_sema){ + NvOsSemaphoreDestroy(prtd->rec_sema); + prtd->rec_sema = NULL; + } + } + + if (tegra_snd_cx->m_FxNotifier.Event & NvAudioFxEventBufferDone) { + + memset(&message, 0, sizeof(NvAudioFxMessage)); + message.Event = NvAudioFxEventBufferDone; + message.pContext = NULL; + if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream; + else + message.hFx = (NvAudioFxHandle)prtd->stdinpath->Stream; + + e = tegra_snd_cx->xrt_fxn.SetProperty( + (NvAudioFxObjectHandle)tegra_snd_cx->m_FxNotifier.hNotifier, + NvAudioFxIoProperty_RemoveEvent, + sizeof(NvAudioFxMessage), + &message); + + tegra_snd_cx->m_FxNotifier.Event &= ~(NvAudioFxEventBufferDone); + } + + if (prtd->stdoutpath) { + tegra_audiofx_destroy_output(prtd->stdoutpath); + kfree(prtd->stdoutpath); + } + + if (prtd->stdinpath) { + tegra_audiofx_destroy_input(prtd->stdinpath); + kfree(prtd->stdinpath); + } + + if (prtd) + kfree(prtd); + + return 0; +} + +static int tegra_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct pcm_runtime_data *prtd; + int ret = 0; + NvError e = NvSuccess; + NvAudioFxMessage message; + + prtd = kzalloc(sizeof(struct pcm_runtime_data), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + runtime->private_data = prtd; + snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); + spin_lock_init(&prtd->lock); + prtd->timeout = INIT_TIMEOUT; + prtd->stdoutpath = 0; + prtd->stdinpath = 0; + prtd->state = NVALSA_INVALID_STATE; + prtd->stream = substream->stream; + + ret = init_mixer(substream); + if (ret) + goto fail; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){ + prtd->mixer_buffer = tegra_snd_cx->mixer_buffer[0]; + prtd->stdoutpath = (StandardPath*)kzalloc(sizeof(StandardPath), + GFP_KERNEL); + if (prtd->stdoutpath == NULL) { + snd_printk(KERN_ERR "pcm_open kzalloc failed \n"); + ret = -ENOMEM; + goto fail; + } + + e = tegra_audiofx_create_output(tegra_snd_cx->m_hRm, + tegra_snd_cx->mixer_handle, + prtd->stdoutpath); + if (e != NvSuccess) { + snd_printk(KERN_ERR "audiofx_create_output failed \n"); + ret = -EFAULT; + goto fail; + } + + memset(&message, 0, sizeof(NvAudioFxMessage)); + message.Event = NvAudioFxEventBufferDone; + message.hFx = (NvAudioFxHandle)prtd->stdoutpath->Stream; + message.pContext = prtd; + + e = tegra_snd_cx->xrt_fxn.SetProperty( + (NvAudioFxObjectHandle)tegra_snd_cx->m_FxNotifier.hNotifier, + NvAudioFxIoProperty_AddEvent, + sizeof(NvAudioFxMessage), + &message); + + if (e != NvSuccess) { + snd_printk(KERN_ERR "TransportSetProperty failed\n"); + ret = -EFAULT; + goto fail; + } + + tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone); + e = NvOsSemaphoreCreate(&prtd->play_sema, 0); + if (e != NvSuccess) { + snd_printk(KERN_ERR "play_sema creation failed\n"); + ret = -EFAULT; + goto fail; + } + + init_completion(&prtd->play_comp); + prtd->play_thread = kthread_run(play_thread, + substream, + "%sthread", + "play"); + if (IS_ERR(prtd->play_thread)) { + snd_printk(KERN_ERR "KTHREAD RUN FAIL\n"); + ret = PTR_ERR(prtd->play_thread); + goto fail; + } + } else { + prtd->mixer_buffer = tegra_snd_cx->mixer_buffer[1]; + prtd->stdinpath = (StandardPath*)kzalloc(sizeof(StandardPath), + GFP_KERNEL); + if (prtd->stdinpath == NULL) { + snd_printk(KERN_ERR "pcm_open kzalloc failed \n"); + ret = -ENOMEM; + goto fail; + } + e = tegra_audiofx_create_input(tegra_snd_cx->m_hRm, + tegra_snd_cx->mixer_handle, + prtd->stdinpath, + NvAudioInputSelect_Record); + if (e != NvSuccess) { + snd_printk(KERN_ERR "audiofx_create_input failed \n"); + ret = -EFAULT; + goto fail; + } + + memset(&message, 0, sizeof(NvAudioFxMessage)); + message.Event = NvAudioFxEventBufferDone; + message.hFx = (NvAudioFxHandle)prtd->stdinpath->Stream; + message.pContext = prtd; + + e = tegra_snd_cx->xrt_fxn.SetProperty( + (NvAudioFxObjectHandle)tegra_snd_cx->m_FxNotifier.hNotifier, + NvAudioFxIoProperty_AddEvent, + sizeof(NvAudioFxMessage), + &message); + if (e != NvSuccess) { + snd_printk(KERN_ERR "TransportSetProperty failed\n"); + ret = -EFAULT; + goto fail; + } + tegra_snd_cx->m_FxNotifier.Event |= (NvAudioFxEventBufferDone); + + e = NvOsSemaphoreCreate(&prtd->rec_sema, 0); + if (e != NvSuccess) { + snd_printk(KERN_ERR "rec_sema creation failed\n"); + ret = -EFAULT; + goto fail; + } + + init_completion(&prtd->rec_comp); + prtd->rec_thread = kthread_run(rec_thread, + substream, + "%sthread", + "rec" ); + if (IS_ERR(prtd->rec_thread)) { + snd_printk(KERN_ERR "Kthread Run Fail\n"); + ret = PTR_ERR(prtd->rec_thread); + goto fail; + } + } + return ret; +fail: + snd_printk(KERN_ERR "tegra_pcm_open - failed \n"); + pcm_common_close(substream); + return ret; +} + +static int tegra_pcm_close(struct snd_pcm_substream *substream) +{ + pcm_common_close(substream); + return 0; +} + +static struct snd_pcm_ops tegra_pcm_ops = { + .open = tegra_pcm_open, + .close = tegra_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = tegra_pcm_hw_params, + .hw_free = tegra_pcm_hw_free, + .prepare = tegra_pcm_prepare, + .trigger = tegra_pcm_trigger, + .pointer = tegra_pcm_pointer, +}; + +static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = tegra_pcm_hardware.buffer_bytes_max; + void *virt_buf_ptr = NULL; + NvRmPhysAddr phy_address; + int ret = 0; + NvError e; + e = NvRmMemHandleCreate(tegra_snd_cx->m_hRm, + &tegra_snd_cx->mem_handle[stream], + size); + + if (e == NvSuccess) { + e = NvRmMemAlloc(tegra_snd_cx->mem_handle[stream], + NULL, + 0, + 0x4, + NvOsMemAttribute_Uncached); + } + + if (e == NvSuccess) { + phy_address = (NvU32)(NvRmMemPin(tegra_snd_cx->mem_handle[stream])); + } + + if (e != NvSuccess) { + NvRmMemHandleFree(tegra_snd_cx->mem_handle[stream]); + ret = -ENOMEM; + goto end; + } + + e = NvRmMemMap(tegra_snd_cx->mem_handle[stream], + 0, + size, + NVOS_MEM_READ_WRITE, + (void**)&virt_buf_ptr); + + if (e != NvSuccess) { + NvRmMemHandleFree(tegra_snd_cx->mem_handle[stream]); + ret = -ENOMEM; + goto end; + } + + tegra_snd_cx->mapped_buf_size = size; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = virt_buf_ptr; + buf->bytes = size; + buf->addr = phy_address; +end: + return ret; +} + +static void DestroyMemoryHandle(NvRmMemHandle hMemHandle) +{ + if (hMemHandle != NULL) { + NvRmMemUnpin(hMemHandle); + NvRmMemHandleFree(hMemHandle); + } +} + +static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + + if (tegra_snd_cx->mixer_buffer[stream]) + tegra_snd_cx->xrt_fxn.MixerUnmapBuffer( + tegra_snd_cx->mixer_buffer[stream]); + + NvRmMemUnmap(tegra_snd_cx->mem_handle[stream],buf->area,buf->bytes); + DestroyMemoryHandle(tegra_snd_cx->mem_handle[stream]); +} + +static void tegra_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + buf = &substream->dma_buffer; + if (!buf) { + continue; + } + tegra_pcm_deallocate_dma_buffer(pcm ,stream); + } +} + + +static int tegra_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + int ret = 0; + + tegra_snd_cx = kzalloc(sizeof(struct tegra_audio_data), + GFP_KERNEL); + if (tegra_snd_cx == NULL) + return -ENOMEM; + + tegra_snd_cx->m_hRm = s_hRmGlobal; + mutex_init(&tegra_snd_cx->lock); + + if (dai->playback.channels_min) { + ret = tegra_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) { + goto out; + } + } + + if (dai->capture.channels_min) { + ret = tegra_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + return 0; + +out: + if (tegra_snd_cx) + kfree(tegra_snd_cx); + snd_printk(KERN_ERR "pcm_new failed\n"); + return ret; +} + +static int tegra_pcm_probe(struct platform_device *pdev) +{ + return 0; +} + +static int tegra_pcm_remove(struct platform_device *pdev) +{ + if (tegra_snd_cx) + kfree(tegra_snd_cx); + + return 0; +} + +struct snd_soc_platform tegra_soc_platform = { + .name = "tegra-audio", + .probe = tegra_pcm_probe, + .remove = tegra_pcm_remove, + .pcm_ops = &tegra_pcm_ops, + .pcm_new = tegra_pcm_new, + .pcm_free = tegra_pcm_free_dma_buffers, +}; + +EXPORT_SYMBOL_GPL(tegra_soc_platform); + +static int __init tegra_soc_platform_init(void) +{ + return snd_soc_register_platform(&tegra_soc_platform); +} + +module_init(tegra_soc_platform_init); + +static void __exit tegra_soc_platform_exit(void) +{ + snd_soc_unregister_platform(&tegra_soc_platform); +} + +module_exit(tegra_soc_platform_exit); + +MODULE_DESCRIPTION("Tegra PCM RPC PCM output"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_sndfx.h b/sound/soc/tegra/tegra_sndfx.h new file mode 100644 index 000000000000..1e9bb82940a4 --- /dev/null +++ b/sound/soc/tegra/tegra_sndfx.h @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2010 NVIDIA Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NVIDIA Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef INCLUDED_nvddk_audiofx_H +#define INCLUDED_nvddk_audiofx_H + + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#include "nvrm_module.h" +#include "nvrm_transport.h" +#include "nvrm_init.h" + +/** @file + * @brief <b>NVIDIA Driver Development Kit: NvAudioFx APIs</b> + * + * @b Description: Declares Interface for NvAudioFx APIs. + */ + +/** + * @brief API Object Handles. + * + * NvAudioFxObjectHandle is the base handle for every type in the API. + */ + +typedef struct NvAudioFxObjectRec *NvAudioFxObjectHandle; + +typedef struct NvAudioFxRec *NvAudioFxHandle; + +typedef struct NvAudioFxIoRec *NvAudioFxIoHandle; + +typedef struct NvAudioFxMixBufferRec *NvAudioFxMixBufferHandle; + +typedef struct NvAudioFxMixerRec *NvAudioFxMixerHandle; + +typedef struct NvAudioFxNotifierRec *NvAudioFxNotifierHandle; + +typedef struct NvAudioFxStreamRec *NvAudioFxStreamHandle; + +/** + * @brief Object IDs. + */ + +typedef NvS32 NvObjectId; +#define NvObjectNullId (0x0) +#define NvAudioFxObjectId (0x10000000) +#define NvAudioFxId (0x11000000) +#define NvAudioFxIoId (0x11100000) +#define NvAudioFxPluginId (0x11200000) +#define NvAudioFxMixBufferId (0x12000000) +#define NvAudioFx3dGroupId (0x14000000) +#define NvAudioFxNotifierId (0x30000000) +#define NvAudioFxConvertId (0x11000001) +#define NvAudioFxDrcId (0x11000002) +#define NvAudioFxEqId (0x11000003) +#define NvAudioFxMixerId (0x11000004) +#define NvAudioFxMixId (0x11000005) +#define NvAudioFxPeqId (0x11000006) +#define NvAudioFxResizeId (0x11000007) +#define NvAudioFxSplitId (0x11000008) +#define NvAudioFxSpreaderId (0x11000009) +#define NvAudioFxSrcId (0x1100000a) +#define NvAudioFxSwitchId (0x1100000b) +#define NvAudioFxVolumeId (0x1100000c) +#define NvAudioFxStreamId (0x11100001) +#define NvAudioFxI2sId (0x11100002) +#define NvAudioFxI2s2Id (0x11100002) +#define NvAudioFxSpdifId (0x11100003) +#define NvAudioFxDefaultPlaybackMixId (0x11300001) +#define NvAudioFxDefaultPlaybackSplitId (0x11300002) +#define NvAudioFxDefaultRecordMixId (0x11300003) +#define NvAudioFxDefaultRecordSplitId (0x11300004) +#define NvAudioFxI2sPlaybackMixId (0x11300005) +#define NvAudioFxI2sRecordSplitId (0x11300006) +#define NvAudioFxI2sLoopbackSplitId (0x11300007) +#define NvAudioFxI2s2PlaybackMixId (0x11300008) +#define NvAudioFxI2s2RecordSplitId (0x11300009) +#define NvAudioFxI2s2LoopbackSplitId (0x1130000a) +#define NvAudioFxSpdifPlaybackMixId (0x1130000b) +#define NvAudioFxSpdifRecordSplitId (0x1130000c) +#define NvAudioFxSpdifLoopbackSplitId (0x1130000d) +#define NvAudioFxMusicMixId (0x1130000e) +#define NvAudioFxMusicSplitId (0x1130000f) +#define NvAudioFxPhoneMixId (0x11300010) +#define NvAudioFxPhoneSplitId (0x11300011) + +// +// Mixer +// +------+ +// | | +// | FX |--@ ScratchSource ------------+ +// | | | +// +------+ | +// +// Source Sink +// +------+ +------+ +// Copy Sink @--| |--@ Loopback Copy Sink @--| |--@ Loopback +// | FX | | FX | +// ----> Sink @--| |--@ Source ------> Sink @--| |--@ Source ----> +// +------+ +------+ +// + +typedef NvS32 NvAudioFxPin; +#define NvAudioFxScratchSourcePin (-4) +#define NvAudioFxLoopbackPin (-3) +#define NvAudioFxCopySinkPin (-2) +#define NvAudioFxInvalidPin (-1) +#define NvAudioFxSinkPin (0) +#define NvAudioFxSourcePin (1) + +typedef enum +{ + NvAudioFxProperty_Attach, + NvAudioFxProperty_Detach, + NvAudioFxProperty_Format, + NvAudioFxProperty_Method, + NvAudioFxProperty_SampleRate, + NvAudioFxProperty_State, + NvAudioFxPinProperty_Format, + NvAudioFxDrcProperty_Drc, + NvAudioFxEqProperty_Eq, + NvAudioFxIoProperty_AddEvent, + NvAudioFxIoProperty_Position, + NvAudioFxIoProperty_RemoveEvent, + NvAudioFxIoProperty_SetMappedPositionBuffer, + NvAudioFxMixerProperty_ProcessBufferSize, + NvAudioFxMixProperty_Headroom, + NvAudioFxNotifierProperty_Connect, + NvAudioFxNotifierProperty_Disconnect, + NvAudioFxPeqProperty_Peq, + NvAudioFxResizeProperty_OutputSize, + NvAudioFxSpreaderProperty_Spreader, + NvAudioFxSrcProperty_SampleRateShift, + NvAudioFxVolumeProperty_Ramping, + NvAudioFxVolumeProperty_Volume, + NvAudioFxProperty_Num, + NvAudioFxProperty_Force32 = 0x7FFFFFFF +} NvAudioFxProperty; + +// Description of the NvAudioFxProperty_Attach and +// NvAudioFxProperty_Detach properties. + +typedef struct NvAudioFxConnectionDescriptorRec +{ + NvAudioFxPin SourcePin; + NvAudioFxHandle hSink; + NvAudioFxPin SinkPin; +} NvAudioFxConnectionDescriptor; + +// Audio format information of the stream or buffer. + +typedef struct NvAudioFxFormatRec +{ + NvU32 FormatTag; + NvU32 SampleRate; + NvU32 Channels; + NvU32 BitsPerSample; + NvU32 ChannelMask; +} NvAudioFxFormat; + +// Description of the NvAudioFxProperty_Method property. + +typedef enum +{ + NvAudioFxMethod_Sliced, + NvAudioFxMethod_Unsliced, + NvAudioFxMethod_Num, + NvAudioFxMethod_Force32 = 0x7FFFFFFF +} NvAudioFxMethod; + +// Description of the position property. + +typedef NvU64 NvAudioFxPosition; + +// Description of supported power states in AudioFx. + +typedef enum +{ + NvAudioFxPowerState_Low, + NvAudioFxPowerState_High, + NvAudioFxPowerState_Full, + NvAudioFxPowerState_Ready, + NvAudioFxPowerState_Off, + NvAudioFxPowerState_Num, + NvAudioFxPowerState_Force32 = 0x7FFFFFFF +} NvAudioFxPowerState; + +// Description of the supported priority states. + +typedef enum +{ + NvAudioFxPriority_Normal, + NvAudioFxPriority_Medium, + NvAudioFxPriority_High, + NvAudioFxPriority_Critical, + NvAudioFxPriority_Num, + NvAudioFxPriority_Force32 = 0x7FFFFFFF +} NvAudioFxPriority; + +// Description of the NvAudioFxProperty_State property. + +typedef enum +{ + NvAudioFxState_Uninitialized, + NvAudioFxState_Initialized, + NvAudioFxState_Stop, + NvAudioFxState_Run, + NvAudioFxState_Pause, + NvAudioFxState_Disable, + NvAudioFxState_Num, + NvAudioFxState_Force32 = 0x7FFFFFFF +} NvAudioFxState; + +// Audio DRC information. + +typedef struct NvAudioFxDrcDescriptorRec +{ + NvS32 EnableDrc; + NvS32 NoiseGateThreshold; + NvS32 LowerCompThreshold; + NvS32 UpperCompThreshold; + NvS32 ClippingThreshold; +} NvAudioFxDrcDescriptor; + +// Audio EQ information. +#define NvAudioFxEqNumFilters (5) +#define NvAudioFxEqNumChannels (2) + +typedef struct NvAudioFxEqDescriptorRec +{ + NvS32 dBGain[NvAudioFxEqNumChannels][NvAudioFxEqNumFilters]; + +}NvAudioFxEqDescriptor; + +// Audio Spreader information. + +typedef struct NvAudioFxSpreaderDescriptorRec +{ + NvU32 SpeakerWidth; +} NvAudioFxSpreaderDescriptor; + +// Parameteric EQ Filter types + +typedef enum +{ + NvAudioFxIirFilter_Undefined, + NvAudioFxIirFilter_Bandpass, + NvAudioFxIirFilter_Highpass, + NvAudioFxIirFilter_Lowpass, + NvAudioFxIirFilter_Num, + NvAudioFxIirFilter_Force32 = 0x7FFFFFFF +} NvAudioFxIirFilter; + +typedef struct NvAudioFxPeqDescriptorRec +{ + NvU32 Enable; + NvU32 FilterType[NvAudioFxIirFilter_Num]; + NvS32 CenterFrequency[NvAudioFxIirFilter_Num]; + NvS32 Bandwidth[NvAudioFxIirFilter_Num]; + NvS32 dBGain[NvAudioFxIirFilter_Num]; + +}NvAudioFxPeqDescriptor; + +// Audio pin-specific format information of the stream or buffer. + +typedef struct NvAudioFxPinFormatDescriptorRec +{ + NvAudioFxFormat Format; + NvAudioFxPin Pin; +} NvAudioFxPinFormatDescriptor; + +// Audio volume information of the stream or buffer. + +typedef struct NvAudioFxVolumeDescriptorRec +{ + NvS32 LeftVolume; + NvS32 RightVolume; + NvU32 Mute; +} NvAudioFxVolumeDescriptor; + +// Description of the NvAudioFxVoiceProperty_SetMappedPositionBuffer property. + +typedef struct NvAudioFxMappedBufferDescriptorRec +{ + NvAudioFxMixBufferHandle hMixBuffer; + NvU32 Offset; +} NvAudioFxMappedBufferDescriptor; + + /** + * @brief Defines the structure for adding a buffer to a stream. + */ + +typedef struct NvAudioFxBufferDescriptorRec +{ + + // A pointer which may be used for already mapped buffers. + void* pAddr; + + // A physical address for accessing the buffer. + NvRmPhysAddr PhysicalAddr; + + // The MixBuffer handle returned from NvAudioFxMixerMapBuffer. + NvAudioFxMixBufferHandle hMixBuffer; + + // Buffer offset in bytes. + NvU32 Offset; + + // Buffer size in bytes. + NvU32 Size; + + // Size of non-Zero Fill data in buffer, usually same as Size + NvU32 ValidSize; + + // The buffer format information. + NvAudioFxFormat Format; +} NvAudioFxBufferDescriptor; + +// Description of the NvAudioFxNotifierProperty_Connect property. + +typedef struct NvAudioFxNotifierConnectionDescriptorRec +{ + NvU8 PortName[16]; +} NvAudioFxNotifierConnectionDescriptor; + +// Description of the NvAudioFxProperty_AddEvent and +// NvAudioFxProperty_RemoveEvent properties. + +typedef NvS32 NvAudioFxEvent; +#define NvAudioFxEventBufferDone (0x1) +#define NvAudioFxEventStateChange (0x2) +#define NvAudioFxEventFormatChange (0x4) +#define NvAudioFxEventEndOfStream (0x8) +#define NvAudioFxEventPowerStateChange (0x10) +#define NvAudioFxEventAll (0xffffffff) + +typedef struct NvAudioFxMessageRec +{ + NvAudioFxEvent Event; + NvAudioFxHandle hFx; + void* pContext; +} NvAudioFxMessage; + +typedef struct NvAudioFxBufferDoneMessageRec +{ + NvAudioFxMessage m; + NvAudioFxPosition Position; +} NvAudioFxBufferDoneMessage; + +typedef struct NvAudioFxStateChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxState State; +} NvAudioFxStateChangeMessage; + +typedef struct NvAudioFxFormatChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxFormat Format; +} NvAudioFxFormatChangeMessage; + +typedef struct NvAudioFxPowerStateChangeMessageRec +{ + NvAudioFxMessage m; + NvAudioFxPowerState PowerState; +} NvAudioFxPowerStateChangeMessage; + + /** + * @brief Initializes and opens the AudioFX Mixer. + * + * @retval NvAudioFxMixerHandle A non-NULL value will be returned if the mixer + * is successfully opened. + */ + + NvAudioFxMixerHandle NvddkAudioFxMixerOpen( + void ); + + /** + * @brief Closes the AudioFX Mixer. This function frees the resources associated + * with the Mixer handle and cannot fail. + * + * @param hMixer A handle from NvAudioFxMixerOpen(). If hMixer is NULL or + * invalid, this API does nothing. + */ + + void NvddkAudioFxMixerClose( + NvAudioFxMixerHandle hMixer ); + + /** + * @brief Creates and initializes an AudioFX object. + * + * @param hMixer A handle from NvAudioFxMixerOpen(). If hMixer is NULL the + * object will be created by the Global Mixer. A NULL handle should only be + * used from the driver and not a normal client application. + * @param Id The ID of the object to create. + * + * @retval NvAudioFxObjectHandle A non-NULL value will be returned if the + * object is successfully created. + */ + + NvAudioFxObjectHandle NvddkAudioFxMixerCreateObject( + NvAudioFxMixerHandle hMixer, + NvObjectId Id ); + + /** + * @brief Destroys the AudioFX object. + * + * @param hObject A handle from NvAudioFxMixerCreateObject. If hObject is + * NULL, this API does nothing. + */ + + void NvddkAudioFxMixerDestroyObject( + NvAudioFxObjectHandle hObject ); + + /** + * @brief Maps a buffer to AudioFX address space. + */ + + NvAudioFxMixBufferHandle NvddkAudioFxMixerMapBuffer( + NvAudioFxMixerHandle hMixer, + NvU32 NvRmMemHandleId, + NvU32 Offset, + NvU32 Size ); + + /** + * @brief Unmaps a buffer from the AudioFX address space. + */ + + void NvddkAudioFxMixerUnmapBuffer( + NvAudioFxMixBufferHandle hMixBuffer ); + + /** + * @brief Adds a buffer to a Stream object. + * + * @param hStream Handle to the Stream object to add the buffer. + * @param pDescriptor Description of the buffer to add to the stream. + * + * @retval NvSuccess Indicates the operation succeeded. + */ + + NvError NvddkAudioFxStreamAddBuffer( + NvAudioFxStreamHandle hStream, + NvAudioFxBufferDescriptor * pDescriptor ); + +/** + * @brief Get the property value from an AudioFX object. + * + * @param hObject Handle of the object to get the property value. + * @param Property The property of interest. + * @param Size Size of the descriptor. + * @param pDescriptor Holds the value of the current property. + * + * @retval NvSuccess Indicates the operation succeeded. + */ + + NvError NvddkAudioFxGetProperty( + NvAudioFxObjectHandle hObject, + NvAudioFxProperty Property, + NvU32 Size, + void* pProperty ); + +/** + * @brief Set a property value on an AudioFX object. + * + * @param hObject Handle of the object to set the property on. + * @param Property The property of interest. + * @param Size Size of the property descriptor. + * @param pProperty Holds the value of the property to set. + * + * @retval NvSuccess Indicates the operation succeeded. + */ + + NvError NvddkAudioFxSetProperty( + NvAudioFxObjectHandle hObject, + NvAudioFxProperty Property, + NvU32 Size, + void* pProperty ); + +/** + * @brief AVP API Function Table + */ + +typedef struct NvddkAudioFxFxnTableRec +{ + NvAudioFxMixerHandle (*MixerOpen)(void); + void (*MixerClose)(NvAudioFxMixerHandle hMixer); + NvAudioFxObjectHandle (*MixerCreateObject)(NvAudioFxMixerHandle hMixer, NvObjectId Id); + void (*MixerDestroyObject)(NvAudioFxObjectHandle hObject); + NvAudioFxMixBufferHandle (*MixerMapBuffer)(NvAudioFxMixerHandle hMixer, NvU32 NvRmMemHandleId, NvU32 Offset, NvU32 Size); + void (*MixerUnmapBuffer)(NvAudioFxMixBufferHandle hMixBuffer); + NvError (*StreamAddBuffer)(NvAudioFxStreamHandle hStream, NvAudioFxBufferDescriptor* pDescriptor); + NvError (*GetProperty)(NvAudioFxObjectHandle hObject, NvAudioFxProperty Property, NvU32 Size, void* pProperty); + NvError (*SetProperty)(NvAudioFxObjectHandle hObject, NvAudioFxProperty Property, NvU32 Size, void* pProperty); + +} NvddkAudioFxFxnTable; + +static const NvU32 NvddkAudioFxFxnTableId = 0x6e766178; // 'nvax' + +typedef enum +{ + NvAudioFxIoctl_Generic = 5020, + NvAudioFxIoctl_ForceWord = 0x7FFFFFFF, + +} NvAudioFxIoctl; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/sound/soc/tegra/tegra_transport.c b/sound/soc/tegra/tegra_transport.c new file mode 100644 index 000000000000..47b62c07f057 --- /dev/null +++ b/sound/soc/tegra/tegra_transport.c @@ -0,0 +1,945 @@ +/* + * sound/soc/tegra/alsa_transport.c + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "tegra_transport.h" +#include <linux/completion.h> + +#define transport_send_message(msg_type) \ + message.Semaphore = 0; \ + message.SendAck = 1; \ + status = NvRmTransportSendMsg(atrans->hRmTransport, \ + &message, \ + sizeof(msg_type), \ + TEGRA_TRANSPORT_SEND_TIMEOUT); \ + if (status != NvSuccess) { \ + snd_printk(KERN_ERR "NvRmTransportSendMsg failed!\n"); \ + goto EXIT_WITH_ERROR; \ + } \ + wait_for_completion(&comp); \ + + +static AlsaTransport* atrans = 0; + + +static NvAudioFxMixerHandle tegra_transport_mixer_open() +{ + NvError status = NvSuccess; + NvAudioFxMixerHandle hMixer = 0; + NvFxTransportMessageMixerOpen message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_MIXER_OPEN; + message.pPrivateData = (void*)∁ + message.phMixer = &hMixer; + + transport_send_message(NvFxTransportMessageMixerOpen); + +EXIT_WITH_ERROR: + return hMixer; +} + +static void tegra_transport_mixer_close(NvAudioFxMixerHandle hMixer) +{ + NvError status = NvSuccess; + NvFxTransportMessageMixerClose message; + struct completion comp; + + init_completion(&comp); + if (hMixer == NULL) { + snd_printk(KERN_ERR "NULL NvAudioFxMixerHandle!\n"); + goto EXIT_WITH_ERROR; + } + + message.Message = NVFXTRANSPORT_MESSAGE_MIXER_CLOSE; + message.pPrivateData = (void*)∁ + message.hMixer = hMixer; + + transport_send_message(NvFxTransportMessageMixerClose); + +EXIT_WITH_ERROR: + return; +} + +static NvAudioFxObjectHandle tegra_transport_mixer_create_object( + NvAudioFxMixerHandle hMixer, + NvObjectId Id) +{ + NvError status = NvSuccess; + NvAudioFxObjectHandle hObject = 0; + NvFxTransportMessageCreateObject message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_CREATE_OBJECT; + message.pPrivateData = (void*)∁ + message.hMixer = hMixer; + message.Id = Id; + message.phObject = &hObject; + + transport_send_message(NvFxTransportMessageCreateObject); + +EXIT_WITH_ERROR: + return hObject; +} + +static void tegra_transport_mixer_destroy_object(NvAudioFxObjectHandle hObject) +{ + NvError status = NvSuccess; + NvFxTransportMessageDestroyObject message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT; + message.pPrivateData = (void*)∁ + message.hObject = hObject; + + transport_send_message(NvFxTransportMessageDestroyObject); + +EXIT_WITH_ERROR: + return; +} + +static NvAudioFxMixBufferHandle tegra_transport_mixer_map_buffer( + NvAudioFxMixerHandle hMixer, + NvU32 NvRmMemHandleId, + NvU32 Offset, + NvU32 Size) +{ + NvError status = NvSuccess; + NvAudioFxMixBufferHandle mixer_buffer = 0; + NvFxTransportMessageMapBuffer message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_MAP_BUFFER; + message.pPrivateData = (void*)∁ + message.hMixer = hMixer; + message.NvRmMemHandleId = NvRmMemHandleId; + message.Offset = Offset; + message.Size = Size; + message.phMixBuffer = &mixer_buffer; + + transport_send_message(NvFxTransportMessageMapBuffer); +EXIT_WITH_ERROR: + return mixer_buffer; +} + + +static void tegra_transport_mixer_unmap_buffer( + NvAudioFxMixBufferHandle mixer_buffer) +{ + NvError status = NvSuccess; + NvFxTransportMessageUnmapBuffer message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER; + message.pPrivateData = (void*)∁ + message.hMixBuffer = mixer_buffer; + + transport_send_message(NvFxTransportMessageUnmapBuffer); + +EXIT_WITH_ERROR: + return; +} + +static NvError tegra_transport_get_property(NvAudioFxObjectHandle hObject, + NvAudioFxProperty Property, + NvU32 Size, + void* pProperty) +{ + NvError status = NvSuccess; + NvError retStatus = NvSuccess; + NvFxTransportMessageGetProperty message; + struct completion comp; + + if (FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE < Size) { + snd_printk(KERN_ERR "Property length too long!\n"); + goto EXIT_WITH_ERROR; + } + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_GET_PROPERTY; + message.pPrivateData = (void*)∁ + message.hObject = hObject; + message.Property = Property; + message.Size = Size; + message.pProperty = pProperty; + message.pReturnError = &retStatus; + + transport_send_message(NvFxTransportMessageGetProperty); + goto EXIT; + +EXIT_WITH_ERROR: + + retStatus = status; + +EXIT: + return retStatus; +} + +static NvError tegra_transport_set_property(NvAudioFxObjectHandle hObject, + NvAudioFxProperty Property, + NvU32 Size, + void* pProperty) +{ + NvError status = NvSuccess; + NvError retStatus = NvSuccess; + NvFxTransportMessageSetProperty message; + struct completion comp; + + if (FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE < Size) { + snd_printk(KERN_ERR "Property length too long!\n"); + goto EXIT_WITH_ERROR; + } + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_SET_PROPERTY; + message.pPrivateData = (void*)∁ + message.hObject = hObject; + message.Property = Property; + message.Size = Size; + NvOsMemcpy(message.PropertyData, pProperty, Size); + message.pReturnError = &retStatus; + + transport_send_message(NvFxTransportMessageSetProperty); + goto EXIT; + +EXIT_WITH_ERROR: + retStatus = status; + +EXIT: + return retStatus; +} + +static NvError tegra_transport_stream_add_buffer(NvAudioFxStreamHandle hStream, + NvAudioFxBufferDescriptor* pDescriptor) +{ + NvError status = NvSuccess; + NvError retStatus; + NvFxTransportMessageStreamAddBuffer message; + struct completion comp; + + init_completion(&comp); + message.Message = NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER; + message.pPrivateData = (void*)∁ + message.hStream = hStream; + message.Descriptor = *pDescriptor; + message.pReturnError = &retStatus; + + transport_send_message(NvFxTransportMessageStreamAddBuffer); + goto EXIT; + +EXIT_WITH_ERROR: + retStatus = status; + +EXIT: + return retStatus; +} + +static void AlsaTransportServiceThread(void *arg) +{ + NvError status = NvSuccess; + static NvFxTransportMessageResultBuffer in; + NvU32 messageSize = 0; + int retry = 0; +#define transport_complete(comp) \ + complete((struct completion*)comp); + + while (retry < 5 ) { + status = NvRmTransportConnect(atrans->hRmTransport, + TEGRA_TRANSPORT_CONNECT_TIMEOUT); + if (status == NvSuccess) + break; + retry++; + } + + if (status) { + snd_printk(KERN_ERR "AlsaTransportServiceThread: Failed to \ + connect to remote end. Giving up ! \n"); + atrans->TransportConnected = 0; + return; + } + + atrans->TransportConnected = 1; + + while (!atrans->ShutDown) { + NvOsSemaphoreWait(atrans->hServiceSema); + if (atrans->ShutDown) + break; + + status = NvRmTransportRecvMsg(atrans->hRmTransport, + &in, + sizeof(NvFxTransportMessageResultBuffer), + &messageSize); + if (status == NvSuccess) { + switch (in.Message.Message) { + case NVFXTRANSPORT_MESSAGE_MIXER_OPEN: { + *in.MixerOpen.phMixer = in.MixerOpen.hMixer; + transport_complete(in.MixerOpen.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_MIXER_CLOSE: { + transport_complete(in.MixerClose.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_CREATE_OBJECT: { + *in.CreateObject.phObject = + in.CreateObject.hObject; + transport_complete(in.CreateObject.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT: { + transport_complete(in.DestroyObject.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_MAP_BUFFER: { + *in.MapBuffer.phMixBuffer = + in.MapBuffer.hMixBuffer; + transport_complete(in.MapBuffer.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER: { + transport_complete(in.UnmapBuffer.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_GET_PROPERTY: { + *in.GetProperty.pReturnError = + in.GetProperty.ReturnError; + if (in.GetProperty.ReturnError == NvSuccess) { + NvOsMemcpy(in.GetProperty.pProperty, + in.GetProperty.PropertyData, + in.GetProperty.Size); + } + transport_complete(in.GetProperty.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_SET_PROPERTY: { + *in.SetProperty.pReturnError = + in.SetProperty.ReturnError; + transport_complete(in.SetProperty.pPrivateData); + } + break; + + case NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER: { + *in.StreamAddBuffer.pReturnError = + in.StreamAddBuffer.ReturnError; + transport_complete(in.StreamAddBuffer.pPrivateData); + } + break; + + default: + break; + } + } + } + +} + +NvError tegra_transport_init(NvddkAudioFxFxnTable* xrt_fxns) +{ + NvError status = NvSuccess; + + xrt_fxns->MixerOpen = tegra_transport_mixer_open; + xrt_fxns->MixerClose = tegra_transport_mixer_close; + xrt_fxns->MixerCreateObject = tegra_transport_mixer_create_object; + xrt_fxns->MixerDestroyObject = tegra_transport_mixer_destroy_object; + xrt_fxns->MixerMapBuffer = tegra_transport_mixer_map_buffer; + xrt_fxns->MixerUnmapBuffer = tegra_transport_mixer_unmap_buffer; + xrt_fxns->StreamAddBuffer = tegra_transport_stream_add_buffer; + xrt_fxns->GetProperty = tegra_transport_get_property; + xrt_fxns->SetProperty = tegra_transport_set_property; + + /* Check if the FX Transport is already open.*/ + if (atrans) { + spin_lock(&atrans->lock); + atrans->RefCount++; + spin_unlock(&atrans->lock); + goto EXIT; + } + /* Map a shared memory buffer.*/ + atrans = (AlsaTransport*)kzalloc(sizeof(AlsaTransport),GFP_KERNEL); + if (!atrans) { + snd_printk(KERN_ERR "AlsaTransportInit kalloc failed! \n"); + goto EXIT_WITH_ERROR; + } + + spin_lock_init(&atrans->lock); + memset(atrans, 0, sizeof(AlsaTransport)); + spin_lock(&atrans->lock); + atrans->RefCount++; + spin_unlock(&atrans->lock); + + atrans->hRmDevice = s_hRmGlobal; + + status = NvOsSemaphoreCreate(&atrans->hServiceSema, 0); + if (status != NvSuccess) { + snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n"); + goto EXIT_WITH_ERROR; + } + + status = NvRmTransportOpen(atrans->hRmDevice, + "ALSA_TRANSPORT", + atrans->hServiceSema, + &atrans->hRmTransport); + if (status != NvSuccess) { + snd_printk(KERN_ERR "NvRmTransportOpen failed!\n"); + goto EXIT_WITH_ERROR; + } + + status = NvOsThreadCreate(AlsaTransportServiceThread, + NULL, + &atrans->hServiceThread); + if (status != NvSuccess) { + snd_printk(KERN_ERR "NvOsThreadCreate failed!\n"); + goto EXIT_WITH_ERROR; + } + + while (!atrans->TransportConnected) { + NvOsThreadYield(); + } + + goto EXIT; + +EXIT_WITH_ERROR: + if (atrans) { + tegra_transport_deinit(); + } + +EXIT: + return status; +} + +void tegra_transport_deinit(void) +{ + if (!atrans) + goto EXIT; + + spin_lock(&atrans->lock); + atrans->RefCount--; + + if (atrans->RefCount > 0){ + spin_unlock(&atrans->lock); + goto EXIT; + } + spin_unlock(&atrans->lock); + + atrans->ShutDown = 1; + + if (atrans->hRmTransport) { + NvRmTransportClose(atrans->hRmTransport); + atrans->hRmTransport = 0; + atrans->TransportConnected = 0; + } + + if (atrans->hServiceThread) { + NvOsSemaphoreSignal(atrans->hServiceSema); + NvOsThreadJoin(atrans->hServiceThread); + atrans->hServiceThread = 0; + } + + if (atrans->hServiceSema) { + NvOsSemaphoreDestroy(atrans->hServiceSema); + atrans->hServiceSema = 0; + } + atrans->hRmDevice = 0; + kfree(atrans); + atrans = 0; + +EXIT: + return; +} + +static void tegra_audiofx_notifier_thread(void *arg) +{ + struct tegra_audio_data *audio_context = (struct tegra_audio_data *)arg; + FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier; + NvError e; + int retry = 0; + NvU32 messageSize; + NvAudioFxMessage* message = + (NvAudioFxMessage*)m_FxNotifier->RcvMessageBuffer; + + while (retry < 5) { + e = NvRmTransportConnect(m_FxNotifier->hTransport, 5000); + if (e == NvSuccess) + break; + + retry++; + } + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvRmTransportConnect failed!\n"); + m_FxNotifier->Connected = 0; + goto EXIT; + } + + m_FxNotifier->Connected = 1; + while (1) { + NvOsSemaphoreWait(m_FxNotifier->hTransportSemaphore); + if (m_FxNotifier->Exit) { + break; + } + + e = NvRmTransportRecvMsg(m_FxNotifier->hTransport, + message, + 256, + &messageSize); + if (e == NvSuccess) { + switch (message->Event) { + case NvAudioFxEventBufferDone:{ + NvAudioFxBufferDoneMessage* bdm = + (NvAudioFxBufferDoneMessage*)message; + struct pcm_runtime_data* prtd = + (struct pcm_runtime_data*)bdm->m.pContext; + + if ((prtd->stream == SNDRV_PCM_STREAM_PLAYBACK) && + (prtd->play_sema != NULL)){ + NvOsSemaphoreSignal(prtd->play_sema); + } + else if(prtd->rec_sema != NULL){ + NvOsSemaphoreSignal(prtd->rec_sema); + } + } + break; + + case NvAudioFxEventStateChange: + break; + + default: + snd_printk(KERN_ERR"Unhandled event\n"); + break; + } + } + } + +EXIT: + return; +} + +GlobalFxList* tegra_audiofx_create_topology(NvAudioFxMixerHandle hMixer) +{ + NvError e = NvSuccess; + NvAudioFxConnectionDescriptor connection; + GlobalFxList* pFxList = 0; + + memset(&connection, 0, sizeof(NvAudioFxConnectionDescriptor)); + pFxList = (GlobalFxList*)kzalloc(sizeof(GlobalFxList), GFP_KERNEL); + if (!pFxList) { + snd_printk(KERN_ERR "kzalloc failed!\n"); + goto EXIT_WITH_ERROR; + } + memset(pFxList, 0, sizeof(GlobalFxList)); + +#define audiofx_create_path(list_index, FxId) \ + pFxList->hFx[list_index] = tegra_transport_mixer_create_object(hMixer,FxId); \ + if(!pFxList->hFx[list_index]) { \ + snd_printk(KERN_ERR "audiofx_create_path failed!\n"); \ + goto EXIT_WITH_ERROR; \ + } + +#define audiofx_connect(src_index, sink_index) \ + connection.SourcePin = NvAudioFxSourcePin; \ + connection.SinkPin = NvAudioFxSinkPin; \ + connection.hSink = (NvAudioFxHandle)pFxList->hFx[sink_index]; \ + e = tegra_transport_set_property(pFxList->hFx[src_index], \ + NvAudioFxProperty_Attach, \ + sizeof(NvAudioFxConnectionDescriptor), \ + &connection); \ + if(e != NvSuccess) { \ + snd_printk(KERN_ERR "audiofx_connect failed!\n"); \ + } + + /* Default Playback Path */ + audiofx_create_path(GlobalFx_DefaultPlaybackMix, + NvAudioFxDefaultPlaybackMixId); + + audiofx_create_path(GlobalFx_DefaultPlaybackSplit, + NvAudioFxDefaultPlaybackSplitId); + + /* I2S Playback Path */ + audiofx_create_path(GlobalFx_I2sPlaybackMix, NvAudioFxI2sPlaybackMixId); + audiofx_create_path(GlobalFx_I2sPlaybackVolume, NvAudioFxVolumeId); + audiofx_create_path(GlobalFx_I2s, NvAudioFxI2sId); + + /* Default Record Path */ + audiofx_create_path(GlobalFx_DefaultRecordMix, + NvAudioFxDefaultRecordMixId); + + audiofx_create_path(GlobalFx_DefaultRecordSplit, + NvAudioFxDefaultRecordSplitId); + + /* I2S Record Path */ + audiofx_create_path(GlobalFx_I2sRecordVolume, NvAudioFxVolumeId); + audiofx_create_path(GlobalFx_I2sRecordSplit, NvAudioFxI2sRecordSplitId); + +#if (0) + /* Simple Playback */ + audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_I2s); +#else + /* Playback */ + audiofx_connect(GlobalFx_DefaultPlaybackMix, GlobalFx_DefaultPlaybackSplit); + audiofx_connect(GlobalFx_DefaultPlaybackSplit, GlobalFx_I2sPlaybackMix); + + /* Wire SPDIF on HDMI connection and unwire it when not connected. */ + audiofx_connect(GlobalFx_I2sPlaybackMix, GlobalFx_I2sPlaybackVolume); + audiofx_connect(GlobalFx_I2sPlaybackVolume, GlobalFx_I2s); +#endif + + /*Record*/ + audiofx_connect(GlobalFx_I2s, GlobalFx_I2sRecordVolume); + audiofx_connect(GlobalFx_I2sRecordVolume, GlobalFx_I2sRecordSplit); + + audiofx_connect(GlobalFx_I2sRecordSplit, GlobalFx_DefaultRecordMix); + audiofx_connect(GlobalFx_DefaultRecordMix, GlobalFx_DefaultRecordSplit); + + goto EXIT; + +EXIT_WITH_ERROR: + /* tegra_audiofx_destroy_topology(pFxList); */ + pFxList = 0; + +EXIT: + return pFxList; +} + +void tegra_audiofx_destroy_topology(GlobalFxList* pFxList) +{ + NvS32 i; + + if (pFxList) { + for (i = 0; i < GlobalFx_Num; i++) { + if (pFxList->hFx[i]) { + /* + TODO + NvddkAudioFxMixerDestroyObject(pFxList->hFx[i]); + Add code to destory + */ + } + } + + kfree(pFxList); + } +} + +NvError tegra_audiofx_createfx(struct tegra_audio_data *audio_context) +{ + NvAudioFxMixerHandle m_hMixer = + (NvAudioFxMixerHandle)audio_context->mixer_handle; + + FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier; + NvRmDeviceHandle m_hRm = (NvRmDeviceHandle)audio_context->m_hRm; + NvError e = NvSuccess; + NvAudioFxNotifierConnectionDescriptor connectionDesciptor; + NvAudioFxMessage message; + + memset(&connectionDesciptor, + 0, + sizeof(NvAudioFxNotifierConnectionDescriptor)); + memset(&message, 0, sizeof(NvAudioFxMessage)); + + e = NvOsSemaphoreCreate(&m_FxNotifier->hTransportSemaphore, 0); + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvOsSemaphoreCreate failed!\n"); + goto EXIT_WITH_ERROR; + } + + m_FxNotifier->hNotifier = + (NvAudioFxNotifierHandle)tegra_transport_mixer_create_object( + m_hMixer, + NvAudioFxNotifierId); + if (!m_FxNotifier->hNotifier) { + snd_printk(KERN_ERR "transport_mixer_create_object failed!\n"); + goto EXIT_WITH_ERROR; + } + + e = NvRmTransportOpen(m_hRm, + 0, + m_FxNotifier->hTransportSemaphore, + &m_FxNotifier->hTransport); + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvRmTransportOpen failed!\n"); + goto EXIT_WITH_ERROR; + } + + NvRmTransportGetPortName(m_FxNotifier->hTransport, + (NvU8*)&connectionDesciptor.PortName, + sizeof(NvU8) * 16); + + e = NvOsThreadCreate(tegra_audiofx_notifier_thread, + audio_context, + &m_FxNotifier->hTransportThread); + if (e != NvSuccess) { + snd_printk(KERN_ERR "NvOsThreadCreate failed!\n"); + goto EXIT_WITH_ERROR; + } + + e = tegra_transport_set_property( + (NvAudioFxObjectHandle)m_FxNotifier->hNotifier, + NvAudioFxNotifierProperty_Connect, + sizeof(NvAudioFxNotifierConnectionDescriptor), + &connectionDesciptor); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + goto EXIT; + +EXIT_WITH_ERROR: + tegra_audiofx_destroyfx(audio_context); + +EXIT: + return e; +} + +void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context) +{ + FxNotifier *m_FxNotifier = (FxNotifier*)&audio_context->m_FxNotifier; + + if (m_FxNotifier->Connected) { + m_FxNotifier->Exit = 1; + NvOsSemaphoreSignal(m_FxNotifier->hTransportSemaphore); + NvOsThreadJoin(m_FxNotifier->hTransportThread); + m_FxNotifier->hTransportThread = 0; + tegra_transport_set_property( + (NvAudioFxObjectHandle)m_FxNotifier->hNotifier, + NvAudioFxNotifierProperty_Disconnect, + 0, + 0); + } + + if (m_FxNotifier->hTransport) { + NvRmTransportClose(m_FxNotifier->hTransport); + m_FxNotifier->hTransport = 0; + } + + if (m_FxNotifier->hNotifier) { + tegra_transport_mixer_destroy_object( + (NvAudioFxObjectHandle)m_FxNotifier->hNotifier); + m_FxNotifier->hNotifier = 0; + } + + if (m_FxNotifier->hTransportSemaphore) { + NvOsSemaphoreDestroy(m_FxNotifier->hTransportSemaphore); + m_FxNotifier->hTransportSemaphore = 0; + } + + return; +} + +#define audiofx_create_object(path_object, FxId) \ + path_object = tegra_transport_mixer_create_object(hMixer, FxId); \ + if(!path_object) { \ + snd_printk(KERN_ERR "audiofx_create_object failed!"); \ + } + +#define audiofx_path_connect(path_object, sink_object) \ + connection.SourcePin = NvAudioFxSourcePin; \ + connection.SinkPin = NvAudioFxSinkPin; \ + connection.hSink = (NvAudioFxHandle)sink_object; \ + e = tegra_transport_set_property(path_object, \ + NvAudioFxProperty_Attach, \ + sizeof(NvAudioFxConnectionDescriptor), \ + &connection); \ + if(e != NvSuccess) { \ + snd_printk(KERN_ERR "audiofx_path_connect failed!"); \ + goto EXIT_WITH_ERROR; \ + } + +NvError tegra_audiofx_create_output(NvRmDeviceHandle hRmDevice, + NvAudioFxMixerHandle hMixer, + StandardPath* pPath) +{ + NvError e = NvSuccess; + NvAudioFxConnectionDescriptor connection; + + /* Standard Output + [stream]->[SRC]->[Convert]->[Resize]->[Volume]->Default Output + */ + + memset(pPath, 0, sizeof(StandardPath)); + + audiofx_create_object(pPath->Stream, NvAudioFxStreamId); + audiofx_create_object(pPath->Src, NvAudioFxSrcId); + audiofx_create_object(pPath->Convert, NvAudioFxConvertId); + audiofx_create_object(pPath->Resize, NvAudioFxResizeId); + audiofx_create_object(pPath->Volume, NvAudioFxVolumeId); + + audiofx_path_connect(pPath->Stream, pPath->Src); + audiofx_path_connect(pPath->Src, pPath->Convert); + audiofx_path_connect(pPath->Convert, pPath->Resize); + audiofx_path_connect(pPath->Resize, pPath->Volume); + + connection.SourcePin = NvAudioFxSourcePin; + connection.hSink = 0; + connection.SinkPin = NvAudioFxSinkPin; + e = tegra_transport_set_property(pPath->Volume, + NvAudioFxProperty_Attach, + sizeof(NvAudioFxConnectionDescriptor), + &connection); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + goto EXIT; + +EXIT_WITH_ERROR: + + tegra_audiofx_destroy_output(pPath); + +EXIT: + + return e; +} + +NvError tegra_audiofx_destroy_output(StandardPath* pPath) +{ + if (pPath->Volume) { + tegra_transport_mixer_destroy_object(pPath->Volume); + pPath->Volume = 0; + } + + if (pPath->Resize) { + tegra_transport_mixer_destroy_object(pPath->Resize); + pPath->Resize = 0; + } + + if (pPath->Convert) { + tegra_transport_mixer_destroy_object(pPath->Convert); + pPath->Convert = 0; + } + + if (pPath->Src) { + tegra_transport_mixer_destroy_object(pPath->Src); + pPath->Src = 0; + } + + if (pPath->Stream) { + tegra_transport_mixer_destroy_object(pPath->Stream); + pPath->Stream = 0; + } + + return NvSuccess; +} + +NvError tegra_audiofx_create_input(NvRmDeviceHandle hRmDevice, + NvAudioFxMixerHandle hMixer, + StandardPath* pInput, + InputSelection InputSelect) +{ + NvError e = NvSuccess; + NvAudioFxConnectionDescriptor connection; + + /* + Standard Input (record or loopback) + + +--------+ (2) +--------+ (3) +---------+ (4) +--------+ (5) + +--| Stream |<----| Resize |<-----| Convert |<-----| SRC |<--From I2S + | | | | |<--+ | (opt.) | | (opt.) | + | +--------+ +--------+ | +---------+ +--------+ + | (1) | + +------------------------------+ + + */ + + memset(pInput, 0, sizeof(StandardPath)); + + audiofx_create_object(pInput->Stream, NvAudioFxStreamId); + audiofx_create_object(pInput->Resize,NvAudioFxResizeId); + audiofx_create_object(pInput->Src,NvAudioFxSrcId); + audiofx_create_object(pInput->Convert,NvAudioFxConvertId); + + /* Wire 1 */ + connection.SourcePin = NvAudioFxSourcePin; + connection.hSink = (NvAudioFxHandle)pInput->Resize; + connection.SinkPin = NvAudioFxCopySinkPin; + e = tegra_transport_set_property(pInput->Stream, + NvAudioFxProperty_Attach, + sizeof(NvAudioFxConnectionDescriptor), + &connection); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + audiofx_path_connect(pInput->Resize, pInput->Stream); + + /* Wire 5 */ + connection.SourcePin = (InputSelect == NvAudioInputSelect_Record) ? + NvAudioFxSourcePin : NvAudioFxLoopbackPin; + connection.hSink = (NvAudioFxHandle)pInput->Resize; + connection.SinkPin = NvAudioFxSinkPin; + e = tegra_transport_set_property(0, + NvAudioFxProperty_Attach, + sizeof(NvAudioFxConnectionDescriptor), + &connection); + if (e != NvSuccess) { + snd_printk(KERN_ERR "tegra_transport_set_property failed!\n"); + goto EXIT_WITH_ERROR; + } + + goto EXIT; + +EXIT_WITH_ERROR: + + tegra_audiofx_destroy_input(pInput); + +EXIT: + + return e; +} + + +NvError tegra_audiofx_destroy_input(StandardPath* pInput) +{ + if (pInput->Src) { + tegra_transport_mixer_destroy_object(pInput->Src); + pInput->Src = 0; + } + + if (pInput->Convert) { + tegra_transport_mixer_destroy_object(pInput->Convert); + pInput->Convert = 0; + } + + if (pInput->Resize) { + tegra_transport_mixer_destroy_object(pInput->Resize); + pInput->Resize = 0; + } + + if (pInput->Stream) { + tegra_transport_mixer_destroy_object(pInput->Stream); + pInput->Stream = 0; + } + + return NvSuccess; +} + diff --git a/sound/soc/tegra/tegra_transport.h b/sound/soc/tegra/tegra_transport.h new file mode 100644 index 000000000000..8af6ac30424c --- /dev/null +++ b/sound/soc/tegra/tegra_transport.h @@ -0,0 +1,426 @@ +/* + * sound/soc/tegra/alsa_transport.h + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _TEGRA_TRANSPORT_H_ +#define _TEGRA_TRANSPORT_H_ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/kthread.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <linux/mutex.h> + +#include <mach/nvrm_linux.h> +#include "nvrm_memmgr.h" +#include "nvassert.h" +#include "nvrm_transport.h" + +#include "tegra_sndfx.h" + + +#define INIT_TIMEOUT 5000 +#define PLAY_TIMEOUT 5000 +#define REC_TIMEOUT 5000 +#define WHISTLER_CODEC_ADDRESS 0x1a +#define WHISTLER_CODEC_BUS 0 +#define NVALSA_BUFFER_COUNT 1 +#define TEGRA_DEFAULT_BUFFER_SIZE 4096 +#define NVALSA_INVALID_STATE -1 + +#define TEGRA_TRANSPORT_SEND_TIMEOUT 5000 +#define TEGRA_TRANSPORT_CONNECT_TIMEOUT 60000 +#define FXTRANSPORT_MSG_BUFFER_SIZE 256 +#define FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE FXTRANSPORT_MSG_BUFFER_SIZE - 32 +#define FXTRANSPORT_MSG_RESULT_DATA_SIZE FXTRANSPORT_MSG_BUFFER_SIZE - 32 + +typedef enum { + NVFXTRANSPORT_MESSAGE_MIXER_OPEN = 0, + NVFXTRANSPORT_MESSAGE_MIXER_CLOSE, + NVFXTRANSPORT_MESSAGE_CREATE_OBJECT, + NVFXTRANSPORT_MESSAGE_DESTROY_OBJECT, + NVFXTRANSPORT_MESSAGE_MAP_BUFFER, + NVFXTRANSPORT_MESSAGE_UNMAP_BUFFER, + NVFXTRANSPORT_MESSAGE_GET_PROPERTY, + NVFXTRANSPORT_MESSAGE_SET_PROPERTY, + NVFXTRANSPORT_MESSAGE_STREAM_ADD_BUFFER, + + NVFXTRANSPORT_MESSAGE_Force32 = 0x7FFFFFFF + +} NVFXTRANSPORT_MESSAGE; + + +typedef struct NvFxTransportMessageRec { +#define NVFXTRANSPORT_MESSAGE_VARS \ + NVFXTRANSPORT_MESSAGE Message; \ + NvOsSemaphoreHandle Semaphore; \ + void* pPrivateData; \ + NvU32 SendAck + + NVFXTRANSPORT_MESSAGE_VARS; + +} NvFxTransportMessage; + +typedef struct NvFxTransportMessageResultRec { +#define NVFXTRANSPORT_MESSAGE_RESULT_VARS \ + NVFXTRANSPORT_MESSAGE Message; \ + NvOsSemaphoreHandle Semaphore; \ + void* pPrivateData + + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + +} NvFxTransportMessageResult; + + +typedef struct NvFxTransportMessageMixerOpenRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxMixerHandle* phMixer; + +} NvFxTransportMessageMixerOpen; + +typedef struct NvFxTransportMessageResultMixerOpenRec { + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + NvAudioFxMixerHandle hMixer; + NvAudioFxMixerHandle* phMixer; + +} NvFxTransportMessageResultMixerOpen; + + +typedef struct NvFxTransportMessageMixerCloseRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxMixerHandle hMixer; + +} NvFxTransportMessageMixerClose; + +typedef NvFxTransportMessageResult NvFxTransportMessageResultMixerClose; + + +typedef struct NvFxTransportMessageCreateObjectRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxMixerHandle hMixer; + NvObjectId Id; + + NvAudioFxObjectHandle* phObject; + +} NvFxTransportMessageCreateObject; + +typedef struct NvFxTransportMessageResultCreateObjectRec { + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + NvAudioFxObjectHandle hObject; + NvAudioFxObjectHandle* phObject; + +} NvFxTransportMessageResultCreateObject; + + +typedef struct NvFxTransportMessageDestroyObjectRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxObjectHandle hObject; + +} NvFxTransportMessageDestroyObject; + +typedef NvFxTransportMessageResult NvFxTransportMessageResultDestroyObject; + + +typedef struct NvFxTransportMessageMapBufferRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxMixerHandle hMixer; + NvU32 NvRmMemHandleId; + NvU32 Offset; + NvU32 Size; + + NvAudioFxMixBufferHandle* phMixBuffer; + +} NvFxTransportMessageMapBuffer; + +typedef struct NvFxTransportMessageResultMapBufferRec { + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + NvAudioFxMixBufferHandle hMixBuffer; + NvAudioFxMixBufferHandle* phMixBuffer; + +} NvFxTransportMessageResultMapBuffer; + + +typedef struct NvFxTransportMessageUnmapBufferRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxMixBufferHandle hMixBuffer; + +} NvFxTransportMessageUnmapBuffer; + +typedef NvFxTransportMessageResult NvFxTransportMessageResultUnmapBuffer; + + +typedef struct NvFxTransportMessageGetPropertyRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxObjectHandle hObject; + NvAudioFxProperty Property; + NvU32 Size; + + void* pProperty; + NvError* pReturnError; + +} NvFxTransportMessageGetProperty; + +typedef struct NvFxTransportMessageResultGetPropertyRec { + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + NvU32 Size; + NvError ReturnError; + void* pProperty; + NvError* pReturnError; + NvU8 PropertyData[FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE]; + +} NvFxTransportMessageResultGetProperty; + + +typedef struct NvFxTransportMessageSetPropertyRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxObjectHandle hObject; + NvAudioFxProperty Property; + NvU32 Size; + NvError* pReturnError; + NvU8 PropertyData[FXTRANSPORT_MSG_BUFFER_PROPERTY_SIZE]; +} NvFxTransportMessageSetProperty; + +typedef struct NvFxTransportMessageResultSetPropertyRec { + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + NvError ReturnError; + NvError* pReturnError; + +} NvFxTransportMessageResultSetProperty; + + +typedef struct NvFxTransportMessageStreamAddBufferRec { + NVFXTRANSPORT_MESSAGE_VARS; + NvAudioFxStreamHandle hStream; + NvAudioFxBufferDescriptor Descriptor; + + NvError* pReturnError; + +} NvFxTransportMessageStreamAddBuffer; + +typedef struct NvFxTransportMessageResultStreamAddBufferRec { + NVFXTRANSPORT_MESSAGE_RESULT_VARS; + NvError ReturnError; + NvError* pReturnError; + +} NvFxTransportMessageResultStreamAddBuffer; + +typedef union NvFxTranspportMessageBuffer { + NvFxTransportMessage Message; + NvFxTransportMessageMixerOpen MixerOpen; + NvFxTransportMessageMixerClose MixerClose; + NvFxTransportMessageCreateObject CreateObject; + NvFxTransportMessageDestroyObject DestroyObject; + NvFxTransportMessageMapBuffer MapBuffer; + NvFxTransportMessageUnmapBuffer UnmapBuffer; + NvFxTransportMessageGetProperty GetProperty; + NvFxTransportMessageSetProperty SetProperty; + NvFxTransportMessageStreamAddBuffer StreamAddBuffer; + +} NvFxTransportMessageBuffer; + +typedef union NvFxTranspportMessageResultBuffer { + NvFxTransportMessageResult Message; + NvFxTransportMessageResultMixerOpen MixerOpen; + NvFxTransportMessageResultMixerClose MixerClose; + NvFxTransportMessageResultCreateObject CreateObject; + NvFxTransportMessageResultDestroyObject DestroyObject; + NvFxTransportMessageResultMapBuffer MapBuffer; + NvFxTransportMessageResultUnmapBuffer UnmapBuffer; + NvFxTransportMessageResultGetProperty GetProperty; + NvFxTransportMessageResultSetProperty SetProperty; + NvFxTransportMessageResultStreamAddBuffer StreamAddBuffer; + +} NvFxTransportMessageResultBuffer; + +typedef struct AlsaTransportRec { + NvddkAudioFxFxnTable* hFxnTable; + NvOsThreadHandle hServiceThread; + NvOsSemaphoreHandle hServiceSema; + + NvRmDeviceHandle hRmDevice; + NvRmTransportHandle hRmTransport; + + volatile NvU32 TransportConnected; + NvU32 RefCount; + NvU32 ShutDown; + spinlock_t lock; + +} AlsaTransport; + + +enum { + /* Default Playback Path*/ + GlobalFx_DefaultPlaybackMix = 0, + GlobalFx_DefaultPlaybackSplit, + + /* I2S Playback Path*/ + GlobalFx_I2sPlaybackMix, + GlobalFx_I2sPlaybackVolume, + GlobalFx_I2s, + + /* I2S2 Playback Path*/ + GlobalFx_I2s2PlaybackMix, + GlobalFx_I2s2PlaybackVolume, + GlobalFx_I2s2, + + /* SPDIF Playback Path*/ + GlobalFx_SpdifPlaybackMix, + GlobalFx_SpdifPlaybackVolume, + GlobalFx_Spdif, + + + /* Default Record Path*/ + GlobalFx_DefaultRecordMix, + GlobalFx_DefaultRecordSplit, + + /* I2S Record Path*/ + GlobalFx_I2sRecordVolume, + GlobalFx_I2sRecordSplit, + + /* I2S2 Record Path*/ + GlobalFx_I2s2RecordVolume, + GlobalFx_I2s2RecordSplit, + + /* SPDIF Record Path*/ + GlobalFx_SpdifRecordVolume, + GlobalFx_SpdifRecordSplit, + + + /* Loopbacks*/ + GlobalFx_I2sLoopbackSplit, + GlobalFx_I2s2LoopbackSplit, + GlobalFx_SpdifLoopbackSplit, + + + /* Music Path*/ + GlobalFx_MusicMix, + GlobalFx_MusicEq, + GlobalFx_MusicDrc, + GlobalFx_MusicSpreader, + GlobalFx_MusicPeq, + GlobalFx_MusicVolume, + GlobalFx_MusicSplit, + + + /* Phone Path*/ + GlobalFx_PhoneMix, + GlobalFx_PhoneSplit, + + GlobalFx_Num + +}; + +typedef struct GlobalFxListRec { + /* Default Playback Path*/ + NvAudioFxObjectHandle hFx[GlobalFx_Num]; +} GlobalFxList; + + +typedef struct FxNotifierRec { + NvAudioFxNotifierHandle hNotifier; + NvRmTransportHandle hTransport; + NvOsSemaphoreHandle hTransportSemaphore; + NvOsThreadHandle hTransportThread; + + NvU32 Exit; + NvU32 Connected; + NvU8 RcvMessageBuffer[256]; + + NvAudioFxEvent Event; + +} FxNotifier; + +typedef enum { + NvAudioInputSelect_Record = 0, + NvAudioInputSelect_Loopback + +} InputSelection; + +typedef struct StandardPathRec { + NvAudioFxObjectHandle Stream; + NvAudioFxObjectHandle Src; + NvAudioFxObjectHandle Convert; + NvAudioFxObjectHandle Volume; + NvAudioFxObjectHandle Resize; + + /*NvAudioFxVolumeDescriptor VolumeDesc;*/ + /*NvU32 VolumeRamping;*/ + /*StandardPosition StandardPosition;*/ + +} StandardPath; + +typedef struct NvAudioBufferRec { + NvRmMemHandle hRmMem; + NvAudioFxMixBufferHandle hMixBuffer; + void* pVirtAddr; + NvU32 Size; +} NvAudioBuffer; + +struct pcm_runtime_data { + spinlock_t lock; + struct task_struct *play_thread,*rec_thread; + int timeout; + int state; + int stream; + NvOsSemaphoreHandle play_sema,rec_sema; + struct completion play_comp; + struct completion rec_comp; + StandardPath* stdoutpath; + StandardPath* stdinpath; + NvU64 cur_pos; + NvU64 last_pos; + NvAudioFxMixBufferHandle mixer_buffer; +}; + +struct tegra_audio_data { + NvddkAudioFxFxnTable xrt_fxn; + NvAudioFxMixerHandle mixer_handle; + FxNotifier m_FxNotifier; + NvRmDeviceHandle m_hRm; + unsigned int mapped_buf_size; + NvAudioFxMixBufferHandle mixer_buffer[2]; + NvRmMemHandle mem_handle[2]; + struct mutex lock; +}; + + +NvError tegra_audiofx_createfx(struct tegra_audio_data *audio_context); +void tegra_audiofx_destroyfx(struct tegra_audio_data *audio_context); +NvError tegra_audiofx_create_output(NvRmDeviceHandle, + NvAudioFxMixerHandle, + StandardPath*); +NvError tegra_audiofx_destroy_output(StandardPath* pPath); +NvError tegra_audiofx_create_input(NvRmDeviceHandle hRmDevice, + NvAudioFxMixerHandle hMixer, + StandardPath* pPath, + InputSelection InputSelect); +NvError tegra_audiofx_destroy_input(StandardPath* pPath); +NvError tegra_transport_init(NvddkAudioFxFxnTable* FxTransportFxFxnTable); +void tegra_transport_deinit(void); + +#endif diff --git a/sound/soc/tegra/tegra_whistler.c b/sound/soc/tegra/tegra_whistler.c new file mode 100644 index 000000000000..bed84bb5b3f0 --- /dev/null +++ b/sound/soc/tegra/tegra_whistler.c @@ -0,0 +1,180 @@ +/* + * sound/soc/tegra/tegra_harmony.c + * + * ALSA SOC driver for NVIDIA Tegra SoCs + * + * Copyright (C) 2010 NVIDIA Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <linux/clk.h> +#include <asm/mach-types.h> +#include <asm/hardware/scoop.h> +#include <linux/io.h> +#include "nvodm_query_discovery.h" +#include "../codecs/wm8753.h" +#include "tegra_transport.h" + +static struct platform_device *tegra_snd_device; +NvU64 codec_guid; + +#define NVODM_CODEC_MAX_CLOCKS 3 + +static unsigned int clock_frequencies[NVODM_CODEC_MAX_CLOCKS]; + +static int set_clock_source_on_codec(NvU64 codec_guid,int IsEnable) +{ + const NvOdmPeripheralConnectivity *p_connectivity = NULL; + unsigned int clock_instances[NVODM_CODEC_MAX_CLOCKS]; + unsigned int num_clocks; + p_connectivity = NvOdmPeripheralGetGuid(codec_guid); + if (p_connectivity == NULL) + return NV_FALSE; + + if (IsEnable) { + if (!NvOdmExternalClockConfig(codec_guid, NV_FALSE, + clock_instances, + clock_frequencies, &num_clocks)) + return NV_FALSE; + } else { + if (!NvOdmExternalClockConfig(codec_guid, + NV_TRUE, + clock_instances, + clock_frequencies, + &num_clocks)); + return NV_FALSE; + } + return NV_TRUE; +} + +static int tegra_whistler_hifi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + /* Set codec DAI configuration */ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(codec_dai,SND_SOC_DAIFMT_I2S); + + if (ret < 0) + return ret; + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8753_MCLK, + clock_frequencies[0] * 1000, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + /* Disabling use of PLL1 since we are using only hifi for now + We may need it when we use voice */ + ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops tegra_whistler_hifi_ops = { + .hw_params = tegra_whistler_hifi_hw_params, +}; + +static int tegra_wm8753_init(struct snd_soc_codec *codec) +{ + return 0; +} + +extern struct snd_soc_dai tegra_i2s_rpc_dai; +extern struct snd_soc_platform tegra_soc_platform; + +static struct snd_soc_dai_link tegra_whistler_dai = { + /* Hifi Playback only for the time being */ + .name = "WM8753", + .stream_name = "WM8753 HiFi", + .cpu_dai = &tegra_i2s_rpc_dai, + .codec_dai = &wm8753_dai[WM8753_DAI_HIFI], + .init = tegra_wm8753_init, + .ops = &tegra_whistler_hifi_ops, +}; + +static struct snd_soc_card tegra_whistler = { + .name = "tegra", + .platform = &tegra_soc_platform, + .dai_link = &tegra_whistler_dai, + .num_links = 1, +}; + +static struct wm8753_setup_data whistler_setup_data = { + .i2c_bus = WHISTLER_CODEC_BUS, + .i2c_address = WHISTLER_CODEC_ADDRESS, +}; + +static struct snd_soc_device tegra_whistler_snd_devdata = { + .card = &tegra_whistler, + .codec_dev = &soc_codec_dev_wm8753, + .codec_data = &whistler_setup_data, +}; + +static int __init tegra_soc_init(void) +{ + int ret; + tegra_snd_device = platform_device_alloc("soc-audio", -1); + if (!tegra_snd_device) + return -ENOMEM; + + platform_set_drvdata(tegra_snd_device, &tegra_whistler_snd_devdata); + tegra_whistler_snd_devdata.dev = &tegra_snd_device->dev; + + ret = platform_device_add(tegra_snd_device); + if (ret) { + snd_printk(KERN_ERR "tegra audio device could not be added \n"); + platform_device_put(tegra_snd_device); + return ret; + } + + codec_guid = NV_ODM_GUID('w','o','l','f','8','7','5','3'); + + set_clock_source_on_codec(codec_guid,NV_TRUE); + if (ret != 0) + platform_device_unregister(tegra_snd_device); + + return ret; +} + +static void __exit tegra_soc_exit(void) +{ + set_clock_source_on_codec(codec_guid,0); + platform_device_unregister(tegra_snd_device); +} + +module_init(tegra_soc_init); +module_exit(tegra_soc_exit); + +/* Module information */ +MODULE_DESCRIPTION("Tegra SoC Sound"); +MODULE_LICENSE("GPL"); |