diff options
author | Nicolas Chauvet <kwizart@gmail.com> | 2013-07-23 00:23:59 +0200 |
---|---|---|
committer | Nicolas Chauvet <kwizart@gmail.com> | 2013-10-12 14:20:36 +0200 |
commit | e792c5bf24d99b2dadbb74e59704f8fce02963f3 (patch) | |
tree | eb96bfef4fcd8bd3b3a768e62a033fa68125ca69 | |
parent | bcc447331c08bcd58847294e33388f4b4ea271d6 (diff) | |
download | kernel-e792c5bf24d99b2dadbb74e59704f8fce02963f3.tar.gz kernel-e792c5bf24d99b2dadbb74e59704f8fce02963f3.tar.xz kernel-e792c5bf24d99b2dadbb74e59704f8fce02963f3.zip |
Add patches from marvin24s tree linux-3.10-ac100
git clone git://gitorious.org/~marvin24/ac100/marvin24s-kernel.git
origin/linux-ac100-3.10
-rw-r--r-- | marvin24s-ac100-3.10.patch | 4044 |
1 files changed, 4044 insertions, 0 deletions
diff --git a/marvin24s-ac100-3.10.patch b/marvin24s-ac100-3.10.patch new file mode 100644 index 00000000..125ef4b5 --- /dev/null +++ b/marvin24s-ac100-3.10.patch @@ -0,0 +1,4044 @@ +diff --git a/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt +index b4fa934..9c65e8e 100644 +--- a/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt ++++ b/Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt +@@ -67,6 +67,7 @@ of the following host1x client modules: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob ++ - nvidia,panel: phandle of a display entity connected to this output + + - hdmi: High Definition Multimedia Interface + +@@ -81,6 +82,7 @@ of the following host1x client modules: + - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing + - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection + - nvidia,edid: supplies a binary EDID blob ++ - nvidia,panel: phandle of a display entity connected to this output + + - tvo: TV encoder output + +diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt +index 05ffecb..208fd3b 100644 +--- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt ++++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt +@@ -29,7 +29,6 @@ Required properties: + * MIC1_N + * MIC2_P + * MIC2_N +- * MICBIAS1 + * DMICDAT + + Board connectors: +@@ -53,7 +52,6 @@ sound { + nvidia,audio-routing = + "Int Spk", "SPK_OUTP", + "Int Spk", "SPK_OUTN", +- "Headset Mic","MICBIAS1", + "MIC1_N", "Headset Mic", + "MIC1_P", "Headset Mic", + "Headset Stereophone", "HP_OUT_R", +diff --git a/Documentation/devicetree/bindings/video/display/chunghwa,claa101wa01a.txt b/Documentation/devicetree/bindings/video/display/chunghwa,claa101wa01a.txt +new file mode 100644 +index 0000000..cfdc7fd +--- /dev/null ++++ b/Documentation/devicetree/bindings/video/display/chunghwa,claa101wa01a.txt +@@ -0,0 +1,8 @@ ++Chunghwa CLAA101WA01A Display Panel ++ ++Required properties: ++- compatible: "chunghwa,claa101wa01a" ++- pnl-supply: regulator controlling power supply to the panel ++- bl-supply: regulator controlling power supply to the backlight ++- pnl-enable-gpios: GPIO that enables the panel ++- bl-enable-gpios: GPIO that enables the backlight +diff --git a/MAINTAINERS b/MAINTAINERS +index 48c7480..74abed3 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2697,12 +2697,13 @@ F: include/drm/exynos* + F: include/uapi/drm/exynos* + + DRM DRIVERS FOR NVIDIA TEGRA +-M: Thierry Reding <thierry.reding@avionic-design.de> ++M: Thierry Reding <thierry.reding@gmail.com> ++M: Terje Bergström <tbergstrom@nvidia.com> + L: dri-devel@lists.freedesktop.org + L: linux-tegra@vger.kernel.org +-T: git git://gitorious.org/thierryreding/linux.git ++T: git git://anongit.freedesktop.org/tegra/linux.git + S: Maintained +-F: drivers/gpu/drm/tegra/ ++F: drivers/gpu/host1x/ + F: Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt + + DSBR100 USB FM RADIO DRIVER +diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi +index a573b94..0d7665e 100644 +--- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi ++++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi +@@ -362,7 +362,7 @@ + }; + + pmc { +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <5000>; + nvidia,cpu-pwr-off-time = <5000>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts +index e7d5de4..f0129c6 100644 +--- a/arch/arm/boot/dts/tegra20-harmony.dts ++++ b/arch/arm/boot/dts/tegra20-harmony.dts +@@ -416,7 +416,7 @@ + + pmc { + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <5000>; + nvidia,cpu-pwr-off-time = <5000>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts +index e3e0c99..994f0b8 100644 +--- a/arch/arm/boot/dts/tegra20-paz00.dts ++++ b/arch/arm/boot/dts/tegra20-paz00.dts +@@ -11,6 +11,14 @@ + }; + + host1x { ++ dc@54200000 { ++ rgb { ++ status = "okay"; ++ nvidia,ddc-i2c-bus = <&lvds_ddc>; ++ nvidia,panel = <&panel>; ++ }; ++ }; ++ + hdmi { + status = "okay"; + +@@ -250,7 +258,7 @@ + status = "okay"; + }; + +- i2c@7000c000 { ++ lvds_ddc: i2c@7000c000 { + status = "okay"; + clock-frequency = <400000>; + +@@ -415,7 +423,7 @@ + + pmc { + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <0>; + nvidia,core-pwr-good-time = <3845 3845>; +@@ -488,6 +496,30 @@ + }; + }; + ++ pwm: pwm { ++ status = "okay"; ++ }; ++ ++ backlight: backlight { ++ compatible = "pwm-backlight"; ++ pwms = <&pwm 2 5000000>; ++ ++ brightness-levels = <0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 255>; ++ default-brightness-level = <10>; ++ }; ++ ++ panel: panel { ++ compatible = "chunghwa,claa101wa01a"; ++ ++ pnl-supply = <&vdd_panel_reg>; ++ pnl-enable-gpios = <&gpio 102 0>; ++ ++ bl-supply = <&bl_panel_reg>; ++ bl-enable-gpios = <&gpio 164 0>; ++ ++ backlight = <&backlight>; ++ }; ++ + regulators { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -501,6 +533,25 @@ + regulator-max-microvolt = <5000000>; + regulator-always-on; + }; ++ ++ vdd_panel_reg: regulator@1 { ++ compatible = "regulator-fixed"; ++ reg = <1>; ++ regulator-name = "+3VS,vdd_pnl"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ gpio = <&gpio 4 0>; /* gpio PA4 */ ++ enable-active-high; ++ }; ++ ++ bl_panel_reg: regulator@2 { ++ compatible = "regulator-fixed"; ++ reg = <2>; ++ regulator-name = "dummy blacklight regulator"; ++ regulator-min-microvolt = <3000000>; ++ regulator-max-microvolt = <3000000>; ++ regulator-always-on; ++ }; + }; + + sound { +@@ -512,7 +563,6 @@ + nvidia,audio-routing = + "Int Spk", "SPKOUT", + "Int Spk", "SPKOUTN", +- "Headset Mic", "MICBIAS1", + "MIC1", "Headset Mic", + "Headset Stereophone", "HPR", + "Headset Stereophone", "HPL", +diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts +index cee4c34..e22ef5f 100644 +--- a/arch/arm/boot/dts/tegra20-seaboard.dts ++++ b/arch/arm/boot/dts/tegra20-seaboard.dts +@@ -517,7 +517,7 @@ + + pmc { + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <5000>; + nvidia,cpu-pwr-off-time = <5000>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi +index 50b3ec1..4b396865 100644 +--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi ++++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi +@@ -458,7 +458,7 @@ + + pmc { + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <5000>; + nvidia,cpu-pwr-off-time = <5000>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts +index 9cc78a1..c7e568d 100644 +--- a/arch/arm/boot/dts/tegra20-trimslice.dts ++++ b/arch/arm/boot/dts/tegra20-trimslice.dts +@@ -301,7 +301,7 @@ + }; + + pmc { +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <5000>; + nvidia,cpu-pwr-off-time = <5000>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts +index dd38f1f..32cddf2 100644 +--- a/arch/arm/boot/dts/tegra20-ventana.dts ++++ b/arch/arm/boot/dts/tegra20-ventana.dts +@@ -493,7 +493,7 @@ + + pmc { + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <100>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts +index d2567f8..954b4a3 100644 +--- a/arch/arm/boot/dts/tegra20-whistler.dts ++++ b/arch/arm/boot/dts/tegra20-whistler.dts +@@ -496,7 +496,7 @@ + + pmc { + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <1000>; + nvidia,core-pwr-good-time = <0 3845>; +diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts +index b732f7c..22cfc03 100644 +--- a/arch/arm/boot/dts/tegra30-beaver.dts ++++ b/arch/arm/boot/dts/tegra30-beaver.dts +@@ -253,7 +253,7 @@ + pmc { + status = "okay"; + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <200>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi +index 01b4c26..4dc3e9b 100644 +--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi ++++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi +@@ -307,7 +307,7 @@ + pmc { + status = "okay"; + nvidia,invert-interrupt; +- nvidia,suspend-mode = <2>; ++ nvidia,suspend-mode = <1>; + nvidia,cpu-pwr-good-time = <2000>; + nvidia,cpu-pwr-off-time = <200>; + nvidia,core-pwr-good-time = <3845 3845>; +diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig +index f7ba3161..594ceed 100644 +--- a/arch/arm/configs/tegra_defconfig ++++ b/arch/arm/configs/tegra_defconfig +@@ -1,3 +1,4 @@ ++CONFIG_SYSVIPC=y + CONFIG_NO_HZ=y + CONFIG_HIGH_RES_TIMERS=y + CONFIG_IKCONFIG=y +@@ -21,8 +22,8 @@ CONFIG_MODULE_FORCE_UNLOAD=y + CONFIG_PARTITION_ADVANCED=y + # CONFIG_IOSCHED_DEADLINE is not set + # CONFIG_IOSCHED_CFQ is not set +-CONFIG_ARCH_TEGRA=y + CONFIG_GPIO_PCA953X=y ++CONFIG_ARCH_TEGRA=y + CONFIG_ARCH_TEGRA_2x_SOC=y + CONFIG_ARCH_TEGRA_3x_SOC=y + CONFIG_ARCH_TEGRA_114_SOC=y +@@ -35,8 +36,9 @@ CONFIG_AEABI=y + CONFIG_HIGHMEM=y + CONFIG_ZBOOT_ROM_TEXT=0x0 + CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_ARM_APPENDED_DTB=y ++CONFIG_ARM_ATAG_DTB_COMPAT=y + CONFIG_KEXEC=y +-CONFIG_AUTO_ZRELADDR=y + CONFIG_CPU_FREQ=y + CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y + CONFIG_CPU_IDLE=y +@@ -81,7 +83,6 @@ CONFIG_DEVTMPFS_MOUNT=y + # CONFIG_FIRMWARE_IN_KERNEL is not set + CONFIG_CMA=y + CONFIG_MTD=y +-CONFIG_MTD_CHAR=y + CONFIG_MTD_M25P80=y + CONFIG_PROC_DEVICETREE=y + CONFIG_BLK_DEV_LOOP=y +@@ -105,8 +106,9 @@ CONFIG_BRCMFMAC=m + CONFIG_RT2X00=y + CONFIG_RT2800USB=m + CONFIG_INPUT_EVDEV=y +-CONFIG_KEYBOARD_TEGRA=y + CONFIG_KEYBOARD_GPIO=y ++CONFIG_KEYBOARD_TEGRA=y ++CONFIG_MOUSE_PS2_ELANTECH=y + CONFIG_INPUT_MISC=y + CONFIG_INPUT_MPU3050=y + # CONFIG_LEGACY_PTYS is not set +@@ -132,12 +134,13 @@ CONFIG_BATTERY_SBS=y + CONFIG_POWER_RESET=y + CONFIG_POWER_RESET_GPIO=y + CONFIG_SENSORS_LM90=y +-CONFIG_MFD_TPS6586X=y +-CONFIG_MFD_TPS65910=y + CONFIG_MFD_MAX8907=y +-CONFIG_MFD_TPS65090=y + CONFIG_MFD_PALMAS=y ++CONFIG_MFD_TPS65090=y ++CONFIG_MFD_TPS6586X=y ++CONFIG_MFD_TPS65910=y + CONFIG_REGULATOR=y ++CONFIG_REGULATOR_DUMMY=y + CONFIG_REGULATOR_FIXED_VOLTAGE=y + CONFIG_REGULATOR_VIRTUAL_CONSUMER=y + CONFIG_REGULATOR_GPIO=y +@@ -155,13 +158,14 @@ CONFIG_USB_VIDEO_CLASS=m + CONFIG_DRM=y + CONFIG_TEGRA_HOST1X=y + CONFIG_DRM_TEGRA=y ++CONFIG_DRM_TEGRA_STAGING=y + CONFIG_BACKLIGHT_LCD_SUPPORT=y + # CONFIG_LCD_CLASS_DEVICE is not set + CONFIG_BACKLIGHT_CLASS_DEVICE=y + # CONFIG_BACKLIGHT_GENERIC is not set +-CONFIG_BACKLIGHT_PWM=y ++CONFIG_DISPLAY_CORE=y ++CONFIG_DISPLAY_PANEL_CLAA101WA01A=y + CONFIG_FRAMEBUFFER_CONSOLE=y +-CONFIG_LOGO=y + CONFIG_SOUND=y + CONFIG_SND=y + # CONFIG_SND_SUPPORT_OLD_API is not set +@@ -182,6 +186,7 @@ CONFIG_USB_ACM=y + CONFIG_USB_WDM=y + CONFIG_USB_STORAGE=y + CONFIG_MMC=y ++CONFIG_MMC_UNSAFE_RESUME=y + CONFIG_MMC_BLOCK_MINORS=16 + CONFIG_MMC_SDHCI=y + CONFIG_MMC_SDHCI_PLTFM=y +@@ -192,6 +197,7 @@ CONFIG_LEDS_GPIO=y + CONFIG_LEDS_TRIGGERS=y + CONFIG_LEDS_TRIGGER_GPIO=y + CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc1" + CONFIG_RTC_DRV_MAX8907=y + CONFIG_RTC_DRV_PALMAS=y + CONFIG_RTC_DRV_TPS6586X=y +@@ -203,7 +209,6 @@ CONFIG_TEGRA20_APB_DMA=y + CONFIG_STAGING=y + CONFIG_SENSORS_ISL29018=y + CONFIG_SENSORS_ISL29028=y +-CONFIG_AK8975=y + CONFIG_MFD_NVEC=y + CONFIG_KEYBOARD_NVEC=y + CONFIG_SERIO_NVEC_PS2=y +@@ -213,6 +218,7 @@ CONFIG_TEGRA_IOMMU_GART=y + CONFIG_TEGRA_IOMMU_SMMU=y + CONFIG_MEMORY=y + CONFIG_IIO=y ++CONFIG_AK8975=y + CONFIG_PWM=y + CONFIG_PWM_TEGRA=y + CONFIG_EXT2_FS=y +diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile +index d011f0a..0412f85 100644 +--- a/arch/arm/mach-tegra/Makefile ++++ b/arch/arm/mach-tegra/Makefile +@@ -17,11 +17,13 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o + obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o + obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o + obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o ++obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o + ifeq ($(CONFIG_CPU_IDLE),y) + obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o + endif + obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_speedo.o + obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o ++obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o + ifeq ($(CONFIG_CPU_IDLE),y) + obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o + endif +diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c +index 0cdba8d..706aa42 100644 +--- a/arch/arm/mach-tegra/cpuidle-tegra20.c ++++ b/arch/arm/mach-tegra/cpuidle-tegra20.c +@@ -177,7 +177,6 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) + { +- u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; + bool entered_lp2 = false; + + if (tegra_pending_sgi()) +@@ -193,16 +192,16 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, + + local_fiq_disable(); + +- tegra_set_cpu_in_lp2(cpu); ++ tegra_set_cpu_in_lp2(); + cpu_pm_enter(); + +- if (cpu == 0) ++ if (dev->cpu == 0) + entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index); + else + entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index); + + cpu_pm_exit(); +- tegra_clear_cpu_in_lp2(cpu); ++ tegra_clear_cpu_in_lp2(); + + local_fiq_enable(); + +@@ -214,8 +213,5 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, + + int __init tegra20_cpuidle_init(void) + { +-#ifdef CONFIG_PM_SLEEP +- tegra_tear_down_cpu = tegra20_tear_down_cpu; +-#endif + return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); + } +diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c +index 3cf9aca..ed2a2a7 100644 +--- a/arch/arm/mach-tegra/cpuidle-tegra30.c ++++ b/arch/arm/mach-tegra/cpuidle-tegra30.c +@@ -114,16 +114,15 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) + { +- u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; + bool entered_lp2 = false; + bool last_cpu; + + local_fiq_disable(); + +- last_cpu = tegra_set_cpu_in_lp2(cpu); ++ last_cpu = tegra_set_cpu_in_lp2(); + cpu_pm_enter(); + +- if (cpu == 0) { ++ if (dev->cpu == 0) { + if (last_cpu) + entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv, + index); +@@ -134,7 +133,7 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, + } + + cpu_pm_exit(); +- tegra_clear_cpu_in_lp2(cpu); ++ tegra_clear_cpu_in_lp2(); + + local_fiq_enable(); + +@@ -146,8 +145,5 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, + + int __init tegra30_cpuidle_init(void) + { +-#ifdef CONFIG_PM_SLEEP +- tegra_tear_down_cpu = tegra30_tear_down_cpu; +-#endif + return cpuidle_register(&tegra_idle_driver, NULL); + } +diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h +index aacc00d..def7968 100644 +--- a/arch/arm/mach-tegra/fuse.h ++++ b/arch/arm/mach-tegra/fuse.h +@@ -19,16 +19,6 @@ + #ifndef __MACH_TEGRA_FUSE_H + #define __MACH_TEGRA_FUSE_H + +-enum tegra_revision { +- TEGRA_REVISION_UNKNOWN = 0, +- TEGRA_REVISION_A01, +- TEGRA_REVISION_A02, +- TEGRA_REVISION_A03, +- TEGRA_REVISION_A03p, +- TEGRA_REVISION_A04, +- TEGRA_REVISION_MAX, +-}; +- + #define SKU_ID_T20 8 + #define SKU_ID_T25SE 20 + #define SKU_ID_AP25 23 +@@ -40,6 +30,17 @@ enum tegra_revision { + #define TEGRA30 0x30 + #define TEGRA114 0x35 + ++#ifndef __ASSEMBLY__ ++enum tegra_revision { ++ TEGRA_REVISION_UNKNOWN = 0, ++ TEGRA_REVISION_A01, ++ TEGRA_REVISION_A02, ++ TEGRA_REVISION_A03, ++ TEGRA_REVISION_A03p, ++ TEGRA_REVISION_A04, ++ TEGRA_REVISION_MAX, ++}; ++ + extern int tegra_sku_id; + extern int tegra_cpu_process_id; + extern int tegra_core_process_id; +@@ -72,5 +73,6 @@ void tegra114_init_speedo_data(void); + #else + static inline void tegra114_init_speedo_data(void) {} + #endif ++#endif /* __ASSEMBLY__ */ + + #endif +diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c +index 184914a..5eedebd 100644 +--- a/arch/arm/mach-tegra/hotplug.c ++++ b/arch/arm/mach-tegra/hotplug.c +@@ -37,7 +37,7 @@ int tegra_cpu_kill(unsigned cpu) + void __ref tegra_cpu_die(unsigned int cpu) + { + /* Clean L1 data cache */ +- tegra_disable_clean_inv_dcache(); ++ tegra_disable_clean_inv_dcache(TEGRA_FLUSH_CACHE_LOUIS); + + /* Shut down the current CPU. */ + tegra_hotplug_shutdown(); +diff --git a/arch/arm/mach-tegra/pm-tegra20.c b/arch/arm/mach-tegra/pm-tegra20.c +new file mode 100644 +index 0000000..47bc40b +--- /dev/null ++++ b/arch/arm/mach-tegra/pm-tegra20.c +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. ++ */ ++#include <linux/kernel.h> ++ ++#include "pm.h" ++ ++#ifdef CONFIG_PM_SLEEP ++static struct tegra_lp1_iram tegra20_lp1_iram; ++extern u32 tegra20_iram_start, tegra20_iram_end; ++extern void tegra20_sleep_core_finish(unsigned long); ++ ++void tegra20_lp1_iram_hook(void) ++{ ++ tegra20_lp1_iram.start_addr = &tegra20_iram_start; ++ tegra20_lp1_iram.end_addr = &tegra20_iram_end; ++ ++ tegra_lp1_iram = &tegra20_lp1_iram; ++} ++ ++void tegra20_sleep_core_init(void) ++{ ++ tegra_sleep_core_finish = tegra20_sleep_core_finish; ++} ++#endif +diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c +new file mode 100644 +index 0000000..6786955 +--- /dev/null ++++ b/arch/arm/mach-tegra/pm-tegra30.c +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. ++ */ ++#include <linux/kernel.h> ++ ++#include "pm.h" ++ ++#ifdef CONFIG_PM_SLEEP ++static struct tegra_lp1_iram tegra30_lp1_iram; ++extern u32 tegra30_iram_start, tegra30_iram_end; ++extern void tegra30_sleep_core_finish(unsigned long); ++ ++void tegra30_lp1_iram_hook(void) ++{ ++ tegra30_lp1_iram.start_addr = &tegra30_iram_start; ++ tegra30_lp1_iram.end_addr = &tegra30_iram_end; ++ ++ tegra_lp1_iram = &tegra30_lp1_iram; ++} ++ ++void tegra30_sleep_core_init(void) ++{ ++ tegra_sleep_core_finish = tegra30_sleep_core_finish; ++} ++#endif +diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c +index 45cf52c..d2b8d56 100644 +--- a/arch/arm/mach-tegra/pm.c ++++ b/arch/arm/mach-tegra/pm.c +@@ -37,12 +37,34 @@ + #include "reset.h" + #include "flowctrl.h" + #include "fuse.h" ++#include "pm.h" + #include "pmc.h" + #include "sleep.h" + + #ifdef CONFIG_PM_SLEEP + static DEFINE_SPINLOCK(tegra_lp2_lock); ++static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA); ++static u32 iram_save_size; ++static void *iram_save_addr; ++struct tegra_lp1_iram *tegra_lp1_iram; + void (*tegra_tear_down_cpu)(void); ++void (*tegra_sleep_core_finish)(unsigned long v2p); ++ ++static void tegra_tear_down_cpu_init(void) ++{ ++ switch (tegra_chip_id) { ++ case TEGRA20: ++ if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) ++ tegra_tear_down_cpu = tegra20_tear_down_cpu; ++ break; ++ case TEGRA30: ++ case TEGRA114: ++ if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || ++ IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) ++ tegra_tear_down_cpu = tegra30_tear_down_cpu; ++ break; ++ } ++} + + /* + * restore_cpu_complex +@@ -91,8 +113,9 @@ static void suspend_cpu_complex(void) + flowctrl_cpu_suspend_enter(cpu); + } + +-void tegra_clear_cpu_in_lp2(int phy_cpu_id) ++void tegra_clear_cpu_in_lp2(void) + { ++ int phy_cpu_id = cpu_logical_map(smp_processor_id()); + u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; + + spin_lock(&tegra_lp2_lock); +@@ -103,8 +126,9 @@ void tegra_clear_cpu_in_lp2(int phy_cpu_id) + spin_unlock(&tegra_lp2_lock); + } + +-bool tegra_set_cpu_in_lp2(int phy_cpu_id) ++bool tegra_set_cpu_in_lp2(void) + { ++ int phy_cpu_id = cpu_logical_map(smp_processor_id()); + bool last_cpu = false; + cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask; + u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; +@@ -160,14 +184,97 @@ enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + return TEGRA_SUSPEND_NONE; + + /* +- * The Tegra devices only support suspending to LP2 currently. ++ * The Tegra devices only support suspending to LP1 currently. + */ +- if (mode > TEGRA_SUSPEND_LP2) +- return TEGRA_SUSPEND_LP2; ++ if (mode > TEGRA_SUSPEND_LP1) ++ return TEGRA_SUSPEND_LP1; + + return mode; + } + ++static int tegra_sleep_core(unsigned long v2p) ++{ ++ setup_mm_for_reboot(); ++ tegra_sleep_core_finish(v2p); ++ ++ /* should never here */ ++ BUG(); ++ ++ return 0; ++} ++ ++/* ++ * tegra_lp1_iram_hook ++ * ++ * Hooking the address of LP1 reset vector and SDRAM self-refresh code in ++ * SDRAM. These codes not be copied to IRAM in this fuction. We need to ++ * copy these code to IRAM before LP0/LP1 suspend and restore the content ++ * of IRAM after resume. ++ */ ++static bool tegra_lp1_iram_hook(void) ++{ ++ switch (tegra_chip_id) { ++ case TEGRA30: ++ tegra30_lp1_iram_hook(); ++ break; ++ case TEGRA20: ++ tegra20_lp1_iram_hook(); ++ break; ++ default: ++ break; ++ } ++ ++ if (!tegra_lp1_iram) ++ return false; ++ ++ iram_save_size = tegra_lp1_iram->end_addr - tegra_lp1_iram->start_addr; ++ iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL); ++ if (!iram_save_addr) ++ return false; ++ ++ return true; ++} ++ ++static bool tegra_sleep_core_init(void) ++{ ++ switch (tegra_chip_id) { ++ case TEGRA30: ++ tegra30_sleep_core_init(); ++ break; ++ case TEGRA20: ++ tegra20_sleep_core_init(); ++ break; ++ default: ++ break; ++ } ++ ++ if (!tegra_sleep_core_finish) ++ return false; ++ ++ return true; ++} ++ ++static void tegra_suspend_enter_lp1(void) ++{ ++ tegra_pmc_suspend(); ++ ++ /* copy the reset vector & SDRAM shutdown code into IRAM */ ++ memcpy(iram_save_addr, iram_code, iram_save_size); ++ memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size); ++ ++ *((u32 *)tegra_cpu_lp1_mask) = 1; ++} ++ ++static void tegra_suspend_exit_lp1(void) ++{ ++ tegra_pmc_resume(); ++ ++ /* restore IRAM */ ++ memcpy(iram_code, iram_save_addr, iram_save_size); ++ ++ *(u32 *)tegra_cpu_lp1_mask = 0; ++} ++ + static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { + [TEGRA_SUSPEND_NONE] = "none", + [TEGRA_SUSPEND_LP2] = "LP2", +@@ -191,18 +298,27 @@ static int __cpuinit tegra_suspend_enter(suspend_state_t state) + + suspend_cpu_complex(); + switch (mode) { ++ case TEGRA_SUSPEND_LP1: ++ tegra_suspend_enter_lp1(); ++ break; + case TEGRA_SUSPEND_LP2: +- tegra_set_cpu_in_lp2(0); ++ tegra_set_cpu_in_lp2(); + break; + default: + break; + } + +- cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); ++ if (mode == TEGRA_SUSPEND_LP2) ++ cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); ++ else ++ cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core); + + switch (mode) { ++ case TEGRA_SUSPEND_LP1: ++ tegra_suspend_exit_lp1(); ++ break; + case TEGRA_SUSPEND_LP2: +- tegra_clear_cpu_in_lp2(0); ++ tegra_clear_cpu_in_lp2(); + break; + default: + break; +@@ -221,11 +337,23 @@ static const struct platform_suspend_ops tegra_suspend_ops = { + + void __init tegra_init_suspend(void) + { +- if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) ++ enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); ++ ++ if (mode == TEGRA_SUSPEND_NONE) + return; + ++ tegra_tear_down_cpu_init(); + tegra_pmc_suspend_init(); + ++ if (mode >= TEGRA_SUSPEND_LP1) { ++ if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) { ++ pr_err("%s: unable to allocate memory for SDRAM" ++ "self-refresh -- LP0/LP1 unavailable\n", ++ __func__); ++ tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2); ++ } ++ } ++ + suspend_set_ops(&tegra_suspend_ops); + } + #endif +diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h +index 778a4aa..ff68746 100644 +--- a/arch/arm/mach-tegra/pm.h ++++ b/arch/arm/mach-tegra/pm.h +@@ -23,13 +23,36 @@ + + #include "pmc.h" + ++struct tegra_lp1_iram { ++ void *start_addr; ++ void *end_addr; ++}; ++extern struct tegra_lp1_iram *tegra_lp1_iram; ++extern void (*tegra_sleep_core_finish)(unsigned long v2p); ++ ++#ifdef CONFIG_ARCH_TEGRA_3x_SOC ++void tegra30_lp1_iram_hook(void); ++void tegra30_sleep_core_init(void); ++#else ++static inline void tegra30_lp1_iram_hook(void) {} ++static inline void void tegra30_sleep_core_init(void) {} ++#endif ++ ++#ifdef CONFIG_ARCH_TEGRA_2x_SOC ++void tegra20_lp1_iram_hook(void); ++void tegra20_sleep_core_init(void); ++#else ++static inline void tegra20_lp1_iram_hook(void) {} ++static inline void void tegra20_sleep_core_init(void) {} ++#endif ++ + extern unsigned long l2x0_saved_regs_addr; + + void save_cpu_arch_register(void); + void restore_cpu_arch_register(void); + +-void tegra_clear_cpu_in_lp2(int phy_cpu_id); +-bool tegra_set_cpu_in_lp2(int phy_cpu_id); ++void tegra_clear_cpu_in_lp2(void); ++bool tegra_set_cpu_in_lp2(void); + + void tegra_idle_lp2_last(void); + extern void (*tegra_tear_down_cpu)(void); +diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c +index 32360e5..617de14 100644 +--- a/arch/arm/mach-tegra/pmc.c ++++ b/arch/arm/mach-tegra/pmc.c +@@ -26,6 +26,8 @@ + #include "pmc.h" + #include "sleep.h" + ++#define TEGRA_POWER_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */ ++#define TEGRA_POWER_SYSCLK_OE (1 << 11) /* system clock enable */ + #define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ + #define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ + #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ +@@ -193,10 +195,28 @@ enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) + return pmc_pm_data.suspend_mode; + } + ++void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) ++{ ++ if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE) ++ return; ++ ++ pmc_pm_data.suspend_mode = mode; ++} ++ ++void tegra_pmc_suspend(void) ++{ ++ tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41); ++} ++ ++void tegra_pmc_resume(void) ++{ ++ tegra_pmc_writel(0x0, PMC_SCRATCH41); ++} ++ + void tegra_pmc_pm_set(enum tegra_suspend_mode mode) + { + u32 reg; +- unsigned long rate = 0; ++ unsigned long rate = 32768; + + reg = tegra_pmc_readl(PMC_CTRL); + reg |= TEGRA_POWER_CPU_PWRREQ_OE; +@@ -224,6 +244,20 @@ void tegra_pmc_suspend_init(void) + reg = tegra_pmc_readl(PMC_CTRL); + reg |= TEGRA_POWER_CPU_PWRREQ_OE; + tegra_pmc_writel(reg, PMC_CTRL); ++ ++ reg = tegra_pmc_readl(PMC_CTRL); ++ ++ if (!pmc_pm_data.sysclkreq_high) ++ reg |= TEGRA_POWER_SYSCLK_POLARITY; ++ else ++ reg &= ~TEGRA_POWER_SYSCLK_POLARITY; ++ ++ /* configure the output inverts while the request is tristated */ ++ tegra_pmc_writel(reg, PMC_CTRL); ++ ++ /* now enable the request */ ++ reg |= TEGRA_POWER_SYSCLK_OE; ++ tegra_pmc_writel(reg, PMC_CTRL); + } + #endif + +diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h +index e1c2df2..549f8c7 100644 +--- a/arch/arm/mach-tegra/pmc.h ++++ b/arch/arm/mach-tegra/pmc.h +@@ -28,6 +28,9 @@ enum tegra_suspend_mode { + + #ifdef CONFIG_PM_SLEEP + enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); ++void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); ++void tegra_pmc_suspend(void); ++void tegra_pmc_resume(void); + void tegra_pmc_pm_set(enum tegra_suspend_mode mode); + void tegra_pmc_suspend_init(void); + #endif +diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S +index e6de88a..3ecf3b1 100644 +--- a/arch/arm/mach-tegra/reset-handler.S ++++ b/arch/arm/mach-tegra/reset-handler.S +@@ -22,11 +22,11 @@ + #include <asm/hardware/cache-l2x0.h> + + #include "flowctrl.h" ++#include "fuse.h" + #include "iomap.h" + #include "reset.h" + #include "sleep.h" + +-#define APB_MISC_GP_HIDREV 0x804 + #define PMC_SCRATCH41 0x140 + + #define RESET_DATA(x) ((TEGRA_RESET_##x)*4) +@@ -49,10 +49,8 @@ ENTRY(tegra_resume) + + #ifdef CONFIG_ARCH_TEGRA_3x_SOC + /* Are we on Tegra20? */ +- mov32 r6, TEGRA_APB_MISC_BASE +- ldr r0, [r6, #APB_MISC_GP_HIDREV] +- and r0, r0, #0xff00 +- cmp r0, #(0x20 << 8) ++ tegra_get_soc_id TEGRA_APB_MISC_BASE, r6 ++ cmp r6, #TEGRA20 + beq 1f @ Yes + /* Clear the flow controller flags for this CPU. */ + mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR +@@ -98,7 +96,7 @@ ENTRY(__tegra_cpu_reset_handler_start) + * Register usage within the reset handler: + * + * Others: scratch +- * R6 = SoC ID << 8 ++ * R6 = SoC ID + * R7 = CPU present (to the OS) mask + * R8 = CPU in LP1 state mask + * R9 = CPU in LP2 state mask +@@ -115,12 +113,10 @@ ENTRY(__tegra_cpu_reset_handler) + + cpsid aif, 0x13 @ SVC mode, interrupts disabled + +- mov32 r6, TEGRA_APB_MISC_BASE +- ldr r6, [r6, #APB_MISC_GP_HIDREV] +- and r6, r6, #0xff00 ++ tegra_get_soc_id TEGRA_APB_MISC_BASE, r6 + #ifdef CONFIG_ARCH_TEGRA_2x_SOC + t20_check: +- cmp r6, #(0x20 << 8) ++ cmp r6, #TEGRA20 + bne after_t20_check + t20_errata: + # Tegra20 is a Cortex-A9 r1p1 +@@ -136,7 +132,7 @@ after_t20_check: + #endif + #ifdef CONFIG_ARCH_TEGRA_3x_SOC + t30_check: +- cmp r6, #(0x30 << 8) ++ cmp r6, #TEGRA30 + bne after_t30_check + t30_errata: + # Tegra30 is a Cortex-A9 r2p9 +@@ -163,7 +159,7 @@ after_errata: + + #ifdef CONFIG_ARCH_TEGRA_2x_SOC + /* Are we on Tegra20? */ +- cmp r6, #(0x20 << 8) ++ cmp r6, #TEGRA20 + bne 1f + /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ + mov32 r5, TEGRA_PMC_BASE +@@ -173,6 +169,19 @@ after_errata: + 1: + #endif + ++ /* Waking up from LP1? */ ++ ldr r8, [r12, #RESET_DATA(MASK_LP1)] ++ tst r8, r11 @ if in_lp1 ++ beq __is_not_lp1 ++ cmp r10, #0 ++ bne __die @ only CPU0 can be here ++ ldr lr, [r12, #RESET_DATA(STARTUP_LP1)] ++ THUMB( add lr, lr, #1 ) @ switch to Thumb mode ++ cmp lr, #0 ++ bleq __die @ no LP1 startup handler ++ bx lr ++__is_not_lp1: ++ + /* Waking up from LP2? */ + ldr r9, [r12, #RESET_DATA(MASK_LP2)] + tst r9, r11 @ if in_lp2 +@@ -210,10 +219,7 @@ __die: + mov32 r7, TEGRA_CLK_RESET_BASE + + /* Are we on Tegra20? */ +- mov32 r6, TEGRA_APB_MISC_BASE +- ldr r0, [r6, #APB_MISC_GP_HIDREV] +- and r0, r0, #0xff00 +- cmp r0, #(0x20 << 8) ++ cmp r6, #TEGRA20 + bne 1f + + #ifdef CONFIG_ARCH_TEGRA_2x_SOC +diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c +index 1ac434e..fd0bbf8 100644 +--- a/arch/arm/mach-tegra/reset.c ++++ b/arch/arm/mach-tegra/reset.c +@@ -81,6 +81,8 @@ void __init tegra_cpu_reset_handler_init(void) + #endif + + #ifdef CONFIG_PM_SLEEP ++ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1] = ++ TEGRA_IRAM_CODE_AREA; + __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] = + virt_to_phys((void *)tegra_resume); + #endif +diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h +index c90d8e9..76a9343 100644 +--- a/arch/arm/mach-tegra/reset.h ++++ b/arch/arm/mach-tegra/reset.h +@@ -39,6 +39,10 @@ void __tegra_cpu_reset_handler_end(void); + void tegra_secondary_startup(void); + + #ifdef CONFIG_PM_SLEEP ++#define tegra_cpu_lp1_mask \ ++ (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ ++ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \ ++ (u32)__tegra_cpu_reset_handler_start))) + #define tegra_cpu_lp2_mask \ + (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ + ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \ +diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S +index e3f2417..8972a8e 100644 +--- a/arch/arm/mach-tegra/sleep-tegra20.S ++++ b/arch/arm/mach-tegra/sleep-tegra20.S +@@ -23,10 +23,49 @@ + #include <asm/assembler.h> + #include <asm/proc-fns.h> + #include <asm/cp15.h> ++#include <asm/cache.h> + + #include "sleep.h" + #include "flowctrl.h" + ++#define EMC_CFG 0xc ++#define EMC_ADR_CFG 0x10 ++#define EMC_REFRESH 0x70 ++#define EMC_NOP 0xdc ++#define EMC_SELF_REF 0xe0 ++#define EMC_REQ_CTRL 0x2b0 ++#define EMC_EMC_STATUS 0x2b4 ++ ++#define CLK_RESET_CCLK_BURST 0x20 ++#define CLK_RESET_CCLK_DIVIDER 0x24 ++#define CLK_RESET_SCLK_BURST 0x28 ++#define CLK_RESET_SCLK_DIVIDER 0x2c ++#define CLK_RESET_PLLC_BASE 0x80 ++#define CLK_RESET_PLLM_BASE 0x90 ++#define CLK_RESET_PLLP_BASE 0xa0 ++ ++#define APB_MISC_XM2CFGCPADCTRL 0x8c8 ++#define APB_MISC_XM2CFGDPADCTRL 0x8cc ++#define APB_MISC_XM2CLKCFGPADCTRL 0x8d0 ++#define APB_MISC_XM2COMPPADCTRL 0x8d4 ++#define APB_MISC_XM2VTTGENPADCTRL 0x8d8 ++#define APB_MISC_XM2CFGCPADCTRL2 0x8e4 ++#define APB_MISC_XM2CFGDPADCTRL2 0x8e8 ++ ++.macro pll_enable, rd, r_car_base, pll_base ++ ldr \rd, [\r_car_base, #\pll_base] ++ tst \rd, #(1 << 30) ++ orreq \rd, \rd, #(1 << 30) ++ streq \rd, [\r_car_base, #\pll_base] ++.endm ++ ++.macro emc_device_mask, rd, base ++ ldr \rd, [\base, #EMC_ADR_CFG] ++ tst \rd, #(0x3 << 24) ++ moveq \rd, #(0x1 << 8) @ just 1 device ++ movne \rd, #(0x3 << 8) @ 2 devices ++.endm ++ + #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) + /* + * tegra20_hotplug_shutdown(void) +@@ -181,6 +220,28 @@ ENTRY(tegra20_cpu_is_resettable_soon) + ENDPROC(tegra20_cpu_is_resettable_soon) + + /* ++ * tegra20_sleep_core_finish(unsigned long v2p) ++ * ++ * Enters suspend in LP0 or LP1 by turning off the mmu and jumping to ++ * tegra20_tear_down_core in IRAM ++ */ ++ENTRY(tegra20_sleep_core_finish) ++ /* Flush, disable the L1 data cache and exit SMP */ ++ bl tegra_disable_clean_inv_dcache ++ ++ mov32 r3, tegra_shut_off_mmu ++ add r3, r3, r0 ++ ++ mov32 r0, tegra20_tear_down_core ++ mov32 r1, tegra20_iram_start ++ sub r0, r0, r1 ++ mov32 r1, TEGRA_IRAM_CODE_AREA ++ add r0, r0, r1 ++ ++ mov pc, r3 ++ENDPROC(tegra20_sleep_core_finish) ++ ++/* + * tegra20_sleep_cpu_secondary_finish(unsigned long v2p) + * + * Enters WFI on secondary CPU by exiting coherency. +@@ -191,6 +252,7 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) + mrc p15, 0, r11, c1, c0, 1 @ save actlr before exiting coherency + + /* Flush and disable the L1 data cache */ ++ mov r0, #TEGRA_FLUSH_CACHE_LOUIS + bl tegra_disable_clean_inv_dcache + + mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41 +@@ -250,6 +312,150 @@ ENTRY(tegra20_tear_down_cpu) + b tegra20_enter_sleep + ENDPROC(tegra20_tear_down_cpu) + ++/* START OF ROUTINES COPIED TO IRAM */ ++ .align L1_CACHE_SHIFT ++ .globl tegra20_iram_start ++tegra20_iram_start: ++ ++/* ++ * tegra20_lp1_reset ++ * ++ * reset vector for LP1 restore; copied into IRAM during suspend. ++ * Brings the system back up to a safe staring point (SDRAM out of ++ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP, ++ * system clock running on the same PLL that it suspended at), and ++ * jumps to tegra_resume to restore virtual addressing and PLLX. ++ * The physical address of tegra_resume expected to be stored in ++ * PMC_SCRATCH41. ++ * ++ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST. ++ */ ++ENTRY(tegra20_lp1_reset) ++ /* ++ * The CPU and system bus are running at 32KHz and executing from ++ * IRAM when this code is executed; immediately switch to CLKM and ++ * enable PLLM, PLLP, PLLC. ++ */ ++ mov32 r0, TEGRA_CLK_RESET_BASE ++ ++ mov r1, #(1 << 28) ++ str r1, [r0, #CLK_RESET_SCLK_BURST] ++ str r1, [r0, #CLK_RESET_CCLK_BURST] ++ mov r1, #0 ++ str r1, [r0, #CLK_RESET_CCLK_DIVIDER] ++ str r1, [r0, #CLK_RESET_SCLK_DIVIDER] ++ ++ pll_enable r1, r0, CLK_RESET_PLLM_BASE ++ pll_enable r1, r0, CLK_RESET_PLLP_BASE ++ pll_enable r1, r0, CLK_RESET_PLLC_BASE ++ ++ adr r2, tegra20_sdram_pad_address ++ adr r4, tegra20_sdram_pad_save ++ mov r5, #0 ++ ++ ldr r6, tegra20_sdram_pad_size ++padload: ++ ldr r7, [r2, r5] @ r7 is the addr in the pad_address ++ ++ ldr r1, [r4, r5] ++ str r1, [r7] @ restore the value in pad_save ++ ++ add r5, r5, #4 ++ cmp r6, r5 ++ bne padload ++ ++padload_done: ++ /* 255uS delay for PLL stabilization */ ++ mov32 r7, TEGRA_TMRUS_BASE ++ wait_for_us r1, r7, r9 ++ add r1, r1, #0xfe ++ wait_until r1, r7, r9 ++ ++ adr r4, tegra20_sclk_save ++ ldr r4, [r4] ++ str r4, [r0, #CLK_RESET_SCLK_BURST] ++ mov32 r4, ((1 << 28) | (4)) @ burst policy is PLLP ++ str r4, [r0, #CLK_RESET_CCLK_BURST] ++ ++ mov32 r0, TEGRA_EMC_BASE ++ ldr r1, [r0, #EMC_CFG] ++ bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP ++ str r1, [r0, #EMC_CFG] ++ ++ mov r1, #0 ++ str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh ++ mov r1, #1 ++ str r1, [r0, #EMC_NOP] ++ str r1, [r0, #EMC_NOP] ++ str r1, [r0, #EMC_REFRESH] ++ ++ emc_device_mask r1, r0 ++ ++exit_selfrefresh_loop: ++ ldr r2, [r0, #EMC_EMC_STATUS] ++ ands r2, r2, r1 ++ bne exit_selfrefresh_loop ++ ++ mov r1, #0 @ unstall all transactions ++ str r1, [r0, #EMC_REQ_CTRL] ++ ++ mov32 r0, TEGRA_PMC_BASE ++ ldr r0, [r0, #PMC_SCRATCH41] ++ mov pc, r0 @ jump to tegra_resume ++ENDPROC(tegra20_lp1_reset) ++ ++/* ++ * tegra20_tear_down_core ++ * ++ * copied into and executed from IRAM ++ * puts memory in self-refresh for LP0 and LP1 ++ */ ++tegra20_tear_down_core: ++ bl tegra20_sdram_self_refresh ++ bl tegra20_switch_cpu_to_clk32k ++ b tegra20_enter_sleep ++ ++/* ++ * tegra20_switch_cpu_to_clk32k ++ * ++ * In LP0 and LP1 all PLLs will be turned off. Switch the CPU and system clock ++ * to the 32KHz clock. ++ */ ++tegra20_switch_cpu_to_clk32k: ++ /* ++ * start by jumping to CLKM to safely disable PLLs, then jump to ++ * CLKS. ++ */ ++ mov r0, #(1 << 28) ++ str r0, [r5, #CLK_RESET_SCLK_BURST] ++ str r0, [r5, #CLK_RESET_CCLK_BURST] ++ mov r0, #0 ++ str r0, [r5, #CLK_RESET_CCLK_DIVIDER] ++ str r0, [r5, #CLK_RESET_SCLK_DIVIDER] ++ ++ /* 2uS delay delay between changing SCLK and disabling PLLs */ ++ mov32 r7, TEGRA_TMRUS_BASE ++ wait_for_us r1, r7, r9 ++ add r1, r1, #2 ++ wait_until r1, r7, r9 ++ ++ /* switch to CLKS */ ++ mov r0, #0 /* brust policy = 32KHz */ ++ str r0, [r5, #CLK_RESET_SCLK_BURST] ++ ++ /* disable PLLM, PLLP and PLLC */ ++ ldr r0, [r5, #CLK_RESET_PLLM_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLM_BASE] ++ ldr r0, [r5, #CLK_RESET_PLLP_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLP_BASE] ++ ldr r0, [r5, #CLK_RESET_PLLC_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLC_BASE] ++ ++ mov pc, lr ++ + /* + * tegra20_enter_sleep + * +@@ -274,4 +480,99 @@ halted: + isb + b halted + ++/* ++ * tegra20_sdram_self_refresh ++ * ++ * called with MMU off and caches disabled ++ * puts sdram in self refresh ++ * must be executed from IRAM ++ */ ++tegra20_sdram_self_refresh: ++ mov32 r1, TEGRA_EMC_BASE @ r1 reserved for emc base addr ++ ++ mov r2, #3 ++ str r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests ++ ++emcidle: ++ ldr r2, [r1, #EMC_EMC_STATUS] ++ tst r2, #4 ++ beq emcidle ++ ++ mov r2, #1 ++ str r2, [r1, #EMC_SELF_REF] ++ ++ emc_device_mask r2, r1 ++ ++emcself: ++ ldr r3, [r1, #EMC_EMC_STATUS] ++ and r3, r3, r2 ++ cmp r3, r2 ++ bne emcself @ loop until DDR in self-refresh ++ ++ adr r2, tegra20_sdram_pad_address ++ adr r3, tegra20_sdram_pad_safe ++ adr r4, tegra20_sdram_pad_save ++ mov r5, #0 ++ ++ ldr r6, tegra20_sdram_pad_size ++padsave: ++ ldr r0, [r2, r5] @ r0 is the addr in the pad_address ++ ++ ldr r1, [r0] ++ str r1, [r4, r5] @ save the content of the addr ++ ++ ldr r1, [r3, r5] ++ str r1, [r0] @ set the save val to the addr ++ ++ add r5, r5, #4 ++ cmp r6, r5 ++ bne padsave ++padsave_done: ++ ++ mov32 r5, TEGRA_CLK_RESET_BASE ++ ldr r0, [r5, #CLK_RESET_SCLK_BURST] ++ adr r2, tegra20_sclk_save ++ str r0, [r2] ++ dsb ++ mov pc, lr ++ ++tegra20_sdram_pad_address: ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CLKCFGPADCTRL ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2COMPPADCTRL ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2VTTGENPADCTRL ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL2 ++ .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL2 ++ ++tegra20_sdram_pad_size: ++ .word tegra20_sdram_pad_size - tegra20_sdram_pad_address ++ ++tegra20_sdram_pad_safe: ++ .word 0x8 ++ .word 0x8 ++ .word 0x0 ++ .word 0x8 ++ .word 0x5500 ++ .word 0x08080040 ++ .word 0x0 ++ ++tegra20_sclk_save: ++ .word 0x0 ++ ++tegra20_sdram_pad_save: ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ ++ .ltorg ++/* dummy symbol for end of IRAM */ ++ .align L1_CACHE_SHIFT ++ .globl tegra20_iram_end ++tegra20_iram_end: ++ b . + #endif +diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S +index d29dfcc..7eb21f4 100644 +--- a/arch/arm/mach-tegra/sleep-tegra30.S ++++ b/arch/arm/mach-tegra/sleep-tegra30.S +@@ -18,12 +18,95 @@ + + #include <asm/assembler.h> + #include <asm/asm-offsets.h> ++#include <asm/cache.h> + + #include "sleep.h" + #include "flowctrl.h" + ++#define EMC_CFG 0xc ++#define EMC_ADR_CFG 0x10 ++#define EMC_TIMING_CONTROL 0x28 ++#define EMC_REFRESH 0x70 ++#define EMC_NOP 0xdc ++#define EMC_SELF_REF 0xe0 ++#define EMC_MRW 0xe8 ++#define EMC_FBIO_CFG5 0x104 ++#define EMC_AUTO_CAL_CONFIG 0x2a4 ++#define EMC_AUTO_CAL_INTERVAL 0x2a8 ++#define EMC_AUTO_CAL_STATUS 0x2ac ++#define EMC_REQ_CTRL 0x2b0 ++#define EMC_CFG_DIG_DLL 0x2bc ++#define EMC_EMC_STATUS 0x2b4 ++#define EMC_ZCAL_INTERVAL 0x2e0 ++#define EMC_ZQ_CAL 0x2ec ++#define EMC_XM2VTTGENPADCTRL 0x310 ++#define EMC_XM2VTTGENPADCTRL2 0x314 ++ ++#define PMC_CTRL 0x0 ++#define PMC_CTRL_SIDE_EFFECT_LP0 (1 << 14) /* enter LP0 when CPU pwr gated */ ++ ++#define PMC_PLLP_WB0_OVERRIDE 0xf8 ++#define PMC_IO_DPD_REQ 0x1b8 ++#define PMC_IO_DPD_STATUS 0x1bc ++ ++#define CLK_RESET_CCLK_BURST 0x20 ++#define CLK_RESET_CCLK_DIVIDER 0x24 ++#define CLK_RESET_SCLK_BURST 0x28 ++#define CLK_RESET_SCLK_DIVIDER 0x2c ++ ++#define CLK_RESET_PLLC_BASE 0x80 ++#define CLK_RESET_PLLC_MISC 0x8c ++#define CLK_RESET_PLLM_BASE 0x90 ++#define CLK_RESET_PLLM_MISC 0x9c ++#define CLK_RESET_PLLP_BASE 0xa0 ++#define CLK_RESET_PLLP_MISC 0xac ++#define CLK_RESET_PLLA_BASE 0xb0 ++#define CLK_RESET_PLLA_MISC 0xbc ++#define CLK_RESET_PLLX_BASE 0xe0 ++#define CLK_RESET_PLLX_MISC 0xe4 ++ ++#define CLK_RESET_CLK_SOURCE_MSELECT 0x3b4 ++ ++#define MSELECT_CLKM (0x3 << 30) ++ ++#define LOCK_DELAY 50 /* safety delay after lock is detected */ ++ + #define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */ + ++.macro emc_device_mask, rd, base ++ ldr \rd, [\base, #EMC_ADR_CFG] ++ tst \rd, #0x1 ++ moveq \rd, #(0x1 << 8) @ just 1 device ++ movne \rd, #(0x3 << 8) @ 2 devices ++.endm ++ ++.macro emc_timing_update, rd, base ++ mov \rd, #1 ++ str \rd, [\base, #EMC_TIMING_CONTROL] ++1001: ++ ldr \rd, [\base, #EMC_EMC_STATUS] ++ tst \rd, #(0x1<<23) @ wait EMC_STATUS_TIMING_UPDATE_STALLED is clear ++ bne 1001b ++.endm ++ ++.macro pll_enable, rd, r_car_base, pll_base, pll_misc ++ ldr \rd, [\r_car_base, #\pll_base] ++ tst \rd, #(1 << 30) ++ orreq \rd, \rd, #(1 << 30) ++ streq \rd, [\r_car_base, #\pll_base] ++ /* Enable lock detector */ ++ ldr \rd, [\r_car_base, #\pll_misc] ++ orr \rd, \rd, #(1 << 18) ++ str \rd, [\r_car_base, #\pll_misc] ++.endm ++ ++.macro pll_locked, rd, r_car_base, pll_base ++1: ++ ldr \rd, [\r_car_base, #\pll_base] ++ tst \rd, #(1 << 27) ++ beq 1b ++.endm ++ + #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) + /* + * tegra30_hotplug_shutdown(void) +@@ -107,6 +190,41 @@ ENDPROC(tegra30_cpu_shutdown) + + #ifdef CONFIG_PM_SLEEP + /* ++ * tegra30_sleep_core_finish(unsigned long v2p) ++ * ++ * Enters suspend in LP0 or LP1 by turning off the MMU and jumping to ++ * tegra30_tear_down_core in IRAM ++ */ ++ENTRY(tegra30_sleep_core_finish) ++ /* Flush, disable the L1 data cache and exit SMP */ ++ bl tegra_disable_clean_inv_dcache ++ ++ /* ++ * Preload all the address literals that are needed for the ++ * CPU power-gating process, to avoid loading from SDRAM which ++ * are not supported once SDRAM is put into self-refresh. ++ * LP0 / LP1 use physical address, since the MMU needs to be ++ * disalbed before putting SDRAM into self-refresh to avoid ++ * memory access due to page table walks. ++ */ ++ mov32 r4, TEGRA_PMC_BASE ++ mov32 r5, TEGRA_CLK_RESET_BASE ++ mov32 r6, TEGRA_FLOW_CTRL_BASE ++ mov32 r7, TEGRA_TMRUS_BASE ++ ++ mov32 r3, tegra_shut_off_mmu ++ add r3, r3, r0 ++ ++ mov32 r0, tegra30_tear_down_core ++ mov32 r1, tegra30_iram_start ++ sub r0, r0, r1 ++ mov32 r1, TEGRA_IRAM_CODE_AREA ++ add r0, r0, r1 ++ ++ mov pc, r3 ++ENDPROC(tegra30_sleep_core_finish) ++ ++/* + * tegra30_sleep_cpu_secondary_finish(unsigned long v2p) + * + * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU. +@@ -115,6 +233,7 @@ ENTRY(tegra30_sleep_cpu_secondary_finish) + mov r7, lr + + /* Flush and disable the L1 data cache */ ++ mov r0, #TEGRA_FLUSH_CACHE_LOUIS + bl tegra_disable_clean_inv_dcache + + /* Powergate this CPU. */ +@@ -135,6 +254,278 @@ ENTRY(tegra30_tear_down_cpu) + b tegra30_enter_sleep + ENDPROC(tegra30_tear_down_cpu) + ++/* START OF ROUTINES COPIED TO IRAM */ ++ .align L1_CACHE_SHIFT ++ .globl tegra30_iram_start ++tegra30_iram_start: ++ ++/* ++ * tegra30_lp1_reset ++ * ++ * reset vector for LP1 restore; copied into IRAM during suspend. ++ * Brings the system back up to a safe staring point (SDRAM out of ++ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX, ++ * system clock running on the same PLL that it suspended at), and ++ * jumps to tegra_resume to restore virtual addressing. ++ * The physical address of tegra_resume expected to be stored in ++ * PMC_SCRATCH41. ++ * ++ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST. ++ */ ++ENTRY(tegra30_lp1_reset) ++ /* ++ * The CPU and system bus are running at 32KHz and executing from ++ * IRAM when this code is executed; immediately switch to CLKM and ++ * enable PLLP, PLLM, PLLC, PLLA and PLLX. ++ */ ++ mov32 r0, TEGRA_CLK_RESET_BASE ++ ++ mov r1, #(1 << 28) ++ str r1, [r0, #CLK_RESET_SCLK_BURST] ++ str r1, [r0, #CLK_RESET_CCLK_BURST] ++ mov r1, #0 ++ str r1, [r0, #CLK_RESET_CCLK_DIVIDER] ++ str r1, [r0, #CLK_RESET_SCLK_DIVIDER] ++ ++ /* enable PLLM via PMC */ ++ mov32 r2, TEGRA_PMC_BASE ++ ldr r1, [r2, #PMC_PLLP_WB0_OVERRIDE] ++ orr r1, r1, #(1 << 12) ++ str r1, [r2, #PMC_PLLP_WB0_OVERRIDE] ++ ++ pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC ++ pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC ++ pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC ++ pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC ++ pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC ++ ++ pll_locked r1, r0, CLK_RESET_PLLM_BASE ++ pll_locked r1, r0, CLK_RESET_PLLP_BASE ++ pll_locked r1, r0, CLK_RESET_PLLA_BASE ++ pll_locked r1, r0, CLK_RESET_PLLC_BASE ++ pll_locked r1, r0, CLK_RESET_PLLX_BASE ++ ++ mov32 r7, TEGRA_TMRUS_BASE ++ ldr r1, [r7] ++ add r1, r1, #LOCK_DELAY ++ wait_until r1, r7, r3 ++ ++ adr r5, tegra30_sdram_pad_save ++ ++ ldr r4, [r5, #0x18] @ restore CLK_SOURCE_MSELECT ++ str r4, [r0, #CLK_RESET_CLK_SOURCE_MSELECT] ++ ++ ldr r4, [r5, #0x1C] @ restore SCLK_BURST ++ str r4, [r0, #CLK_RESET_SCLK_BURST] ++ ++ mov32 r4, ((1 << 28) | (0x8)) @ burst policy is PLLX ++ str r4, [r0, #CLK_RESET_CCLK_BURST] ++ ++ /* Restore pad power state to normal */ ++ ldr r1, [r5, #0x14] @ PMC_IO_DPD_STATUS ++ mvn r1, r1 ++ bic r1, r1, #(1 << 31) ++ orr r1, r1, #(1 << 30) ++ str r1, [r2, #PMC_IO_DPD_REQ] @ DPD_OFF ++ ++ mov32 r0, TEGRA_EMC_BASE @ r0 reserved for emc base ++ ++ ldr r1, [r5, #0xC] @ restore EMC_XM2VTTGENPADCTRL ++ str r1, [r0, #EMC_XM2VTTGENPADCTRL] ++ ldr r1, [r5, #0x10] @ restore EMC_XM2VTTGENPADCTRL2 ++ str r1, [r0, #EMC_XM2VTTGENPADCTRL2] ++ ldr r1, [r5, #0x8] @ restore EMC_AUTO_CAL_INTERVAL ++ str r1, [r0, #EMC_AUTO_CAL_INTERVAL] ++ ++ /* Relock DLL */ ++ ldr r1, [r0, #EMC_CFG_DIG_DLL] ++ orr r1, r1, #(1 << 30) @ set DLL_RESET ++ str r1, [r0, #EMC_CFG_DIG_DLL] ++ ++ emc_timing_update r1, r0 ++ ++ ldr r1, [r0, #EMC_AUTO_CAL_CONFIG] ++ orr r1, r1, #(1 << 31) @ set AUTO_CAL_ACTIVE ++ str r1, [r0, #EMC_AUTO_CAL_CONFIG] ++ ++emc_wait_auto_cal_onetime: ++ ldr r1, [r0, #EMC_AUTO_CAL_STATUS] ++ tst r1, #(1 << 31) @ wait until AUTO_CAL_ACTIVE is cleared ++ bne emc_wait_auto_cal_onetime ++ ++ ldr r1, [r0, #EMC_CFG] ++ bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP_PD ++ str r1, [r0, #EMC_CFG] ++ ++ mov r1, #0 ++ str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh ++ mov r1, #1 ++ str r1, [r0, #EMC_NOP] ++ str r1, [r0, #EMC_NOP] ++ str r1, [r0, #EMC_REFRESH] ++ ++ emc_device_mask r1, r0 ++ ++exit_selfrefresh_loop: ++ ldr r2, [r0, #EMC_EMC_STATUS] ++ ands r2, r2, r1 ++ bne exit_selfrefresh_loop ++ ++ lsr r1, r1, #8 @ devSel, bit0:dev0, bit1:dev1 ++ ++ mov32 r7, TEGRA_TMRUS_BASE ++ ldr r2, [r0, #EMC_FBIO_CFG5] ++ ++ and r2, r2, #3 @ check DRAM_TYPE ++ cmp r2, #2 ++ beq emc_lpddr2 ++ ++ /* Issue a ZQ_CAL for dev0 - DDR3 */ ++ mov32 r2, 0x80000011 @ DEV_SELECTION=2, LENGTH=LONG, CMD=1 ++ str r2, [r0, #EMC_ZQ_CAL] ++ ldr r2, [r7] ++ add r2, r2, #10 ++ wait_until r2, r7, r3 ++ ++ tst r1, #2 ++ beq zcal_done ++ ++ /* Issue a ZQ_CAL for dev1 - DDR3 */ ++ mov32 r2, 0x40000011 @ DEV_SELECTION=1, LENGTH=LONG, CMD=1 ++ str r2, [r0, #EMC_ZQ_CAL] ++ ldr r2, [r7] ++ add r2, r2, #10 ++ wait_until r2, r7, r3 ++ b zcal_done ++ ++emc_lpddr2: ++ /* Issue a ZQ_CAL for dev0 - LPDDR2 */ ++ mov32 r2, 0x800A00AB @ DEV_SELECTION=2, MA=10, OP=0xAB ++ str r2, [r0, #EMC_MRW] ++ ldr r2, [r7] ++ add r2, r2, #1 ++ wait_until r2, r7, r3 ++ ++ tst r1, #2 ++ beq zcal_done ++ ++ /* Issue a ZQ_CAL for dev0 - LPDDR2 */ ++ mov32 r2, 0x400A00AB @ DEV_SELECTION=1, MA=10, OP=0xAB ++ str r2, [r0, #EMC_MRW] ++ ldr r2, [r7] ++ add r2, r2, #1 ++ wait_until r2, r7, r3 ++ ++zcal_done: ++ mov r1, #0 @ unstall all transactions ++ str r1, [r0, #EMC_REQ_CTRL] ++ ldr r1, [r5, #0x4] @ restore EMC_ZCAL_INTERVAL ++ str r1, [r0, #EMC_ZCAL_INTERVAL] ++ ldr r1, [r5, #0x0] @ restore EMC_CFG ++ str r1, [r0, #EMC_CFG] ++ ++ mov32 r0, TEGRA_PMC_BASE ++ ldr r0, [r0, #PMC_SCRATCH41] ++ mov pc, r0 @ jump to tegra_resume ++ENDPROC(tegra30_lp1_reset) ++ ++ .align L1_CACHE_SHIFT ++ .type tegra30_sdram_pad_save, %object ++tegra30_sdram_pad_save: ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ .word 0 ++ ++tegra30_sdram_pad_address: ++ .word TEGRA_EMC_BASE + EMC_CFG @0x0 ++ .word TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL @0x4 ++ .word TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL @0x8 ++ .word TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL @0xc ++ .word TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2 @0x10 ++ .word TEGRA_PMC_BASE + PMC_IO_DPD_STATUS @0x14 ++ .word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18 ++ .word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c ++ ++tegra30_sdram_pad_size: ++ .word tegra30_sdram_pad_address - tegra30_sdram_pad_save ++ ++/* ++ * tegra30_tear_down_core ++ * ++ * copied into and executed from IRAM ++ * puts memory in self-refresh for LP0 and LP1 ++ */ ++tegra30_tear_down_core: ++ bl tegra30_sdram_self_refresh ++ bl tegra30_switch_cpu_to_clk32k ++ b tegra30_enter_sleep ++ ++/* ++ * tegra30_switch_cpu_to_clk32k ++ * ++ * In LP0 and LP1 all PLLs will be turned off. Switching the CPU and System CLK ++ * to the 32KHz clock. ++ * r4 = TEGRA_PMC_BASE ++ * r5 = TEGRA_CLK_RESET_BASE ++ * r6 = TEGRA_FLOW_CTRL_BASE ++ * r7 = TEGRA_TMRUS_BASE ++ */ ++tegra30_switch_cpu_to_clk32k: ++ /* ++ * start by jumping to CLKM to safely disable PLLs, then jump to ++ * CLKS. ++ */ ++ mov r0, #(1 << 28) ++ str r0, [r5, #CLK_RESET_SCLK_BURST] ++ /* 2uS delay delay between changing SCLK and CCLK */ ++ wait_for_us r1, r7, r9 ++ add r1, r1, #2 ++ wait_until r1, r7, r9 ++ str r0, [r5, #CLK_RESET_CCLK_BURST] ++ mov r0, #0 ++ str r0, [r5, #CLK_RESET_CCLK_DIVIDER] ++ str r0, [r5, #CLK_RESET_SCLK_DIVIDER] ++ ++ /* switch the clock source of mselect to be CLK_M */ ++ ldr r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT] ++ orr r0, r0, #MSELECT_CLKM ++ str r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT] ++ ++ /* 2uS delay delay between changing SCLK and disabling PLLs */ ++ wait_for_us r1, r7, r9 ++ add r1, r1, #2 ++ wait_until r1, r7, r9 ++ ++ /* disable PLLM via PMC in LP1 */ ++ ldr r0, [r4, #PMC_PLLP_WB0_OVERRIDE] ++ bic r0, r0, #(1 << 12) ++ str r0, [r4, #PMC_PLLP_WB0_OVERRIDE] ++ ++ /* disable PLLP, PLLA, PLLC and PLLX */ ++ ldr r0, [r5, #CLK_RESET_PLLP_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLP_BASE] ++ ldr r0, [r5, #CLK_RESET_PLLA_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLA_BASE] ++ ldr r0, [r5, #CLK_RESET_PLLC_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLC_BASE] ++ ldr r0, [r5, #CLK_RESET_PLLX_BASE] ++ bic r0, r0, #(1 << 30) ++ str r0, [r5, #CLK_RESET_PLLX_BASE] ++ ++ /* switch to CLKS */ ++ mov r0, #0 /* brust policy = 32KHz */ ++ str r0, [r5, #CLK_RESET_SCLK_BURST] ++ ++ mov pc, lr ++ + /* + * tegra30_enter_sleep + * +@@ -167,4 +558,105 @@ halted: + /* !!!FIXME!!! Implement halt failure handler */ + b halted + ++/* ++ * tegra30_sdram_self_refresh ++ * ++ * called with MMU off and caches disabled ++ * must be executed from IRAM ++ * r4 = TEGRA_PMC_BASE ++ * r5 = TEGRA_CLK_RESET_BASE ++ * r6 = TEGRA_FLOW_CTRL_BASE ++ * r7 = TEGRA_TMRUS_BASE ++ */ ++tegra30_sdram_self_refresh: ++ ++ adr r2, tegra30_sdram_pad_address ++ adr r8, tegra30_sdram_pad_save ++ mov r9, #0 ++ ++ ldr r3, tegra30_sdram_pad_size ++padsave: ++ ldr r0, [r2, r9] @ r0 is the addr in the pad_address ++ ++ ldr r1, [r0] ++ str r1, [r8, r9] @ save the content of the addr ++ ++ add r9, r9, #4 ++ cmp r3, r9 ++ bne padsave ++padsave_done: ++ ++ dsb ++ ++ mov32 r0, TEGRA_EMC_BASE @ r0 reserved for emc base addr ++ ++ mov r1, #0 ++ str r1, [r0, #EMC_ZCAL_INTERVAL] ++ str r1, [r0, #EMC_AUTO_CAL_INTERVAL] ++ ldr r1, [r0, #EMC_CFG] ++ bic r1, r1, #(1 << 28) ++ str r1, [r0, #EMC_CFG] @ disable DYN_SELF_REF ++ ++ emc_timing_update r1, r0 ++ ++ ldr r1, [r7] ++ add r1, r1, #5 ++ wait_until r1, r7, r2 ++ ++emc_wait_auto_cal: ++ ldr r1, [r0, #EMC_AUTO_CAL_STATUS] ++ tst r1, #(1 << 31) @ wait until AUTO_CAL_ACTIVE is cleared ++ bne emc_wait_auto_cal ++ ++ mov r1, #3 ++ str r1, [r0, #EMC_REQ_CTRL] @ stall incoming DRAM requests ++ ++emcidle: ++ ldr r1, [r0, #EMC_EMC_STATUS] ++ tst r1, #4 ++ beq emcidle ++ ++ mov r1, #1 ++ str r1, [r0, #EMC_SELF_REF] ++ ++ emc_device_mask r1, r0 ++ ++emcself: ++ ldr r2, [r0, #EMC_EMC_STATUS] ++ and r2, r2, r1 ++ cmp r2, r1 ++ bne emcself @ loop until DDR in self-refresh ++ ++ /* Put VTTGEN in the lowest power mode */ ++ ldr r1, [r0, #EMC_XM2VTTGENPADCTRL] ++ mov32 r2, 0xF8F8FFFF @ clear XM2VTTGEN_DRVUP and XM2VTTGEN_DRVDN ++ and r1, r1, r2 ++ str r1, [r0, #EMC_XM2VTTGENPADCTRL] ++ ldr r1, [r0, #EMC_XM2VTTGENPADCTRL2] ++ orr r1, r1, #7 @ set E_NO_VTTGEN ++ str r1, [r0, #EMC_XM2VTTGENPADCTRL2] ++ ++ emc_timing_update r1, r0 ++ ++ ldr r1, [r4, #PMC_CTRL] ++ tst r1, #PMC_CTRL_SIDE_EFFECT_LP0 ++ bne pmc_io_dpd_skip ++ /* ++ * Put DDR_DATA, DISC_ADDR_CMD, DDR_ADDR_CMD, POP_ADDR_CMD, POP_CLK ++ * and COMP in the lowest power mode when LP1. ++ */ ++ mov32 r1, 0x8EC00000 ++ str r1, [r4, #PMC_IO_DPD_REQ] ++pmc_io_dpd_skip: ++ ++ dsb ++ ++ mov pc, lr ++ ++ .ltorg ++/* dummy symbol for end of IRAM */ ++ .align L1_CACHE_SHIFT ++ .global tegra30_iram_end ++tegra30_iram_end: ++ b . + #endif +diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S +index 364d845..193ed7d 100644 +--- a/arch/arm/mach-tegra/sleep.S ++++ b/arch/arm/mach-tegra/sleep.S +@@ -56,7 +56,9 @@ ENTRY(tegra_disable_clean_inv_dcache) + isb + + /* Flush the D-cache */ +- bl v7_flush_dcache_louis ++ cmp r0, #TEGRA_FLUSH_CACHE_ALL ++ blne v7_flush_dcache_louis ++ bleq v7_flush_dcache_all + + /* Trun off coherency */ + exit_smp r4, r5 +@@ -73,9 +75,12 @@ ENDPROC(tegra_disable_clean_inv_dcache) + * tegra?_tear_down_cpu + */ + ENTRY(tegra_sleep_cpu_finish) ++ mov r4, r0 + /* Flush and disable the L1 data cache */ ++ mov r0, #TEGRA_FLUSH_CACHE_ALL + bl tegra_disable_clean_inv_dcache + ++ mov r0, r4 + mov32 r6, tegra_tear_down_cpu + ldr r1, [r6] + add r1, r1, r0 +@@ -106,9 +111,11 @@ ENTRY(tegra_shut_off_mmu) + isb + #ifdef CONFIG_CACHE_L2X0 + /* Disable L2 cache */ +- mov32 r4, TEGRA_ARM_PERIF_BASE + 0x3000 +- mov r5, #0 +- str r5, [r4, #L2X0_CTRL] ++ check_cpu_part_num 0xc09, r9, r10 ++ movweq r2, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000) ++ movteq r2, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000) ++ moveq r3, #0 ++ streq r3, [r2, #L2X0_CTRL] + #endif + mov pc, r0 + ENDPROC(tegra_shut_off_mmu) +diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h +index 2080fb1..13b076d 100644 +--- a/arch/arm/mach-tegra/sleep.h ++++ b/arch/arm/mach-tegra/sleep.h +@@ -27,6 +27,8 @@ + + IO_PPSB_VIRT) + #define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT) + ++#define TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) ++ + /* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */ + #define PMC_SCRATCH37 0x130 + #define PMC_SCRATCH38 0x134 +@@ -39,7 +41,27 @@ + #define CPU_NOT_RESETTABLE 0 + #endif + ++/* flag of tegra_disable_clean_inv_dcache to do LoUIS or all */ ++#define TEGRA_FLUSH_CACHE_LOUIS 0 ++#define TEGRA_FLUSH_CACHE_ALL 1 ++ + #ifdef __ASSEMBLY__ ++/* waits until the microsecond counter (base) ticks, for exact timing loops */ ++.macro wait_for_us, rd, base, tmp ++ ldr \rd, [\base] ++1001: ldr \tmp, [\base] ++ cmp \rd, \tmp ++ beq 1001b ++.endm ++ ++/* waits until the microsecond counter (base) is > rn */ ++.macro wait_until, rn, base, tmp ++ add \rn, \rn, #1 ++1002: ldr \tmp, [\base] ++ cmp \tmp, \rn ++ bmi 1002b ++.endm ++ + /* returns the offset of the flow controller halt register for a cpu */ + .macro cpu_to_halt_reg rd, rcpu + cmp \rcpu, #0 +@@ -70,19 +92,40 @@ + movt \reg, #:upper16:\val + .endm + ++/* Marco to check CPU part num */ ++.macro check_cpu_part_num part_num, tmp1, tmp2 ++ mrc p15, 0, \tmp1, c0, c0, 0 ++ ubfx \tmp1, \tmp1, #4, #12 ++ mov32 \tmp2, \part_num ++ cmp \tmp1, \tmp2 ++.endm ++ + /* Macro to exit SMP coherency. */ + .macro exit_smp, tmp1, tmp2 + mrc p15, 0, \tmp1, c1, c0, 1 @ ACTLR + bic \tmp1, \tmp1, #(1<<6) | (1<<0) @ clear ACTLR.SMP | ACTLR.FW + mcr p15, 0, \tmp1, c1, c0, 1 @ ACTLR + isb +- cpu_id \tmp1 +- mov \tmp1, \tmp1, lsl #2 +- mov \tmp2, #0xf +- mov \tmp2, \tmp2, lsl \tmp1 +- mov32 \tmp1, TEGRA_ARM_PERIF_VIRT + 0xC +- str \tmp2, [\tmp1] @ invalidate SCU tags for CPU ++#ifdef CONFIG_HAVE_ARM_SCU ++ check_cpu_part_num 0xc09, \tmp1, \tmp2 ++ mrceq p15, 0, \tmp1, c0, c0, 5 ++ andeq \tmp1, \tmp1, #0xF ++ moveq \tmp1, \tmp1, lsl #2 ++ moveq \tmp2, #0xf ++ moveq \tmp2, \tmp2, lsl \tmp1 ++ ldreq \tmp1, =(TEGRA_ARM_PERIF_VIRT + 0xC) ++ streq \tmp2, [\tmp1] @ invalidate SCU tags for CPU + dsb ++#endif ++.endm ++ ++/* Macro to check Tegra revision */ ++#define APB_MISC_GP_HIDREV 0x804 ++.macro tegra_get_soc_id base, tmp1 ++ mov32 \tmp1, \base ++ ldr \tmp1, [\tmp1, #APB_MISC_GP_HIDREV] ++ and \tmp1, \tmp1, #0xff00 ++ mov \tmp1, \tmp1, lsr #8 + .endm + + /* Macro to resume & re-enable L2 cache */ +@@ -121,7 +164,7 @@ void tegra_pen_lock(void); + void tegra_pen_unlock(void); + void tegra_resume(void); + int tegra_sleep_cpu_finish(unsigned long); +-void tegra_disable_clean_inv_dcache(void); ++void tegra_disable_clean_inv_dcache(u32 flag); + + #ifdef CONFIG_HOTPLUG_CPU + void tegra20_hotplug_shutdown(void); +diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h +index a1607d6..790ddf1 100644 +--- a/drivers/gpu/host1x/dev.h ++++ b/drivers/gpu/host1x/dev.h +@@ -73,7 +73,7 @@ struct host1x_syncpt_ops { + void (*restore_wait_base)(struct host1x_syncpt *syncpt); + void (*load_wait_base)(struct host1x_syncpt *syncpt); + u32 (*load)(struct host1x_syncpt *syncpt); +- void (*cpu_incr)(struct host1x_syncpt *syncpt); ++ int (*cpu_incr)(struct host1x_syncpt *syncpt); + int (*patch_wait)(struct host1x_syncpt *syncpt, void *patch_addr); + }; + +@@ -157,10 +157,10 @@ static inline u32 host1x_hw_syncpt_load(struct host1x *host, + return host->syncpt_op->load(sp); + } + +-static inline void host1x_hw_syncpt_cpu_incr(struct host1x *host, +- struct host1x_syncpt *sp) ++static inline int host1x_hw_syncpt_cpu_incr(struct host1x *host, ++ struct host1x_syncpt *sp) + { +- host->syncpt_op->cpu_incr(sp); ++ return host->syncpt_op->cpu_incr(sp); + } + + static inline int host1x_hw_syncpt_patch_wait(struct host1x *host, +diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c +index 8c04943..5360e5a 100644 +--- a/drivers/gpu/host1x/drm/dc.c ++++ b/drivers/gpu/host1x/drm/dc.c +@@ -79,6 +79,9 @@ static int tegra_plane_disable(struct drm_plane *plane) + struct tegra_plane *p = to_tegra_plane(plane); + unsigned long value; + ++ if (!plane->crtc) ++ return 0; ++ + value = WINDOW_A_SELECT << p->index; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); + +@@ -140,6 +143,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) + static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, + struct drm_framebuffer *fb) + { ++ unsigned int format = tegra_dc_format(fb->pixel_format); + struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); + unsigned long value; + +@@ -150,6 +154,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, + + tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); + tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); ++ tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH); + + value = GENERAL_UPDATE | WIN_A_UPDATE; + tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); +diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c +index 2b561c9..e184b00 100644 +--- a/drivers/gpu/host1x/drm/drm.c ++++ b/drivers/gpu/host1x/drm/drm.c +@@ -148,6 +148,7 @@ int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm) + dev_err(host1x->dev, + "DRM setup failed for %s: %d\n", + dev_name(client->dev), err); ++ mutex_unlock(&host1x->clients_lock); + return err; + } + } +@@ -175,6 +176,7 @@ int host1x_drm_exit(struct host1x_drm *host1x) + dev_err(host1x->dev, + "DRM cleanup failed for %s: %d\n", + dev_name(client->dev), err); ++ mutex_unlock(&host1x->clients_lock); + return err; + } + } +@@ -257,6 +259,13 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) + if (err < 0) + return err; + ++ /* ++ * We don't use the drm_irq_install() helpers provided by the DRM ++ * core, so we need to set this manually in order to allow the ++ * DRM_IOCTL_WAIT_VBLANK to operate correctly. ++ */ ++ drm->irq_enabled = 1; ++ + err = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (err < 0) + return err; +@@ -378,8 +387,7 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data, + if (!sp) + return -EINVAL; + +- host1x_syncpt_incr(sp); +- return 0; ++ return host1x_syncpt_incr(sp); + } + + static int tegra_syncpt_wait(struct drm_device *drm, void *data, +@@ -605,7 +613,7 @@ static void tegra_debugfs_cleanup(struct drm_minor *minor) + #endif + + struct drm_driver tegra_drm_driver = { +- .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM, ++ .driver_features = DRIVER_MODESET | DRIVER_GEM, + .load = tegra_drm_load, + .unload = tegra_drm_unload, + .open = tegra_drm_open, +diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/host1x/drm/drm.h +index 02ce020..755943e 100644 +--- a/drivers/gpu/host1x/drm/drm.h ++++ b/drivers/gpu/host1x/drm/drm.h +@@ -16,6 +16,7 @@ + #include <drm/drm_fb_helper.h> + #include <drm/drm_fixed.h> + #include <uapi/drm/tegra_drm.h> ++#include <video/display.h> + + #include "host1x.h" + +@@ -202,6 +203,9 @@ struct tegra_output { + + struct drm_encoder encoder; + struct drm_connector connector; ++ struct display_entity stream; ++ struct display_entity *panel; ++ struct display_entity_notifier display_notifier; + }; + + static inline struct tegra_output *encoder_to_output(struct drm_encoder *e) +@@ -214,6 +218,18 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c) + return container_of(c, struct tegra_output, connector); + } + ++static inline ++struct tegra_output *display_entity_to_output(struct display_entity *e) ++{ ++ return container_of(e, struct tegra_output, stream); ++} ++ ++static inline struct tegra_output * ++display_notifier_to_output(struct display_entity_notifier *n) ++{ ++ return container_of(n, struct tegra_output, display_notifier); ++} ++ + static inline int tegra_output_enable(struct tegra_output *output) + { + if (output && output->ops && output->ops->enable) +diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c +index 6a45ae0..27ffcf1 100644 +--- a/drivers/gpu/host1x/drm/gr2d.c ++++ b/drivers/gpu/host1x/drm/gr2d.c +@@ -84,7 +84,7 @@ static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm, + + gem = drm_gem_object_lookup(drm, file, handle); + if (!gem) +- return 0; ++ return NULL; + + mutex_lock(&drm->struct_mutex); + drm_gem_object_unreference(gem); +@@ -135,8 +135,10 @@ static int gr2d_submit(struct host1x_drm_context *context, + goto fail; + + bo = host1x_bo_lookup(drm, file, cmdbuf.handle); +- if (!bo) ++ if (!bo) { ++ err = -ENOENT; + goto fail; ++ } + + host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); + num_cmdbufs--; +@@ -158,8 +160,10 @@ static int gr2d_submit(struct host1x_drm_context *context, + reloc->cmdbuf = cmdbuf; + reloc->target = target; + +- if (!reloc->target || !reloc->cmdbuf) ++ if (!reloc->target || !reloc->cmdbuf) { ++ err = -ENOENT; + goto fail; ++ } + } + + err = copy_from_user(job->waitchk, waitchks, +@@ -281,7 +285,7 @@ static int gr2d_probe(struct platform_device *pdev) + if (!gr2d->channel) + return -ENOMEM; + +- *syncpts = host1x_syncpt_request(dev, 0); ++ *syncpts = host1x_syncpt_request(dev, false); + if (!(*syncpts)) { + host1x_channel_free(gr2d->channel); + return -ENOMEM; +diff --git a/drivers/gpu/host1x/drm/output.c b/drivers/gpu/host1x/drm/output.c +index 8140fc6..7bd7a37 100644 +--- a/drivers/gpu/host1x/drm/output.c ++++ b/drivers/gpu/host1x/drm/output.c +@@ -105,6 +105,29 @@ static const struct drm_encoder_funcs encoder_funcs = { + + static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode) + { ++ struct tegra_output *output = encoder_to_output(encoder); ++ enum display_entity_state state; ++ ++ if (!output->panel) ++ return; ++ ++ switch (mode) { ++ case DRM_MODE_DPMS_ON: ++ state = DISPLAY_ENTITY_STATE_ON; ++ break; ++ case DRM_MODE_DPMS_STANDBY: ++ state = DISPLAY_ENTITY_STATE_STANDBY; ++ break; ++ case DRM_MODE_DPMS_SUSPEND: ++ case DRM_MODE_DPMS_OFF: ++ state = DISPLAY_ENTITY_STATE_OFF; ++ break; ++ default: ++ dev_warn(output->dev, "unknown DPMS state, ignoring\n"); ++ return; ++ } ++ ++ display_entity_set_state(output->panel, state); + } + + static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder, +@@ -129,9 +152,14 @@ static void tegra_encoder_mode_set(struct drm_encoder *encoder, + struct tegra_output *output = encoder_to_output(encoder); + int err; + +- err = tegra_output_enable(output); ++ if (output->panel) ++ err = display_entity_set_state(output->panel, ++ DISPLAY_ENTITY_STATE_ON); ++ else ++ err = tegra_output_enable(output); ++ + if (err < 0) +- dev_err(encoder->dev->dev, "tegra_output_enable(): %d\n", err); ++ dev_err(encoder->dev->dev, "cannot enable output: %d\n", err); + } + + static const struct drm_encoder_helper_funcs encoder_helper_funcs = { +@@ -185,6 +213,71 @@ int tegra_output_parse_dt(struct tegra_output *output) + return 0; + } + ++static int display_notify_callback(struct display_entity_notifier *notifier, ++ struct display_entity *entity, int event) ++{ ++ struct tegra_output *output = display_notifier_to_output(notifier); ++ struct device_node *pnode; ++ ++ switch (event) { ++ case DISPLAY_ENTITY_NOTIFIER_CONNECT: ++ if (output->panel) ++ break; ++ ++ pnode = of_parse_phandle(output->of_node, "nvidia,panel", 0); ++ if (!pnode) ++ break; ++ ++ if (entity->dev && entity->dev->of_node == pnode) { ++ dev_dbg(output->dev, "connecting panel\n"); ++ output->panel = display_entity_get(entity); ++ display_entity_connect(&output->stream, output->panel); ++ /* TODO set state of panel according to current DPMS ++ * state? */ ++ } ++ of_node_put(pnode); ++ ++ break; ++ ++ case DISPLAY_ENTITY_NOTIFIER_DISCONNECT: ++ if (!output->panel || output->panel != entity) ++ break; ++ ++ dev_dbg(output->dev, "disconnecting panel\n"); ++ display_entity_disconnect(&output->stream, output->panel); ++ output->panel = NULL; ++ display_entity_put(output->panel); ++ ++ break; ++ ++ default: ++ dev_dbg(output->dev, "unhandled display event\n"); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int tegra_output_set_stream(struct display_entity *entity, ++ enum display_entity_stream_state state) ++{ ++ struct tegra_output *output = display_entity_to_output(entity); ++ ++ switch (state) { ++ case DISPLAY_ENTITY_STATE_OFF: ++ case DISPLAY_ENTITY_STATE_STANDBY: ++ return output->ops->disable(output); ++ case DISPLAY_ENTITY_STATE_ON: ++ return output->ops->enable(output); ++ } ++ ++ return 0; ++} ++ ++static struct display_entity_video_ops tegra_output_video_ops = { ++ .set_stream = tegra_output_set_stream, ++}; ++ + int tegra_output_init(struct drm_device *drm, struct tegra_output *output) + { + int connector, encoder, err; +@@ -250,6 +343,23 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) + + output->encoder.possible_crtcs = 0x3; + ++ /* register display entity */ ++ memset(&output->stream, 0, sizeof(output->stream)); ++ output->stream.dev = output->dev; ++ output->stream.ops.video = &tegra_output_video_ops; ++ err = display_entity_register(&output->stream); ++ if (err) { ++ dev_err(output->dev, "cannot register display entity\n"); ++ return err; ++ } ++ ++ /* register display notifier */ ++ output->display_notifier.dev = NULL; ++ output->display_notifier.notify = display_notify_callback; ++ err = display_entity_register_notifier(&output->display_notifier); ++ if (err) ++ return err; ++ + return 0; + + free_hpd: +@@ -260,6 +370,12 @@ free_hpd: + + int tegra_output_exit(struct tegra_output *output) + { ++ if (output->panel) ++ display_entity_put(output->panel); ++ ++ display_entity_unregister_notifier(&output->display_notifier); ++ display_entity_unregister(&output->stream); ++ + if (gpio_is_valid(output->hpd_gpio)) { + free_irq(output->hpd_irq, output); + gpio_free(output->hpd_gpio); +diff --git a/drivers/gpu/host1x/hw/cdma_hw.c b/drivers/gpu/host1x/hw/cdma_hw.c +index 590b69d..2ee4ad5 100644 +--- a/drivers/gpu/host1x/hw/cdma_hw.c ++++ b/drivers/gpu/host1x/hw/cdma_hw.c +@@ -44,7 +44,7 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr, + u32 i; + + for (i = 0; i < syncpt_incrs; i++) +- host1x_syncpt_cpu_incr(cdma->timeout.syncpt); ++ host1x_syncpt_incr(cdma->timeout.syncpt); + + /* after CPU incr, ensure shadow is up to date */ + host1x_syncpt_load(cdma->timeout.syncpt); +diff --git a/drivers/gpu/host1x/hw/syncpt_hw.c b/drivers/gpu/host1x/hw/syncpt_hw.c +index 6117499..0cf6095 100644 +--- a/drivers/gpu/host1x/hw/syncpt_hw.c ++++ b/drivers/gpu/host1x/hw/syncpt_hw.c +@@ -77,21 +77,19 @@ static u32 syncpt_load(struct host1x_syncpt *sp) + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. + */ +-static void syncpt_cpu_incr(struct host1x_syncpt *sp) ++static int syncpt_cpu_incr(struct host1x_syncpt *sp) + { + struct host1x *host = sp->host; + u32 reg_offset = sp->id / 32; + + if (!host1x_syncpt_client_managed(sp) && +- host1x_syncpt_idle(sp)) { +- dev_err(host->dev, "Trying to increment syncpoint id %d beyond max\n", +- sp->id); +- host1x_debug_dump(sp->host); +- return; +- } ++ host1x_syncpt_idle(sp)) ++ return -EINVAL; + host1x_sync_writel(host, BIT_MASK(sp->id), + HOST1X_SYNC_SYNCPT_CPU_INCR(reg_offset)); + wmb(); ++ ++ return 0; + } + + /* remove a wait pointed to by patch_addr */ +diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c +index f665d67..cc80766 100644 +--- a/drivers/gpu/host1x/job.c ++++ b/drivers/gpu/host1x/job.c +@@ -228,17 +228,15 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) + void *cmdbuf_page_addr = NULL; + + /* pin & patch the relocs for one gather */ +- while (i < job->num_relocs) { ++ for (i = 0; i < job->num_relocs; i++) { + struct host1x_reloc *reloc = &job->relocarray[i]; + u32 reloc_addr = (job->reloc_addr_phys[i] + + reloc->target_offset) >> reloc->shift; + u32 *target; + + /* skip all other gathers */ +- if (!(reloc->cmdbuf && cmdbuf == reloc->cmdbuf)) { +- i++; ++ if (cmdbuf != reloc->cmdbuf) + continue; +- } + + if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) { + if (cmdbuf_page_addr) +@@ -257,9 +255,6 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) + + target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK); + *target = reloc_addr; +- +- /* mark this gather as handled */ +- reloc->cmdbuf = 0; + } + + if (cmdbuf_page_addr) +@@ -268,15 +263,15 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) + return 0; + } + +-static int check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf, ++static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf, + unsigned int offset) + { + offset *= sizeof(u32); + + if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset) +- return -EINVAL; ++ return false; + +- return 0; ++ return true; + } + + struct host1x_firewall { +@@ -307,10 +302,10 @@ static int check_mask(struct host1x_firewall *fw) + + if (mask & 1) { + if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) { +- bool bad_reloc = check_reloc(fw->reloc, +- fw->cmdbuf_id, +- fw->offset); +- if (!fw->num_relocs || bad_reloc) ++ if (!fw->num_relocs) ++ return -EINVAL; ++ if (!check_reloc(fw->reloc, fw->cmdbuf_id, ++ fw->offset)) + return -EINVAL; + fw->reloc++; + fw->num_relocs--; +@@ -330,14 +325,14 @@ static int check_incr(struct host1x_firewall *fw) + u32 count = fw->count; + u32 reg = fw->reg; + +- while (fw) { ++ while (count) { + if (fw->words == 0) + return -EINVAL; + + if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) { +- bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id, +- fw->offset); +- if (!fw->num_relocs || bad_reloc) ++ if (!fw->num_relocs) ++ return -EINVAL; ++ if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset)) + return -EINVAL; + fw->reloc++; + fw->num_relocs--; +@@ -361,9 +356,9 @@ static int check_nonincr(struct host1x_firewall *fw) + return -EINVAL; + + if (is_addr_reg) { +- bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id, +- fw->offset); +- if (!fw->num_relocs || bad_reloc) ++ if (!fw->num_relocs) ++ return -EINVAL; ++ if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset)) + return -EINVAL; + fw->reloc++; + fw->num_relocs--; +@@ -376,69 +371,58 @@ static int check_nonincr(struct host1x_firewall *fw) + return 0; + } + +-static int validate(struct host1x_job *job, struct device *dev, +- struct host1x_job_gather *g) ++static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g) + { +- u32 *cmdbuf_base; ++ u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped + ++ (g->offset / sizeof(u32)); + int err = 0; +- struct host1x_firewall fw; + +- fw.job = job; +- fw.dev = dev; +- fw.reloc = job->relocarray; +- fw.num_relocs = job->num_relocs; +- fw.cmdbuf_id = g->bo; +- +- fw.offset = 0; +- fw.class = 0; +- +- if (!job->is_addr_reg) ++ if (!fw->job->is_addr_reg) + return 0; + +- cmdbuf_base = host1x_bo_mmap(g->bo); +- if (!cmdbuf_base) +- return -ENOMEM; ++ fw->words = g->words; ++ fw->cmdbuf_id = g->bo; ++ fw->offset = 0; + +- fw.words = g->words; +- while (fw.words && !err) { +- u32 word = cmdbuf_base[fw.offset]; ++ while (fw->words && !err) { ++ u32 word = cmdbuf_base[fw->offset]; + u32 opcode = (word & 0xf0000000) >> 28; + +- fw.mask = 0; +- fw.reg = 0; +- fw.count = 0; +- fw.words--; +- fw.offset++; ++ fw->mask = 0; ++ fw->reg = 0; ++ fw->count = 0; ++ fw->words--; ++ fw->offset++; + + switch (opcode) { + case 0: +- fw.class = word >> 6 & 0x3ff; +- fw.mask = word & 0x3f; +- fw.reg = word >> 16 & 0xfff; +- err = check_mask(&fw); ++ fw->class = word >> 6 & 0x3ff; ++ fw->mask = word & 0x3f; ++ fw->reg = word >> 16 & 0xfff; ++ err = check_mask(fw); + if (err) + goto out; + break; + case 1: +- fw.reg = word >> 16 & 0xfff; +- fw.count = word & 0xffff; +- err = check_incr(&fw); ++ fw->reg = word >> 16 & 0xfff; ++ fw->count = word & 0xffff; ++ err = check_incr(fw); + if (err) + goto out; + break; + + case 2: +- fw.reg = word >> 16 & 0xfff; +- fw.count = word & 0xffff; +- err = check_nonincr(&fw); ++ fw->reg = word >> 16 & 0xfff; ++ fw->count = word & 0xffff; ++ err = check_nonincr(fw); + if (err) + goto out; + break; + + case 3: +- fw.mask = word & 0xffff; +- fw.reg = word >> 16 & 0xfff; +- err = check_mask(&fw); ++ fw->mask = word & 0xffff; ++ fw->reg = word >> 16 & 0xfff; ++ err = check_mask(fw); + if (err) + goto out; + break; +@@ -453,21 +437,26 @@ static int validate(struct host1x_job *job, struct device *dev, + } + + /* No relocs should remain at this point */ +- if (fw.num_relocs) ++ if (fw->num_relocs) + err = -EINVAL; + + out: +- host1x_bo_munmap(g->bo, cmdbuf_base); +- + return err; + } + + static inline int copy_gathers(struct host1x_job *job, struct device *dev) + { ++ struct host1x_firewall fw; + size_t size = 0; + size_t offset = 0; + int i; + ++ fw.job = job; ++ fw.dev = dev; ++ fw.reloc = job->relocarray; ++ fw.num_relocs = job->num_relocs; ++ fw.class = 0; ++ + for (i = 0; i < job->num_gathers; i++) { + struct host1x_job_gather *g = &job->gathers[i]; + size += g->words * sizeof(u32); +@@ -488,14 +477,19 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev) + struct host1x_job_gather *g = &job->gathers[i]; + void *gather; + ++ /* Copy the gather */ + gather = host1x_bo_mmap(g->bo); + memcpy(job->gather_copy_mapped + offset, gather + g->offset, + g->words * sizeof(u32)); + host1x_bo_munmap(g->bo, gather); + ++ /* Store the location in the buffer */ + g->base = job->gather_copy; + g->offset = offset; +- g->bo = NULL; ++ ++ /* Validate the job */ ++ if (validate(&fw, g)) ++ return -EINVAL; + + offset += g->words * sizeof(u32); + } +@@ -540,20 +534,11 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) + if (job->gathers[j].bo == g->bo) + job->gathers[j].handled = true; + +- err = 0; +- +- if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) +- err = validate(job, dev, g); +- ++ err = do_relocs(job, g->bo); + if (err) +- dev_err(dev, "Job invalid (err=%d)\n", err); +- +- if (!err) +- err = do_relocs(job, g->bo); +- +- if (!err) +- err = do_waitchks(job, host, g->bo); ++ break; + ++ err = do_waitchks(job, host, g->bo); + if (err) + break; + } +diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c +index 4b49345..409745b 100644 +--- a/drivers/gpu/host1x/syncpt.c ++++ b/drivers/gpu/host1x/syncpt.c +@@ -32,7 +32,7 @@ + + static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host, + struct device *dev, +- int client_managed) ++ bool client_managed) + { + int i; + struct host1x_syncpt *sp = host->syncpt; +@@ -40,7 +40,8 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host, + + for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++) + ; +- if (sp->dev) ++ ++ if (i >= host->info->nb_pts) + return NULL; + + name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id, +@@ -128,22 +129,11 @@ u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp) + } + + /* +- * Write a cpu syncpoint increment to the hardware, without touching +- * the cache. Caller is responsible for host being powered. +- */ +-void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp) +-{ +- host1x_hw_syncpt_cpu_incr(sp->host, sp); +-} +- +-/* + * Increment syncpoint value from cpu, updating cache + */ +-void host1x_syncpt_incr(struct host1x_syncpt *sp) ++int host1x_syncpt_incr(struct host1x_syncpt *sp) + { +- if (host1x_syncpt_client_managed(sp)) +- host1x_syncpt_incr_max(sp, 1); +- host1x_syncpt_cpu_incr(sp); ++ return host1x_hw_syncpt_cpu_incr(sp->host, sp); + } + + /* +@@ -331,7 +321,7 @@ int host1x_syncpt_init(struct host1x *host) + host1x_syncpt_restore(host); + + /* Allocate sync point to use for clearing waits for expired fences */ +- host->nop_sp = _host1x_syncpt_alloc(host, NULL, 0); ++ host->nop_sp = _host1x_syncpt_alloc(host, NULL, false); + if (!host->nop_sp) + return -ENOMEM; + +@@ -339,7 +329,7 @@ int host1x_syncpt_init(struct host1x *host) + } + + struct host1x_syncpt *host1x_syncpt_request(struct device *dev, +- int client_managed) ++ bool client_managed) + { + struct host1x *host = dev_get_drvdata(dev->parent); + return _host1x_syncpt_alloc(host, dev, client_managed); +@@ -353,7 +343,7 @@ void host1x_syncpt_free(struct host1x_syncpt *sp) + kfree(sp->name); + sp->dev = NULL; + sp->name = NULL; +- sp->client_managed = 0; ++ sp->client_managed = false; + } + + void host1x_syncpt_deinit(struct host1x *host) +diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h +index c998061..267c0b9 100644 +--- a/drivers/gpu/host1x/syncpt.h ++++ b/drivers/gpu/host1x/syncpt.h +@@ -36,7 +36,7 @@ struct host1x_syncpt { + atomic_t max_val; + u32 base_val; + const char *name; +- int client_managed; ++ bool client_managed; + struct host1x *host; + struct device *dev; + +@@ -94,7 +94,7 @@ static inline bool host1x_syncpt_check_max(struct host1x_syncpt *sp, u32 real) + } + + /* Return true if sync point is client managed. */ +-static inline int host1x_syncpt_client_managed(struct host1x_syncpt *sp) ++static inline bool host1x_syncpt_client_managed(struct host1x_syncpt *sp) + { + return sp->client_managed; + } +@@ -115,9 +115,6 @@ static inline bool host1x_syncpt_idle(struct host1x_syncpt *sp) + /* Return pointer to struct denoting sync point id. */ + struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id); + +-/* Request incrementing a sync point. */ +-void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp); +- + /* Load current value from hardware to the shadow register. */ + u32 host1x_syncpt_load(struct host1x_syncpt *sp); + +@@ -133,8 +130,8 @@ void host1x_syncpt_restore(struct host1x *host); + /* Read current wait base value into shadow register and return it. */ + u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp); + +-/* Increment sync point and its max. */ +-void host1x_syncpt_incr(struct host1x_syncpt *sp); ++/* Request incrementing a sync point. */ ++int host1x_syncpt_incr(struct host1x_syncpt *sp); + + /* Indicate future operations by incrementing the sync point max. */ + u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs); +@@ -157,7 +154,7 @@ u32 host1x_syncpt_id(struct host1x_syncpt *sp); + + /* Allocate a sync point for a device. */ + struct host1x_syncpt *host1x_syncpt_request(struct device *dev, +- int client_managed); ++ bool client_managed); + + /* Free a sync point. */ + void host1x_syncpt_free(struct host1x_syncpt *sp); +diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c +index 197c393..5a5c639 100644 +--- a/drivers/staging/nvec/nvec.c ++++ b/drivers/staging/nvec/nvec.c +@@ -33,7 +33,6 @@ + #include <linux/mfd/core.h> + #include <linux/mutex.h> + #include <linux/notifier.h> +-#include <linux/platform_device.h> + #include <linux/slab.h> + #include <linux/spinlock.h> + #include <linux/workqueue.h> +@@ -751,8 +750,6 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec) + writel(0, nvec->base + I2C_SL_ADDR2); + + enable_irq(nvec->irq); +- +- clk_disable_unprepare(nvec->i2c_clk); + } + + #ifdef CONFIG_PM_SLEEP +@@ -772,11 +769,31 @@ static void nvec_power_off(void) + nvec_write_async(nvec_power_handle, ap_pwr_down, 2); + } + ++/* ++ * Parse common device tree data ++ */ ++static int nvec_i2c_parse_dt_pdata(struct nvec_chip *nvec) ++{ ++ nvec->gpio = of_get_named_gpio(nvec->dev->of_node, "request-gpios", 0); ++ ++ if (nvec->gpio < 0) { ++ dev_err(nvec->dev, "no gpio specified"); ++ return -ENODEV; ++ } ++ ++ if (of_property_read_u32(nvec->dev->of_node, "slave-addr", ++ &nvec->i2c_addr)) { ++ dev_err(nvec->dev, "no i2c address specified"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ + static int tegra_nvec_probe(struct platform_device *pdev) + { + int err, ret; + struct clk *i2c_clk; +- struct nvec_platform_data *pdata = pdev->dev.platform_data; + struct nvec_chip *nvec; + struct nvec_msg *msg; + struct resource *res; +@@ -785,6 +802,11 @@ static int tegra_nvec_probe(struct platform_device *pdev) + unmute_speakers[] = { NVEC_OEM0, 0x10, 0x59, 0x95 }, + enable_event[7] = { NVEC_SYS, CNF_EVENT_REPORTING, true }; + ++ if(!pdev->dev.of_node) { ++ dev_err(&pdev->dev, "must be instantiated using device tree\n"); ++ return -ENODEV; ++ } ++ + nvec = devm_kzalloc(&pdev->dev, sizeof(struct nvec_chip), GFP_KERNEL); + if (nvec == NULL) { + dev_err(&pdev->dev, "failed to reserve memory\n"); +@@ -793,25 +815,9 @@ static int tegra_nvec_probe(struct platform_device *pdev) + platform_set_drvdata(pdev, nvec); + nvec->dev = &pdev->dev; + +- if (pdata) { +- nvec->gpio = pdata->gpio; +- nvec->i2c_addr = pdata->i2c_addr; +- } else if (nvec->dev->of_node) { +- nvec->gpio = of_get_named_gpio(nvec->dev->of_node, +- "request-gpios", 0); +- if (nvec->gpio < 0) { +- dev_err(&pdev->dev, "no gpio specified"); +- return -ENODEV; +- } +- if (of_property_read_u32(nvec->dev->of_node, +- "slave-addr", &nvec->i2c_addr)) { +- dev_err(&pdev->dev, "no i2c address specified"); +- return -ENODEV; +- } +- } else { +- dev_err(&pdev->dev, "no platform data\n"); +- return -ENODEV; +- } ++ err = nvec_i2c_parse_dt_pdata(nvec); ++ if (err < 0) ++ return err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); +@@ -864,9 +870,6 @@ static int tegra_nvec_probe(struct platform_device *pdev) + + tegra_init_i2c_slave(nvec); + +- clk_prepare_enable(i2c_clk); +- +- + /* enable event reporting */ + nvec_toggle_global_events(nvec, true); + +diff --git a/drivers/staging/nvec/nvec.h b/drivers/staging/nvec/nvec.h +index 2b1316d..e880518 100644 +--- a/drivers/staging/nvec/nvec.h ++++ b/drivers/staging/nvec/nvec.h +@@ -103,31 +103,6 @@ struct nvec_msg { + }; + + /** +- * struct nvec_subdev - A subdevice of nvec, such as nvec_kbd +- * @name: The name of the sub device +- * @platform_data: Platform data +- * @id: Identifier of the sub device +- */ +-struct nvec_subdev { +- const char *name; +- void *platform_data; +- int id; +-}; +- +-/** +- * struct nvec_platform_data - platform data for a tegra slave controller +- * @i2c_addr: number of i2c slave adapter the ec is connected to +- * @gpio: gpio number for the ec request line +- * +- * Platform data, to be used in board definitions. For an example, take a +- * look at the paz00 board in arch/arm/mach-tegra/board-paz00.c +- */ +-struct nvec_platform_data { +- int i2c_addr; +- int gpio; +-}; +- +-/** + * struct nvec_chip - A single connection to an NVIDIA Embedded controller + * @dev: The device + * @gpio: The same as for &struct nvec_platform_data +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 2e937bd..6d9788d 100644 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -2475,6 +2475,7 @@ source "drivers/video/omap2/Kconfig" + source "drivers/video/exynos/Kconfig" + source "drivers/video/mmp/Kconfig" + source "drivers/video/backlight/Kconfig" ++source "drivers/video/display/Kconfig" + + if VT + source "drivers/video/console/Kconfig" +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index e8bae8d..d7fd4a2 100644 +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -15,6 +15,7 @@ fb-objs := $(fb-y) + obj-$(CONFIG_VT) += console/ + obj-$(CONFIG_LOGO) += logo/ + obj-y += backlight/ ++obj-y += display/ + + obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ + +diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig +new file mode 100644 +index 0000000..165776f +--- /dev/null ++++ b/drivers/video/display/Kconfig +@@ -0,0 +1,16 @@ ++menuconfig DISPLAY_CORE ++ tristate "Display Core" ++ ---help--- ++ Support common display framework for graphics devices. ++ ++if DISPLAY_CORE ++ ++config DISPLAY_PANEL_CLAA101WA01A ++ tristate "Chunghwa CLAA101WA01A Display Panel" ++ select BACKLIGHT_PWM ++ ---help--- ++ Support for the Chunghwa CLAA101WA01A Display Panel. ++ ++ If you are in doubt, say N. ++ ++endif # DISPLAY_CORE +diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile +new file mode 100644 +index 0000000..2e6cf49 +--- /dev/null ++++ b/drivers/video/display/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_DISPLAY_CORE) += display-core.o ++obj-$(CONFIG_DISPLAY_PANEL_CLAA101WA01A) += panel-claa101wa01a.o +diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c +new file mode 100644 +index 0000000..46295fb +--- /dev/null ++++ b/drivers/video/display/display-core.c +@@ -0,0 +1,361 @@ ++/* ++ * Display Core ++ * ++ * Copyright (C) 2012 Renesas Solutions Corp. ++ * ++ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/export.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <video/videomode.h> ++#include <video/display.h> ++ ++static LIST_HEAD(display_entity_list); ++static LIST_HEAD(display_entity_notifiers); ++static DEFINE_MUTEX(display_entity_mutex); ++ ++/* ----------------------------------------------------------------------------- ++ * Control operations ++ */ ++ ++/** ++ * display_entity_set_state - Set the display entity operation state ++ * @entity: The display entity ++ * @state: Display entity operation state ++ * ++ * See &enum display_entity_state for information regarding the entity states. ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++int display_entity_set_state(struct display_entity *entity, ++ enum display_entity_state state) ++{ ++ int ret; ++ ++ if (entity->state == state) ++ return 0; ++ ++ if (!entity->ops.ctrl || !entity->ops.ctrl->set_state) ++ return 0; ++ ++ ret = entity->ops.ctrl->set_state(entity, state); ++ if (ret < 0) ++ return ret; ++ ++ entity->state = state; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(display_entity_set_state); ++ ++/** ++ * display_entity_update - Update the display ++ * @entity: The display entity ++ * ++ * Make the display entity ready to receive pixel data and start frame transfer. ++ * This operation can only be called if the display entity is in STANDBY or ON ++ * state. ++ * ++ * The display entity will call the upstream entity in the video chain to start ++ * the video stream. ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++int display_entity_update(struct display_entity *entity) ++{ ++ if (!entity->ops.ctrl || !entity->ops.ctrl->update) ++ return 0; ++ ++ return entity->ops.ctrl->update(entity); ++} ++EXPORT_SYMBOL_GPL(display_entity_update); ++ ++/** ++ * display_entity_get_modes - Get video modes supported by the display entity ++ * @entity The display entity ++ * @modes: Pointer to an array of modes ++ * ++ * Fill the modes argument with a pointer to an array of video modes. The array ++ * is owned by the display entity. ++ * ++ * Return the number of supported modes on success (including 0 if no mode is ++ * supported) or a negative error code otherwise. ++ */ ++int display_entity_get_modes(struct display_entity *entity, ++ const struct videomode **modes) ++{ ++ if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes) ++ return 0; ++ ++ return entity->ops.ctrl->get_modes(entity, modes); ++} ++EXPORT_SYMBOL_GPL(display_entity_get_modes); ++ ++/** ++ * display_entity_get_size - Get display entity physical size ++ * @entity: The display entity ++ * @width: Physical width in millimeters ++ * @height: Physical height in millimeters ++ * ++ * When applicable, for instance for display panels, retrieve the display ++ * physical size in millimeters. ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++int display_entity_get_size(struct display_entity *entity, ++ unsigned int *width, unsigned int *height) ++{ ++ if (!entity->ops.ctrl || !entity->ops.ctrl->get_size) ++ return -EOPNOTSUPP; ++ ++ return entity->ops.ctrl->get_size(entity, width, height); ++} ++EXPORT_SYMBOL_GPL(display_entity_get_size); ++ ++/** ++ * display_entity_get_params - Get display entity interface parameters ++ * @entity: The display entity ++ * @params: Pointer to interface parameters ++ * ++ * Fill the parameters structure pointed to by the params argument with display ++ * entity interface parameters. ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++int display_entity_get_params(struct display_entity *entity, ++ struct display_entity_interface_params *params) ++{ ++ if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes) ++ return -EOPNOTSUPP; ++ ++ return entity->ops.ctrl->get_params(entity, params); ++} ++EXPORT_SYMBOL_GPL(display_entity_get_params); ++ ++/* ----------------------------------------------------------------------------- ++ * Video operations ++ */ ++ ++/** ++ * display_entity_set_stream - Control the video stream state ++ * @entity: The display entity ++ * @state: Display video stream state ++ * ++ * Control the video stream state at the entity video output. ++ * ++ * See &enum display_entity_stream_state for information regarding the stream ++ * states. ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++int display_entity_set_stream(struct display_entity *entity, ++ enum display_entity_stream_state state) ++{ ++ if (!entity->ops.video || !entity->ops.video->set_stream) ++ return 0; ++ ++ return entity->ops.video->set_stream(entity, state); ++} ++EXPORT_SYMBOL_GPL(display_entity_set_stream); ++ ++/* ----------------------------------------------------------------------------- ++ * Connections ++ */ ++ ++/** ++ * display_entity_connect - Connect two entities through a video stream ++ * @source: The video stream source ++ * @sink: The video stream sink ++ * ++ * Set the sink entity source field to the source entity. ++ */ ++ ++/** ++ * display_entity_disconnect - Disconnect two previously connected entities ++ * @source: The video stream source ++ * @sink: The video stream sink ++ * ++ * Break a connection between two previously connected entities. The source ++ * entity source field is reset to NULL. ++ */ ++ ++/* ----------------------------------------------------------------------------- ++ * Registration and notification ++ */ ++ ++static void display_entity_release(struct kref *ref) ++{ ++ struct display_entity *entity = ++ container_of(ref, struct display_entity, ref); ++ ++ if (entity->release) ++ entity->release(entity); ++} ++ ++/** ++ * display_entity_get - get a reference to a display entity ++ * @display_entity: the display entity ++ * ++ * Return the display entity pointer. ++ */ ++struct display_entity *display_entity_get(struct display_entity *entity) ++{ ++ if (entity == NULL) ++ return NULL; ++ ++ kref_get(&entity->ref); ++ return entity; ++} ++EXPORT_SYMBOL_GPL(display_entity_get); ++ ++/** ++ * display_entity_put - release a reference to a display entity ++ * @display_entity: the display entity ++ * ++ * Releasing the last reference to a display entity releases the display entity ++ * itself. ++ */ ++void display_entity_put(struct display_entity *entity) ++{ ++ kref_put(&entity->ref, display_entity_release); ++} ++EXPORT_SYMBOL_GPL(display_entity_put); ++ ++static int display_entity_notifier_match(struct display_entity *entity, ++ struct display_entity_notifier *notifier) ++{ ++ return notifier->dev == NULL || notifier->dev == entity->dev; ++} ++ ++/** ++ * display_entity_register_notifier - register a display entity notifier ++ * @notifier: display entity notifier structure we want to register ++ * ++ * Display entity notifiers are called to notify drivers of display ++ * entity-related events for matching display_entitys. ++ * ++ * Notifiers and display_entitys are matched through the device they correspond ++ * to. If the notifier dev field is equal to the display entity dev field the ++ * notifier will be called when an event is reported. Notifiers with a NULL dev ++ * field act as catch-all and will be called for all display_entitys. ++ * ++ * Supported events are ++ * ++ * - DISPLAY_ENTITY_NOTIFIER_CONNECT reports display entity connection and is ++ * sent at display entity or notifier registration time ++ * - DISPLAY_ENTITY_NOTIFIER_DISCONNECT reports display entity disconnection and ++ * is sent at display entity unregistration time ++ * ++ * Registering a notifier sends DISPLAY_ENTITY_NOTIFIER_CONNECT events for all ++ * previously registered display_entitys that match the notifiers. ++ * ++ * Return 0 on success. ++ */ ++int display_entity_register_notifier(struct display_entity_notifier *notifier) ++{ ++ struct display_entity *entity; ++ ++ mutex_lock(&display_entity_mutex); ++ list_add_tail(¬ifier->list, &display_entity_notifiers); ++ ++ list_for_each_entry(entity, &display_entity_list, list) { ++ if (!display_entity_notifier_match(entity, notifier)) ++ continue; ++ ++ if (notifier->notify(notifier, entity, ++ DISPLAY_ENTITY_NOTIFIER_CONNECT)) ++ break; ++ } ++ mutex_unlock(&display_entity_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(display_entity_register_notifier); ++ ++/** ++ * display_entity_unregister_notifier - unregister a display entity notifier ++ * @notifier: display entity notifier to be unregistered ++ * ++ * Unregistration guarantees that the notifier will never be called upon return ++ * of this function. ++ */ ++void display_entity_unregister_notifier(struct display_entity_notifier *notifier) ++{ ++ mutex_lock(&display_entity_mutex); ++ list_del(¬ifier->list); ++ mutex_unlock(&display_entity_mutex); ++} ++EXPORT_SYMBOL_GPL(display_entity_unregister_notifier); ++ ++/** ++ * display_entity_register - register a display entity ++ * @display_entity: display entity to be registered ++ * ++ * Register the display entity and send the DISPLAY_ENTITY_NOTIFIER_CONNECT ++ * event to all matching registered notifiers. ++ * ++ * Return 0 on success. ++ */ ++int __must_check __display_entity_register(struct display_entity *entity, ++ struct module *owner) ++{ ++ struct display_entity_notifier *notifier; ++ ++ kref_init(&entity->ref); ++ entity->owner = owner; ++ entity->state = DISPLAY_ENTITY_STATE_OFF; ++ ++ mutex_lock(&display_entity_mutex); ++ list_add(&entity->list, &display_entity_list); ++ ++ list_for_each_entry(notifier, &display_entity_notifiers, list) { ++ if (!display_entity_notifier_match(entity, notifier)) ++ continue; ++ ++ if (notifier->notify(notifier, entity, ++ DISPLAY_ENTITY_NOTIFIER_CONNECT)) ++ break; ++ } ++ mutex_unlock(&display_entity_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(__display_entity_register); ++ ++/** ++ * display_entity_unregister - unregister a display entity ++ * @display_entity: display entity to be unregistered ++ * ++ * Unregister the display entity and send the DISPLAY_ENTITY_NOTIFIER_DISCONNECT ++ * event to all matching registered notifiers. ++ */ ++void display_entity_unregister(struct display_entity *entity) ++{ ++ struct display_entity_notifier *notifier; ++ ++ mutex_lock(&display_entity_mutex); ++ list_for_each_entry(notifier, &display_entity_notifiers, list) { ++ if (!display_entity_notifier_match(entity, notifier)) ++ continue; ++ ++ notifier->notify(notifier, entity, ++ DISPLAY_ENTITY_NOTIFIER_DISCONNECT); ++ } ++ ++ list_del(&entity->list); ++ mutex_unlock(&display_entity_mutex); ++ ++ display_entity_put(entity); ++} ++EXPORT_SYMBOL_GPL(display_entity_unregister); ++ ++MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); ++MODULE_DESCRIPTION("Display Core"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/display/panel-claa101wa01a.c b/drivers/video/display/panel-claa101wa01a.c +new file mode 100644 +index 0000000..b76ebe0 +--- /dev/null ++++ b/drivers/video/display/panel-claa101wa01a.c +@@ -0,0 +1,263 @@ ++/* ++ * CLAA101WA01A Display Panel ++ * ++ * Copyright (C) 2013 NVIDIA CORPORATION. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/err.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/regulator/consumer.h> ++#include <linux/of.h> ++#include <linux/of_gpio.h> ++#include <linux/backlight.h> ++ ++#include <video/display.h> ++ ++#define CLAA101WA01A_WIDTH 223 ++#define CLAA101WA01A_HEIGHT 125 ++ ++struct panel_claa101 { ++ struct display_entity entity; ++ struct backlight_device *backlight; ++ struct regulator *vdd_pnl; ++ struct regulator *vdd_bl; ++ /* Enable GPIOs */ ++ int pnl_enable; ++ int bl_enable; ++}; ++ ++#define to_panel_claa101(p) container_of(p, struct panel_claa101, entity) ++ ++static int panel_claa101_off(struct panel_claa101 *panel) ++{ ++ /* TODO error checking? */ ++ if (panel->backlight) { ++ panel->backlight->props.state |= BL_CORE_FBBLANK; ++ backlight_update_status(panel->backlight); ++ } ++ ++ gpio_set_value_cansleep(panel->bl_enable, 0); ++ usleep_range(10000, 10000); ++ regulator_disable(panel->vdd_bl); ++ usleep_range(200000, 200000); ++ gpio_set_value_cansleep(panel->pnl_enable, 0); ++ regulator_disable(panel->vdd_pnl); ++ ++ if (panel->entity.source) ++ display_entity_set_stream(panel->entity.source, ++ DISPLAY_ENTITY_STREAM_STOPPED); ++ ++ return 0; ++} ++ ++static int panel_claa101_on(struct panel_claa101 *panel) ++{ ++ /* TODO error checking? */ ++ if (panel->entity.source) ++ display_entity_set_stream(panel->entity.source, ++ DISPLAY_ENTITY_STREAM_CONTINUOUS); ++ ++ regulator_enable(panel->vdd_pnl); ++ gpio_set_value_cansleep(panel->pnl_enable, 1); ++ usleep_range(200000, 200000); ++ regulator_enable(panel->vdd_bl); ++ usleep_range(10000, 10000); ++ gpio_set_value_cansleep(panel->bl_enable, 1); ++ ++ if (panel->backlight) { ++ panel->backlight->props.state &= ~BL_CORE_FBBLANK; ++ backlight_update_status(panel->backlight); ++ } ++ ++ return 0; ++} ++ ++static int panel_claa101_set_state(struct display_entity *entity, ++ enum display_entity_state state) ++{ ++ struct panel_claa101 *panel = to_panel_claa101(entity); ++ ++ switch (state) { ++ case DISPLAY_ENTITY_STATE_OFF: ++ case DISPLAY_ENTITY_STATE_STANDBY: ++ /* OFF and STANDBY are the same to us. Avoid unbalanced calls to ++ * off() when we are switching between these two states. */ ++ if (entity->state == DISPLAY_ENTITY_STATE_OFF || ++ entity->state == DISPLAY_ENTITY_STATE_STANDBY) ++ return 0; ++ ++ return panel_claa101_off(panel); ++ ++ case DISPLAY_ENTITY_STATE_ON: ++ return panel_claa101_on(panel); ++ } ++ ++ return 0; ++} ++ ++static int panel_claa101_get_modes(struct display_entity *entity, ++ const struct videomode **modes) ++{ ++ /* TODO get modes from EDID? */ ++ return 0; ++} ++ ++static int panel_claa101_get_size(struct display_entity *entity, ++ unsigned int *width, unsigned int *height) ++{ ++ *width = CLAA101WA01A_WIDTH; ++ *height = CLAA101WA01A_HEIGHT; ++ ++ return 0; ++} ++ ++static int panel_claa101_get_params(struct display_entity *entity, ++ struct display_entity_interface_params *params) ++{ ++ return 0; ++} ++ ++static const struct display_entity_control_ops panel_claa101_control_ops = { ++ .set_state = panel_claa101_set_state, ++ .get_modes = panel_claa101_get_modes, ++ .get_size = panel_claa101_get_size, ++ .get_params = panel_claa101_get_params, ++}; ++ ++static int __init panel_claa101_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct panel_claa101 *panel; ++ struct device_node *node; ++ int err; ++ ++ panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); ++ if (!panel) ++ return -ENOMEM; ++ ++ panel->vdd_pnl = devm_regulator_get(dev, "pnl"); ++ if (IS_ERR(panel->vdd_pnl)) { ++ dev_err(dev, "cannot get vdd regulator\n"); ++ return PTR_ERR(panel->vdd_pnl); ++ } ++ ++ panel->vdd_bl = devm_regulator_get(dev, "bl"); ++ if (IS_ERR(panel->vdd_bl)) { ++ dev_err(dev, "cannot get bl regulator\n"); ++ return PTR_ERR(panel->vdd_bl); ++ } ++ ++ err = of_get_named_gpio(dev->of_node, "pnl-enable-gpios", 0); ++ if (err < 0) { ++ dev_err(dev, "cannot find panel enable GPIO!\n"); ++ return err; ++ } ++ panel->pnl_enable = err; ++ err = devm_gpio_request_one(dev, panel->pnl_enable, ++ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "panel"); ++ if (err < 0) { ++ dev_err(dev, "cannot acquire panel enable GPIO!\n"); ++ return err; ++ } ++ ++ err = of_get_named_gpio(dev->of_node, "bl-enable-gpios", 0); ++ if (err < 0) { ++ dev_err(dev, "cannot find backlight enable GPIO!\n"); ++ return err; ++ } ++ panel->bl_enable = err; ++ err = devm_gpio_request_one(dev, panel->bl_enable, ++ GPIOF_DIR_OUT | GPIOF_INIT_LOW, "backlight"); ++ if (err < 0) { ++ dev_err(dev, "cannot acquire backlight enable GPIO!\n"); ++ return err; ++ } ++ ++ node = of_parse_phandle(dev->of_node, "backlight", 0); ++ if (node) { ++ panel->backlight = of_find_backlight_by_node(node); ++ if (!panel->backlight) ++ return -EPROBE_DEFER; ++ } ++ ++ panel->entity.dev = dev; ++ panel->entity.ops.ctrl = &panel_claa101_control_ops; ++ err = display_entity_register(&panel->entity); ++ if (err < 0) { ++ if (panel->backlight) ++ put_device(&panel->backlight->dev); ++ return err; ++ } ++ ++ platform_set_drvdata(pdev, panel); ++ ++ return 0; ++} ++ ++static int __exit panel_claa101_remove(struct platform_device *pdev) ++{ ++ struct panel_claa101 *panel = platform_get_drvdata(pdev); ++ ++ display_entity_unregister(&panel->entity); ++ if (panel->backlight) ++ put_device(&panel->backlight->dev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int panel_claa101_suspend(struct device *dev) ++{ ++ struct panel_claa101 *panel = dev_get_drvdata(dev); ++ return panel_claa101_off(panel); ++} ++ ++static int panel_claa101_resume(struct device *dev) ++{ ++ struct panel_claa101 *panel = dev_get_drvdata(dev); ++ return panel_claa101_on(panel); ++} ++#endif ++ ++#ifdef CONFIG_OF ++static struct of_device_id panel_claa101_of_match[] = { ++ { .compatible = "chunghwa,claa101wa01a", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, panel_claa101_of_match); ++#else ++#endif ++ ++static const struct dev_pm_ops panel_claa101_dev_pm_ops = { ++ .suspend = panel_claa101_suspend, ++ .resume = panel_claa101_resume, ++}; ++ ++static struct platform_driver panel_claa101_driver = { ++ .probe = panel_claa101_probe, ++ .remove = panel_claa101_remove, ++ .driver = { ++ .name = "panel_claa101wa01a", ++ .owner = THIS_MODULE, ++#ifdef CONFIG_PM ++ .pm = &panel_claa101_dev_pm_ops, ++#endif ++#ifdef CONFIG_OF ++ .of_match_table = of_match_ptr(panel_claa101_of_match), ++#endif ++ }, ++}; ++ ++module_platform_driver(panel_claa101_driver); ++ ++MODULE_AUTHOR("Alexandre Courbot <acourbot@nvidia.com>"); ++MODULE_DESCRIPTION("Chunghwa CLAA101WA01A Display Panel"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h +index 642789b..46da5cd 100644 +--- a/include/linux/clk/tegra.h ++++ b/include/linux/clk/tegra.h +@@ -120,8 +120,13 @@ static inline void tegra_cpu_clock_resume(void) + } + #endif + ++#ifdef CONFIG_ARCH_TEGRA + void tegra_periph_reset_deassert(struct clk *c); + void tegra_periph_reset_assert(struct clk *c); ++#else ++static inline void tegra_periph_reset_deassert(struct clk *c) {} ++static inline void tegra_periph_reset_assert(struct clk *c) {} ++#endif + void tegra_clocks_init(void); + void tegra_clocks_apply_init_table(void); + +diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h +index 6e132a2..73bde4e 100644 +--- a/include/uapi/drm/tegra_drm.h ++++ b/include/uapi/drm/tegra_drm.h +@@ -17,6 +17,8 @@ + #ifndef _UAPI_TEGRA_DRM_H_ + #define _UAPI_TEGRA_DRM_H_ + ++#include <drm/drm.h> ++ + struct drm_tegra_gem_create { + __u64 size; + __u32 flags; +diff --git a/include/video/display.h b/include/video/display.h +new file mode 100644 +index 0000000..90d18ca +--- /dev/null ++++ b/include/video/display.h +@@ -0,0 +1,150 @@ ++/* ++ * Display Core ++ * ++ * Copyright (C) 2012 Renesas Solutions Corp. ++ * ++ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __DISPLAY_H__ ++#define __DISPLAY_H__ ++ ++#include <linux/kref.h> ++#include <linux/list.h> ++#include <linux/module.h> ++ ++/* ----------------------------------------------------------------------------- ++ * Display Entity ++ */ ++ ++struct display_entity; ++struct videomode; ++ ++#define DISPLAY_ENTITY_NOTIFIER_CONNECT 1 ++#define DISPLAY_ENTITY_NOTIFIER_DISCONNECT 2 ++ ++struct display_entity_notifier { ++ int (*notify)(struct display_entity_notifier *, struct display_entity *, ++ int); ++ struct device *dev; ++ struct list_head list; ++}; ++ ++/** ++ * enum display_entity_state - State of a display entity ++ * @DISPLAY_ENTITY_STATE_OFF: The entity is turned off completely, possibly ++ * including its power supplies. Communication with a display entity in ++ * that state is not possible. ++ * @DISPLAY_ENTITY_STATE_STANDBY: The entity is in a low-power state. Full ++ * communication with the display entity is supported, including pixel data ++ * transfer, but the output is kept blanked. ++ * @DISPLAY_ENTITY_STATE_ON: The entity is fully operational. ++ */ ++enum display_entity_state { ++ DISPLAY_ENTITY_STATE_OFF, ++ DISPLAY_ENTITY_STATE_STANDBY, ++ DISPLAY_ENTITY_STATE_ON, ++}; ++ ++/** ++ * enum display_entity_stream_state - State of a video stream ++ * @DISPLAY_ENTITY_STREAM_STOPPED: The video stream is stopped, no frames are ++ * transferred. ++ * @DISPLAY_ENTITY_STREAM_SINGLE_SHOT: The video stream has been started for ++ * single shot operation. The source entity will transfer a single frame ++ * and then stop. ++ * @DISPLAY_ENTITY_STREAM_CONTINUOUS: The video stream is running, frames are ++ * transferred continuously by the source entity. ++ */ ++enum display_entity_stream_state { ++ DISPLAY_ENTITY_STREAM_STOPPED, ++ DISPLAY_ENTITY_STREAM_SINGLE_SHOT, ++ DISPLAY_ENTITY_STREAM_CONTINUOUS, ++}; ++ ++enum display_entity_interface_type { ++ DISPLAY_ENTITY_INTERFACE_DPI, ++}; ++ ++struct display_entity_interface_params { ++ enum display_entity_interface_type type; ++}; ++ ++struct display_entity_control_ops { ++ int (*set_state)(struct display_entity *ent, ++ enum display_entity_state state); ++ int (*update)(struct display_entity *ent); ++ int (*get_modes)(struct display_entity *ent, ++ const struct videomode **modes); ++ int (*get_params)(struct display_entity *ent, ++ struct display_entity_interface_params *params); ++ int (*get_size)(struct display_entity *ent, ++ unsigned int *width, unsigned int *height); ++}; ++ ++struct display_entity_video_ops { ++ int (*set_stream)(struct display_entity *ent, ++ enum display_entity_stream_state state); ++}; ++ ++struct display_entity { ++ struct list_head list; ++ struct device *dev; ++ struct module *owner; ++ struct kref ref; ++ ++ struct display_entity *source; ++ ++ struct { ++ const struct display_entity_control_ops *ctrl; ++ const struct display_entity_video_ops *video; ++ } ops; ++ ++ void(*release)(struct display_entity *ent); ++ ++ enum display_entity_state state; ++}; ++ ++int display_entity_set_state(struct display_entity *entity, ++ enum display_entity_state state); ++int display_entity_update(struct display_entity *entity); ++int display_entity_get_modes(struct display_entity *entity, ++ const struct videomode **modes); ++int display_entity_get_params(struct display_entity *entity, ++ struct display_entity_interface_params *params); ++int display_entity_get_size(struct display_entity *entity, ++ unsigned int *width, unsigned int *height); ++ ++int display_entity_set_stream(struct display_entity *entity, ++ enum display_entity_stream_state state); ++ ++static inline void display_entity_connect(struct display_entity *source, ++ struct display_entity *sink) ++{ ++ sink->source = source; ++} ++ ++static inline void display_entity_disconnect(struct display_entity *source, ++ struct display_entity *sink) ++{ ++ sink->source = NULL; ++} ++ ++struct display_entity *display_entity_get(struct display_entity *entity); ++void display_entity_put(struct display_entity *entity); ++ ++int __must_check __display_entity_register(struct display_entity *entity, ++ struct module *owner); ++void display_entity_unregister(struct display_entity *entity); ++ ++int display_entity_register_notifier(struct display_entity_notifier *notifier); ++void display_entity_unregister_notifier(struct display_entity_notifier *notifier); ++ ++#define display_entity_register(display_entity) \ ++ __display_entity_register(display_entity, THIS_MODULE) ++ ++#endif /* __DISPLAY_H__ */ +diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c +index f2e62e4..6bda3aa 100644 +--- a/sound/soc/codecs/alc5632.c ++++ b/sound/soc/codecs/alc5632.c +@@ -551,8 +551,10 @@ static const struct snd_soc_dapm_route alc5632_dapm_routes[] = { + {"Left LineIn", NULL, "LINEINL"}, + {"Right LineIn", NULL, "LINEINR"}, + {"Phone", NULL, "PHONEP"}, +- {"MIC1 Pre Amp", NULL, "MIC1"}, +- {"MIC2 Pre Amp", NULL, "MIC2"}, ++ {"MICBIAS1", NULL, "MIC1"}, ++ {"MIC1 Pre Amp", NULL, "MICBIAS1"}, ++ {"MICBIAS2", NULL, "MIC2"}, ++ {"MIC2 Pre Amp", NULL, "MICBIAS2"}, + {"MIC1 PGA", NULL, "MIC1 Pre Amp"}, + {"MIC2 PGA", NULL, "MIC2 Pre Amp"}, + +diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c +index 48d05d9..1b1e775 100644 +--- a/sound/soc/tegra/tegra_alc5632.c ++++ b/sound/soc/tegra/tegra_alc5632.c +@@ -36,6 +36,7 @@ + struct tegra_alc5632 { + struct tegra_asoc_utils_data util_data; + int gpio_hp_det; ++ int gpio_spkr_en; + }; + + static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream, +@@ -91,8 +92,24 @@ static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = { + .debounce_time = 150, + }; + ++static int tegra_alc5632_event_int_spk(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ struct snd_soc_dapm_context *dapm = w->dapm; ++ struct snd_soc_card *card = dapm->card; ++ struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(card); ++ ++ if (!gpio_is_valid(machine->gpio_spkr_en)) ++ return 0; ++ ++ gpio_set_value_cansleep(machine->gpio_spkr_en, ++ SND_SOC_DAPM_EVENT_ON(event)); ++ ++ return 0; ++} ++ + static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = { +- SND_SOC_DAPM_SPK("Int Spk", NULL), ++ SND_SOC_DAPM_SPK("Int Spk", tegra_alc5632_event_int_spk), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Digital Mic", NULL), +@@ -172,6 +189,11 @@ static int tegra_alc5632_probe(struct platform_device *pdev) + if (alc5632->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + ++ alc5632->gpio_spkr_en = of_get_named_gpio(pdev->dev.of_node, ++ "nvidia,spkr-en-gpios", 0); ++ if (alc5632->gpio_spkr_en == -ENODEV) ++ return -EPROBE_DEFER; ++ + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; +@@ -201,6 +223,16 @@ static int tegra_alc5632_probe(struct platform_device *pdev) + + tegra_alc5632_dai.platform_of_node = tegra_alc5632_dai.cpu_of_node; + ++ if (gpio_is_valid(alc5632->gpio_spkr_en)) { ++ ++ ret = devm_gpio_request(&pdev->dev, alc5632->gpio_spkr_en, ++ "spkr_en"); ++ if (ret) { ++ dev_err(card->dev, "cannot get spkr_en gpio\n"); ++ return ret; ++ } ++ } ++ + ret = tegra_asoc_utils_init(&alc5632->util_data, &pdev->dev); + if (ret) + goto err; |